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);
13584 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13585 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13587 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13589 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13590 this.store.on('load', this.onTouchViewLoad, this);
13591 this.store.on('loadexception', this.onTouchViewLoadException, this);
13593 if(this.hiddenName){
13595 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13597 this.hiddenField.dom.value =
13598 this.hiddenValue !== undefined ? this.hiddenValue :
13599 this.value !== undefined ? this.value : '';
13601 this.el.dom.removeAttribute('name');
13602 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13606 this.choices = this.el.select('ul.select2-choices', true).first();
13607 this.searchField = this.el.select('ul li.select2-search-field', true).first();
13610 if(this.removable && !this.multiple){
13611 var close = this.closeTriggerEl();
13613 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13614 close.on('click', this.removeBtnClick, this, close);
13618 * fix the bug in Safari iOS8
13622 this.inputEl().on("focus", function(e){
13623 if(_this.disabled){
13626 document.activeElement.blur();
13634 renderTouchView : function()
13636 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13637 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13639 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13640 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13642 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13643 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13644 this.touchViewBodyEl.setStyle('overflow', 'auto');
13646 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13647 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13649 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13650 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13654 showTouchView : function()
13660 this.touchViewHeaderEl.hide();
13662 if(this.fieldLabel.length){
13663 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13664 this.touchViewHeaderEl.show();
13667 this.touchViewEl.show();
13669 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13670 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13672 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13674 if(this.fieldLabel.length){
13675 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13678 this.touchViewBodyEl.setHeight(bodyHeight);
13682 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13684 this.touchViewEl.addClass('in');
13687 this.doTouchViewQuery();
13691 hideTouchView : function()
13693 this.touchViewEl.removeClass('in');
13697 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13699 this.touchViewEl.setStyle('display', 'none');
13704 setTouchViewValue : function()
13711 Roo.each(this.tickItems, function(o){
13716 this.hideTouchView();
13719 doTouchViewQuery : function()
13728 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13732 if(!this.alwaysQuery || this.mode == 'local'){
13733 this.onTouchViewLoad();
13740 onTouchViewBeforeLoad : function(combo,opts)
13746 onTouchViewLoad : function()
13748 if(this.store.getCount() < 1){
13749 this.onTouchViewEmptyResults();
13753 this.clearTouchView();
13755 var rawValue = this.getRawValue();
13757 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13759 this.tickItems = [];
13761 this.store.data.each(function(d, rowIndex){
13762 var row = this.touchViewListGroup.createChild(template);
13764 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13767 html : d.data[this.displayField]
13770 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
13771 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
13775 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13776 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13779 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13780 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13781 this.tickItems.push(d.data);
13784 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13788 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13790 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13792 if(this.fieldLabel.length){
13793 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13796 var listHeight = this.touchViewListGroup.getHeight();
13800 if(firstChecked && listHeight > bodyHeight){
13801 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13806 onTouchViewLoadException : function()
13808 this.hideTouchView();
13811 onTouchViewEmptyResults : function()
13813 this.clearTouchView();
13815 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13817 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13821 clearTouchView : function()
13823 this.touchViewListGroup.dom.innerHTML = '';
13826 onTouchViewClick : function(e, el, o)
13828 e.preventDefault();
13831 var rowIndex = o.rowIndex;
13833 var r = this.store.getAt(rowIndex);
13835 if(!this.multiple){
13836 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13837 c.dom.removeAttribute('checked');
13840 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13842 this.setFromData(r.data);
13844 var close = this.closeTriggerEl();
13850 this.hideTouchView();
13852 this.fireEvent('select', this, r, rowIndex);
13857 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13858 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13859 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13863 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13864 this.addItem(r.data);
13865 this.tickItems.push(r.data);
13871 * @cfg {Boolean} grow
13875 * @cfg {Number} growMin
13879 * @cfg {Number} growMax
13888 Roo.apply(Roo.bootstrap.ComboBox, {
13892 cls: 'modal-header',
13914 cls: 'list-group-item',
13918 cls: 'roo-combobox-list-group-item-value'
13922 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13936 listItemCheckbox : {
13938 cls: 'list-group-item',
13942 cls: 'roo-combobox-list-group-item-value'
13946 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13962 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13967 cls: 'modal-footer',
13975 cls: 'col-xs-6 text-left',
13978 cls: 'btn btn-danger roo-touch-view-cancel',
13984 cls: 'col-xs-6 text-right',
13987 cls: 'btn btn-success roo-touch-view-ok',
13998 Roo.apply(Roo.bootstrap.ComboBox, {
14000 touchViewTemplate : {
14002 cls: 'modal fade roo-combobox-touch-view',
14006 cls: 'modal-dialog',
14007 style : 'position:fixed', // we have to fix position....
14011 cls: 'modal-content',
14013 Roo.bootstrap.ComboBox.header,
14014 Roo.bootstrap.ComboBox.body,
14015 Roo.bootstrap.ComboBox.footer
14024 * Ext JS Library 1.1.1
14025 * Copyright(c) 2006-2007, Ext JS, LLC.
14027 * Originally Released Under LGPL - original licence link has changed is not relivant.
14030 * <script type="text/javascript">
14035 * @extends Roo.util.Observable
14036 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14037 * This class also supports single and multi selection modes. <br>
14038 * Create a data model bound view:
14040 var store = new Roo.data.Store(...);
14042 var view = new Roo.View({
14044 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14046 singleSelect: true,
14047 selectedClass: "ydataview-selected",
14051 // listen for node click?
14052 view.on("click", function(vw, index, node, e){
14053 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14057 dataModel.load("foobar.xml");
14059 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14061 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14062 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14064 * Note: old style constructor is still suported (container, template, config)
14067 * Create a new View
14068 * @param {Object} config The config object
14071 Roo.View = function(config, depreciated_tpl, depreciated_config){
14073 this.parent = false;
14075 if (typeof(depreciated_tpl) == 'undefined') {
14076 // new way.. - universal constructor.
14077 Roo.apply(this, config);
14078 this.el = Roo.get(this.el);
14081 this.el = Roo.get(config);
14082 this.tpl = depreciated_tpl;
14083 Roo.apply(this, depreciated_config);
14085 this.wrapEl = this.el.wrap().wrap();
14086 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14089 if(typeof(this.tpl) == "string"){
14090 this.tpl = new Roo.Template(this.tpl);
14092 // support xtype ctors..
14093 this.tpl = new Roo.factory(this.tpl, Roo);
14097 this.tpl.compile();
14102 * @event beforeclick
14103 * Fires before a click is processed. Returns false to cancel the default action.
14104 * @param {Roo.View} this
14105 * @param {Number} index The index of the target node
14106 * @param {HTMLElement} node The target node
14107 * @param {Roo.EventObject} e The raw event object
14109 "beforeclick" : true,
14112 * Fires when a template node is clicked.
14113 * @param {Roo.View} this
14114 * @param {Number} index The index of the target node
14115 * @param {HTMLElement} node The target node
14116 * @param {Roo.EventObject} e The raw event object
14121 * Fires when a template node is double clicked.
14122 * @param {Roo.View} this
14123 * @param {Number} index The index of the target node
14124 * @param {HTMLElement} node The target node
14125 * @param {Roo.EventObject} e The raw event object
14129 * @event contextmenu
14130 * Fires when a template node is right clicked.
14131 * @param {Roo.View} this
14132 * @param {Number} index The index of the target node
14133 * @param {HTMLElement} node The target node
14134 * @param {Roo.EventObject} e The raw event object
14136 "contextmenu" : true,
14138 * @event selectionchange
14139 * Fires when the selected nodes change.
14140 * @param {Roo.View} this
14141 * @param {Array} selections Array of the selected nodes
14143 "selectionchange" : true,
14146 * @event beforeselect
14147 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14148 * @param {Roo.View} this
14149 * @param {HTMLElement} node The node to be selected
14150 * @param {Array} selections Array of currently selected nodes
14152 "beforeselect" : true,
14154 * @event preparedata
14155 * Fires on every row to render, to allow you to change the data.
14156 * @param {Roo.View} this
14157 * @param {Object} data to be rendered (change this)
14159 "preparedata" : true
14167 "click": this.onClick,
14168 "dblclick": this.onDblClick,
14169 "contextmenu": this.onContextMenu,
14173 this.selections = [];
14175 this.cmp = new Roo.CompositeElementLite([]);
14177 this.store = Roo.factory(this.store, Roo.data);
14178 this.setStore(this.store, true);
14181 if ( this.footer && this.footer.xtype) {
14183 var fctr = this.wrapEl.appendChild(document.createElement("div"));
14185 this.footer.dataSource = this.store;
14186 this.footer.container = fctr;
14187 this.footer = Roo.factory(this.footer, Roo);
14188 fctr.insertFirst(this.el);
14190 // this is a bit insane - as the paging toolbar seems to detach the el..
14191 // dom.parentNode.parentNode.parentNode
14192 // they get detached?
14196 Roo.View.superclass.constructor.call(this);
14201 Roo.extend(Roo.View, Roo.util.Observable, {
14204 * @cfg {Roo.data.Store} store Data store to load data from.
14209 * @cfg {String|Roo.Element} el The container element.
14214 * @cfg {String|Roo.Template} tpl The template used by this View
14218 * @cfg {String} dataName the named area of the template to use as the data area
14219 * Works with domtemplates roo-name="name"
14223 * @cfg {String} selectedClass The css class to add to selected nodes
14225 selectedClass : "x-view-selected",
14227 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14232 * @cfg {String} text to display on mask (default Loading)
14236 * @cfg {Boolean} multiSelect Allow multiple selection
14238 multiSelect : false,
14240 * @cfg {Boolean} singleSelect Allow single selection
14242 singleSelect: false,
14245 * @cfg {Boolean} toggleSelect - selecting
14247 toggleSelect : false,
14250 * @cfg {Boolean} tickable - selecting
14255 * Returns the element this view is bound to.
14256 * @return {Roo.Element}
14258 getEl : function(){
14259 return this.wrapEl;
14265 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14267 refresh : function(){
14268 //Roo.log('refresh');
14271 // if we are using something like 'domtemplate', then
14272 // the what gets used is:
14273 // t.applySubtemplate(NAME, data, wrapping data..)
14274 // the outer template then get' applied with
14275 // the store 'extra data'
14276 // and the body get's added to the
14277 // roo-name="data" node?
14278 // <span class='roo-tpl-{name}'></span> ?????
14282 this.clearSelections();
14283 this.el.update("");
14285 var records = this.store.getRange();
14286 if(records.length < 1) {
14288 // is this valid?? = should it render a template??
14290 this.el.update(this.emptyText);
14294 if (this.dataName) {
14295 this.el.update(t.apply(this.store.meta)); //????
14296 el = this.el.child('.roo-tpl-' + this.dataName);
14299 for(var i = 0, len = records.length; i < len; i++){
14300 var data = this.prepareData(records[i].data, i, records[i]);
14301 this.fireEvent("preparedata", this, data, i, records[i]);
14303 var d = Roo.apply({}, data);
14306 Roo.apply(d, {'roo-id' : Roo.id()});
14310 Roo.each(this.parent.item, function(item){
14311 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14314 Roo.apply(d, {'roo-data-checked' : 'checked'});
14318 html[html.length] = Roo.util.Format.trim(
14320 t.applySubtemplate(this.dataName, d, this.store.meta) :
14327 el.update(html.join(""));
14328 this.nodes = el.dom.childNodes;
14329 this.updateIndexes(0);
14334 * Function to override to reformat the data that is sent to
14335 * the template for each node.
14336 * DEPRICATED - use the preparedata event handler.
14337 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14338 * a JSON object for an UpdateManager bound view).
14340 prepareData : function(data, index, record)
14342 this.fireEvent("preparedata", this, data, index, record);
14346 onUpdate : function(ds, record){
14347 // Roo.log('on update');
14348 this.clearSelections();
14349 var index = this.store.indexOf(record);
14350 var n = this.nodes[index];
14351 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14352 n.parentNode.removeChild(n);
14353 this.updateIndexes(index, index);
14359 onAdd : function(ds, records, index)
14361 //Roo.log(['on Add', ds, records, index] );
14362 this.clearSelections();
14363 if(this.nodes.length == 0){
14367 var n = this.nodes[index];
14368 for(var i = 0, len = records.length; i < len; i++){
14369 var d = this.prepareData(records[i].data, i, records[i]);
14371 this.tpl.insertBefore(n, d);
14374 this.tpl.append(this.el, d);
14377 this.updateIndexes(index);
14380 onRemove : function(ds, record, index){
14381 // Roo.log('onRemove');
14382 this.clearSelections();
14383 var el = this.dataName ?
14384 this.el.child('.roo-tpl-' + this.dataName) :
14387 el.dom.removeChild(this.nodes[index]);
14388 this.updateIndexes(index);
14392 * Refresh an individual node.
14393 * @param {Number} index
14395 refreshNode : function(index){
14396 this.onUpdate(this.store, this.store.getAt(index));
14399 updateIndexes : function(startIndex, endIndex){
14400 var ns = this.nodes;
14401 startIndex = startIndex || 0;
14402 endIndex = endIndex || ns.length - 1;
14403 for(var i = startIndex; i <= endIndex; i++){
14404 ns[i].nodeIndex = i;
14409 * Changes the data store this view uses and refresh the view.
14410 * @param {Store} store
14412 setStore : function(store, initial){
14413 if(!initial && this.store){
14414 this.store.un("datachanged", this.refresh);
14415 this.store.un("add", this.onAdd);
14416 this.store.un("remove", this.onRemove);
14417 this.store.un("update", this.onUpdate);
14418 this.store.un("clear", this.refresh);
14419 this.store.un("beforeload", this.onBeforeLoad);
14420 this.store.un("load", this.onLoad);
14421 this.store.un("loadexception", this.onLoad);
14425 store.on("datachanged", this.refresh, this);
14426 store.on("add", this.onAdd, this);
14427 store.on("remove", this.onRemove, this);
14428 store.on("update", this.onUpdate, this);
14429 store.on("clear", this.refresh, this);
14430 store.on("beforeload", this.onBeforeLoad, this);
14431 store.on("load", this.onLoad, this);
14432 store.on("loadexception", this.onLoad, this);
14440 * onbeforeLoad - masks the loading area.
14443 onBeforeLoad : function(store,opts)
14445 //Roo.log('onBeforeLoad');
14447 this.el.update("");
14449 this.el.mask(this.mask ? this.mask : "Loading" );
14451 onLoad : function ()
14458 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14459 * @param {HTMLElement} node
14460 * @return {HTMLElement} The template node
14462 findItemFromChild : function(node){
14463 var el = this.dataName ?
14464 this.el.child('.roo-tpl-' + this.dataName,true) :
14467 if(!node || node.parentNode == el){
14470 var p = node.parentNode;
14471 while(p && p != el){
14472 if(p.parentNode == el){
14481 onClick : function(e){
14482 var item = this.findItemFromChild(e.getTarget());
14484 var index = this.indexOf(item);
14485 if(this.onItemClick(item, index, e) !== false){
14486 this.fireEvent("click", this, index, item, e);
14489 this.clearSelections();
14494 onContextMenu : function(e){
14495 var item = this.findItemFromChild(e.getTarget());
14497 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14502 onDblClick : function(e){
14503 var item = this.findItemFromChild(e.getTarget());
14505 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14509 onItemClick : function(item, index, e)
14511 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14514 if (this.toggleSelect) {
14515 var m = this.isSelected(item) ? 'unselect' : 'select';
14518 _t[m](item, true, false);
14521 if(this.multiSelect || this.singleSelect){
14522 if(this.multiSelect && e.shiftKey && this.lastSelection){
14523 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14525 this.select(item, this.multiSelect && e.ctrlKey);
14526 this.lastSelection = item;
14529 if(!this.tickable){
14530 e.preventDefault();
14538 * Get the number of selected nodes.
14541 getSelectionCount : function(){
14542 return this.selections.length;
14546 * Get the currently selected nodes.
14547 * @return {Array} An array of HTMLElements
14549 getSelectedNodes : function(){
14550 return this.selections;
14554 * Get the indexes of the selected nodes.
14557 getSelectedIndexes : function(){
14558 var indexes = [], s = this.selections;
14559 for(var i = 0, len = s.length; i < len; i++){
14560 indexes.push(s[i].nodeIndex);
14566 * Clear all selections
14567 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14569 clearSelections : function(suppressEvent){
14570 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14571 this.cmp.elements = this.selections;
14572 this.cmp.removeClass(this.selectedClass);
14573 this.selections = [];
14574 if(!suppressEvent){
14575 this.fireEvent("selectionchange", this, this.selections);
14581 * Returns true if the passed node is selected
14582 * @param {HTMLElement/Number} node The node or node index
14583 * @return {Boolean}
14585 isSelected : function(node){
14586 var s = this.selections;
14590 node = this.getNode(node);
14591 return s.indexOf(node) !== -1;
14596 * @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
14597 * @param {Boolean} keepExisting (optional) true to keep existing selections
14598 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14600 select : function(nodeInfo, keepExisting, suppressEvent){
14601 if(nodeInfo instanceof Array){
14603 this.clearSelections(true);
14605 for(var i = 0, len = nodeInfo.length; i < len; i++){
14606 this.select(nodeInfo[i], true, true);
14610 var node = this.getNode(nodeInfo);
14611 if(!node || this.isSelected(node)){
14612 return; // already selected.
14615 this.clearSelections(true);
14618 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14619 Roo.fly(node).addClass(this.selectedClass);
14620 this.selections.push(node);
14621 if(!suppressEvent){
14622 this.fireEvent("selectionchange", this, this.selections);
14630 * @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
14631 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14632 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14634 unselect : function(nodeInfo, keepExisting, suppressEvent)
14636 if(nodeInfo instanceof Array){
14637 Roo.each(this.selections, function(s) {
14638 this.unselect(s, nodeInfo);
14642 var node = this.getNode(nodeInfo);
14643 if(!node || !this.isSelected(node)){
14644 //Roo.log("not selected");
14645 return; // not selected.
14649 Roo.each(this.selections, function(s) {
14651 Roo.fly(node).removeClass(this.selectedClass);
14658 this.selections= ns;
14659 this.fireEvent("selectionchange", this, this.selections);
14663 * Gets a template node.
14664 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14665 * @return {HTMLElement} The node or null if it wasn't found
14667 getNode : function(nodeInfo){
14668 if(typeof nodeInfo == "string"){
14669 return document.getElementById(nodeInfo);
14670 }else if(typeof nodeInfo == "number"){
14671 return this.nodes[nodeInfo];
14677 * Gets a range template nodes.
14678 * @param {Number} startIndex
14679 * @param {Number} endIndex
14680 * @return {Array} An array of nodes
14682 getNodes : function(start, end){
14683 var ns = this.nodes;
14684 start = start || 0;
14685 end = typeof end == "undefined" ? ns.length - 1 : end;
14688 for(var i = start; i <= end; i++){
14692 for(var i = start; i >= end; i--){
14700 * Finds the index of the passed node
14701 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14702 * @return {Number} The index of the node or -1
14704 indexOf : function(node){
14705 node = this.getNode(node);
14706 if(typeof node.nodeIndex == "number"){
14707 return node.nodeIndex;
14709 var ns = this.nodes;
14710 for(var i = 0, len = ns.length; i < len; i++){
14721 * based on jquery fullcalendar
14725 Roo.bootstrap = Roo.bootstrap || {};
14727 * @class Roo.bootstrap.Calendar
14728 * @extends Roo.bootstrap.Component
14729 * Bootstrap Calendar class
14730 * @cfg {Boolean} loadMask (true|false) default false
14731 * @cfg {Object} header generate the user specific header of the calendar, default false
14734 * Create a new Container
14735 * @param {Object} config The config object
14740 Roo.bootstrap.Calendar = function(config){
14741 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14745 * Fires when a date is selected
14746 * @param {DatePicker} this
14747 * @param {Date} date The selected date
14751 * @event monthchange
14752 * Fires when the displayed month changes
14753 * @param {DatePicker} this
14754 * @param {Date} date The selected month
14756 'monthchange': true,
14758 * @event evententer
14759 * Fires when mouse over an event
14760 * @param {Calendar} this
14761 * @param {event} Event
14763 'evententer': true,
14765 * @event eventleave
14766 * Fires when the mouse leaves an
14767 * @param {Calendar} this
14770 'eventleave': true,
14772 * @event eventclick
14773 * Fires when the mouse click an
14774 * @param {Calendar} this
14783 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
14786 * @cfg {Number} startDay
14787 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14795 getAutoCreate : function(){
14798 var fc_button = function(name, corner, style, content ) {
14799 return Roo.apply({},{
14801 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
14803 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14806 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14817 style : 'width:100%',
14824 cls : 'fc-header-left',
14826 fc_button('prev', 'left', 'arrow', '‹' ),
14827 fc_button('next', 'right', 'arrow', '›' ),
14828 { tag: 'span', cls: 'fc-header-space' },
14829 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
14837 cls : 'fc-header-center',
14841 cls: 'fc-header-title',
14844 html : 'month / year'
14852 cls : 'fc-header-right',
14854 /* fc_button('month', 'left', '', 'month' ),
14855 fc_button('week', '', '', 'week' ),
14856 fc_button('day', 'right', '', 'day' )
14868 header = this.header;
14871 var cal_heads = function() {
14873 // fixme - handle this.
14875 for (var i =0; i < Date.dayNames.length; i++) {
14876 var d = Date.dayNames[i];
14879 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14880 html : d.substring(0,3)
14884 ret[0].cls += ' fc-first';
14885 ret[6].cls += ' fc-last';
14888 var cal_cell = function(n) {
14891 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14896 cls: 'fc-day-number',
14900 cls: 'fc-day-content',
14904 style: 'position: relative;' // height: 17px;
14916 var cal_rows = function() {
14919 for (var r = 0; r < 6; r++) {
14926 for (var i =0; i < Date.dayNames.length; i++) {
14927 var d = Date.dayNames[i];
14928 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14931 row.cn[0].cls+=' fc-first';
14932 row.cn[0].cn[0].style = 'min-height:90px';
14933 row.cn[6].cls+=' fc-last';
14937 ret[0].cls += ' fc-first';
14938 ret[4].cls += ' fc-prev-last';
14939 ret[5].cls += ' fc-last';
14946 cls: 'fc-border-separate',
14947 style : 'width:100%',
14955 cls : 'fc-first fc-last',
14973 cls : 'fc-content',
14974 style : "position: relative;",
14977 cls : 'fc-view fc-view-month fc-grid',
14978 style : 'position: relative',
14979 unselectable : 'on',
14982 cls : 'fc-event-container',
14983 style : 'position:absolute;z-index:8;top:0;left:0;'
15001 initEvents : function()
15004 throw "can not find store for calendar";
15010 style: "text-align:center",
15014 style: "background-color:white;width:50%;margin:250 auto",
15018 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15029 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15031 var size = this.el.select('.fc-content', true).first().getSize();
15032 this.maskEl.setSize(size.width, size.height);
15033 this.maskEl.enableDisplayMode("block");
15034 if(!this.loadMask){
15035 this.maskEl.hide();
15038 this.store = Roo.factory(this.store, Roo.data);
15039 this.store.on('load', this.onLoad, this);
15040 this.store.on('beforeload', this.onBeforeLoad, this);
15044 this.cells = this.el.select('.fc-day',true);
15045 //Roo.log(this.cells);
15046 this.textNodes = this.el.query('.fc-day-number');
15047 this.cells.addClassOnOver('fc-state-hover');
15049 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15050 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15051 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15052 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15054 this.on('monthchange', this.onMonthChange, this);
15056 this.update(new Date().clearTime());
15059 resize : function() {
15060 var sz = this.el.getSize();
15062 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15063 this.el.select('.fc-day-content div',true).setHeight(34);
15068 showPrevMonth : function(e){
15069 this.update(this.activeDate.add("mo", -1));
15071 showToday : function(e){
15072 this.update(new Date().clearTime());
15075 showNextMonth : function(e){
15076 this.update(this.activeDate.add("mo", 1));
15080 showPrevYear : function(){
15081 this.update(this.activeDate.add("y", -1));
15085 showNextYear : function(){
15086 this.update(this.activeDate.add("y", 1));
15091 update : function(date)
15093 var vd = this.activeDate;
15094 this.activeDate = date;
15095 // if(vd && this.el){
15096 // var t = date.getTime();
15097 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15098 // Roo.log('using add remove');
15100 // this.fireEvent('monthchange', this, date);
15102 // this.cells.removeClass("fc-state-highlight");
15103 // this.cells.each(function(c){
15104 // if(c.dateValue == t){
15105 // c.addClass("fc-state-highlight");
15106 // setTimeout(function(){
15107 // try{c.dom.firstChild.focus();}catch(e){}
15117 var days = date.getDaysInMonth();
15119 var firstOfMonth = date.getFirstDateOfMonth();
15120 var startingPos = firstOfMonth.getDay()-this.startDay;
15122 if(startingPos < this.startDay){
15126 var pm = date.add(Date.MONTH, -1);
15127 var prevStart = pm.getDaysInMonth()-startingPos;
15129 this.cells = this.el.select('.fc-day',true);
15130 this.textNodes = this.el.query('.fc-day-number');
15131 this.cells.addClassOnOver('fc-state-hover');
15133 var cells = this.cells.elements;
15134 var textEls = this.textNodes;
15136 Roo.each(cells, function(cell){
15137 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15140 days += startingPos;
15142 // convert everything to numbers so it's fast
15143 var day = 86400000;
15144 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15147 //Roo.log(prevStart);
15149 var today = new Date().clearTime().getTime();
15150 var sel = date.clearTime().getTime();
15151 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15152 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15153 var ddMatch = this.disabledDatesRE;
15154 var ddText = this.disabledDatesText;
15155 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15156 var ddaysText = this.disabledDaysText;
15157 var format = this.format;
15159 var setCellClass = function(cal, cell){
15163 //Roo.log('set Cell Class');
15165 var t = d.getTime();
15169 cell.dateValue = t;
15171 cell.className += " fc-today";
15172 cell.className += " fc-state-highlight";
15173 cell.title = cal.todayText;
15176 // disable highlight in other month..
15177 //cell.className += " fc-state-highlight";
15182 cell.className = " fc-state-disabled";
15183 cell.title = cal.minText;
15187 cell.className = " fc-state-disabled";
15188 cell.title = cal.maxText;
15192 if(ddays.indexOf(d.getDay()) != -1){
15193 cell.title = ddaysText;
15194 cell.className = " fc-state-disabled";
15197 if(ddMatch && format){
15198 var fvalue = d.dateFormat(format);
15199 if(ddMatch.test(fvalue)){
15200 cell.title = ddText.replace("%0", fvalue);
15201 cell.className = " fc-state-disabled";
15205 if (!cell.initialClassName) {
15206 cell.initialClassName = cell.dom.className;
15209 cell.dom.className = cell.initialClassName + ' ' + cell.className;
15214 for(; i < startingPos; i++) {
15215 textEls[i].innerHTML = (++prevStart);
15216 d.setDate(d.getDate()+1);
15218 cells[i].className = "fc-past fc-other-month";
15219 setCellClass(this, cells[i]);
15224 for(; i < days; i++){
15225 intDay = i - startingPos + 1;
15226 textEls[i].innerHTML = (intDay);
15227 d.setDate(d.getDate()+1);
15229 cells[i].className = ''; // "x-date-active";
15230 setCellClass(this, cells[i]);
15234 for(; i < 42; i++) {
15235 textEls[i].innerHTML = (++extraDays);
15236 d.setDate(d.getDate()+1);
15238 cells[i].className = "fc-future fc-other-month";
15239 setCellClass(this, cells[i]);
15242 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15244 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15246 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15247 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15249 if(totalRows != 6){
15250 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15251 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15254 this.fireEvent('monthchange', this, date);
15258 if(!this.internalRender){
15259 var main = this.el.dom.firstChild;
15260 var w = main.offsetWidth;
15261 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15262 Roo.fly(main).setWidth(w);
15263 this.internalRender = true;
15264 // opera does not respect the auto grow header center column
15265 // then, after it gets a width opera refuses to recalculate
15266 // without a second pass
15267 if(Roo.isOpera && !this.secondPass){
15268 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15269 this.secondPass = true;
15270 this.update.defer(10, this, [date]);
15277 findCell : function(dt) {
15278 dt = dt.clearTime().getTime();
15280 this.cells.each(function(c){
15281 //Roo.log("check " +c.dateValue + '?=' + dt);
15282 if(c.dateValue == dt){
15292 findCells : function(ev) {
15293 var s = ev.start.clone().clearTime().getTime();
15295 var e= ev.end.clone().clearTime().getTime();
15298 this.cells.each(function(c){
15299 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15301 if(c.dateValue > e){
15304 if(c.dateValue < s){
15313 // findBestRow: function(cells)
15317 // for (var i =0 ; i < cells.length;i++) {
15318 // ret = Math.max(cells[i].rows || 0,ret);
15325 addItem : function(ev)
15327 // look for vertical location slot in
15328 var cells = this.findCells(ev);
15330 // ev.row = this.findBestRow(cells);
15332 // work out the location.
15336 for(var i =0; i < cells.length; i++) {
15338 cells[i].row = cells[0].row;
15341 cells[i].row = cells[i].row + 1;
15351 if (crow.start.getY() == cells[i].getY()) {
15353 crow.end = cells[i];
15370 cells[0].events.push(ev);
15372 this.calevents.push(ev);
15375 clearEvents: function() {
15377 if(!this.calevents){
15381 Roo.each(this.cells.elements, function(c){
15387 Roo.each(this.calevents, function(e) {
15388 Roo.each(e.els, function(el) {
15389 el.un('mouseenter' ,this.onEventEnter, this);
15390 el.un('mouseleave' ,this.onEventLeave, this);
15395 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15401 renderEvents: function()
15405 this.cells.each(function(c) {
15414 if(c.row != c.events.length){
15415 r = 4 - (4 - (c.row - c.events.length));
15418 c.events = ev.slice(0, r);
15419 c.more = ev.slice(r);
15421 if(c.more.length && c.more.length == 1){
15422 c.events.push(c.more.pop());
15425 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15429 this.cells.each(function(c) {
15431 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15434 for (var e = 0; e < c.events.length; e++){
15435 var ev = c.events[e];
15436 var rows = ev.rows;
15438 for(var i = 0; i < rows.length; i++) {
15440 // how many rows should it span..
15443 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15444 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15446 unselectable : "on",
15449 cls: 'fc-event-inner',
15453 // cls: 'fc-event-time',
15454 // html : cells.length > 1 ? '' : ev.time
15458 cls: 'fc-event-title',
15459 html : String.format('{0}', ev.title)
15466 cls: 'ui-resizable-handle ui-resizable-e',
15467 html : '  '
15474 cfg.cls += ' fc-event-start';
15476 if ((i+1) == rows.length) {
15477 cfg.cls += ' fc-event-end';
15480 var ctr = _this.el.select('.fc-event-container',true).first();
15481 var cg = ctr.createChild(cfg);
15483 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15484 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15486 var r = (c.more.length) ? 1 : 0;
15487 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15488 cg.setWidth(ebox.right - sbox.x -2);
15490 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15491 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15492 cg.on('click', _this.onEventClick, _this, ev);
15503 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15504 style : 'position: absolute',
15505 unselectable : "on",
15508 cls: 'fc-event-inner',
15512 cls: 'fc-event-title',
15520 cls: 'ui-resizable-handle ui-resizable-e',
15521 html : '  '
15527 var ctr = _this.el.select('.fc-event-container',true).first();
15528 var cg = ctr.createChild(cfg);
15530 var sbox = c.select('.fc-day-content',true).first().getBox();
15531 var ebox = c.select('.fc-day-content',true).first().getBox();
15533 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15534 cg.setWidth(ebox.right - sbox.x -2);
15536 cg.on('click', _this.onMoreEventClick, _this, c.more);
15546 onEventEnter: function (e, el,event,d) {
15547 this.fireEvent('evententer', this, el, event);
15550 onEventLeave: function (e, el,event,d) {
15551 this.fireEvent('eventleave', this, el, event);
15554 onEventClick: function (e, el,event,d) {
15555 this.fireEvent('eventclick', this, el, event);
15558 onMonthChange: function () {
15562 onMoreEventClick: function(e, el, more)
15566 this.calpopover.placement = 'right';
15567 this.calpopover.setTitle('More');
15569 this.calpopover.setContent('');
15571 var ctr = this.calpopover.el.select('.popover-content', true).first();
15573 Roo.each(more, function(m){
15575 cls : 'fc-event-hori fc-event-draggable',
15578 var cg = ctr.createChild(cfg);
15580 cg.on('click', _this.onEventClick, _this, m);
15583 this.calpopover.show(el);
15588 onLoad: function ()
15590 this.calevents = [];
15593 if(this.store.getCount() > 0){
15594 this.store.data.each(function(d){
15597 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15598 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15599 time : d.data.start_time,
15600 title : d.data.title,
15601 description : d.data.description,
15602 venue : d.data.venue
15607 this.renderEvents();
15609 if(this.calevents.length && this.loadMask){
15610 this.maskEl.hide();
15614 onBeforeLoad: function()
15616 this.clearEvents();
15618 this.maskEl.show();
15632 * @class Roo.bootstrap.Popover
15633 * @extends Roo.bootstrap.Component
15634 * Bootstrap Popover class
15635 * @cfg {String} html contents of the popover (or false to use children..)
15636 * @cfg {String} title of popover (or false to hide)
15637 * @cfg {String} placement how it is placed
15638 * @cfg {String} trigger click || hover (or false to trigger manually)
15639 * @cfg {String} over what (parent or false to trigger manually.)
15640 * @cfg {Number} delay - delay before showing
15643 * Create a new Popover
15644 * @param {Object} config The config object
15647 Roo.bootstrap.Popover = function(config){
15648 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15651 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
15653 title: 'Fill in a title',
15656 placement : 'right',
15657 trigger : 'hover', // hover
15663 can_build_overlaid : false,
15665 getChildContainer : function()
15667 return this.el.select('.popover-content',true).first();
15670 getAutoCreate : function(){
15671 Roo.log('make popover?');
15673 cls : 'popover roo-dynamic',
15674 style: 'display:block',
15680 cls : 'popover-inner',
15684 cls: 'popover-title',
15688 cls : 'popover-content',
15699 setTitle: function(str)
15702 this.el.select('.popover-title',true).first().dom.innerHTML = str;
15704 setContent: function(str)
15707 this.el.select('.popover-content',true).first().dom.innerHTML = str;
15709 // as it get's added to the bottom of the page.
15710 onRender : function(ct, position)
15712 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15714 var cfg = Roo.apply({}, this.getAutoCreate());
15718 cfg.cls += ' ' + this.cls;
15721 cfg.style = this.style;
15723 //Roo.log("adding to ");
15724 this.el = Roo.get(document.body).createChild(cfg, position);
15730 initEvents : function()
15732 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15733 this.el.enableDisplayMode('block');
15735 if (this.over === false) {
15738 if (this.triggers === false) {
15741 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15742 var triggers = this.trigger ? this.trigger.split(' ') : [];
15743 Roo.each(triggers, function(trigger) {
15745 if (trigger == 'click') {
15746 on_el.on('click', this.toggle, this);
15747 } else if (trigger != 'manual') {
15748 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
15749 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15751 on_el.on(eventIn ,this.enter, this);
15752 on_el.on(eventOut, this.leave, this);
15763 toggle : function () {
15764 this.hoverState == 'in' ? this.leave() : this.enter();
15767 enter : function () {
15770 clearTimeout(this.timeout);
15772 this.hoverState = 'in';
15774 if (!this.delay || !this.delay.show) {
15779 this.timeout = setTimeout(function () {
15780 if (_t.hoverState == 'in') {
15783 }, this.delay.show)
15785 leave : function() {
15786 clearTimeout(this.timeout);
15788 this.hoverState = 'out';
15790 if (!this.delay || !this.delay.hide) {
15795 this.timeout = setTimeout(function () {
15796 if (_t.hoverState == 'out') {
15799 }, this.delay.hide)
15802 show : function (on_el)
15805 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15808 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15809 if (this.html !== false) {
15810 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15812 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15813 if (!this.title.length) {
15814 this.el.select('.popover-title',true).hide();
15817 var placement = typeof this.placement == 'function' ?
15818 this.placement.call(this, this.el, on_el) :
15821 var autoToken = /\s?auto?\s?/i;
15822 var autoPlace = autoToken.test(placement);
15824 placement = placement.replace(autoToken, '') || 'top';
15828 //this.el.setXY([0,0]);
15830 this.el.dom.style.display='block';
15831 this.el.addClass(placement);
15833 //this.el.appendTo(on_el);
15835 var p = this.getPosition();
15836 var box = this.el.getBox();
15841 var align = Roo.bootstrap.Popover.alignment[placement];
15842 this.el.alignTo(on_el, align[0],align[1]);
15843 //var arrow = this.el.select('.arrow',true).first();
15844 //arrow.set(align[2],
15846 this.el.addClass('in');
15849 if (this.el.hasClass('fade')) {
15856 this.el.setXY([0,0]);
15857 this.el.removeClass('in');
15859 this.hoverState = null;
15865 Roo.bootstrap.Popover.alignment = {
15866 'left' : ['r-l', [-10,0], 'right'],
15867 'right' : ['l-r', [10,0], 'left'],
15868 'bottom' : ['t-b', [0,10], 'top'],
15869 'top' : [ 'b-t', [0,-10], 'bottom']
15880 * @class Roo.bootstrap.Progress
15881 * @extends Roo.bootstrap.Component
15882 * Bootstrap Progress class
15883 * @cfg {Boolean} striped striped of the progress bar
15884 * @cfg {Boolean} active animated of the progress bar
15888 * Create a new Progress
15889 * @param {Object} config The config object
15892 Roo.bootstrap.Progress = function(config){
15893 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15896 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
15901 getAutoCreate : function(){
15909 cfg.cls += ' progress-striped';
15913 cfg.cls += ' active';
15932 * @class Roo.bootstrap.ProgressBar
15933 * @extends Roo.bootstrap.Component
15934 * Bootstrap ProgressBar class
15935 * @cfg {Number} aria_valuenow aria-value now
15936 * @cfg {Number} aria_valuemin aria-value min
15937 * @cfg {Number} aria_valuemax aria-value max
15938 * @cfg {String} label label for the progress bar
15939 * @cfg {String} panel (success | info | warning | danger )
15940 * @cfg {String} role role of the progress bar
15941 * @cfg {String} sr_only text
15945 * Create a new ProgressBar
15946 * @param {Object} config The config object
15949 Roo.bootstrap.ProgressBar = function(config){
15950 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15953 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
15957 aria_valuemax : 100,
15963 getAutoCreate : function()
15968 cls: 'progress-bar',
15969 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15981 cfg.role = this.role;
15984 if(this.aria_valuenow){
15985 cfg['aria-valuenow'] = this.aria_valuenow;
15988 if(this.aria_valuemin){
15989 cfg['aria-valuemin'] = this.aria_valuemin;
15992 if(this.aria_valuemax){
15993 cfg['aria-valuemax'] = this.aria_valuemax;
15996 if(this.label && !this.sr_only){
15997 cfg.html = this.label;
16001 cfg.cls += ' progress-bar-' + this.panel;
16007 update : function(aria_valuenow)
16009 this.aria_valuenow = aria_valuenow;
16011 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16026 * @class Roo.bootstrap.TabGroup
16027 * @extends Roo.bootstrap.Column
16028 * Bootstrap Column class
16029 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16030 * @cfg {Boolean} carousel true to make the group behave like a carousel
16031 * @cfg {Boolean} bullets show bullets for the panels
16032 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16033 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16034 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16037 * Create a new TabGroup
16038 * @param {Object} config The config object
16041 Roo.bootstrap.TabGroup = function(config){
16042 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16044 this.navId = Roo.id();
16047 Roo.bootstrap.TabGroup.register(this);
16051 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16054 transition : false,
16059 slideOnTouch : false,
16061 getAutoCreate : function()
16063 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16065 cfg.cls += ' tab-content';
16067 Roo.log('get auto create...............');
16069 if (this.carousel) {
16070 cfg.cls += ' carousel slide';
16073 cls : 'carousel-inner'
16076 if(this.bullets && !Roo.isTouch){
16079 cls : 'carousel-bullets',
16083 if(this.bullets_cls){
16084 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16087 for (var i = 0; i < this.bullets; i++){
16089 cls : 'bullet bullet-' + i
16097 cfg.cn[0].cn = bullets;
16104 initEvents: function()
16106 Roo.log('-------- init events on tab group ---------');
16112 if(Roo.isTouch && this.slideOnTouch){
16113 this.el.on("touchstart", this.onTouchStart, this);
16116 if(this.autoslide){
16119 this.slideFn = window.setInterval(function() {
16120 _this.showPanelNext();
16126 onTouchStart : function(e, el, o)
16128 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16132 this.showPanelNext();
16135 getChildContainer : function()
16137 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16141 * register a Navigation item
16142 * @param {Roo.bootstrap.NavItem} the navitem to add
16144 register : function(item)
16146 this.tabs.push( item);
16147 item.navId = this.navId; // not really needed..
16152 getActivePanel : function()
16155 Roo.each(this.tabs, function(t) {
16165 getPanelByName : function(n)
16168 Roo.each(this.tabs, function(t) {
16169 if (t.tabId == n) {
16177 indexOfPanel : function(p)
16180 Roo.each(this.tabs, function(t,i) {
16181 if (t.tabId == p.tabId) {
16190 * show a specific panel
16191 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16192 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16194 showPanel : function (pan)
16196 if(this.transition || typeof(pan) == 'undefined'){
16197 Roo.log("waiting for the transitionend");
16201 if (typeof(pan) == 'number') {
16202 pan = this.tabs[pan];
16205 if (typeof(pan) == 'string') {
16206 pan = this.getPanelByName(pan);
16209 var cur = this.getActivePanel();
16212 Roo.log('pan or acitve pan is undefined');
16216 if (pan.tabId == this.getActivePanel().tabId) {
16220 if (false === cur.fireEvent('beforedeactivate')) {
16224 if(this.bullets > 0 && !Roo.isTouch){
16225 this.setActiveBullet(this.indexOfPanel(pan));
16228 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16230 this.transition = true;
16231 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16232 var lr = dir == 'next' ? 'left' : 'right';
16233 pan.el.addClass(dir); // or prev
16234 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16235 cur.el.addClass(lr); // or right
16236 pan.el.addClass(lr);
16239 cur.el.on('transitionend', function() {
16240 Roo.log("trans end?");
16242 pan.el.removeClass([lr,dir]);
16243 pan.setActive(true);
16245 cur.el.removeClass([lr]);
16246 cur.setActive(false);
16248 _this.transition = false;
16250 }, this, { single: true } );
16255 cur.setActive(false);
16256 pan.setActive(true);
16261 showPanelNext : function()
16263 var i = this.indexOfPanel(this.getActivePanel());
16265 if (i >= this.tabs.length - 1 && !this.autoslide) {
16269 if (i >= this.tabs.length - 1 && this.autoslide) {
16273 this.showPanel(this.tabs[i+1]);
16276 showPanelPrev : function()
16278 var i = this.indexOfPanel(this.getActivePanel());
16280 if (i < 1 && !this.autoslide) {
16284 if (i < 1 && this.autoslide) {
16285 i = this.tabs.length;
16288 this.showPanel(this.tabs[i-1]);
16292 addBullet: function()
16294 if(!this.bullets || Roo.isTouch){
16297 var ctr = this.el.select('.carousel-bullets',true).first();
16298 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16299 var bullet = ctr.createChild({
16300 cls : 'bullet bullet-' + i
16301 },ctr.dom.lastChild);
16306 bullet.on('click', (function(e, el, o, ii, t){
16308 e.preventDefault();
16310 this.showPanel(ii);
16312 if(this.autoslide && this.slideFn){
16313 clearInterval(this.slideFn);
16314 this.slideFn = window.setInterval(function() {
16315 _this.showPanelNext();
16319 }).createDelegate(this, [i, bullet], true));
16324 setActiveBullet : function(i)
16330 Roo.each(this.el.select('.bullet', true).elements, function(el){
16331 el.removeClass('selected');
16334 var bullet = this.el.select('.bullet-' + i, true).first();
16340 bullet.addClass('selected');
16351 Roo.apply(Roo.bootstrap.TabGroup, {
16355 * register a Navigation Group
16356 * @param {Roo.bootstrap.NavGroup} the navgroup to add
16358 register : function(navgrp)
16360 this.groups[navgrp.navId] = navgrp;
16364 * fetch a Navigation Group based on the navigation ID
16365 * if one does not exist , it will get created.
16366 * @param {string} the navgroup to add
16367 * @returns {Roo.bootstrap.NavGroup} the navgroup
16369 get: function(navId) {
16370 if (typeof(this.groups[navId]) == 'undefined') {
16371 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16373 return this.groups[navId] ;
16388 * @class Roo.bootstrap.TabPanel
16389 * @extends Roo.bootstrap.Component
16390 * Bootstrap TabPanel class
16391 * @cfg {Boolean} active panel active
16392 * @cfg {String} html panel content
16393 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16394 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16398 * Create a new TabPanel
16399 * @param {Object} config The config object
16402 Roo.bootstrap.TabPanel = function(config){
16403 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16407 * Fires when the active status changes
16408 * @param {Roo.bootstrap.TabPanel} this
16409 * @param {Boolean} state the new state
16414 * @event beforedeactivate
16415 * Fires before a tab is de-activated - can be used to do validation on a form.
16416 * @param {Roo.bootstrap.TabPanel} this
16417 * @return {Boolean} false if there is an error
16420 'beforedeactivate': true
16423 this.tabId = this.tabId || Roo.id();
16427 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16434 getAutoCreate : function(){
16437 // item is needed for carousel - not sure if it has any effect otherwise
16438 cls: 'tab-pane item',
16439 html: this.html || ''
16443 cfg.cls += ' active';
16447 cfg.tabId = this.tabId;
16454 initEvents: function()
16456 Roo.log('-------- init events on tab panel ---------');
16458 var p = this.parent();
16459 this.navId = this.navId || p.navId;
16461 if (typeof(this.navId) != 'undefined') {
16462 // not really needed.. but just in case.. parent should be a NavGroup.
16463 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16464 Roo.log(['register', tg, this]);
16467 var i = tg.tabs.length - 1;
16469 if(this.active && tg.bullets > 0 && i < tg.bullets){
16470 tg.setActiveBullet(i);
16477 onRender : function(ct, position)
16479 // Roo.log("Call onRender: " + this.xtype);
16481 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16489 setActive: function(state)
16491 Roo.log("panel - set active " + this.tabId + "=" + state);
16493 this.active = state;
16495 this.el.removeClass('active');
16497 } else if (!this.el.hasClass('active')) {
16498 this.el.addClass('active');
16501 this.fireEvent('changed', this, state);
16518 * @class Roo.bootstrap.DateField
16519 * @extends Roo.bootstrap.Input
16520 * Bootstrap DateField class
16521 * @cfg {Number} weekStart default 0
16522 * @cfg {String} viewMode default empty, (months|years)
16523 * @cfg {String} minViewMode default empty, (months|years)
16524 * @cfg {Number} startDate default -Infinity
16525 * @cfg {Number} endDate default Infinity
16526 * @cfg {Boolean} todayHighlight default false
16527 * @cfg {Boolean} todayBtn default false
16528 * @cfg {Boolean} calendarWeeks default false
16529 * @cfg {Object} daysOfWeekDisabled default empty
16530 * @cfg {Boolean} singleMode default false (true | false)
16532 * @cfg {Boolean} keyboardNavigation default true
16533 * @cfg {String} language default en
16536 * Create a new DateField
16537 * @param {Object} config The config object
16540 Roo.bootstrap.DateField = function(config){
16541 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16545 * Fires when this field show.
16546 * @param {Roo.bootstrap.DateField} this
16547 * @param {Mixed} date The date value
16552 * Fires when this field hide.
16553 * @param {Roo.bootstrap.DateField} this
16554 * @param {Mixed} date The date value
16559 * Fires when select a date.
16560 * @param {Roo.bootstrap.DateField} this
16561 * @param {Mixed} date The date value
16565 * @event beforeselect
16566 * Fires when before select a date.
16567 * @param {Roo.bootstrap.DateField} this
16568 * @param {Mixed} date The date value
16570 beforeselect : true
16574 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16577 * @cfg {String} format
16578 * The default date format string which can be overriden for localization support. The format must be
16579 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16583 * @cfg {String} altFormats
16584 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16585 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16587 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16595 todayHighlight : false,
16601 keyboardNavigation: true,
16603 calendarWeeks: false,
16605 startDate: -Infinity,
16609 daysOfWeekDisabled: [],
16613 singleMode : false,
16615 UTCDate: function()
16617 return new Date(Date.UTC.apply(Date, arguments));
16620 UTCToday: function()
16622 var today = new Date();
16623 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16626 getDate: function() {
16627 var d = this.getUTCDate();
16628 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16631 getUTCDate: function() {
16635 setDate: function(d) {
16636 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16639 setUTCDate: function(d) {
16641 this.setValue(this.formatDate(this.date));
16644 onRender: function(ct, position)
16647 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16649 this.language = this.language || 'en';
16650 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16651 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16653 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16654 this.format = this.format || 'm/d/y';
16655 this.isInline = false;
16656 this.isInput = true;
16657 this.component = this.el.select('.add-on', true).first() || false;
16658 this.component = (this.component && this.component.length === 0) ? false : this.component;
16659 this.hasInput = this.component && this.inputEL().length;
16661 if (typeof(this.minViewMode === 'string')) {
16662 switch (this.minViewMode) {
16664 this.minViewMode = 1;
16667 this.minViewMode = 2;
16670 this.minViewMode = 0;
16675 if (typeof(this.viewMode === 'string')) {
16676 switch (this.viewMode) {
16689 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16691 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16693 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16695 this.picker().on('mousedown', this.onMousedown, this);
16696 this.picker().on('click', this.onClick, this);
16698 this.picker().addClass('datepicker-dropdown');
16700 this.startViewMode = this.viewMode;
16702 if(this.singleMode){
16703 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16704 v.setVisibilityMode(Roo.Element.DISPLAY);
16708 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16709 v.setStyle('width', '189px');
16713 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16714 if(!this.calendarWeeks){
16719 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16720 v.attr('colspan', function(i, val){
16721 return parseInt(val) + 1;
16726 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16728 this.setStartDate(this.startDate);
16729 this.setEndDate(this.endDate);
16731 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16738 if(this.isInline) {
16743 picker : function()
16745 return this.pickerEl;
16746 // return this.el.select('.datepicker', true).first();
16749 fillDow: function()
16751 var dowCnt = this.weekStart;
16760 if(this.calendarWeeks){
16768 while (dowCnt < this.weekStart + 7) {
16772 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16776 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16779 fillMonths: function()
16782 var months = this.picker().select('>.datepicker-months td', true).first();
16784 months.dom.innerHTML = '';
16790 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16793 months.createChild(month);
16800 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;
16802 if (this.date < this.startDate) {
16803 this.viewDate = new Date(this.startDate);
16804 } else if (this.date > this.endDate) {
16805 this.viewDate = new Date(this.endDate);
16807 this.viewDate = new Date(this.date);
16815 var d = new Date(this.viewDate),
16816 year = d.getUTCFullYear(),
16817 month = d.getUTCMonth(),
16818 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16819 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16820 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16821 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16822 currentDate = this.date && this.date.valueOf(),
16823 today = this.UTCToday();
16825 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16827 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16829 // this.picker.select('>tfoot th.today').
16830 // .text(dates[this.language].today)
16831 // .toggle(this.todayBtn !== false);
16833 this.updateNavArrows();
16836 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16838 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16840 prevMonth.setUTCDate(day);
16842 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16844 var nextMonth = new Date(prevMonth);
16846 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16848 nextMonth = nextMonth.valueOf();
16850 var fillMonths = false;
16852 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16854 while(prevMonth.valueOf() < nextMonth) {
16857 if (prevMonth.getUTCDay() === this.weekStart) {
16859 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16867 if(this.calendarWeeks){
16868 // ISO 8601: First week contains first thursday.
16869 // ISO also states week starts on Monday, but we can be more abstract here.
16871 // Start of current week: based on weekstart/current date
16872 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16873 // Thursday of this week
16874 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16875 // First Thursday of year, year from thursday
16876 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16877 // Calendar week: ms between thursdays, div ms per day, div 7 days
16878 calWeek = (th - yth) / 864e5 / 7 + 1;
16880 fillMonths.cn.push({
16888 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16890 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16893 if (this.todayHighlight &&
16894 prevMonth.getUTCFullYear() == today.getFullYear() &&
16895 prevMonth.getUTCMonth() == today.getMonth() &&
16896 prevMonth.getUTCDate() == today.getDate()) {
16897 clsName += ' today';
16900 if (currentDate && prevMonth.valueOf() === currentDate) {
16901 clsName += ' active';
16904 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16905 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16906 clsName += ' disabled';
16909 fillMonths.cn.push({
16911 cls: 'day ' + clsName,
16912 html: prevMonth.getDate()
16915 prevMonth.setDate(prevMonth.getDate()+1);
16918 var currentYear = this.date && this.date.getUTCFullYear();
16919 var currentMonth = this.date && this.date.getUTCMonth();
16921 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16923 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16924 v.removeClass('active');
16926 if(currentYear === year && k === currentMonth){
16927 v.addClass('active');
16930 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16931 v.addClass('disabled');
16937 year = parseInt(year/10, 10) * 10;
16939 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16941 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16944 for (var i = -1; i < 11; i++) {
16945 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16947 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16955 showMode: function(dir)
16958 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16961 Roo.each(this.picker().select('>div',true).elements, function(v){
16962 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16965 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16970 if(this.isInline) {
16974 this.picker().removeClass(['bottom', 'top']);
16976 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16978 * place to the top of element!
16982 this.picker().addClass('top');
16983 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16988 this.picker().addClass('bottom');
16990 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16993 parseDate : function(value)
16995 if(!value || value instanceof Date){
16998 var v = Date.parseDate(value, this.format);
16999 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17000 v = Date.parseDate(value, 'Y-m-d');
17002 if(!v && this.altFormats){
17003 if(!this.altFormatsArray){
17004 this.altFormatsArray = this.altFormats.split("|");
17006 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17007 v = Date.parseDate(value, this.altFormatsArray[i]);
17013 formatDate : function(date, fmt)
17015 return (!date || !(date instanceof Date)) ?
17016 date : date.dateFormat(fmt || this.format);
17019 onFocus : function()
17021 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17025 onBlur : function()
17027 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17029 var d = this.inputEl().getValue();
17038 this.picker().show();
17042 this.fireEvent('show', this, this.date);
17047 if(this.isInline) {
17050 this.picker().hide();
17051 this.viewMode = this.startViewMode;
17054 this.fireEvent('hide', this, this.date);
17058 onMousedown: function(e)
17060 e.stopPropagation();
17061 e.preventDefault();
17066 Roo.bootstrap.DateField.superclass.keyup.call(this);
17070 setValue: function(v)
17072 if(this.fireEvent('beforeselect', this, v) !== false){
17073 var d = new Date(this.parseDate(v) ).clearTime();
17075 if(isNaN(d.getTime())){
17076 this.date = this.viewDate = '';
17077 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17081 v = this.formatDate(d);
17083 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17085 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17089 this.fireEvent('select', this, this.date);
17093 getValue: function()
17095 return this.formatDate(this.date);
17098 fireKey: function(e)
17100 if (!this.picker().isVisible()){
17101 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17107 var dateChanged = false,
17109 newDate, newViewDate;
17114 e.preventDefault();
17118 if (!this.keyboardNavigation) {
17121 dir = e.keyCode == 37 ? -1 : 1;
17124 newDate = this.moveYear(this.date, dir);
17125 newViewDate = this.moveYear(this.viewDate, dir);
17126 } else if (e.shiftKey){
17127 newDate = this.moveMonth(this.date, dir);
17128 newViewDate = this.moveMonth(this.viewDate, dir);
17130 newDate = new Date(this.date);
17131 newDate.setUTCDate(this.date.getUTCDate() + dir);
17132 newViewDate = new Date(this.viewDate);
17133 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17135 if (this.dateWithinRange(newDate)){
17136 this.date = newDate;
17137 this.viewDate = newViewDate;
17138 this.setValue(this.formatDate(this.date));
17140 e.preventDefault();
17141 dateChanged = true;
17146 if (!this.keyboardNavigation) {
17149 dir = e.keyCode == 38 ? -1 : 1;
17151 newDate = this.moveYear(this.date, dir);
17152 newViewDate = this.moveYear(this.viewDate, dir);
17153 } else if (e.shiftKey){
17154 newDate = this.moveMonth(this.date, dir);
17155 newViewDate = this.moveMonth(this.viewDate, dir);
17157 newDate = new Date(this.date);
17158 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17159 newViewDate = new Date(this.viewDate);
17160 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17162 if (this.dateWithinRange(newDate)){
17163 this.date = newDate;
17164 this.viewDate = newViewDate;
17165 this.setValue(this.formatDate(this.date));
17167 e.preventDefault();
17168 dateChanged = true;
17172 this.setValue(this.formatDate(this.date));
17174 e.preventDefault();
17177 this.setValue(this.formatDate(this.date));
17191 onClick: function(e)
17193 e.stopPropagation();
17194 e.preventDefault();
17196 var target = e.getTarget();
17198 if(target.nodeName.toLowerCase() === 'i'){
17199 target = Roo.get(target).dom.parentNode;
17202 var nodeName = target.nodeName;
17203 var className = target.className;
17204 var html = target.innerHTML;
17205 //Roo.log(nodeName);
17207 switch(nodeName.toLowerCase()) {
17209 switch(className) {
17215 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17216 switch(this.viewMode){
17218 this.viewDate = this.moveMonth(this.viewDate, dir);
17222 this.viewDate = this.moveYear(this.viewDate, dir);
17228 var date = new Date();
17229 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17231 this.setValue(this.formatDate(this.date));
17238 if (className.indexOf('disabled') < 0) {
17239 this.viewDate.setUTCDate(1);
17240 if (className.indexOf('month') > -1) {
17241 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17243 var year = parseInt(html, 10) || 0;
17244 this.viewDate.setUTCFullYear(year);
17248 if(this.singleMode){
17249 this.setValue(this.formatDate(this.viewDate));
17260 //Roo.log(className);
17261 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17262 var day = parseInt(html, 10) || 1;
17263 var year = this.viewDate.getUTCFullYear(),
17264 month = this.viewDate.getUTCMonth();
17266 if (className.indexOf('old') > -1) {
17273 } else if (className.indexOf('new') > -1) {
17281 //Roo.log([year,month,day]);
17282 this.date = this.UTCDate(year, month, day,0,0,0,0);
17283 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17285 //Roo.log(this.formatDate(this.date));
17286 this.setValue(this.formatDate(this.date));
17293 setStartDate: function(startDate)
17295 this.startDate = startDate || -Infinity;
17296 if (this.startDate !== -Infinity) {
17297 this.startDate = this.parseDate(this.startDate);
17300 this.updateNavArrows();
17303 setEndDate: function(endDate)
17305 this.endDate = endDate || Infinity;
17306 if (this.endDate !== Infinity) {
17307 this.endDate = this.parseDate(this.endDate);
17310 this.updateNavArrows();
17313 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17315 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17316 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17317 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17319 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17320 return parseInt(d, 10);
17323 this.updateNavArrows();
17326 updateNavArrows: function()
17328 if(this.singleMode){
17332 var d = new Date(this.viewDate),
17333 year = d.getUTCFullYear(),
17334 month = d.getUTCMonth();
17336 Roo.each(this.picker().select('.prev', true).elements, function(v){
17338 switch (this.viewMode) {
17341 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17347 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17354 Roo.each(this.picker().select('.next', true).elements, function(v){
17356 switch (this.viewMode) {
17359 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17365 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17373 moveMonth: function(date, dir)
17378 var new_date = new Date(date.valueOf()),
17379 day = new_date.getUTCDate(),
17380 month = new_date.getUTCMonth(),
17381 mag = Math.abs(dir),
17383 dir = dir > 0 ? 1 : -1;
17386 // If going back one month, make sure month is not current month
17387 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17389 return new_date.getUTCMonth() == month;
17391 // If going forward one month, make sure month is as expected
17392 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17394 return new_date.getUTCMonth() != new_month;
17396 new_month = month + dir;
17397 new_date.setUTCMonth(new_month);
17398 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17399 if (new_month < 0 || new_month > 11) {
17400 new_month = (new_month + 12) % 12;
17403 // For magnitudes >1, move one month at a time...
17404 for (var i=0; i<mag; i++) {
17405 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17406 new_date = this.moveMonth(new_date, dir);
17408 // ...then reset the day, keeping it in the new month
17409 new_month = new_date.getUTCMonth();
17410 new_date.setUTCDate(day);
17412 return new_month != new_date.getUTCMonth();
17415 // Common date-resetting loop -- if date is beyond end of month, make it
17418 new_date.setUTCDate(--day);
17419 new_date.setUTCMonth(new_month);
17424 moveYear: function(date, dir)
17426 return this.moveMonth(date, dir*12);
17429 dateWithinRange: function(date)
17431 return date >= this.startDate && date <= this.endDate;
17437 this.picker().remove();
17442 Roo.apply(Roo.bootstrap.DateField, {
17453 html: '<i class="fa fa-arrow-left"/>'
17463 html: '<i class="fa fa-arrow-right"/>'
17505 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17506 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17507 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17508 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17509 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17522 navFnc: 'FullYear',
17527 navFnc: 'FullYear',
17532 Roo.apply(Roo.bootstrap.DateField, {
17536 cls: 'datepicker dropdown-menu roo-dynamic',
17540 cls: 'datepicker-days',
17544 cls: 'table-condensed',
17546 Roo.bootstrap.DateField.head,
17550 Roo.bootstrap.DateField.footer
17557 cls: 'datepicker-months',
17561 cls: 'table-condensed',
17563 Roo.bootstrap.DateField.head,
17564 Roo.bootstrap.DateField.content,
17565 Roo.bootstrap.DateField.footer
17572 cls: 'datepicker-years',
17576 cls: 'table-condensed',
17578 Roo.bootstrap.DateField.head,
17579 Roo.bootstrap.DateField.content,
17580 Roo.bootstrap.DateField.footer
17599 * @class Roo.bootstrap.TimeField
17600 * @extends Roo.bootstrap.Input
17601 * Bootstrap DateField class
17605 * Create a new TimeField
17606 * @param {Object} config The config object
17609 Roo.bootstrap.TimeField = function(config){
17610 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17614 * Fires when this field show.
17615 * @param {Roo.bootstrap.DateField} thisthis
17616 * @param {Mixed} date The date value
17621 * Fires when this field hide.
17622 * @param {Roo.bootstrap.DateField} this
17623 * @param {Mixed} date The date value
17628 * Fires when select a date.
17629 * @param {Roo.bootstrap.DateField} this
17630 * @param {Mixed} date The date value
17636 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17639 * @cfg {String} format
17640 * The default time format string which can be overriden for localization support. The format must be
17641 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17645 onRender: function(ct, position)
17648 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17650 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17652 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17654 this.pop = this.picker().select('>.datepicker-time',true).first();
17655 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17657 this.picker().on('mousedown', this.onMousedown, this);
17658 this.picker().on('click', this.onClick, this);
17660 this.picker().addClass('datepicker-dropdown');
17665 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17666 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17667 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17668 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17669 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17670 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17674 fireKey: function(e){
17675 if (!this.picker().isVisible()){
17676 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17682 e.preventDefault();
17690 this.onTogglePeriod();
17693 this.onIncrementMinutes();
17696 this.onDecrementMinutes();
17705 onClick: function(e) {
17706 e.stopPropagation();
17707 e.preventDefault();
17710 picker : function()
17712 return this.el.select('.datepicker', true).first();
17715 fillTime: function()
17717 var time = this.pop.select('tbody', true).first();
17719 time.dom.innerHTML = '';
17734 cls: 'hours-up glyphicon glyphicon-chevron-up'
17754 cls: 'minutes-up glyphicon glyphicon-chevron-up'
17775 cls: 'timepicker-hour',
17790 cls: 'timepicker-minute',
17805 cls: 'btn btn-primary period',
17827 cls: 'hours-down glyphicon glyphicon-chevron-down'
17847 cls: 'minutes-down glyphicon glyphicon-chevron-down'
17865 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17872 var hours = this.time.getHours();
17873 var minutes = this.time.getMinutes();
17886 hours = hours - 12;
17890 hours = '0' + hours;
17894 minutes = '0' + minutes;
17897 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17898 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17899 this.pop.select('button', true).first().dom.innerHTML = period;
17905 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17907 var cls = ['bottom'];
17909 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17916 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17921 this.picker().addClass(cls.join('-'));
17925 Roo.each(cls, function(c){
17927 _this.picker().setTop(_this.inputEl().getHeight());
17931 _this.picker().setTop(0 - _this.picker().getHeight());
17936 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17940 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17947 onFocus : function()
17949 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17953 onBlur : function()
17955 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17961 this.picker().show();
17966 this.fireEvent('show', this, this.date);
17971 this.picker().hide();
17974 this.fireEvent('hide', this, this.date);
17977 setTime : function()
17980 this.setValue(this.time.format(this.format));
17982 this.fireEvent('select', this, this.date);
17987 onMousedown: function(e){
17988 e.stopPropagation();
17989 e.preventDefault();
17992 onIncrementHours: function()
17994 Roo.log('onIncrementHours');
17995 this.time = this.time.add(Date.HOUR, 1);
18000 onDecrementHours: function()
18002 Roo.log('onDecrementHours');
18003 this.time = this.time.add(Date.HOUR, -1);
18007 onIncrementMinutes: function()
18009 Roo.log('onIncrementMinutes');
18010 this.time = this.time.add(Date.MINUTE, 1);
18014 onDecrementMinutes: function()
18016 Roo.log('onDecrementMinutes');
18017 this.time = this.time.add(Date.MINUTE, -1);
18021 onTogglePeriod: function()
18023 Roo.log('onTogglePeriod');
18024 this.time = this.time.add(Date.HOUR, 12);
18031 Roo.apply(Roo.bootstrap.TimeField, {
18061 cls: 'btn btn-info ok',
18073 Roo.apply(Roo.bootstrap.TimeField, {
18077 cls: 'datepicker dropdown-menu',
18081 cls: 'datepicker-time',
18085 cls: 'table-condensed',
18087 Roo.bootstrap.TimeField.content,
18088 Roo.bootstrap.TimeField.footer
18107 * @class Roo.bootstrap.MonthField
18108 * @extends Roo.bootstrap.Input
18109 * Bootstrap MonthField class
18111 * @cfg {String} language default en
18114 * Create a new MonthField
18115 * @param {Object} config The config object
18118 Roo.bootstrap.MonthField = function(config){
18119 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18124 * Fires when this field show.
18125 * @param {Roo.bootstrap.MonthField} this
18126 * @param {Mixed} date The date value
18131 * Fires when this field hide.
18132 * @param {Roo.bootstrap.MonthField} this
18133 * @param {Mixed} date The date value
18138 * Fires when select a date.
18139 * @param {Roo.bootstrap.MonthField} this
18140 * @param {String} oldvalue The old value
18141 * @param {String} newvalue The new value
18147 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
18149 onRender: function(ct, position)
18152 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18154 this.language = this.language || 'en';
18155 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18156 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18158 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18159 this.isInline = false;
18160 this.isInput = true;
18161 this.component = this.el.select('.add-on', true).first() || false;
18162 this.component = (this.component && this.component.length === 0) ? false : this.component;
18163 this.hasInput = this.component && this.inputEL().length;
18165 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18167 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18169 this.picker().on('mousedown', this.onMousedown, this);
18170 this.picker().on('click', this.onClick, this);
18172 this.picker().addClass('datepicker-dropdown');
18174 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18175 v.setStyle('width', '189px');
18182 if(this.isInline) {
18188 setValue: function(v, suppressEvent)
18190 var o = this.getValue();
18192 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18196 if(suppressEvent !== true){
18197 this.fireEvent('select', this, o, v);
18202 getValue: function()
18207 onClick: function(e)
18209 e.stopPropagation();
18210 e.preventDefault();
18212 var target = e.getTarget();
18214 if(target.nodeName.toLowerCase() === 'i'){
18215 target = Roo.get(target).dom.parentNode;
18218 var nodeName = target.nodeName;
18219 var className = target.className;
18220 var html = target.innerHTML;
18222 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18226 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18228 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18234 picker : function()
18236 return this.pickerEl;
18239 fillMonths: function()
18242 var months = this.picker().select('>.datepicker-months td', true).first();
18244 months.dom.innerHTML = '';
18250 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18253 months.createChild(month);
18262 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18263 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18266 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18267 e.removeClass('active');
18269 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18270 e.addClass('active');
18277 if(this.isInline) {
18281 this.picker().removeClass(['bottom', 'top']);
18283 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18285 * place to the top of element!
18289 this.picker().addClass('top');
18290 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18295 this.picker().addClass('bottom');
18297 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18300 onFocus : function()
18302 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18306 onBlur : function()
18308 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18310 var d = this.inputEl().getValue();
18319 this.picker().show();
18320 this.picker().select('>.datepicker-months', true).first().show();
18324 this.fireEvent('show', this, this.date);
18329 if(this.isInline) {
18332 this.picker().hide();
18333 this.fireEvent('hide', this, this.date);
18337 onMousedown: function(e)
18339 e.stopPropagation();
18340 e.preventDefault();
18345 Roo.bootstrap.MonthField.superclass.keyup.call(this);
18349 fireKey: function(e)
18351 if (!this.picker().isVisible()){
18352 if (e.keyCode == 27) {// allow escape to hide and re-show picker
18363 e.preventDefault();
18367 dir = e.keyCode == 37 ? -1 : 1;
18369 this.vIndex = this.vIndex + dir;
18371 if(this.vIndex < 0){
18375 if(this.vIndex > 11){
18379 if(isNaN(this.vIndex)){
18383 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18389 dir = e.keyCode == 38 ? -1 : 1;
18391 this.vIndex = this.vIndex + dir * 4;
18393 if(this.vIndex < 0){
18397 if(this.vIndex > 11){
18401 if(isNaN(this.vIndex)){
18405 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18410 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18411 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18415 e.preventDefault();
18418 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18419 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18435 this.picker().remove();
18440 Roo.apply(Roo.bootstrap.MonthField, {
18459 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18460 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18465 Roo.apply(Roo.bootstrap.MonthField, {
18469 cls: 'datepicker dropdown-menu roo-dynamic',
18473 cls: 'datepicker-months',
18477 cls: 'table-condensed',
18479 Roo.bootstrap.DateField.content
18499 * @class Roo.bootstrap.CheckBox
18500 * @extends Roo.bootstrap.Input
18501 * Bootstrap CheckBox class
18503 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18504 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18505 * @cfg {String} boxLabel The text that appears beside the checkbox
18506 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18507 * @cfg {Boolean} checked initnal the element
18508 * @cfg {Boolean} inline inline the element (default false)
18509 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18512 * Create a new CheckBox
18513 * @param {Object} config The config object
18516 Roo.bootstrap.CheckBox = function(config){
18517 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18522 * Fires when the element is checked or unchecked.
18523 * @param {Roo.bootstrap.CheckBox} this This input
18524 * @param {Boolean} checked The new checked value
18531 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18533 inputType: 'checkbox',
18541 getAutoCreate : function()
18543 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18549 cfg.cls = 'form-group ' + this.inputType; //input-group
18552 cfg.cls += ' ' + this.inputType + '-inline';
18558 type : this.inputType,
18559 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18560 cls : 'roo-' + this.inputType, //'form-box',
18561 placeholder : this.placeholder || ''
18565 if (this.weight) { // Validity check?
18566 cfg.cls += " " + this.inputType + "-" + this.weight;
18569 if (this.disabled) {
18570 input.disabled=true;
18574 input.checked = this.checked;
18578 input.name = this.name;
18582 input.cls += ' input-' + this.size;
18587 ['xs','sm','md','lg'].map(function(size){
18588 if (settings[size]) {
18589 cfg.cls += ' col-' + size + '-' + settings[size];
18593 var inputblock = input;
18595 if (this.before || this.after) {
18598 cls : 'input-group',
18603 inputblock.cn.push({
18605 cls : 'input-group-addon',
18610 inputblock.cn.push(input);
18613 inputblock.cn.push({
18615 cls : 'input-group-addon',
18622 if (align ==='left' && this.fieldLabel.length) {
18623 Roo.log("left and has label");
18629 cls : 'control-label col-md-' + this.labelWidth,
18630 html : this.fieldLabel
18634 cls : "col-md-" + (12 - this.labelWidth),
18641 } else if ( this.fieldLabel.length) {
18646 tag: this.boxLabel ? 'span' : 'label',
18648 cls: 'control-label box-input-label',
18649 //cls : 'input-group-addon',
18650 html : this.fieldLabel
18660 Roo.log(" no label && no align");
18661 cfg.cn = [ inputblock ] ;
18666 var boxLabelCfg = {
18668 //'for': id, // box label is handled by onclick - so no for...
18670 html: this.boxLabel
18674 boxLabelCfg.tooltip = this.tooltip;
18677 cfg.cn.push(boxLabelCfg);
18687 * return the real input element.
18689 inputEl: function ()
18691 return this.el.select('input.roo-' + this.inputType,true).first();
18694 labelEl: function()
18696 return this.el.select('label.control-label',true).first();
18698 /* depricated... */
18702 return this.labelEl();
18705 initEvents : function()
18707 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18709 this.inputEl().on('click', this.onClick, this);
18711 if (this.boxLabel) {
18712 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18715 this.startValue = this.getValue();
18718 Roo.bootstrap.CheckBox.register(this);
18722 onClick : function()
18724 this.setChecked(!this.checked);
18727 setChecked : function(state,suppressEvent)
18729 this.startValue = this.getValue();
18731 if(this.inputType == 'radio'){
18733 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18734 e.dom.checked = false;
18737 this.inputEl().dom.checked = true;
18739 this.inputEl().dom.value = this.inputValue;
18741 if(suppressEvent !== true){
18742 this.fireEvent('check', this, true);
18750 this.checked = state;
18752 this.inputEl().dom.checked = state;
18754 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18756 if(suppressEvent !== true){
18757 this.fireEvent('check', this, state);
18763 getValue : function()
18765 if(this.inputType == 'radio'){
18766 return this.getGroupValue();
18769 return this.inputEl().getValue();
18773 getGroupValue : function()
18775 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18779 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18782 setValue : function(v,suppressEvent)
18784 if(this.inputType == 'radio'){
18785 this.setGroupValue(v, suppressEvent);
18789 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18794 setGroupValue : function(v, suppressEvent)
18796 this.startValue = this.getValue();
18798 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18799 e.dom.checked = false;
18801 if(e.dom.value == v){
18802 e.dom.checked = true;
18806 if(suppressEvent !== true){
18807 this.fireEvent('check', this, true);
18815 validate : function()
18819 (this.inputType == 'radio' && this.validateRadio()) ||
18820 (this.inputType == 'checkbox' && this.validateCheckbox())
18826 this.markInvalid();
18830 validateRadio : function()
18834 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18835 if(!e.dom.checked){
18847 validateCheckbox : function()
18850 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18853 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18861 for(var i in group){
18866 r = (group[i].getValue() == group[i].inputValue) ? true : false;
18873 * Mark this field as valid
18875 markValid : function()
18877 if(this.allowBlank){
18883 this.fireEvent('valid', this);
18885 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18888 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18895 if(this.inputType == 'radio'){
18896 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18897 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18898 e.findParent('.form-group', false, true).addClass(_this.validClass);
18905 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18906 this.el.findParent('.form-group', false, true).addClass(this.validClass);
18910 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18916 for(var i in group){
18917 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18918 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18923 * Mark this field as invalid
18924 * @param {String} msg The validation message
18926 markInvalid : function(msg)
18928 if(this.allowBlank){
18934 this.fireEvent('invalid', this, msg);
18936 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18939 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18943 label.markInvalid();
18946 if(this.inputType == 'radio'){
18947 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18948 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18949 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18956 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18957 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18961 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18967 for(var i in group){
18968 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18969 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18976 Roo.apply(Roo.bootstrap.CheckBox, {
18981 * register a CheckBox Group
18982 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18984 register : function(checkbox)
18986 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18987 this.groups[checkbox.groupId] = {};
18990 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18994 this.groups[checkbox.groupId][checkbox.name] = checkbox;
18998 * fetch a CheckBox Group based on the group ID
18999 * @param {string} the group ID
19000 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19002 get: function(groupId) {
19003 if (typeof(this.groups[groupId]) == 'undefined') {
19007 return this.groups[groupId] ;
19019 *<div class="radio">
19021 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19022 Option one is this and that—be sure to include why it's great
19029 *<label class="radio-inline">fieldLabel</label>
19030 *<label class="radio-inline">
19031 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19039 * @class Roo.bootstrap.Radio
19040 * @extends Roo.bootstrap.CheckBox
19041 * Bootstrap Radio class
19044 * Create a new Radio
19045 * @param {Object} config The config object
19048 Roo.bootstrap.Radio = function(config){
19049 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19053 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
19055 inputType: 'radio',
19059 getAutoCreate : function()
19061 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19062 align = align || 'left'; // default...
19069 tag : this.inline ? 'span' : 'div',
19074 var inline = this.inline ? ' radio-inline' : '';
19078 // does not need for, as we wrap the input with it..
19080 cls : 'control-label box-label' + inline,
19083 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19087 //cls : 'control-label' + inline,
19088 html : this.fieldLabel,
19089 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19098 type : this.inputType,
19099 //value : (!this.checked) ? this.valueOff : this.inputValue,
19100 value : this.inputValue,
19102 placeholder : this.placeholder || '' // ?? needed????
19105 if (this.weight) { // Validity check?
19106 input.cls += " radio-" + this.weight;
19108 if (this.disabled) {
19109 input.disabled=true;
19113 input.checked = this.checked;
19117 input.name = this.name;
19121 input.cls += ' input-' + this.size;
19124 //?? can span's inline have a width??
19127 ['xs','sm','md','lg'].map(function(size){
19128 if (settings[size]) {
19129 cfg.cls += ' col-' + size + '-' + settings[size];
19133 var inputblock = input;
19135 if (this.before || this.after) {
19138 cls : 'input-group',
19143 inputblock.cn.push({
19145 cls : 'input-group-addon',
19149 inputblock.cn.push(input);
19151 inputblock.cn.push({
19153 cls : 'input-group-addon',
19161 if (this.fieldLabel && this.fieldLabel.length) {
19162 cfg.cn.push(fieldLabel);
19165 // normal bootstrap puts the input inside the label.
19166 // however with our styled version - it has to go after the input.
19168 //lbl.cn.push(inputblock);
19172 cls: 'radio' + inline,
19179 cfg.cn.push( lblwrap);
19184 html: this.boxLabel
19193 initEvents : function()
19195 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19197 this.inputEl().on('click', this.onClick, this);
19198 if (this.boxLabel) {
19199 //Roo.log('find label');
19200 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
19205 inputEl: function ()
19207 return this.el.select('input.roo-radio',true).first();
19209 onClick : function()
19212 this.setChecked(true);
19215 setChecked : function(state,suppressEvent)
19218 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19219 v.dom.checked = false;
19222 Roo.log(this.inputEl().dom);
19223 this.checked = state;
19224 this.inputEl().dom.checked = state;
19226 if(suppressEvent !== true){
19227 this.fireEvent('check', this, state);
19230 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19234 getGroupValue : function()
19237 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19238 if(v.dom.checked == true){
19239 value = v.dom.value;
19247 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
19248 * @return {Mixed} value The field value
19250 getValue : function(){
19251 return this.getGroupValue();
19257 //<script type="text/javascript">
19260 * Based Ext JS Library 1.1.1
19261 * Copyright(c) 2006-2007, Ext JS, LLC.
19267 * @class Roo.HtmlEditorCore
19268 * @extends Roo.Component
19269 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19271 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19274 Roo.HtmlEditorCore = function(config){
19277 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19282 * @event initialize
19283 * Fires when the editor is fully initialized (including the iframe)
19284 * @param {Roo.HtmlEditorCore} this
19289 * Fires when the editor is first receives the focus. Any insertion must wait
19290 * until after this event.
19291 * @param {Roo.HtmlEditorCore} this
19295 * @event beforesync
19296 * Fires before the textarea is updated with content from the editor iframe. Return false
19297 * to cancel the sync.
19298 * @param {Roo.HtmlEditorCore} this
19299 * @param {String} html
19303 * @event beforepush
19304 * Fires before the iframe editor is updated with content from the textarea. Return false
19305 * to cancel the push.
19306 * @param {Roo.HtmlEditorCore} this
19307 * @param {String} html
19312 * Fires when the textarea is updated with content from the editor iframe.
19313 * @param {Roo.HtmlEditorCore} this
19314 * @param {String} html
19319 * Fires when the iframe editor is updated with content from the textarea.
19320 * @param {Roo.HtmlEditorCore} this
19321 * @param {String} html
19326 * @event editorevent
19327 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19328 * @param {Roo.HtmlEditorCore} this
19334 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19336 // defaults : white / black...
19337 this.applyBlacklists();
19344 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
19348 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
19354 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
19359 * @cfg {Number} height (in pixels)
19363 * @cfg {Number} width (in pixels)
19368 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19371 stylesheets: false,
19376 // private properties
19377 validationEvent : false,
19379 initialized : false,
19381 sourceEditMode : false,
19382 onFocus : Roo.emptyFn,
19384 hideMode:'offsets',
19388 // blacklist + whitelisted elements..
19395 * Protected method that will not generally be called directly. It
19396 * is called when the editor initializes the iframe with HTML contents. Override this method if you
19397 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19399 getDocMarkup : function(){
19403 // inherit styels from page...??
19404 if (this.stylesheets === false) {
19406 Roo.get(document.head).select('style').each(function(node) {
19407 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19410 Roo.get(document.head).select('link').each(function(node) {
19411 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19414 } else if (!this.stylesheets.length) {
19416 st = '<style type="text/css">' +
19417 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19423 st += '<style type="text/css">' +
19424 'IMG { cursor: pointer } ' +
19428 return '<html><head>' + st +
19429 //<style type="text/css">' +
19430 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19432 ' </head><body class="roo-htmleditor-body"></body></html>';
19436 onRender : function(ct, position)
19439 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19440 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19443 this.el.dom.style.border = '0 none';
19444 this.el.dom.setAttribute('tabIndex', -1);
19445 this.el.addClass('x-hidden hide');
19449 if(Roo.isIE){ // fix IE 1px bogus margin
19450 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19454 this.frameId = Roo.id();
19458 var iframe = this.owner.wrap.createChild({
19460 cls: 'form-control', // bootstrap..
19462 name: this.frameId,
19463 frameBorder : 'no',
19464 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19469 this.iframe = iframe.dom;
19471 this.assignDocWin();
19473 this.doc.designMode = 'on';
19476 this.doc.write(this.getDocMarkup());
19480 var task = { // must defer to wait for browser to be ready
19482 //console.log("run task?" + this.doc.readyState);
19483 this.assignDocWin();
19484 if(this.doc.body || this.doc.readyState == 'complete'){
19486 this.doc.designMode="on";
19490 Roo.TaskMgr.stop(task);
19491 this.initEditor.defer(10, this);
19498 Roo.TaskMgr.start(task);
19503 onResize : function(w, h)
19505 Roo.log('resize: ' +w + ',' + h );
19506 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19510 if(typeof w == 'number'){
19512 this.iframe.style.width = w + 'px';
19514 if(typeof h == 'number'){
19516 this.iframe.style.height = h + 'px';
19518 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19525 * Toggles the editor between standard and source edit mode.
19526 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19528 toggleSourceEdit : function(sourceEditMode){
19530 this.sourceEditMode = sourceEditMode === true;
19532 if(this.sourceEditMode){
19534 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19537 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19538 //this.iframe.className = '';
19541 //this.setSize(this.owner.wrap.getSize());
19542 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19549 * Protected method that will not generally be called directly. If you need/want
19550 * custom HTML cleanup, this is the method you should override.
19551 * @param {String} html The HTML to be cleaned
19552 * return {String} The cleaned HTML
19554 cleanHtml : function(html){
19555 html = String(html);
19556 if(html.length > 5){
19557 if(Roo.isSafari){ // strip safari nonsense
19558 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19561 if(html == ' '){
19568 * HTML Editor -> Textarea
19569 * Protected method that will not generally be called directly. Syncs the contents
19570 * of the editor iframe with the textarea.
19572 syncValue : function(){
19573 if(this.initialized){
19574 var bd = (this.doc.body || this.doc.documentElement);
19575 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19576 var html = bd.innerHTML;
19578 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19579 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19581 html = '<div style="'+m[0]+'">' + html + '</div>';
19584 html = this.cleanHtml(html);
19585 // fix up the special chars.. normaly like back quotes in word...
19586 // however we do not want to do this with chinese..
19587 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19588 var cc = b.charCodeAt();
19590 (cc >= 0x4E00 && cc < 0xA000 ) ||
19591 (cc >= 0x3400 && cc < 0x4E00 ) ||
19592 (cc >= 0xf900 && cc < 0xfb00 )
19598 if(this.owner.fireEvent('beforesync', this, html) !== false){
19599 this.el.dom.value = html;
19600 this.owner.fireEvent('sync', this, html);
19606 * Protected method that will not generally be called directly. Pushes the value of the textarea
19607 * into the iframe editor.
19609 pushValue : function(){
19610 if(this.initialized){
19611 var v = this.el.dom.value.trim();
19613 // if(v.length < 1){
19617 if(this.owner.fireEvent('beforepush', this, v) !== false){
19618 var d = (this.doc.body || this.doc.documentElement);
19620 this.cleanUpPaste();
19621 this.el.dom.value = d.innerHTML;
19622 this.owner.fireEvent('push', this, v);
19628 deferFocus : function(){
19629 this.focus.defer(10, this);
19633 focus : function(){
19634 if(this.win && !this.sourceEditMode){
19641 assignDocWin: function()
19643 var iframe = this.iframe;
19646 this.doc = iframe.contentWindow.document;
19647 this.win = iframe.contentWindow;
19649 // if (!Roo.get(this.frameId)) {
19652 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19653 // this.win = Roo.get(this.frameId).dom.contentWindow;
19655 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19659 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19660 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19665 initEditor : function(){
19666 //console.log("INIT EDITOR");
19667 this.assignDocWin();
19671 this.doc.designMode="on";
19673 this.doc.write(this.getDocMarkup());
19676 var dbody = (this.doc.body || this.doc.documentElement);
19677 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19678 // this copies styles from the containing element into thsi one..
19679 // not sure why we need all of this..
19680 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19682 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19683 //ss['background-attachment'] = 'fixed'; // w3c
19684 dbody.bgProperties = 'fixed'; // ie
19685 //Roo.DomHelper.applyStyles(dbody, ss);
19686 Roo.EventManager.on(this.doc, {
19687 //'mousedown': this.onEditorEvent,
19688 'mouseup': this.onEditorEvent,
19689 'dblclick': this.onEditorEvent,
19690 'click': this.onEditorEvent,
19691 'keyup': this.onEditorEvent,
19696 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19698 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19699 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19701 this.initialized = true;
19703 this.owner.fireEvent('initialize', this);
19708 onDestroy : function(){
19714 //for (var i =0; i < this.toolbars.length;i++) {
19715 // // fixme - ask toolbars for heights?
19716 // this.toolbars[i].onDestroy();
19719 //this.wrap.dom.innerHTML = '';
19720 //this.wrap.remove();
19725 onFirstFocus : function(){
19727 this.assignDocWin();
19730 this.activated = true;
19733 if(Roo.isGecko){ // prevent silly gecko errors
19735 var s = this.win.getSelection();
19736 if(!s.focusNode || s.focusNode.nodeType != 3){
19737 var r = s.getRangeAt(0);
19738 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19743 this.execCmd('useCSS', true);
19744 this.execCmd('styleWithCSS', false);
19747 this.owner.fireEvent('activate', this);
19751 adjustFont: function(btn){
19752 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19753 //if(Roo.isSafari){ // safari
19756 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19757 if(Roo.isSafari){ // safari
19758 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19759 v = (v < 10) ? 10 : v;
19760 v = (v > 48) ? 48 : v;
19761 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19766 v = Math.max(1, v+adjust);
19768 this.execCmd('FontSize', v );
19771 onEditorEvent : function(e)
19773 this.owner.fireEvent('editorevent', this, e);
19774 // this.updateToolbar();
19775 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19778 insertTag : function(tg)
19780 // could be a bit smarter... -> wrap the current selected tRoo..
19781 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19783 range = this.createRange(this.getSelection());
19784 var wrappingNode = this.doc.createElement(tg.toLowerCase());
19785 wrappingNode.appendChild(range.extractContents());
19786 range.insertNode(wrappingNode);
19793 this.execCmd("formatblock", tg);
19797 insertText : function(txt)
19801 var range = this.createRange();
19802 range.deleteContents();
19803 //alert(Sender.getAttribute('label'));
19805 range.insertNode(this.doc.createTextNode(txt));
19811 * Executes a Midas editor command on the editor document and performs necessary focus and
19812 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19813 * @param {String} cmd The Midas command
19814 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19816 relayCmd : function(cmd, value){
19818 this.execCmd(cmd, value);
19819 this.owner.fireEvent('editorevent', this);
19820 //this.updateToolbar();
19821 this.owner.deferFocus();
19825 * Executes a Midas editor command directly on the editor document.
19826 * For visual commands, you should use {@link #relayCmd} instead.
19827 * <b>This should only be called after the editor is initialized.</b>
19828 * @param {String} cmd The Midas command
19829 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19831 execCmd : function(cmd, value){
19832 this.doc.execCommand(cmd, false, value === undefined ? null : value);
19839 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19841 * @param {String} text | dom node..
19843 insertAtCursor : function(text)
19848 if(!this.activated){
19854 var r = this.doc.selection.createRange();
19865 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19869 // from jquery ui (MIT licenced)
19871 var win = this.win;
19873 if (win.getSelection && win.getSelection().getRangeAt) {
19874 range = win.getSelection().getRangeAt(0);
19875 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19876 range.insertNode(node);
19877 } else if (win.document.selection && win.document.selection.createRange) {
19878 // no firefox support
19879 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19880 win.document.selection.createRange().pasteHTML(txt);
19882 // no firefox support
19883 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19884 this.execCmd('InsertHTML', txt);
19893 mozKeyPress : function(e){
19895 var c = e.getCharCode(), cmd;
19898 c = String.fromCharCode(c).toLowerCase();
19912 this.cleanUpPaste.defer(100, this);
19920 e.preventDefault();
19928 fixKeys : function(){ // load time branching for fastest keydown performance
19930 return function(e){
19931 var k = e.getKey(), r;
19934 r = this.doc.selection.createRange();
19937 r.pasteHTML('    ');
19944 r = this.doc.selection.createRange();
19946 var target = r.parentElement();
19947 if(!target || target.tagName.toLowerCase() != 'li'){
19949 r.pasteHTML('<br />');
19955 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19956 this.cleanUpPaste.defer(100, this);
19962 }else if(Roo.isOpera){
19963 return function(e){
19964 var k = e.getKey();
19968 this.execCmd('InsertHTML','    ');
19971 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19972 this.cleanUpPaste.defer(100, this);
19977 }else if(Roo.isSafari){
19978 return function(e){
19979 var k = e.getKey();
19983 this.execCmd('InsertText','\t');
19987 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19988 this.cleanUpPaste.defer(100, this);
19996 getAllAncestors: function()
19998 var p = this.getSelectedNode();
20001 a.push(p); // push blank onto stack..
20002 p = this.getParentElement();
20006 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20010 a.push(this.doc.body);
20014 lastSelNode : false,
20017 getSelection : function()
20019 this.assignDocWin();
20020 return Roo.isIE ? this.doc.selection : this.win.getSelection();
20023 getSelectedNode: function()
20025 // this may only work on Gecko!!!
20027 // should we cache this!!!!
20032 var range = this.createRange(this.getSelection()).cloneRange();
20035 var parent = range.parentElement();
20037 var testRange = range.duplicate();
20038 testRange.moveToElementText(parent);
20039 if (testRange.inRange(range)) {
20042 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20045 parent = parent.parentElement;
20050 // is ancestor a text element.
20051 var ac = range.commonAncestorContainer;
20052 if (ac.nodeType == 3) {
20053 ac = ac.parentNode;
20056 var ar = ac.childNodes;
20059 var other_nodes = [];
20060 var has_other_nodes = false;
20061 for (var i=0;i<ar.length;i++) {
20062 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
20065 // fullly contained node.
20067 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20072 // probably selected..
20073 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20074 other_nodes.push(ar[i]);
20078 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
20083 has_other_nodes = true;
20085 if (!nodes.length && other_nodes.length) {
20086 nodes= other_nodes;
20088 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20094 createRange: function(sel)
20096 // this has strange effects when using with
20097 // top toolbar - not sure if it's a great idea.
20098 //this.editor.contentWindow.focus();
20099 if (typeof sel != "undefined") {
20101 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20103 return this.doc.createRange();
20106 return this.doc.createRange();
20109 getParentElement: function()
20112 this.assignDocWin();
20113 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20115 var range = this.createRange(sel);
20118 var p = range.commonAncestorContainer;
20119 while (p.nodeType == 3) { // text node
20130 * Range intersection.. the hard stuff...
20134 * [ -- selected range --- ]
20138 * if end is before start or hits it. fail.
20139 * if start is after end or hits it fail.
20141 * if either hits (but other is outside. - then it's not
20147 // @see http://www.thismuchiknow.co.uk/?p=64.
20148 rangeIntersectsNode : function(range, node)
20150 var nodeRange = node.ownerDocument.createRange();
20152 nodeRange.selectNode(node);
20154 nodeRange.selectNodeContents(node);
20157 var rangeStartRange = range.cloneRange();
20158 rangeStartRange.collapse(true);
20160 var rangeEndRange = range.cloneRange();
20161 rangeEndRange.collapse(false);
20163 var nodeStartRange = nodeRange.cloneRange();
20164 nodeStartRange.collapse(true);
20166 var nodeEndRange = nodeRange.cloneRange();
20167 nodeEndRange.collapse(false);
20169 return rangeStartRange.compareBoundaryPoints(
20170 Range.START_TO_START, nodeEndRange) == -1 &&
20171 rangeEndRange.compareBoundaryPoints(
20172 Range.START_TO_START, nodeStartRange) == 1;
20176 rangeCompareNode : function(range, node)
20178 var nodeRange = node.ownerDocument.createRange();
20180 nodeRange.selectNode(node);
20182 nodeRange.selectNodeContents(node);
20186 range.collapse(true);
20188 nodeRange.collapse(true);
20190 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20191 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
20193 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20195 var nodeIsBefore = ss == 1;
20196 var nodeIsAfter = ee == -1;
20198 if (nodeIsBefore && nodeIsAfter) {
20201 if (!nodeIsBefore && nodeIsAfter) {
20202 return 1; //right trailed.
20205 if (nodeIsBefore && !nodeIsAfter) {
20206 return 2; // left trailed.
20212 // private? - in a new class?
20213 cleanUpPaste : function()
20215 // cleans up the whole document..
20216 Roo.log('cleanuppaste');
20218 this.cleanUpChildren(this.doc.body);
20219 var clean = this.cleanWordChars(this.doc.body.innerHTML);
20220 if (clean != this.doc.body.innerHTML) {
20221 this.doc.body.innerHTML = clean;
20226 cleanWordChars : function(input) {// change the chars to hex code
20227 var he = Roo.HtmlEditorCore;
20229 var output = input;
20230 Roo.each(he.swapCodes, function(sw) {
20231 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20233 output = output.replace(swapper, sw[1]);
20240 cleanUpChildren : function (n)
20242 if (!n.childNodes.length) {
20245 for (var i = n.childNodes.length-1; i > -1 ; i--) {
20246 this.cleanUpChild(n.childNodes[i]);
20253 cleanUpChild : function (node)
20256 //console.log(node);
20257 if (node.nodeName == "#text") {
20258 // clean up silly Windows -- stuff?
20261 if (node.nodeName == "#comment") {
20262 node.parentNode.removeChild(node);
20263 // clean up silly Windows -- stuff?
20266 var lcname = node.tagName.toLowerCase();
20267 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20268 // whitelist of tags..
20270 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20272 node.parentNode.removeChild(node);
20277 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20279 // remove <a name=....> as rendering on yahoo mailer is borked with this.
20280 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20282 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20283 // remove_keep_children = true;
20286 if (remove_keep_children) {
20287 this.cleanUpChildren(node);
20288 // inserts everything just before this node...
20289 while (node.childNodes.length) {
20290 var cn = node.childNodes[0];
20291 node.removeChild(cn);
20292 node.parentNode.insertBefore(cn, node);
20294 node.parentNode.removeChild(node);
20298 if (!node.attributes || !node.attributes.length) {
20299 this.cleanUpChildren(node);
20303 function cleanAttr(n,v)
20306 if (v.match(/^\./) || v.match(/^\//)) {
20309 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20312 if (v.match(/^#/)) {
20315 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20316 node.removeAttribute(n);
20320 var cwhite = this.cwhite;
20321 var cblack = this.cblack;
20323 function cleanStyle(n,v)
20325 if (v.match(/expression/)) { //XSS?? should we even bother..
20326 node.removeAttribute(n);
20330 var parts = v.split(/;/);
20333 Roo.each(parts, function(p) {
20334 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20338 var l = p.split(':').shift().replace(/\s+/g,'');
20339 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20341 if ( cwhite.length && cblack.indexOf(l) > -1) {
20342 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20343 //node.removeAttribute(n);
20347 // only allow 'c whitelisted system attributes'
20348 if ( cwhite.length && cwhite.indexOf(l) < 0) {
20349 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20350 //node.removeAttribute(n);
20360 if (clean.length) {
20361 node.setAttribute(n, clean.join(';'));
20363 node.removeAttribute(n);
20369 for (var i = node.attributes.length-1; i > -1 ; i--) {
20370 var a = node.attributes[i];
20373 if (a.name.toLowerCase().substr(0,2)=='on') {
20374 node.removeAttribute(a.name);
20377 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20378 node.removeAttribute(a.name);
20381 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20382 cleanAttr(a.name,a.value); // fixme..
20385 if (a.name == 'style') {
20386 cleanStyle(a.name,a.value);
20389 /// clean up MS crap..
20390 // tecnically this should be a list of valid class'es..
20393 if (a.name == 'class') {
20394 if (a.value.match(/^Mso/)) {
20395 node.className = '';
20398 if (a.value.match(/body/)) {
20399 node.className = '';
20410 this.cleanUpChildren(node);
20416 * Clean up MS wordisms...
20418 cleanWord : function(node)
20423 this.cleanWord(this.doc.body);
20426 if (node.nodeName == "#text") {
20427 // clean up silly Windows -- stuff?
20430 if (node.nodeName == "#comment") {
20431 node.parentNode.removeChild(node);
20432 // clean up silly Windows -- stuff?
20436 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20437 node.parentNode.removeChild(node);
20441 // remove - but keep children..
20442 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20443 while (node.childNodes.length) {
20444 var cn = node.childNodes[0];
20445 node.removeChild(cn);
20446 node.parentNode.insertBefore(cn, node);
20448 node.parentNode.removeChild(node);
20449 this.iterateChildren(node, this.cleanWord);
20453 if (node.className.length) {
20455 var cn = node.className.split(/\W+/);
20457 Roo.each(cn, function(cls) {
20458 if (cls.match(/Mso[a-zA-Z]+/)) {
20463 node.className = cna.length ? cna.join(' ') : '';
20465 node.removeAttribute("class");
20469 if (node.hasAttribute("lang")) {
20470 node.removeAttribute("lang");
20473 if (node.hasAttribute("style")) {
20475 var styles = node.getAttribute("style").split(";");
20477 Roo.each(styles, function(s) {
20478 if (!s.match(/:/)) {
20481 var kv = s.split(":");
20482 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20485 // what ever is left... we allow.
20488 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20489 if (!nstyle.length) {
20490 node.removeAttribute('style');
20493 this.iterateChildren(node, this.cleanWord);
20499 * iterateChildren of a Node, calling fn each time, using this as the scole..
20500 * @param {DomNode} node node to iterate children of.
20501 * @param {Function} fn method of this class to call on each item.
20503 iterateChildren : function(node, fn)
20505 if (!node.childNodes.length) {
20508 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20509 fn.call(this, node.childNodes[i])
20515 * cleanTableWidths.
20517 * Quite often pasting from word etc.. results in tables with column and widths.
20518 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20521 cleanTableWidths : function(node)
20526 this.cleanTableWidths(this.doc.body);
20531 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20534 Roo.log(node.tagName);
20535 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20536 this.iterateChildren(node, this.cleanTableWidths);
20539 if (node.hasAttribute('width')) {
20540 node.removeAttribute('width');
20544 if (node.hasAttribute("style")) {
20547 var styles = node.getAttribute("style").split(";");
20549 Roo.each(styles, function(s) {
20550 if (!s.match(/:/)) {
20553 var kv = s.split(":");
20554 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20557 // what ever is left... we allow.
20560 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20561 if (!nstyle.length) {
20562 node.removeAttribute('style');
20566 this.iterateChildren(node, this.cleanTableWidths);
20574 domToHTML : function(currentElement, depth, nopadtext) {
20576 depth = depth || 0;
20577 nopadtext = nopadtext || false;
20579 if (!currentElement) {
20580 return this.domToHTML(this.doc.body);
20583 //Roo.log(currentElement);
20585 var allText = false;
20586 var nodeName = currentElement.nodeName;
20587 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20589 if (nodeName == '#text') {
20591 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20596 if (nodeName != 'BODY') {
20599 // Prints the node tagName, such as <A>, <IMG>, etc
20602 for(i = 0; i < currentElement.attributes.length;i++) {
20604 var aname = currentElement.attributes.item(i).name;
20605 if (!currentElement.attributes.item(i).value.length) {
20608 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20611 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20620 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20623 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20628 // Traverse the tree
20630 var currentElementChild = currentElement.childNodes.item(i);
20631 var allText = true;
20632 var innerHTML = '';
20634 while (currentElementChild) {
20635 // Formatting code (indent the tree so it looks nice on the screen)
20636 var nopad = nopadtext;
20637 if (lastnode == 'SPAN') {
20641 if (currentElementChild.nodeName == '#text') {
20642 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20643 toadd = nopadtext ? toadd : toadd.trim();
20644 if (!nopad && toadd.length > 80) {
20645 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20647 innerHTML += toadd;
20650 currentElementChild = currentElement.childNodes.item(i);
20656 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20658 // Recursively traverse the tree structure of the child node
20659 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20660 lastnode = currentElementChild.nodeName;
20662 currentElementChild=currentElement.childNodes.item(i);
20668 // The remaining code is mostly for formatting the tree
20669 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20674 ret+= "</"+tagName+">";
20680 applyBlacklists : function()
20682 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20683 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20687 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20688 if (b.indexOf(tag) > -1) {
20691 this.white.push(tag);
20695 Roo.each(w, function(tag) {
20696 if (b.indexOf(tag) > -1) {
20699 if (this.white.indexOf(tag) > -1) {
20702 this.white.push(tag);
20707 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20708 if (w.indexOf(tag) > -1) {
20711 this.black.push(tag);
20715 Roo.each(b, function(tag) {
20716 if (w.indexOf(tag) > -1) {
20719 if (this.black.indexOf(tag) > -1) {
20722 this.black.push(tag);
20727 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20728 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20732 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20733 if (b.indexOf(tag) > -1) {
20736 this.cwhite.push(tag);
20740 Roo.each(w, function(tag) {
20741 if (b.indexOf(tag) > -1) {
20744 if (this.cwhite.indexOf(tag) > -1) {
20747 this.cwhite.push(tag);
20752 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20753 if (w.indexOf(tag) > -1) {
20756 this.cblack.push(tag);
20760 Roo.each(b, function(tag) {
20761 if (w.indexOf(tag) > -1) {
20764 if (this.cblack.indexOf(tag) > -1) {
20767 this.cblack.push(tag);
20772 setStylesheets : function(stylesheets)
20774 if(typeof(stylesheets) == 'string'){
20775 Roo.get(this.iframe.contentDocument.head).createChild({
20777 rel : 'stylesheet',
20786 Roo.each(stylesheets, function(s) {
20791 Roo.get(_this.iframe.contentDocument.head).createChild({
20793 rel : 'stylesheet',
20802 removeStylesheets : function()
20806 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20811 // hide stuff that is not compatible
20825 * @event specialkey
20829 * @cfg {String} fieldClass @hide
20832 * @cfg {String} focusClass @hide
20835 * @cfg {String} autoCreate @hide
20838 * @cfg {String} inputType @hide
20841 * @cfg {String} invalidClass @hide
20844 * @cfg {String} invalidText @hide
20847 * @cfg {String} msgFx @hide
20850 * @cfg {String} validateOnBlur @hide
20854 Roo.HtmlEditorCore.white = [
20855 'area', 'br', 'img', 'input', 'hr', 'wbr',
20857 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
20858 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
20859 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
20860 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
20861 'table', 'ul', 'xmp',
20863 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
20866 'dir', 'menu', 'ol', 'ul', 'dl',
20872 Roo.HtmlEditorCore.black = [
20873 // 'embed', 'object', // enable - backend responsiblity to clean thiese
20875 'base', 'basefont', 'bgsound', 'blink', 'body',
20876 'frame', 'frameset', 'head', 'html', 'ilayer',
20877 'iframe', 'layer', 'link', 'meta', 'object',
20878 'script', 'style' ,'title', 'xml' // clean later..
20880 Roo.HtmlEditorCore.clean = [
20881 'script', 'style', 'title', 'xml'
20883 Roo.HtmlEditorCore.remove = [
20888 Roo.HtmlEditorCore.ablack = [
20892 Roo.HtmlEditorCore.aclean = [
20893 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
20897 Roo.HtmlEditorCore.pwhite= [
20898 'http', 'https', 'mailto'
20901 // white listed style attributes.
20902 Roo.HtmlEditorCore.cwhite= [
20903 // 'text-align', /// default is to allow most things..
20909 // black listed style attributes.
20910 Roo.HtmlEditorCore.cblack= [
20911 // 'font-size' -- this can be set by the project
20915 Roo.HtmlEditorCore.swapCodes =[
20934 * @class Roo.bootstrap.HtmlEditor
20935 * @extends Roo.bootstrap.TextArea
20936 * Bootstrap HtmlEditor class
20939 * Create a new HtmlEditor
20940 * @param {Object} config The config object
20943 Roo.bootstrap.HtmlEditor = function(config){
20944 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20945 if (!this.toolbars) {
20946 this.toolbars = [];
20948 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20951 * @event initialize
20952 * Fires when the editor is fully initialized (including the iframe)
20953 * @param {HtmlEditor} this
20958 * Fires when the editor is first receives the focus. Any insertion must wait
20959 * until after this event.
20960 * @param {HtmlEditor} this
20964 * @event beforesync
20965 * Fires before the textarea is updated with content from the editor iframe. Return false
20966 * to cancel the sync.
20967 * @param {HtmlEditor} this
20968 * @param {String} html
20972 * @event beforepush
20973 * Fires before the iframe editor is updated with content from the textarea. Return false
20974 * to cancel the push.
20975 * @param {HtmlEditor} this
20976 * @param {String} html
20981 * Fires when the textarea is updated with content from the editor iframe.
20982 * @param {HtmlEditor} this
20983 * @param {String} html
20988 * Fires when the iframe editor is updated with content from the textarea.
20989 * @param {HtmlEditor} this
20990 * @param {String} html
20994 * @event editmodechange
20995 * Fires when the editor switches edit modes
20996 * @param {HtmlEditor} this
20997 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20999 editmodechange: true,
21001 * @event editorevent
21002 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21003 * @param {HtmlEditor} this
21007 * @event firstfocus
21008 * Fires when on first focus - needed by toolbars..
21009 * @param {HtmlEditor} this
21014 * Auto save the htmlEditor value as a file into Events
21015 * @param {HtmlEditor} this
21019 * @event savedpreview
21020 * preview the saved version of htmlEditor
21021 * @param {HtmlEditor} this
21028 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
21032 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21037 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21042 * @cfg {Number} height (in pixels)
21046 * @cfg {Number} width (in pixels)
21051 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21054 stylesheets: false,
21059 // private properties
21060 validationEvent : false,
21062 initialized : false,
21065 onFocus : Roo.emptyFn,
21067 hideMode:'offsets',
21070 tbContainer : false,
21072 toolbarContainer :function() {
21073 return this.wrap.select('.x-html-editor-tb',true).first();
21077 * Protected method that will not generally be called directly. It
21078 * is called when the editor creates its toolbar. Override this method if you need to
21079 * add custom toolbar buttons.
21080 * @param {HtmlEditor} editor
21082 createToolbar : function(){
21084 Roo.log("create toolbars");
21086 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21087 this.toolbars[0].render(this.toolbarContainer());
21091 // if (!editor.toolbars || !editor.toolbars.length) {
21092 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21095 // for (var i =0 ; i < editor.toolbars.length;i++) {
21096 // editor.toolbars[i] = Roo.factory(
21097 // typeof(editor.toolbars[i]) == 'string' ?
21098 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
21099 // Roo.bootstrap.HtmlEditor);
21100 // editor.toolbars[i].init(editor);
21106 onRender : function(ct, position)
21108 // Roo.log("Call onRender: " + this.xtype);
21110 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21112 this.wrap = this.inputEl().wrap({
21113 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21116 this.editorcore.onRender(ct, position);
21118 if (this.resizable) {
21119 this.resizeEl = new Roo.Resizable(this.wrap, {
21123 minHeight : this.height,
21124 height: this.height,
21125 handles : this.resizable,
21128 resize : function(r, w, h) {
21129 _t.onResize(w,h); // -something
21135 this.createToolbar(this);
21138 if(!this.width && this.resizable){
21139 this.setSize(this.wrap.getSize());
21141 if (this.resizeEl) {
21142 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21143 // should trigger onReize..
21149 onResize : function(w, h)
21151 Roo.log('resize: ' +w + ',' + h );
21152 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21156 if(this.inputEl() ){
21157 if(typeof w == 'number'){
21158 var aw = w - this.wrap.getFrameWidth('lr');
21159 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21162 if(typeof h == 'number'){
21163 var tbh = -11; // fixme it needs to tool bar size!
21164 for (var i =0; i < this.toolbars.length;i++) {
21165 // fixme - ask toolbars for heights?
21166 tbh += this.toolbars[i].el.getHeight();
21167 //if (this.toolbars[i].footer) {
21168 // tbh += this.toolbars[i].footer.el.getHeight();
21176 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21177 ah -= 5; // knock a few pixes off for look..
21178 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21182 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21183 this.editorcore.onResize(ew,eh);
21188 * Toggles the editor between standard and source edit mode.
21189 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21191 toggleSourceEdit : function(sourceEditMode)
21193 this.editorcore.toggleSourceEdit(sourceEditMode);
21195 if(this.editorcore.sourceEditMode){
21196 Roo.log('editor - showing textarea');
21199 // Roo.log(this.syncValue());
21201 this.inputEl().removeClass(['hide', 'x-hidden']);
21202 this.inputEl().dom.removeAttribute('tabIndex');
21203 this.inputEl().focus();
21205 Roo.log('editor - hiding textarea');
21207 // Roo.log(this.pushValue());
21210 this.inputEl().addClass(['hide', 'x-hidden']);
21211 this.inputEl().dom.setAttribute('tabIndex', -1);
21212 //this.deferFocus();
21215 if(this.resizable){
21216 this.setSize(this.wrap.getSize());
21219 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21222 // private (for BoxComponent)
21223 adjustSize : Roo.BoxComponent.prototype.adjustSize,
21225 // private (for BoxComponent)
21226 getResizeEl : function(){
21230 // private (for BoxComponent)
21231 getPositionEl : function(){
21236 initEvents : function(){
21237 this.originalValue = this.getValue();
21241 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21244 // markInvalid : Roo.emptyFn,
21246 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21249 // clearInvalid : Roo.emptyFn,
21251 setValue : function(v){
21252 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21253 this.editorcore.pushValue();
21258 deferFocus : function(){
21259 this.focus.defer(10, this);
21263 focus : function(){
21264 this.editorcore.focus();
21270 onDestroy : function(){
21276 for (var i =0; i < this.toolbars.length;i++) {
21277 // fixme - ask toolbars for heights?
21278 this.toolbars[i].onDestroy();
21281 this.wrap.dom.innerHTML = '';
21282 this.wrap.remove();
21287 onFirstFocus : function(){
21288 //Roo.log("onFirstFocus");
21289 this.editorcore.onFirstFocus();
21290 for (var i =0; i < this.toolbars.length;i++) {
21291 this.toolbars[i].onFirstFocus();
21297 syncValue : function()
21299 this.editorcore.syncValue();
21302 pushValue : function()
21304 this.editorcore.pushValue();
21308 // hide stuff that is not compatible
21322 * @event specialkey
21326 * @cfg {String} fieldClass @hide
21329 * @cfg {String} focusClass @hide
21332 * @cfg {String} autoCreate @hide
21335 * @cfg {String} inputType @hide
21338 * @cfg {String} invalidClass @hide
21341 * @cfg {String} invalidText @hide
21344 * @cfg {String} msgFx @hide
21347 * @cfg {String} validateOnBlur @hide
21356 Roo.namespace('Roo.bootstrap.htmleditor');
21358 * @class Roo.bootstrap.HtmlEditorToolbar1
21363 new Roo.bootstrap.HtmlEditor({
21366 new Roo.bootstrap.HtmlEditorToolbar1({
21367 disable : { fonts: 1 , format: 1, ..., ... , ...],
21373 * @cfg {Object} disable List of elements to disable..
21374 * @cfg {Array} btns List of additional buttons.
21378 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21381 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21384 Roo.apply(this, config);
21386 // default disabled, based on 'good practice'..
21387 this.disable = this.disable || {};
21388 Roo.applyIf(this.disable, {
21391 specialElements : true
21393 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21395 this.editor = config.editor;
21396 this.editorcore = config.editor.editorcore;
21398 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21400 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21401 // dont call parent... till later.
21403 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
21408 editorcore : false,
21413 "h1","h2","h3","h4","h5","h6",
21415 "abbr", "acronym", "address", "cite", "samp", "var",
21419 onRender : function(ct, position)
21421 // Roo.log("Call onRender: " + this.xtype);
21423 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21425 this.el.dom.style.marginBottom = '0';
21427 var editorcore = this.editorcore;
21428 var editor= this.editor;
21431 var btn = function(id,cmd , toggle, handler){
21433 var event = toggle ? 'toggle' : 'click';
21438 xns: Roo.bootstrap,
21441 enableToggle:toggle !== false,
21443 pressed : toggle ? false : null,
21446 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21447 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21456 xns: Roo.bootstrap,
21457 glyphicon : 'font',
21461 xns: Roo.bootstrap,
21465 Roo.each(this.formats, function(f) {
21466 style.menu.items.push({
21468 xns: Roo.bootstrap,
21469 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21474 editorcore.insertTag(this.tagname);
21481 children.push(style);
21484 btn('bold',false,true);
21485 btn('italic',false,true);
21486 btn('align-left', 'justifyleft',true);
21487 btn('align-center', 'justifycenter',true);
21488 btn('align-right' , 'justifyright',true);
21489 btn('link', false, false, function(btn) {
21490 //Roo.log("create link?");
21491 var url = prompt(this.createLinkText, this.defaultLinkValue);
21492 if(url && url != 'http:/'+'/'){
21493 this.editorcore.relayCmd('createlink', url);
21496 btn('list','insertunorderedlist',true);
21497 btn('pencil', false,true, function(btn){
21500 this.toggleSourceEdit(btn.pressed);
21506 xns: Roo.bootstrap,
21511 xns: Roo.bootstrap,
21516 cog.menu.items.push({
21518 xns: Roo.bootstrap,
21519 html : Clean styles,
21524 editorcore.insertTag(this.tagname);
21533 this.xtype = 'NavSimplebar';
21535 for(var i=0;i< children.length;i++) {
21537 this.buttons.add(this.addxtypeChild(children[i]));
21541 editor.on('editorevent', this.updateToolbar, this);
21543 onBtnClick : function(id)
21545 this.editorcore.relayCmd(id);
21546 this.editorcore.focus();
21550 * Protected method that will not generally be called directly. It triggers
21551 * a toolbar update by reading the markup state of the current selection in the editor.
21553 updateToolbar: function(){
21555 if(!this.editorcore.activated){
21556 this.editor.onFirstFocus(); // is this neeed?
21560 var btns = this.buttons;
21561 var doc = this.editorcore.doc;
21562 btns.get('bold').setActive(doc.queryCommandState('bold'));
21563 btns.get('italic').setActive(doc.queryCommandState('italic'));
21564 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21566 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21567 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21568 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21570 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21571 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21574 var ans = this.editorcore.getAllAncestors();
21575 if (this.formatCombo) {
21578 var store = this.formatCombo.store;
21579 this.formatCombo.setValue("");
21580 for (var i =0; i < ans.length;i++) {
21581 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21583 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21591 // hides menus... - so this cant be on a menu...
21592 Roo.bootstrap.MenuMgr.hideAll();
21594 Roo.bootstrap.MenuMgr.hideAll();
21595 //this.editorsyncValue();
21597 onFirstFocus: function() {
21598 this.buttons.each(function(item){
21602 toggleSourceEdit : function(sourceEditMode){
21605 if(sourceEditMode){
21606 Roo.log("disabling buttons");
21607 this.buttons.each( function(item){
21608 if(item.cmd != 'pencil'){
21614 Roo.log("enabling buttons");
21615 if(this.editorcore.initialized){
21616 this.buttons.each( function(item){
21622 Roo.log("calling toggole on editor");
21623 // tell the editor that it's been pressed..
21624 this.editor.toggleSourceEdit(sourceEditMode);
21634 * @class Roo.bootstrap.Table.AbstractSelectionModel
21635 * @extends Roo.util.Observable
21636 * Abstract base class for grid SelectionModels. It provides the interface that should be
21637 * implemented by descendant classes. This class should not be directly instantiated.
21640 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21641 this.locked = false;
21642 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21646 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21647 /** @ignore Called by the grid automatically. Do not call directly. */
21648 init : function(grid){
21654 * Locks the selections.
21657 this.locked = true;
21661 * Unlocks the selections.
21663 unlock : function(){
21664 this.locked = false;
21668 * Returns true if the selections are locked.
21669 * @return {Boolean}
21671 isLocked : function(){
21672 return this.locked;
21676 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21677 * @class Roo.bootstrap.Table.RowSelectionModel
21678 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21679 * It supports multiple selections and keyboard selection/navigation.
21681 * @param {Object} config
21684 Roo.bootstrap.Table.RowSelectionModel = function(config){
21685 Roo.apply(this, config);
21686 this.selections = new Roo.util.MixedCollection(false, function(o){
21691 this.lastActive = false;
21695 * @event selectionchange
21696 * Fires when the selection changes
21697 * @param {SelectionModel} this
21699 "selectionchange" : true,
21701 * @event afterselectionchange
21702 * Fires after the selection changes (eg. by key press or clicking)
21703 * @param {SelectionModel} this
21705 "afterselectionchange" : true,
21707 * @event beforerowselect
21708 * Fires when a row is selected being selected, return false to cancel.
21709 * @param {SelectionModel} this
21710 * @param {Number} rowIndex The selected index
21711 * @param {Boolean} keepExisting False if other selections will be cleared
21713 "beforerowselect" : true,
21716 * Fires when a row is selected.
21717 * @param {SelectionModel} this
21718 * @param {Number} rowIndex The selected index
21719 * @param {Roo.data.Record} r The record
21721 "rowselect" : true,
21723 * @event rowdeselect
21724 * Fires when a row is deselected.
21725 * @param {SelectionModel} this
21726 * @param {Number} rowIndex The selected index
21728 "rowdeselect" : true
21730 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21731 this.locked = false;
21734 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21736 * @cfg {Boolean} singleSelect
21737 * True to allow selection of only one row at a time (defaults to false)
21739 singleSelect : false,
21742 initEvents : function(){
21744 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21745 this.grid.on("mousedown", this.handleMouseDown, this);
21746 }else{ // allow click to work like normal
21747 this.grid.on("rowclick", this.handleDragableRowClick, this);
21750 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21751 "up" : function(e){
21753 this.selectPrevious(e.shiftKey);
21754 }else if(this.last !== false && this.lastActive !== false){
21755 var last = this.last;
21756 this.selectRange(this.last, this.lastActive-1);
21757 this.grid.getView().focusRow(this.lastActive);
21758 if(last !== false){
21762 this.selectFirstRow();
21764 this.fireEvent("afterselectionchange", this);
21766 "down" : function(e){
21768 this.selectNext(e.shiftKey);
21769 }else if(this.last !== false && this.lastActive !== false){
21770 var last = this.last;
21771 this.selectRange(this.last, this.lastActive+1);
21772 this.grid.getView().focusRow(this.lastActive);
21773 if(last !== false){
21777 this.selectFirstRow();
21779 this.fireEvent("afterselectionchange", this);
21784 var view = this.grid.view;
21785 view.on("refresh", this.onRefresh, this);
21786 view.on("rowupdated", this.onRowUpdated, this);
21787 view.on("rowremoved", this.onRemove, this);
21791 onRefresh : function(){
21792 var ds = this.grid.dataSource, i, v = this.grid.view;
21793 var s = this.selections;
21794 s.each(function(r){
21795 if((i = ds.indexOfId(r.id)) != -1){
21804 onRemove : function(v, index, r){
21805 this.selections.remove(r);
21809 onRowUpdated : function(v, index, r){
21810 if(this.isSelected(r)){
21811 v.onRowSelect(index);
21817 * @param {Array} records The records to select
21818 * @param {Boolean} keepExisting (optional) True to keep existing selections
21820 selectRecords : function(records, keepExisting){
21822 this.clearSelections();
21824 var ds = this.grid.dataSource;
21825 for(var i = 0, len = records.length; i < len; i++){
21826 this.selectRow(ds.indexOf(records[i]), true);
21831 * Gets the number of selected rows.
21834 getCount : function(){
21835 return this.selections.length;
21839 * Selects the first row in the grid.
21841 selectFirstRow : function(){
21846 * Select the last row.
21847 * @param {Boolean} keepExisting (optional) True to keep existing selections
21849 selectLastRow : function(keepExisting){
21850 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21854 * Selects the row immediately following the last selected row.
21855 * @param {Boolean} keepExisting (optional) True to keep existing selections
21857 selectNext : function(keepExisting){
21858 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21859 this.selectRow(this.last+1, keepExisting);
21860 this.grid.getView().focusRow(this.last);
21865 * Selects the row that precedes the last selected row.
21866 * @param {Boolean} keepExisting (optional) True to keep existing selections
21868 selectPrevious : function(keepExisting){
21870 this.selectRow(this.last-1, keepExisting);
21871 this.grid.getView().focusRow(this.last);
21876 * Returns the selected records
21877 * @return {Array} Array of selected records
21879 getSelections : function(){
21880 return [].concat(this.selections.items);
21884 * Returns the first selected record.
21887 getSelected : function(){
21888 return this.selections.itemAt(0);
21893 * Clears all selections.
21895 clearSelections : function(fast){
21900 var ds = this.grid.dataSource;
21901 var s = this.selections;
21902 s.each(function(r){
21903 this.deselectRow(ds.indexOfId(r.id));
21907 this.selections.clear();
21914 * Selects all rows.
21916 selectAll : function(){
21920 this.selections.clear();
21921 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21922 this.selectRow(i, true);
21927 * Returns True if there is a selection.
21928 * @return {Boolean}
21930 hasSelection : function(){
21931 return this.selections.length > 0;
21935 * Returns True if the specified row is selected.
21936 * @param {Number/Record} record The record or index of the record to check
21937 * @return {Boolean}
21939 isSelected : function(index){
21940 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21941 return (r && this.selections.key(r.id) ? true : false);
21945 * Returns True if the specified record id is selected.
21946 * @param {String} id The id of record to check
21947 * @return {Boolean}
21949 isIdSelected : function(id){
21950 return (this.selections.key(id) ? true : false);
21954 handleMouseDown : function(e, t){
21955 var view = this.grid.getView(), rowIndex;
21956 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21959 if(e.shiftKey && this.last !== false){
21960 var last = this.last;
21961 this.selectRange(last, rowIndex, e.ctrlKey);
21962 this.last = last; // reset the last
21963 view.focusRow(rowIndex);
21965 var isSelected = this.isSelected(rowIndex);
21966 if(e.button !== 0 && isSelected){
21967 view.focusRow(rowIndex);
21968 }else if(e.ctrlKey && isSelected){
21969 this.deselectRow(rowIndex);
21970 }else if(!isSelected){
21971 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21972 view.focusRow(rowIndex);
21975 this.fireEvent("afterselectionchange", this);
21978 handleDragableRowClick : function(grid, rowIndex, e)
21980 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21981 this.selectRow(rowIndex, false);
21982 grid.view.focusRow(rowIndex);
21983 this.fireEvent("afterselectionchange", this);
21988 * Selects multiple rows.
21989 * @param {Array} rows Array of the indexes of the row to select
21990 * @param {Boolean} keepExisting (optional) True to keep existing selections
21992 selectRows : function(rows, keepExisting){
21994 this.clearSelections();
21996 for(var i = 0, len = rows.length; i < len; i++){
21997 this.selectRow(rows[i], true);
22002 * Selects a range of rows. All rows in between startRow and endRow are also selected.
22003 * @param {Number} startRow The index of the first row in the range
22004 * @param {Number} endRow The index of the last row in the range
22005 * @param {Boolean} keepExisting (optional) True to retain existing selections
22007 selectRange : function(startRow, endRow, keepExisting){
22012 this.clearSelections();
22014 if(startRow <= endRow){
22015 for(var i = startRow; i <= endRow; i++){
22016 this.selectRow(i, true);
22019 for(var i = startRow; i >= endRow; i--){
22020 this.selectRow(i, true);
22026 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22027 * @param {Number} startRow The index of the first row in the range
22028 * @param {Number} endRow The index of the last row in the range
22030 deselectRange : function(startRow, endRow, preventViewNotify){
22034 for(var i = startRow; i <= endRow; i++){
22035 this.deselectRow(i, preventViewNotify);
22041 * @param {Number} row The index of the row to select
22042 * @param {Boolean} keepExisting (optional) True to keep existing selections
22044 selectRow : function(index, keepExisting, preventViewNotify){
22045 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22048 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22049 if(!keepExisting || this.singleSelect){
22050 this.clearSelections();
22052 var r = this.grid.dataSource.getAt(index);
22053 this.selections.add(r);
22054 this.last = this.lastActive = index;
22055 if(!preventViewNotify){
22056 this.grid.getView().onRowSelect(index);
22058 this.fireEvent("rowselect", this, index, r);
22059 this.fireEvent("selectionchange", this);
22065 * @param {Number} row The index of the row to deselect
22067 deselectRow : function(index, preventViewNotify){
22071 if(this.last == index){
22074 if(this.lastActive == index){
22075 this.lastActive = false;
22077 var r = this.grid.dataSource.getAt(index);
22078 this.selections.remove(r);
22079 if(!preventViewNotify){
22080 this.grid.getView().onRowDeselect(index);
22082 this.fireEvent("rowdeselect", this, index);
22083 this.fireEvent("selectionchange", this);
22087 restoreLast : function(){
22089 this.last = this._last;
22094 acceptsNav : function(row, col, cm){
22095 return !cm.isHidden(col) && cm.isCellEditable(col, row);
22099 onEditorKey : function(field, e){
22100 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22105 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22107 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22109 }else if(k == e.ENTER && !e.ctrlKey){
22113 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22115 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22117 }else if(k == e.ESC){
22121 g.startEditing(newCell[0], newCell[1]);
22126 * Ext JS Library 1.1.1
22127 * Copyright(c) 2006-2007, Ext JS, LLC.
22129 * Originally Released Under LGPL - original licence link has changed is not relivant.
22132 * <script type="text/javascript">
22136 * @class Roo.bootstrap.PagingToolbar
22137 * @extends Roo.bootstrap.NavSimplebar
22138 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22140 * Create a new PagingToolbar
22141 * @param {Object} config The config object
22142 * @param {Roo.data.Store} store
22144 Roo.bootstrap.PagingToolbar = function(config)
22146 // old args format still supported... - xtype is prefered..
22147 // created from xtype...
22149 this.ds = config.dataSource;
22151 if (config.store && !this.ds) {
22152 this.store= Roo.factory(config.store, Roo.data);
22153 this.ds = this.store;
22154 this.ds.xmodule = this.xmodule || false;
22157 this.toolbarItems = [];
22158 if (config.items) {
22159 this.toolbarItems = config.items;
22162 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22167 this.bind(this.ds);
22170 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22174 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22176 * @cfg {Roo.data.Store} dataSource
22177 * The underlying data store providing the paged data
22180 * @cfg {String/HTMLElement/Element} container
22181 * container The id or element that will contain the toolbar
22184 * @cfg {Boolean} displayInfo
22185 * True to display the displayMsg (defaults to false)
22188 * @cfg {Number} pageSize
22189 * The number of records to display per page (defaults to 20)
22193 * @cfg {String} displayMsg
22194 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22196 displayMsg : 'Displaying {0} - {1} of {2}',
22198 * @cfg {String} emptyMsg
22199 * The message to display when no records are found (defaults to "No data to display")
22201 emptyMsg : 'No data to display',
22203 * Customizable piece of the default paging text (defaults to "Page")
22206 beforePageText : "Page",
22208 * Customizable piece of the default paging text (defaults to "of %0")
22211 afterPageText : "of {0}",
22213 * Customizable piece of the default paging text (defaults to "First Page")
22216 firstText : "First Page",
22218 * Customizable piece of the default paging text (defaults to "Previous Page")
22221 prevText : "Previous Page",
22223 * Customizable piece of the default paging text (defaults to "Next Page")
22226 nextText : "Next Page",
22228 * Customizable piece of the default paging text (defaults to "Last Page")
22231 lastText : "Last Page",
22233 * Customizable piece of the default paging text (defaults to "Refresh")
22236 refreshText : "Refresh",
22240 onRender : function(ct, position)
22242 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22243 this.navgroup.parentId = this.id;
22244 this.navgroup.onRender(this.el, null);
22245 // add the buttons to the navgroup
22247 if(this.displayInfo){
22248 Roo.log(this.el.select('ul.navbar-nav',true).first());
22249 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22250 this.displayEl = this.el.select('.x-paging-info', true).first();
22251 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22252 // this.displayEl = navel.el.select('span',true).first();
22258 Roo.each(_this.buttons, function(e){ // this might need to use render????
22259 Roo.factory(e).onRender(_this.el, null);
22263 Roo.each(_this.toolbarItems, function(e) {
22264 _this.navgroup.addItem(e);
22268 this.first = this.navgroup.addItem({
22269 tooltip: this.firstText,
22271 icon : 'fa fa-backward',
22273 preventDefault: true,
22274 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22277 this.prev = this.navgroup.addItem({
22278 tooltip: this.prevText,
22280 icon : 'fa fa-step-backward',
22282 preventDefault: true,
22283 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
22285 //this.addSeparator();
22288 var field = this.navgroup.addItem( {
22290 cls : 'x-paging-position',
22292 html : this.beforePageText +
22293 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22294 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
22297 this.field = field.el.select('input', true).first();
22298 this.field.on("keydown", this.onPagingKeydown, this);
22299 this.field.on("focus", function(){this.dom.select();});
22302 this.afterTextEl = field.el.select('.x-paging-after',true).first();
22303 //this.field.setHeight(18);
22304 //this.addSeparator();
22305 this.next = this.navgroup.addItem({
22306 tooltip: this.nextText,
22308 html : ' <i class="fa fa-step-forward">',
22310 preventDefault: true,
22311 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
22313 this.last = this.navgroup.addItem({
22314 tooltip: this.lastText,
22315 icon : 'fa fa-forward',
22318 preventDefault: true,
22319 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
22321 //this.addSeparator();
22322 this.loading = this.navgroup.addItem({
22323 tooltip: this.refreshText,
22324 icon: 'fa fa-refresh',
22325 preventDefault: true,
22326 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22332 updateInfo : function(){
22333 if(this.displayEl){
22334 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22335 var msg = count == 0 ?
22339 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
22341 this.displayEl.update(msg);
22346 onLoad : function(ds, r, o){
22347 this.cursor = o.params ? o.params.start : 0;
22348 var d = this.getPageData(),
22352 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22353 this.field.dom.value = ap;
22354 this.first.setDisabled(ap == 1);
22355 this.prev.setDisabled(ap == 1);
22356 this.next.setDisabled(ap == ps);
22357 this.last.setDisabled(ap == ps);
22358 this.loading.enable();
22363 getPageData : function(){
22364 var total = this.ds.getTotalCount();
22367 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22368 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22373 onLoadError : function(){
22374 this.loading.enable();
22378 onPagingKeydown : function(e){
22379 var k = e.getKey();
22380 var d = this.getPageData();
22382 var v = this.field.dom.value, pageNum;
22383 if(!v || isNaN(pageNum = parseInt(v, 10))){
22384 this.field.dom.value = d.activePage;
22387 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22388 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22391 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))
22393 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22394 this.field.dom.value = pageNum;
22395 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22398 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22400 var v = this.field.dom.value, pageNum;
22401 var increment = (e.shiftKey) ? 10 : 1;
22402 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22405 if(!v || isNaN(pageNum = parseInt(v, 10))) {
22406 this.field.dom.value = d.activePage;
22409 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22411 this.field.dom.value = parseInt(v, 10) + increment;
22412 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22413 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22420 beforeLoad : function(){
22422 this.loading.disable();
22427 onClick : function(which){
22436 ds.load({params:{start: 0, limit: this.pageSize}});
22439 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22442 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22445 var total = ds.getTotalCount();
22446 var extra = total % this.pageSize;
22447 var lastStart = extra ? (total - extra) : total-this.pageSize;
22448 ds.load({params:{start: lastStart, limit: this.pageSize}});
22451 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22457 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22458 * @param {Roo.data.Store} store The data store to unbind
22460 unbind : function(ds){
22461 ds.un("beforeload", this.beforeLoad, this);
22462 ds.un("load", this.onLoad, this);
22463 ds.un("loadexception", this.onLoadError, this);
22464 ds.un("remove", this.updateInfo, this);
22465 ds.un("add", this.updateInfo, this);
22466 this.ds = undefined;
22470 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22471 * @param {Roo.data.Store} store The data store to bind
22473 bind : function(ds){
22474 ds.on("beforeload", this.beforeLoad, this);
22475 ds.on("load", this.onLoad, this);
22476 ds.on("loadexception", this.onLoadError, this);
22477 ds.on("remove", this.updateInfo, this);
22478 ds.on("add", this.updateInfo, this);
22489 * @class Roo.bootstrap.MessageBar
22490 * @extends Roo.bootstrap.Component
22491 * Bootstrap MessageBar class
22492 * @cfg {String} html contents of the MessageBar
22493 * @cfg {String} weight (info | success | warning | danger) default info
22494 * @cfg {String} beforeClass insert the bar before the given class
22495 * @cfg {Boolean} closable (true | false) default false
22496 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22499 * Create a new Element
22500 * @param {Object} config The config object
22503 Roo.bootstrap.MessageBar = function(config){
22504 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22507 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22513 beforeClass: 'bootstrap-sticky-wrap',
22515 getAutoCreate : function(){
22519 cls: 'alert alert-dismissable alert-' + this.weight,
22524 html: this.html || ''
22530 cfg.cls += ' alert-messages-fixed';
22544 onRender : function(ct, position)
22546 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22549 var cfg = Roo.apply({}, this.getAutoCreate());
22553 cfg.cls += ' ' + this.cls;
22556 cfg.style = this.style;
22558 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22560 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22563 this.el.select('>button.close').on('click', this.hide, this);
22569 if (!this.rendered) {
22575 this.fireEvent('show', this);
22581 if (!this.rendered) {
22587 this.fireEvent('hide', this);
22590 update : function()
22592 // var e = this.el.dom.firstChild;
22594 // if(this.closable){
22595 // e = e.nextSibling;
22598 // e.data = this.html || '';
22600 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22616 * @class Roo.bootstrap.Graph
22617 * @extends Roo.bootstrap.Component
22618 * Bootstrap Graph class
22622 @cfg {String} graphtype bar | vbar | pie
22623 @cfg {number} g_x coodinator | centre x (pie)
22624 @cfg {number} g_y coodinator | centre y (pie)
22625 @cfg {number} g_r radius (pie)
22626 @cfg {number} g_height height of the chart (respected by all elements in the set)
22627 @cfg {number} g_width width of the chart (respected by all elements in the set)
22628 @cfg {Object} title The title of the chart
22631 -opts (object) options for the chart
22633 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22634 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22636 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.
22637 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22639 o stretch (boolean)
22641 -opts (object) options for the pie
22644 o startAngle (number)
22645 o endAngle (number)
22649 * Create a new Input
22650 * @param {Object} config The config object
22653 Roo.bootstrap.Graph = function(config){
22654 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22660 * The img click event for the img.
22661 * @param {Roo.EventObject} e
22667 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22678 //g_colors: this.colors,
22685 getAutoCreate : function(){
22696 onRender : function(ct,position){
22697 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22698 this.raphael = Raphael(this.el.dom);
22700 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22701 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22702 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22703 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22705 r.text(160, 10, "Single Series Chart").attr(txtattr);
22706 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22707 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22708 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22710 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22711 r.barchart(330, 10, 300, 220, data1);
22712 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22713 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22716 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22717 // r.barchart(30, 30, 560, 250, xdata, {
22718 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22719 // axis : "0 0 1 1",
22720 // axisxlabels : xdata
22721 // //yvalues : cols,
22724 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22726 // this.load(null,xdata,{
22727 // axis : "0 0 1 1",
22728 // axisxlabels : xdata
22733 load : function(graphtype,xdata,opts){
22734 this.raphael.clear();
22736 graphtype = this.graphtype;
22741 var r = this.raphael,
22742 fin = function () {
22743 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22745 fout = function () {
22746 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22748 pfin = function() {
22749 this.sector.stop();
22750 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22753 this.label[0].stop();
22754 this.label[0].attr({ r: 7.5 });
22755 this.label[1].attr({ "font-weight": 800 });
22758 pfout = function() {
22759 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22762 this.label[0].animate({ r: 5 }, 500, "bounce");
22763 this.label[1].attr({ "font-weight": 400 });
22769 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22772 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22775 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
22776 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22778 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22785 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22790 setTitle: function(o)
22795 initEvents: function() {
22798 this.el.on('click', this.onClick, this);
22802 onClick : function(e)
22804 Roo.log('img onclick');
22805 this.fireEvent('click', this, e);
22817 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22820 * @class Roo.bootstrap.dash.NumberBox
22821 * @extends Roo.bootstrap.Component
22822 * Bootstrap NumberBox class
22823 * @cfg {String} headline Box headline
22824 * @cfg {String} content Box content
22825 * @cfg {String} icon Box icon
22826 * @cfg {String} footer Footer text
22827 * @cfg {String} fhref Footer href
22830 * Create a new NumberBox
22831 * @param {Object} config The config object
22835 Roo.bootstrap.dash.NumberBox = function(config){
22836 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22840 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
22849 getAutoCreate : function(){
22853 cls : 'small-box ',
22861 cls : 'roo-headline',
22862 html : this.headline
22866 cls : 'roo-content',
22867 html : this.content
22881 cls : 'ion ' + this.icon
22890 cls : 'small-box-footer',
22891 href : this.fhref || '#',
22895 cfg.cn.push(footer);
22902 onRender : function(ct,position){
22903 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22910 setHeadline: function (value)
22912 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22915 setFooter: function (value, href)
22917 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22920 this.el.select('a.small-box-footer',true).first().attr('href', href);
22925 setContent: function (value)
22927 this.el.select('.roo-content',true).first().dom.innerHTML = value;
22930 initEvents: function()
22944 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22947 * @class Roo.bootstrap.dash.TabBox
22948 * @extends Roo.bootstrap.Component
22949 * Bootstrap TabBox class
22950 * @cfg {String} title Title of the TabBox
22951 * @cfg {String} icon Icon of the TabBox
22952 * @cfg {Boolean} showtabs (true|false) show the tabs default true
22953 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22956 * Create a new TabBox
22957 * @param {Object} config The config object
22961 Roo.bootstrap.dash.TabBox = function(config){
22962 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22967 * When a pane is added
22968 * @param {Roo.bootstrap.dash.TabPane} pane
22972 * @event activatepane
22973 * When a pane is activated
22974 * @param {Roo.bootstrap.dash.TabPane} pane
22976 "activatepane" : true
22984 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
22989 tabScrollable : false,
22991 getChildContainer : function()
22993 return this.el.select('.tab-content', true).first();
22996 getAutoCreate : function(){
23000 cls: 'pull-left header',
23008 cls: 'fa ' + this.icon
23014 cls: 'nav nav-tabs pull-right',
23020 if(this.tabScrollable){
23027 cls: 'nav nav-tabs pull-right',
23038 cls: 'nav-tabs-custom',
23043 cls: 'tab-content no-padding',
23051 initEvents : function()
23053 //Roo.log('add add pane handler');
23054 this.on('addpane', this.onAddPane, this);
23057 * Updates the box title
23058 * @param {String} html to set the title to.
23060 setTitle : function(value)
23062 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23064 onAddPane : function(pane)
23066 this.panes.push(pane);
23067 //Roo.log('addpane');
23069 // tabs are rendere left to right..
23070 if(!this.showtabs){
23074 var ctr = this.el.select('.nav-tabs', true).first();
23077 var existing = ctr.select('.nav-tab',true);
23078 var qty = existing.getCount();;
23081 var tab = ctr.createChild({
23083 cls : 'nav-tab' + (qty ? '' : ' active'),
23091 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23094 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23096 pane.el.addClass('active');
23101 onTabClick : function(ev,un,ob,pane)
23103 //Roo.log('tab - prev default');
23104 ev.preventDefault();
23107 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23108 pane.tab.addClass('active');
23109 //Roo.log(pane.title);
23110 this.getChildContainer().select('.tab-pane',true).removeClass('active');
23111 // technically we should have a deactivate event.. but maybe add later.
23112 // and it should not de-activate the selected tab...
23113 this.fireEvent('activatepane', pane);
23114 pane.el.addClass('active');
23115 pane.fireEvent('activate');
23120 getActivePane : function()
23123 Roo.each(this.panes, function(p) {
23124 if(p.el.hasClass('active')){
23145 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23147 * @class Roo.bootstrap.TabPane
23148 * @extends Roo.bootstrap.Component
23149 * Bootstrap TabPane class
23150 * @cfg {Boolean} active (false | true) Default false
23151 * @cfg {String} title title of panel
23155 * Create a new TabPane
23156 * @param {Object} config The config object
23159 Roo.bootstrap.dash.TabPane = function(config){
23160 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23166 * When a pane is activated
23167 * @param {Roo.bootstrap.dash.TabPane} pane
23174 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
23179 // the tabBox that this is attached to.
23182 getAutoCreate : function()
23190 cfg.cls += ' active';
23195 initEvents : function()
23197 //Roo.log('trigger add pane handler');
23198 this.parent().fireEvent('addpane', this)
23202 * Updates the tab title
23203 * @param {String} html to set the title to.
23205 setTitle: function(str)
23211 this.tab.select('a', true).first().dom.innerHTML = str;
23228 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23231 * @class Roo.bootstrap.menu.Menu
23232 * @extends Roo.bootstrap.Component
23233 * Bootstrap Menu class - container for Menu
23234 * @cfg {String} html Text of the menu
23235 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23236 * @cfg {String} icon Font awesome icon
23237 * @cfg {String} pos Menu align to (top | bottom) default bottom
23241 * Create a new Menu
23242 * @param {Object} config The config object
23246 Roo.bootstrap.menu.Menu = function(config){
23247 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23251 * @event beforeshow
23252 * Fires before this menu is displayed
23253 * @param {Roo.bootstrap.menu.Menu} this
23257 * @event beforehide
23258 * Fires before this menu is hidden
23259 * @param {Roo.bootstrap.menu.Menu} this
23264 * Fires after this menu is displayed
23265 * @param {Roo.bootstrap.menu.Menu} this
23270 * Fires after this menu is hidden
23271 * @param {Roo.bootstrap.menu.Menu} this
23276 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23277 * @param {Roo.bootstrap.menu.Menu} this
23278 * @param {Roo.EventObject} e
23285 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
23289 weight : 'default',
23294 getChildContainer : function() {
23295 if(this.isSubMenu){
23299 return this.el.select('ul.dropdown-menu', true).first();
23302 getAutoCreate : function()
23307 cls : 'roo-menu-text',
23315 cls : 'fa ' + this.icon
23326 cls : 'dropdown-button btn btn-' + this.weight,
23331 cls : 'dropdown-toggle btn btn-' + this.weight,
23341 cls : 'dropdown-menu'
23347 if(this.pos == 'top'){
23348 cfg.cls += ' dropup';
23351 if(this.isSubMenu){
23354 cls : 'dropdown-menu'
23361 onRender : function(ct, position)
23363 this.isSubMenu = ct.hasClass('dropdown-submenu');
23365 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23368 initEvents : function()
23370 if(this.isSubMenu){
23374 this.hidden = true;
23376 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23377 this.triggerEl.on('click', this.onTriggerPress, this);
23379 this.buttonEl = this.el.select('button.dropdown-button', true).first();
23380 this.buttonEl.on('click', this.onClick, this);
23386 if(this.isSubMenu){
23390 return this.el.select('ul.dropdown-menu', true).first();
23393 onClick : function(e)
23395 this.fireEvent("click", this, e);
23398 onTriggerPress : function(e)
23400 if (this.isVisible()) {
23407 isVisible : function(){
23408 return !this.hidden;
23413 this.fireEvent("beforeshow", this);
23415 this.hidden = false;
23416 this.el.addClass('open');
23418 Roo.get(document).on("mouseup", this.onMouseUp, this);
23420 this.fireEvent("show", this);
23427 this.fireEvent("beforehide", this);
23429 this.hidden = true;
23430 this.el.removeClass('open');
23432 Roo.get(document).un("mouseup", this.onMouseUp);
23434 this.fireEvent("hide", this);
23437 onMouseUp : function()
23451 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23454 * @class Roo.bootstrap.menu.Item
23455 * @extends Roo.bootstrap.Component
23456 * Bootstrap MenuItem class
23457 * @cfg {Boolean} submenu (true | false) default false
23458 * @cfg {String} html text of the item
23459 * @cfg {String} href the link
23460 * @cfg {Boolean} disable (true | false) default false
23461 * @cfg {Boolean} preventDefault (true | false) default true
23462 * @cfg {String} icon Font awesome icon
23463 * @cfg {String} pos Submenu align to (left | right) default right
23467 * Create a new Item
23468 * @param {Object} config The config object
23472 Roo.bootstrap.menu.Item = function(config){
23473 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23477 * Fires when the mouse is hovering over this menu
23478 * @param {Roo.bootstrap.menu.Item} this
23479 * @param {Roo.EventObject} e
23484 * Fires when the mouse exits this menu
23485 * @param {Roo.bootstrap.menu.Item} this
23486 * @param {Roo.EventObject} e
23492 * The raw click event for the entire grid.
23493 * @param {Roo.EventObject} e
23499 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23504 preventDefault: true,
23509 getAutoCreate : function()
23514 cls : 'roo-menu-item-text',
23522 cls : 'fa ' + this.icon
23531 href : this.href || '#',
23538 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23542 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23544 if(this.pos == 'left'){
23545 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23552 initEvents : function()
23554 this.el.on('mouseover', this.onMouseOver, this);
23555 this.el.on('mouseout', this.onMouseOut, this);
23557 this.el.select('a', true).first().on('click', this.onClick, this);
23561 onClick : function(e)
23563 if(this.preventDefault){
23564 e.preventDefault();
23567 this.fireEvent("click", this, e);
23570 onMouseOver : function(e)
23572 if(this.submenu && this.pos == 'left'){
23573 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23576 this.fireEvent("mouseover", this, e);
23579 onMouseOut : function(e)
23581 this.fireEvent("mouseout", this, e);
23593 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23596 * @class Roo.bootstrap.menu.Separator
23597 * @extends Roo.bootstrap.Component
23598 * Bootstrap Separator class
23601 * Create a new Separator
23602 * @param {Object} config The config object
23606 Roo.bootstrap.menu.Separator = function(config){
23607 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23610 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23612 getAutoCreate : function(){
23633 * @class Roo.bootstrap.Tooltip
23634 * Bootstrap Tooltip class
23635 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23636 * to determine which dom element triggers the tooltip.
23638 * It needs to add support for additional attributes like tooltip-position
23641 * Create a new Toolti
23642 * @param {Object} config The config object
23645 Roo.bootstrap.Tooltip = function(config){
23646 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23649 Roo.apply(Roo.bootstrap.Tooltip, {
23651 * @function init initialize tooltip monitoring.
23655 currentTip : false,
23656 currentRegion : false,
23662 Roo.get(document).on('mouseover', this.enter ,this);
23663 Roo.get(document).on('mouseout', this.leave, this);
23666 this.currentTip = new Roo.bootstrap.Tooltip();
23669 enter : function(ev)
23671 var dom = ev.getTarget();
23673 //Roo.log(['enter',dom]);
23674 var el = Roo.fly(dom);
23675 if (this.currentEl) {
23677 //Roo.log(this.currentEl);
23678 //Roo.log(this.currentEl.contains(dom));
23679 if (this.currentEl == el) {
23682 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23688 if (this.currentTip.el) {
23689 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23694 // you can not look for children, as if el is the body.. then everythign is the child..
23695 if (!el.attr('tooltip')) { //
23696 if (!el.select("[tooltip]").elements.length) {
23699 // is the mouse over this child...?
23700 bindEl = el.select("[tooltip]").first();
23701 var xy = ev.getXY();
23702 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23703 //Roo.log("not in region.");
23706 //Roo.log("child element over..");
23709 this.currentEl = bindEl;
23710 this.currentTip.bind(bindEl);
23711 this.currentRegion = Roo.lib.Region.getRegion(dom);
23712 this.currentTip.enter();
23715 leave : function(ev)
23717 var dom = ev.getTarget();
23718 //Roo.log(['leave',dom]);
23719 if (!this.currentEl) {
23724 if (dom != this.currentEl.dom) {
23727 var xy = ev.getXY();
23728 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23731 // only activate leave if mouse cursor is outside... bounding box..
23736 if (this.currentTip) {
23737 this.currentTip.leave();
23739 //Roo.log('clear currentEl');
23740 this.currentEl = false;
23745 'left' : ['r-l', [-2,0], 'right'],
23746 'right' : ['l-r', [2,0], 'left'],
23747 'bottom' : ['t-b', [0,2], 'top'],
23748 'top' : [ 'b-t', [0,-2], 'bottom']
23754 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
23759 delay : null, // can be { show : 300 , hide: 500}
23763 hoverState : null, //???
23765 placement : 'bottom',
23767 getAutoCreate : function(){
23774 cls : 'tooltip-arrow'
23777 cls : 'tooltip-inner'
23784 bind : function(el)
23790 enter : function () {
23792 if (this.timeout != null) {
23793 clearTimeout(this.timeout);
23796 this.hoverState = 'in';
23797 //Roo.log("enter - show");
23798 if (!this.delay || !this.delay.show) {
23803 this.timeout = setTimeout(function () {
23804 if (_t.hoverState == 'in') {
23807 }, this.delay.show);
23811 clearTimeout(this.timeout);
23813 this.hoverState = 'out';
23814 if (!this.delay || !this.delay.hide) {
23820 this.timeout = setTimeout(function () {
23821 //Roo.log("leave - timeout");
23823 if (_t.hoverState == 'out') {
23825 Roo.bootstrap.Tooltip.currentEl = false;
23833 this.render(document.body);
23836 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23838 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23840 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23842 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23844 var placement = typeof this.placement == 'function' ?
23845 this.placement.call(this, this.el, on_el) :
23848 var autoToken = /\s?auto?\s?/i;
23849 var autoPlace = autoToken.test(placement);
23851 placement = placement.replace(autoToken, '') || 'top';
23855 //this.el.setXY([0,0]);
23857 //this.el.dom.style.display='block';
23859 //this.el.appendTo(on_el);
23861 var p = this.getPosition();
23862 var box = this.el.getBox();
23868 var align = Roo.bootstrap.Tooltip.alignment[placement];
23870 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23872 if(placement == 'top' || placement == 'bottom'){
23874 placement = 'right';
23877 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23878 placement = 'left';
23882 align = Roo.bootstrap.Tooltip.alignment[placement];
23884 this.el.alignTo(this.bindEl, align[0],align[1]);
23885 //var arrow = this.el.select('.arrow',true).first();
23886 //arrow.set(align[2],
23888 this.el.addClass(placement);
23890 this.el.addClass('in fade');
23892 this.hoverState = null;
23894 if (this.el.hasClass('fade')) {
23905 //this.el.setXY([0,0]);
23906 this.el.removeClass('in');
23922 * @class Roo.bootstrap.LocationPicker
23923 * @extends Roo.bootstrap.Component
23924 * Bootstrap LocationPicker class
23925 * @cfg {Number} latitude Position when init default 0
23926 * @cfg {Number} longitude Position when init default 0
23927 * @cfg {Number} zoom default 15
23928 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23929 * @cfg {Boolean} mapTypeControl default false
23930 * @cfg {Boolean} disableDoubleClickZoom default false
23931 * @cfg {Boolean} scrollwheel default true
23932 * @cfg {Boolean} streetViewControl default false
23933 * @cfg {Number} radius default 0
23934 * @cfg {String} locationName
23935 * @cfg {Boolean} draggable default true
23936 * @cfg {Boolean} enableAutocomplete default false
23937 * @cfg {Boolean} enableReverseGeocode default true
23938 * @cfg {String} markerTitle
23941 * Create a new LocationPicker
23942 * @param {Object} config The config object
23946 Roo.bootstrap.LocationPicker = function(config){
23948 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23953 * Fires when the picker initialized.
23954 * @param {Roo.bootstrap.LocationPicker} this
23955 * @param {Google Location} location
23959 * @event positionchanged
23960 * Fires when the picker position changed.
23961 * @param {Roo.bootstrap.LocationPicker} this
23962 * @param {Google Location} location
23964 positionchanged : true,
23967 * Fires when the map resize.
23968 * @param {Roo.bootstrap.LocationPicker} this
23973 * Fires when the map show.
23974 * @param {Roo.bootstrap.LocationPicker} this
23979 * Fires when the map hide.
23980 * @param {Roo.bootstrap.LocationPicker} this
23985 * Fires when click the map.
23986 * @param {Roo.bootstrap.LocationPicker} this
23987 * @param {Map event} e
23991 * @event mapRightClick
23992 * Fires when right click the map.
23993 * @param {Roo.bootstrap.LocationPicker} this
23994 * @param {Map event} e
23996 mapRightClick : true,
23998 * @event markerClick
23999 * Fires when click the marker.
24000 * @param {Roo.bootstrap.LocationPicker} this
24001 * @param {Map event} e
24003 markerClick : true,
24005 * @event markerRightClick
24006 * Fires when right click the marker.
24007 * @param {Roo.bootstrap.LocationPicker} this
24008 * @param {Map event} e
24010 markerRightClick : true,
24012 * @event OverlayViewDraw
24013 * Fires when OverlayView Draw
24014 * @param {Roo.bootstrap.LocationPicker} this
24016 OverlayViewDraw : true,
24018 * @event OverlayViewOnAdd
24019 * Fires when OverlayView Draw
24020 * @param {Roo.bootstrap.LocationPicker} this
24022 OverlayViewOnAdd : true,
24024 * @event OverlayViewOnRemove
24025 * Fires when OverlayView Draw
24026 * @param {Roo.bootstrap.LocationPicker} this
24028 OverlayViewOnRemove : true,
24030 * @event OverlayViewShow
24031 * Fires when OverlayView Draw
24032 * @param {Roo.bootstrap.LocationPicker} this
24033 * @param {Pixel} cpx
24035 OverlayViewShow : true,
24037 * @event OverlayViewHide
24038 * Fires when OverlayView Draw
24039 * @param {Roo.bootstrap.LocationPicker} this
24041 OverlayViewHide : true,
24043 * @event loadexception
24044 * Fires when load google lib failed.
24045 * @param {Roo.bootstrap.LocationPicker} this
24047 loadexception : true
24052 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
24054 gMapContext: false,
24060 mapTypeControl: false,
24061 disableDoubleClickZoom: false,
24063 streetViewControl: false,
24067 enableAutocomplete: false,
24068 enableReverseGeocode: true,
24071 getAutoCreate: function()
24076 cls: 'roo-location-picker'
24082 initEvents: function(ct, position)
24084 if(!this.el.getWidth() || this.isApplied()){
24088 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24093 initial: function()
24095 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24096 this.fireEvent('loadexception', this);
24100 if(!this.mapTypeId){
24101 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24104 this.gMapContext = this.GMapContext();
24106 this.initOverlayView();
24108 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24112 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24113 _this.setPosition(_this.gMapContext.marker.position);
24116 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24117 _this.fireEvent('mapClick', this, event);
24121 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24122 _this.fireEvent('mapRightClick', this, event);
24126 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24127 _this.fireEvent('markerClick', this, event);
24131 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24132 _this.fireEvent('markerRightClick', this, event);
24136 this.setPosition(this.gMapContext.location);
24138 this.fireEvent('initial', this, this.gMapContext.location);
24141 initOverlayView: function()
24145 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24149 _this.fireEvent('OverlayViewDraw', _this);
24154 _this.fireEvent('OverlayViewOnAdd', _this);
24157 onRemove: function()
24159 _this.fireEvent('OverlayViewOnRemove', _this);
24162 show: function(cpx)
24164 _this.fireEvent('OverlayViewShow', _this, cpx);
24169 _this.fireEvent('OverlayViewHide', _this);
24175 fromLatLngToContainerPixel: function(event)
24177 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24180 isApplied: function()
24182 return this.getGmapContext() == false ? false : true;
24185 getGmapContext: function()
24187 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24190 GMapContext: function()
24192 var position = new google.maps.LatLng(this.latitude, this.longitude);
24194 var _map = new google.maps.Map(this.el.dom, {
24197 mapTypeId: this.mapTypeId,
24198 mapTypeControl: this.mapTypeControl,
24199 disableDoubleClickZoom: this.disableDoubleClickZoom,
24200 scrollwheel: this.scrollwheel,
24201 streetViewControl: this.streetViewControl,
24202 locationName: this.locationName,
24203 draggable: this.draggable,
24204 enableAutocomplete: this.enableAutocomplete,
24205 enableReverseGeocode: this.enableReverseGeocode
24208 var _marker = new google.maps.Marker({
24209 position: position,
24211 title: this.markerTitle,
24212 draggable: this.draggable
24219 location: position,
24220 radius: this.radius,
24221 locationName: this.locationName,
24222 addressComponents: {
24223 formatted_address: null,
24224 addressLine1: null,
24225 addressLine2: null,
24227 streetNumber: null,
24231 stateOrProvince: null
24234 domContainer: this.el.dom,
24235 geodecoder: new google.maps.Geocoder()
24239 drawCircle: function(center, radius, options)
24241 if (this.gMapContext.circle != null) {
24242 this.gMapContext.circle.setMap(null);
24246 options = Roo.apply({}, options, {
24247 strokeColor: "#0000FF",
24248 strokeOpacity: .35,
24250 fillColor: "#0000FF",
24254 options.map = this.gMapContext.map;
24255 options.radius = radius;
24256 options.center = center;
24257 this.gMapContext.circle = new google.maps.Circle(options);
24258 return this.gMapContext.circle;
24264 setPosition: function(location)
24266 this.gMapContext.location = location;
24267 this.gMapContext.marker.setPosition(location);
24268 this.gMapContext.map.panTo(location);
24269 this.drawCircle(location, this.gMapContext.radius, {});
24273 if (this.gMapContext.settings.enableReverseGeocode) {
24274 this.gMapContext.geodecoder.geocode({
24275 latLng: this.gMapContext.location
24276 }, function(results, status) {
24278 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24279 _this.gMapContext.locationName = results[0].formatted_address;
24280 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24282 _this.fireEvent('positionchanged', this, location);
24289 this.fireEvent('positionchanged', this, location);
24294 google.maps.event.trigger(this.gMapContext.map, "resize");
24296 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24298 this.fireEvent('resize', this);
24301 setPositionByLatLng: function(latitude, longitude)
24303 this.setPosition(new google.maps.LatLng(latitude, longitude));
24306 getCurrentPosition: function()
24309 latitude: this.gMapContext.location.lat(),
24310 longitude: this.gMapContext.location.lng()
24314 getAddressName: function()
24316 return this.gMapContext.locationName;
24319 getAddressComponents: function()
24321 return this.gMapContext.addressComponents;
24324 address_component_from_google_geocode: function(address_components)
24328 for (var i = 0; i < address_components.length; i++) {
24329 var component = address_components[i];
24330 if (component.types.indexOf("postal_code") >= 0) {
24331 result.postalCode = component.short_name;
24332 } else if (component.types.indexOf("street_number") >= 0) {
24333 result.streetNumber = component.short_name;
24334 } else if (component.types.indexOf("route") >= 0) {
24335 result.streetName = component.short_name;
24336 } else if (component.types.indexOf("neighborhood") >= 0) {
24337 result.city = component.short_name;
24338 } else if (component.types.indexOf("locality") >= 0) {
24339 result.city = component.short_name;
24340 } else if (component.types.indexOf("sublocality") >= 0) {
24341 result.district = component.short_name;
24342 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24343 result.stateOrProvince = component.short_name;
24344 } else if (component.types.indexOf("country") >= 0) {
24345 result.country = component.short_name;
24349 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24350 result.addressLine2 = "";
24354 setZoomLevel: function(zoom)
24356 this.gMapContext.map.setZoom(zoom);
24369 this.fireEvent('show', this);
24380 this.fireEvent('hide', this);
24385 Roo.apply(Roo.bootstrap.LocationPicker, {
24387 OverlayView : function(map, options)
24389 options = options || {};
24403 * @class Roo.bootstrap.Alert
24404 * @extends Roo.bootstrap.Component
24405 * Bootstrap Alert class
24406 * @cfg {String} title The title of alert
24407 * @cfg {String} html The content of alert
24408 * @cfg {String} weight ( success | info | warning | danger )
24409 * @cfg {String} faicon font-awesomeicon
24412 * Create a new alert
24413 * @param {Object} config The config object
24417 Roo.bootstrap.Alert = function(config){
24418 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24422 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
24429 getAutoCreate : function()
24438 cls : 'roo-alert-icon'
24443 cls : 'roo-alert-title',
24448 cls : 'roo-alert-text',
24455 cfg.cn[0].cls += ' fa ' + this.faicon;
24459 cfg.cls += ' alert-' + this.weight;
24465 initEvents: function()
24467 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24470 setTitle : function(str)
24472 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24475 setText : function(str)
24477 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24480 setWeight : function(weight)
24483 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24486 this.weight = weight;
24488 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24491 setIcon : function(icon)
24494 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24497 this.faicon = icon;
24499 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24520 * @class Roo.bootstrap.UploadCropbox
24521 * @extends Roo.bootstrap.Component
24522 * Bootstrap UploadCropbox class
24523 * @cfg {String} emptyText show when image has been loaded
24524 * @cfg {String} rotateNotify show when image too small to rotate
24525 * @cfg {Number} errorTimeout default 3000
24526 * @cfg {Number} minWidth default 300
24527 * @cfg {Number} minHeight default 300
24528 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24529 * @cfg {Boolean} isDocument (true|false) default false
24530 * @cfg {String} url action url
24531 * @cfg {String} paramName default 'imageUpload'
24532 * @cfg {String} method default POST
24533 * @cfg {Boolean} loadMask (true|false) default true
24534 * @cfg {Boolean} loadingText default 'Loading...'
24537 * Create a new UploadCropbox
24538 * @param {Object} config The config object
24541 Roo.bootstrap.UploadCropbox = function(config){
24542 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24546 * @event beforeselectfile
24547 * Fire before select file
24548 * @param {Roo.bootstrap.UploadCropbox} this
24550 "beforeselectfile" : true,
24553 * Fire after initEvent
24554 * @param {Roo.bootstrap.UploadCropbox} this
24559 * Fire after initEvent
24560 * @param {Roo.bootstrap.UploadCropbox} this
24561 * @param {String} data
24566 * Fire when preparing the file data
24567 * @param {Roo.bootstrap.UploadCropbox} this
24568 * @param {Object} file
24573 * Fire when get exception
24574 * @param {Roo.bootstrap.UploadCropbox} this
24575 * @param {XMLHttpRequest} xhr
24577 "exception" : true,
24579 * @event beforeloadcanvas
24580 * Fire before load the canvas
24581 * @param {Roo.bootstrap.UploadCropbox} this
24582 * @param {String} src
24584 "beforeloadcanvas" : true,
24587 * Fire when trash image
24588 * @param {Roo.bootstrap.UploadCropbox} this
24593 * Fire when download the image
24594 * @param {Roo.bootstrap.UploadCropbox} this
24598 * @event footerbuttonclick
24599 * Fire when footerbuttonclick
24600 * @param {Roo.bootstrap.UploadCropbox} this
24601 * @param {String} type
24603 "footerbuttonclick" : true,
24607 * @param {Roo.bootstrap.UploadCropbox} this
24612 * Fire when rotate the image
24613 * @param {Roo.bootstrap.UploadCropbox} this
24614 * @param {String} pos
24619 * Fire when inspect the file
24620 * @param {Roo.bootstrap.UploadCropbox} this
24621 * @param {Object} file
24626 * Fire when xhr upload the file
24627 * @param {Roo.bootstrap.UploadCropbox} this
24628 * @param {Object} data
24633 * Fire when arrange the file data
24634 * @param {Roo.bootstrap.UploadCropbox} this
24635 * @param {Object} formData
24640 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24643 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24645 emptyText : 'Click to upload image',
24646 rotateNotify : 'Image is too small to rotate',
24647 errorTimeout : 3000,
24661 cropType : 'image/jpeg',
24663 canvasLoaded : false,
24664 isDocument : false,
24666 paramName : 'imageUpload',
24668 loadingText : 'Loading...',
24671 getAutoCreate : function()
24675 cls : 'roo-upload-cropbox',
24679 cls : 'roo-upload-cropbox-selector',
24684 cls : 'roo-upload-cropbox-body',
24685 style : 'cursor:pointer',
24689 cls : 'roo-upload-cropbox-preview'
24693 cls : 'roo-upload-cropbox-thumb'
24697 cls : 'roo-upload-cropbox-empty-notify',
24698 html : this.emptyText
24702 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24703 html : this.rotateNotify
24709 cls : 'roo-upload-cropbox-footer',
24712 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24722 onRender : function(ct, position)
24724 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24726 if (this.buttons.length) {
24728 Roo.each(this.buttons, function(bb) {
24730 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24732 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24738 this.maskEl = this.el;
24742 initEvents : function()
24744 this.urlAPI = (window.createObjectURL && window) ||
24745 (window.URL && URL.revokeObjectURL && URL) ||
24746 (window.webkitURL && webkitURL);
24748 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24749 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24751 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24752 this.selectorEl.hide();
24754 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24755 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24757 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24758 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24759 this.thumbEl.hide();
24761 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24762 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24764 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24765 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24766 this.errorEl.hide();
24768 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24769 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24770 this.footerEl.hide();
24772 this.setThumbBoxSize();
24778 this.fireEvent('initial', this);
24785 window.addEventListener("resize", function() { _this.resize(); } );
24787 this.bodyEl.on('click', this.beforeSelectFile, this);
24790 this.bodyEl.on('touchstart', this.onTouchStart, this);
24791 this.bodyEl.on('touchmove', this.onTouchMove, this);
24792 this.bodyEl.on('touchend', this.onTouchEnd, this);
24796 this.bodyEl.on('mousedown', this.onMouseDown, this);
24797 this.bodyEl.on('mousemove', this.onMouseMove, this);
24798 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24799 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24800 Roo.get(document).on('mouseup', this.onMouseUp, this);
24803 this.selectorEl.on('change', this.onFileSelected, this);
24809 this.baseScale = 1;
24811 this.baseRotate = 1;
24812 this.dragable = false;
24813 this.pinching = false;
24816 this.cropData = false;
24817 this.notifyEl.dom.innerHTML = this.emptyText;
24819 this.selectorEl.dom.value = '';
24823 resize : function()
24825 if(this.fireEvent('resize', this) != false){
24826 this.setThumbBoxPosition();
24827 this.setCanvasPosition();
24831 onFooterButtonClick : function(e, el, o, type)
24834 case 'rotate-left' :
24835 this.onRotateLeft(e);
24837 case 'rotate-right' :
24838 this.onRotateRight(e);
24841 this.beforeSelectFile(e);
24856 this.fireEvent('footerbuttonclick', this, type);
24859 beforeSelectFile : function(e)
24861 e.preventDefault();
24863 if(this.fireEvent('beforeselectfile', this) != false){
24864 this.selectorEl.dom.click();
24868 onFileSelected : function(e)
24870 e.preventDefault();
24872 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24876 var file = this.selectorEl.dom.files[0];
24878 if(this.fireEvent('inspect', this, file) != false){
24879 this.prepare(file);
24884 trash : function(e)
24886 this.fireEvent('trash', this);
24889 download : function(e)
24891 this.fireEvent('download', this);
24894 loadCanvas : function(src)
24896 if(this.fireEvent('beforeloadcanvas', this, src) != false){
24900 this.imageEl = document.createElement('img');
24904 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24906 this.imageEl.src = src;
24910 onLoadCanvas : function()
24912 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24913 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24915 this.bodyEl.un('click', this.beforeSelectFile, this);
24917 this.notifyEl.hide();
24918 this.thumbEl.show();
24919 this.footerEl.show();
24921 this.baseRotateLevel();
24923 if(this.isDocument){
24924 this.setThumbBoxSize();
24927 this.setThumbBoxPosition();
24929 this.baseScaleLevel();
24935 this.canvasLoaded = true;
24938 this.maskEl.unmask();
24943 setCanvasPosition : function()
24945 if(!this.canvasEl){
24949 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24950 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24952 this.previewEl.setLeft(pw);
24953 this.previewEl.setTop(ph);
24957 onMouseDown : function(e)
24961 this.dragable = true;
24962 this.pinching = false;
24964 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24965 this.dragable = false;
24969 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24970 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24974 onMouseMove : function(e)
24978 if(!this.canvasLoaded){
24982 if (!this.dragable){
24986 var minX = Math.ceil(this.thumbEl.getLeft(true));
24987 var minY = Math.ceil(this.thumbEl.getTop(true));
24989 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24990 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24992 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24993 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24995 x = x - this.mouseX;
24996 y = y - this.mouseY;
24998 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24999 var bgY = Math.ceil(y + this.previewEl.getTop(true));
25001 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25002 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25004 this.previewEl.setLeft(bgX);
25005 this.previewEl.setTop(bgY);
25007 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25008 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25011 onMouseUp : function(e)
25015 this.dragable = false;
25018 onMouseWheel : function(e)
25022 this.startScale = this.scale;
25024 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25026 if(!this.zoomable()){
25027 this.scale = this.startScale;
25036 zoomable : function()
25038 var minScale = this.thumbEl.getWidth() / this.minWidth;
25040 if(this.minWidth < this.minHeight){
25041 minScale = this.thumbEl.getHeight() / this.minHeight;
25044 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25045 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25049 (this.rotate == 0 || this.rotate == 180) &&
25051 width > this.imageEl.OriginWidth ||
25052 height > this.imageEl.OriginHeight ||
25053 (width < this.minWidth && height < this.minHeight)
25061 (this.rotate == 90 || this.rotate == 270) &&
25063 width > this.imageEl.OriginWidth ||
25064 height > this.imageEl.OriginHeight ||
25065 (width < this.minHeight && height < this.minWidth)
25072 !this.isDocument &&
25073 (this.rotate == 0 || this.rotate == 180) &&
25075 width < this.minWidth ||
25076 width > this.imageEl.OriginWidth ||
25077 height < this.minHeight ||
25078 height > this.imageEl.OriginHeight
25085 !this.isDocument &&
25086 (this.rotate == 90 || this.rotate == 270) &&
25088 width < this.minHeight ||
25089 width > this.imageEl.OriginWidth ||
25090 height < this.minWidth ||
25091 height > this.imageEl.OriginHeight
25101 onRotateLeft : function(e)
25103 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25105 var minScale = this.thumbEl.getWidth() / this.minWidth;
25107 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25108 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25110 this.startScale = this.scale;
25112 while (this.getScaleLevel() < minScale){
25114 this.scale = this.scale + 1;
25116 if(!this.zoomable()){
25121 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25122 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25127 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25134 this.scale = this.startScale;
25136 this.onRotateFail();
25141 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25143 if(this.isDocument){
25144 this.setThumbBoxSize();
25145 this.setThumbBoxPosition();
25146 this.setCanvasPosition();
25151 this.fireEvent('rotate', this, 'left');
25155 onRotateRight : function(e)
25157 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25159 var minScale = this.thumbEl.getWidth() / this.minWidth;
25161 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25162 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25164 this.startScale = this.scale;
25166 while (this.getScaleLevel() < minScale){
25168 this.scale = this.scale + 1;
25170 if(!this.zoomable()){
25175 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25176 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25181 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25188 this.scale = this.startScale;
25190 this.onRotateFail();
25195 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25197 if(this.isDocument){
25198 this.setThumbBoxSize();
25199 this.setThumbBoxPosition();
25200 this.setCanvasPosition();
25205 this.fireEvent('rotate', this, 'right');
25208 onRotateFail : function()
25210 this.errorEl.show(true);
25214 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25219 this.previewEl.dom.innerHTML = '';
25221 var canvasEl = document.createElement("canvas");
25223 var contextEl = canvasEl.getContext("2d");
25225 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25226 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25227 var center = this.imageEl.OriginWidth / 2;
25229 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25230 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25231 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25232 center = this.imageEl.OriginHeight / 2;
25235 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25237 contextEl.translate(center, center);
25238 contextEl.rotate(this.rotate * Math.PI / 180);
25240 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25242 this.canvasEl = document.createElement("canvas");
25244 this.contextEl = this.canvasEl.getContext("2d");
25246 switch (this.rotate) {
25249 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25250 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25252 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25257 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25258 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25260 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25261 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);
25265 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25270 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25271 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25273 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25274 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);
25278 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);
25283 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25284 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25286 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25287 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25291 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);
25298 this.previewEl.appendChild(this.canvasEl);
25300 this.setCanvasPosition();
25305 if(!this.canvasLoaded){
25309 var imageCanvas = document.createElement("canvas");
25311 var imageContext = imageCanvas.getContext("2d");
25313 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25314 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25316 var center = imageCanvas.width / 2;
25318 imageContext.translate(center, center);
25320 imageContext.rotate(this.rotate * Math.PI / 180);
25322 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25324 var canvas = document.createElement("canvas");
25326 var context = canvas.getContext("2d");
25328 canvas.width = this.minWidth;
25329 canvas.height = this.minHeight;
25331 switch (this.rotate) {
25334 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25335 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25337 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25338 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25340 var targetWidth = this.minWidth - 2 * x;
25341 var targetHeight = this.minHeight - 2 * y;
25345 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25346 scale = targetWidth / width;
25349 if(x > 0 && y == 0){
25350 scale = targetHeight / height;
25353 if(x > 0 && y > 0){
25354 scale = targetWidth / width;
25356 if(width < height){
25357 scale = targetHeight / height;
25361 context.scale(scale, scale);
25363 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25364 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25366 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25367 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25369 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25374 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25375 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25377 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25378 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25380 var targetWidth = this.minWidth - 2 * x;
25381 var targetHeight = this.minHeight - 2 * y;
25385 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25386 scale = targetWidth / width;
25389 if(x > 0 && y == 0){
25390 scale = targetHeight / height;
25393 if(x > 0 && y > 0){
25394 scale = targetWidth / width;
25396 if(width < height){
25397 scale = targetHeight / height;
25401 context.scale(scale, scale);
25403 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25404 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25406 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25407 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25409 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25411 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25416 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25417 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25419 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25420 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25422 var targetWidth = this.minWidth - 2 * x;
25423 var targetHeight = this.minHeight - 2 * y;
25427 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25428 scale = targetWidth / width;
25431 if(x > 0 && y == 0){
25432 scale = targetHeight / height;
25435 if(x > 0 && y > 0){
25436 scale = targetWidth / width;
25438 if(width < height){
25439 scale = targetHeight / height;
25443 context.scale(scale, scale);
25445 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25446 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25448 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25449 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25451 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25452 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25454 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25459 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25460 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25462 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25463 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25465 var targetWidth = this.minWidth - 2 * x;
25466 var targetHeight = this.minHeight - 2 * y;
25470 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25471 scale = targetWidth / width;
25474 if(x > 0 && y == 0){
25475 scale = targetHeight / height;
25478 if(x > 0 && y > 0){
25479 scale = targetWidth / width;
25481 if(width < height){
25482 scale = targetHeight / height;
25486 context.scale(scale, scale);
25488 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25489 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25491 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25492 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25494 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25496 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25503 this.cropData = canvas.toDataURL(this.cropType);
25505 if(this.fireEvent('crop', this, this.cropData) !== false){
25506 this.process(this.file, this.cropData);
25513 setThumbBoxSize : function()
25517 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25518 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25519 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25521 this.minWidth = width;
25522 this.minHeight = height;
25524 if(this.rotate == 90 || this.rotate == 270){
25525 this.minWidth = height;
25526 this.minHeight = width;
25531 width = Math.ceil(this.minWidth * height / this.minHeight);
25533 if(this.minWidth > this.minHeight){
25535 height = Math.ceil(this.minHeight * width / this.minWidth);
25538 this.thumbEl.setStyle({
25539 width : width + 'px',
25540 height : height + 'px'
25547 setThumbBoxPosition : function()
25549 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25550 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25552 this.thumbEl.setLeft(x);
25553 this.thumbEl.setTop(y);
25557 baseRotateLevel : function()
25559 this.baseRotate = 1;
25562 typeof(this.exif) != 'undefined' &&
25563 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25564 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25566 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25569 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25573 baseScaleLevel : function()
25577 if(this.isDocument){
25579 if(this.baseRotate == 6 || this.baseRotate == 8){
25581 height = this.thumbEl.getHeight();
25582 this.baseScale = height / this.imageEl.OriginWidth;
25584 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25585 width = this.thumbEl.getWidth();
25586 this.baseScale = width / this.imageEl.OriginHeight;
25592 height = this.thumbEl.getHeight();
25593 this.baseScale = height / this.imageEl.OriginHeight;
25595 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25596 width = this.thumbEl.getWidth();
25597 this.baseScale = width / this.imageEl.OriginWidth;
25603 if(this.baseRotate == 6 || this.baseRotate == 8){
25605 width = this.thumbEl.getHeight();
25606 this.baseScale = width / this.imageEl.OriginHeight;
25608 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25609 height = this.thumbEl.getWidth();
25610 this.baseScale = height / this.imageEl.OriginHeight;
25613 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25614 height = this.thumbEl.getWidth();
25615 this.baseScale = height / this.imageEl.OriginHeight;
25617 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25618 width = this.thumbEl.getHeight();
25619 this.baseScale = width / this.imageEl.OriginWidth;
25626 width = this.thumbEl.getWidth();
25627 this.baseScale = width / this.imageEl.OriginWidth;
25629 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25630 height = this.thumbEl.getHeight();
25631 this.baseScale = height / this.imageEl.OriginHeight;
25634 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25636 height = this.thumbEl.getHeight();
25637 this.baseScale = height / this.imageEl.OriginHeight;
25639 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25640 width = this.thumbEl.getWidth();
25641 this.baseScale = width / this.imageEl.OriginWidth;
25649 getScaleLevel : function()
25651 return this.baseScale * Math.pow(1.1, this.scale);
25654 onTouchStart : function(e)
25656 if(!this.canvasLoaded){
25657 this.beforeSelectFile(e);
25661 var touches = e.browserEvent.touches;
25667 if(touches.length == 1){
25668 this.onMouseDown(e);
25672 if(touches.length != 2){
25678 for(var i = 0, finger; finger = touches[i]; i++){
25679 coords.push(finger.pageX, finger.pageY);
25682 var x = Math.pow(coords[0] - coords[2], 2);
25683 var y = Math.pow(coords[1] - coords[3], 2);
25685 this.startDistance = Math.sqrt(x + y);
25687 this.startScale = this.scale;
25689 this.pinching = true;
25690 this.dragable = false;
25694 onTouchMove : function(e)
25696 if(!this.pinching && !this.dragable){
25700 var touches = e.browserEvent.touches;
25707 this.onMouseMove(e);
25713 for(var i = 0, finger; finger = touches[i]; i++){
25714 coords.push(finger.pageX, finger.pageY);
25717 var x = Math.pow(coords[0] - coords[2], 2);
25718 var y = Math.pow(coords[1] - coords[3], 2);
25720 this.endDistance = Math.sqrt(x + y);
25722 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25724 if(!this.zoomable()){
25725 this.scale = this.startScale;
25733 onTouchEnd : function(e)
25735 this.pinching = false;
25736 this.dragable = false;
25740 process : function(file, crop)
25743 this.maskEl.mask(this.loadingText);
25746 this.xhr = new XMLHttpRequest();
25748 file.xhr = this.xhr;
25750 this.xhr.open(this.method, this.url, true);
25753 "Accept": "application/json",
25754 "Cache-Control": "no-cache",
25755 "X-Requested-With": "XMLHttpRequest"
25758 for (var headerName in headers) {
25759 var headerValue = headers[headerName];
25761 this.xhr.setRequestHeader(headerName, headerValue);
25767 this.xhr.onload = function()
25769 _this.xhrOnLoad(_this.xhr);
25772 this.xhr.onerror = function()
25774 _this.xhrOnError(_this.xhr);
25777 var formData = new FormData();
25779 formData.append('returnHTML', 'NO');
25782 formData.append('crop', crop);
25785 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25786 formData.append(this.paramName, file, file.name);
25789 if(typeof(file.filename) != 'undefined'){
25790 formData.append('filename', file.filename);
25793 if(typeof(file.mimetype) != 'undefined'){
25794 formData.append('mimetype', file.mimetype);
25797 if(this.fireEvent('arrange', this, formData) != false){
25798 this.xhr.send(formData);
25802 xhrOnLoad : function(xhr)
25805 this.maskEl.unmask();
25808 if (xhr.readyState !== 4) {
25809 this.fireEvent('exception', this, xhr);
25813 var response = Roo.decode(xhr.responseText);
25815 if(!response.success){
25816 this.fireEvent('exception', this, xhr);
25820 var response = Roo.decode(xhr.responseText);
25822 this.fireEvent('upload', this, response);
25826 xhrOnError : function()
25829 this.maskEl.unmask();
25832 Roo.log('xhr on error');
25834 var response = Roo.decode(xhr.responseText);
25840 prepare : function(file)
25843 this.maskEl.mask(this.loadingText);
25849 if(typeof(file) === 'string'){
25850 this.loadCanvas(file);
25854 if(!file || !this.urlAPI){
25859 this.cropType = file.type;
25863 if(this.fireEvent('prepare', this, this.file) != false){
25865 var reader = new FileReader();
25867 reader.onload = function (e) {
25868 if (e.target.error) {
25869 Roo.log(e.target.error);
25873 var buffer = e.target.result,
25874 dataView = new DataView(buffer),
25876 maxOffset = dataView.byteLength - 4,
25880 if (dataView.getUint16(0) === 0xffd8) {
25881 while (offset < maxOffset) {
25882 markerBytes = dataView.getUint16(offset);
25884 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25885 markerLength = dataView.getUint16(offset + 2) + 2;
25886 if (offset + markerLength > dataView.byteLength) {
25887 Roo.log('Invalid meta data: Invalid segment size.');
25891 if(markerBytes == 0xffe1){
25892 _this.parseExifData(
25899 offset += markerLength;
25909 var url = _this.urlAPI.createObjectURL(_this.file);
25911 _this.loadCanvas(url);
25916 reader.readAsArrayBuffer(this.file);
25922 parseExifData : function(dataView, offset, length)
25924 var tiffOffset = offset + 10,
25928 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25929 // No Exif data, might be XMP data instead
25933 // Check for the ASCII code for "Exif" (0x45786966):
25934 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25935 // No Exif data, might be XMP data instead
25938 if (tiffOffset + 8 > dataView.byteLength) {
25939 Roo.log('Invalid Exif data: Invalid segment size.');
25942 // Check for the two null bytes:
25943 if (dataView.getUint16(offset + 8) !== 0x0000) {
25944 Roo.log('Invalid Exif data: Missing byte alignment offset.');
25947 // Check the byte alignment:
25948 switch (dataView.getUint16(tiffOffset)) {
25950 littleEndian = true;
25953 littleEndian = false;
25956 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25959 // Check for the TIFF tag marker (0x002A):
25960 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25961 Roo.log('Invalid Exif data: Missing TIFF marker.');
25964 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25965 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25967 this.parseExifTags(
25970 tiffOffset + dirOffset,
25975 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25980 if (dirOffset + 6 > dataView.byteLength) {
25981 Roo.log('Invalid Exif data: Invalid directory offset.');
25984 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25985 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25986 if (dirEndOffset + 4 > dataView.byteLength) {
25987 Roo.log('Invalid Exif data: Invalid directory size.');
25990 for (i = 0; i < tagsNumber; i += 1) {
25994 dirOffset + 2 + 12 * i, // tag offset
25998 // Return the offset to the next directory:
25999 return dataView.getUint32(dirEndOffset, littleEndian);
26002 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
26004 var tag = dataView.getUint16(offset, littleEndian);
26006 this.exif[tag] = this.getExifValue(
26010 dataView.getUint16(offset + 2, littleEndian), // tag type
26011 dataView.getUint32(offset + 4, littleEndian), // tag length
26016 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26018 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26027 Roo.log('Invalid Exif data: Invalid tag type.');
26031 tagSize = tagType.size * length;
26032 // Determine if the value is contained in the dataOffset bytes,
26033 // or if the value at the dataOffset is a pointer to the actual data:
26034 dataOffset = tagSize > 4 ?
26035 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26036 if (dataOffset + tagSize > dataView.byteLength) {
26037 Roo.log('Invalid Exif data: Invalid data offset.');
26040 if (length === 1) {
26041 return tagType.getValue(dataView, dataOffset, littleEndian);
26044 for (i = 0; i < length; i += 1) {
26045 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26048 if (tagType.ascii) {
26050 // Concatenate the chars:
26051 for (i = 0; i < values.length; i += 1) {
26053 // Ignore the terminating NULL byte(s):
26054 if (c === '\u0000') {
26066 Roo.apply(Roo.bootstrap.UploadCropbox, {
26068 'Orientation': 0x0112
26072 1: 0, //'top-left',
26074 3: 180, //'bottom-right',
26075 // 4: 'bottom-left',
26077 6: 90, //'right-top',
26078 // 7: 'right-bottom',
26079 8: 270 //'left-bottom'
26083 // byte, 8-bit unsigned int:
26085 getValue: function (dataView, dataOffset) {
26086 return dataView.getUint8(dataOffset);
26090 // ascii, 8-bit byte:
26092 getValue: function (dataView, dataOffset) {
26093 return String.fromCharCode(dataView.getUint8(dataOffset));
26098 // short, 16 bit int:
26100 getValue: function (dataView, dataOffset, littleEndian) {
26101 return dataView.getUint16(dataOffset, littleEndian);
26105 // long, 32 bit int:
26107 getValue: function (dataView, dataOffset, littleEndian) {
26108 return dataView.getUint32(dataOffset, littleEndian);
26112 // rational = two long values, first is numerator, second is denominator:
26114 getValue: function (dataView, dataOffset, littleEndian) {
26115 return dataView.getUint32(dataOffset, littleEndian) /
26116 dataView.getUint32(dataOffset + 4, littleEndian);
26120 // slong, 32 bit signed int:
26122 getValue: function (dataView, dataOffset, littleEndian) {
26123 return dataView.getInt32(dataOffset, littleEndian);
26127 // srational, two slongs, first is numerator, second is denominator:
26129 getValue: function (dataView, dataOffset, littleEndian) {
26130 return dataView.getInt32(dataOffset, littleEndian) /
26131 dataView.getInt32(dataOffset + 4, littleEndian);
26141 cls : 'btn-group roo-upload-cropbox-rotate-left',
26142 action : 'rotate-left',
26146 cls : 'btn btn-default',
26147 html : '<i class="fa fa-undo"></i>'
26153 cls : 'btn-group roo-upload-cropbox-picture',
26154 action : 'picture',
26158 cls : 'btn btn-default',
26159 html : '<i class="fa fa-picture-o"></i>'
26165 cls : 'btn-group roo-upload-cropbox-rotate-right',
26166 action : 'rotate-right',
26170 cls : 'btn btn-default',
26171 html : '<i class="fa fa-repeat"></i>'
26179 cls : 'btn-group roo-upload-cropbox-rotate-left',
26180 action : 'rotate-left',
26184 cls : 'btn btn-default',
26185 html : '<i class="fa fa-undo"></i>'
26191 cls : 'btn-group roo-upload-cropbox-download',
26192 action : 'download',
26196 cls : 'btn btn-default',
26197 html : '<i class="fa fa-download"></i>'
26203 cls : 'btn-group roo-upload-cropbox-crop',
26208 cls : 'btn btn-default',
26209 html : '<i class="fa fa-crop"></i>'
26215 cls : 'btn-group roo-upload-cropbox-trash',
26220 cls : 'btn btn-default',
26221 html : '<i class="fa fa-trash"></i>'
26227 cls : 'btn-group roo-upload-cropbox-rotate-right',
26228 action : 'rotate-right',
26232 cls : 'btn btn-default',
26233 html : '<i class="fa fa-repeat"></i>'
26241 cls : 'btn-group roo-upload-cropbox-rotate-left',
26242 action : 'rotate-left',
26246 cls : 'btn btn-default',
26247 html : '<i class="fa fa-undo"></i>'
26253 cls : 'btn-group roo-upload-cropbox-rotate-right',
26254 action : 'rotate-right',
26258 cls : 'btn btn-default',
26259 html : '<i class="fa fa-repeat"></i>'
26272 * @class Roo.bootstrap.DocumentManager
26273 * @extends Roo.bootstrap.Component
26274 * Bootstrap DocumentManager class
26275 * @cfg {String} paramName default 'imageUpload'
26276 * @cfg {String} method default POST
26277 * @cfg {String} url action url
26278 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26279 * @cfg {Boolean} multiple multiple upload default true
26280 * @cfg {Number} thumbSize default 300
26281 * @cfg {String} fieldLabel
26282 * @cfg {Number} labelWidth default 4
26283 * @cfg {String} labelAlign (left|top) default left
26284 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26287 * Create a new DocumentManager
26288 * @param {Object} config The config object
26291 Roo.bootstrap.DocumentManager = function(config){
26292 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26297 * Fire when initial the DocumentManager
26298 * @param {Roo.bootstrap.DocumentManager} this
26303 * inspect selected file
26304 * @param {Roo.bootstrap.DocumentManager} this
26305 * @param {File} file
26310 * Fire when xhr load exception
26311 * @param {Roo.bootstrap.DocumentManager} this
26312 * @param {XMLHttpRequest} xhr
26314 "exception" : true,
26317 * prepare the form data
26318 * @param {Roo.bootstrap.DocumentManager} this
26319 * @param {Object} formData
26324 * Fire when remove the file
26325 * @param {Roo.bootstrap.DocumentManager} this
26326 * @param {Object} file
26331 * Fire after refresh the file
26332 * @param {Roo.bootstrap.DocumentManager} this
26337 * Fire after click the image
26338 * @param {Roo.bootstrap.DocumentManager} this
26339 * @param {Object} file
26344 * Fire when upload a image and editable set to true
26345 * @param {Roo.bootstrap.DocumentManager} this
26346 * @param {Object} file
26350 * @event beforeselectfile
26351 * Fire before select file
26352 * @param {Roo.bootstrap.DocumentManager} this
26354 "beforeselectfile" : true,
26357 * Fire before process file
26358 * @param {Roo.bootstrap.DocumentManager} this
26359 * @param {Object} file
26366 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
26375 paramName : 'imageUpload',
26378 labelAlign : 'left',
26385 getAutoCreate : function()
26387 var managerWidget = {
26389 cls : 'roo-document-manager',
26393 cls : 'roo-document-manager-selector',
26398 cls : 'roo-document-manager-uploader',
26402 cls : 'roo-document-manager-upload-btn',
26403 html : '<i class="fa fa-plus"></i>'
26414 cls : 'column col-md-12',
26419 if(this.fieldLabel.length){
26424 cls : 'column col-md-12',
26425 html : this.fieldLabel
26429 cls : 'column col-md-12',
26434 if(this.labelAlign == 'left'){
26438 cls : 'column col-md-' + this.labelWidth,
26439 html : this.fieldLabel
26443 cls : 'column col-md-' + (12 - this.labelWidth),
26453 cls : 'row clearfix',
26461 initEvents : function()
26463 this.managerEl = this.el.select('.roo-document-manager', true).first();
26464 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26466 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26467 this.selectorEl.hide();
26470 this.selectorEl.attr('multiple', 'multiple');
26473 this.selectorEl.on('change', this.onFileSelected, this);
26475 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26476 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26478 this.uploader.on('click', this.onUploaderClick, this);
26480 this.renderProgressDialog();
26484 window.addEventListener("resize", function() { _this.refresh(); } );
26486 this.fireEvent('initial', this);
26489 renderProgressDialog : function()
26493 this.progressDialog = new Roo.bootstrap.Modal({
26494 cls : 'roo-document-manager-progress-dialog',
26495 allow_close : false,
26505 btnclick : function() {
26506 _this.uploadCancel();
26512 this.progressDialog.render(Roo.get(document.body));
26514 this.progress = new Roo.bootstrap.Progress({
26515 cls : 'roo-document-manager-progress',
26520 this.progress.render(this.progressDialog.getChildContainer());
26522 this.progressBar = new Roo.bootstrap.ProgressBar({
26523 cls : 'roo-document-manager-progress-bar',
26526 aria_valuemax : 12,
26530 this.progressBar.render(this.progress.getChildContainer());
26533 onUploaderClick : function(e)
26535 e.preventDefault();
26537 if(this.fireEvent('beforeselectfile', this) != false){
26538 this.selectorEl.dom.click();
26543 onFileSelected : function(e)
26545 e.preventDefault();
26547 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26551 Roo.each(this.selectorEl.dom.files, function(file){
26552 if(this.fireEvent('inspect', this, file) != false){
26553 this.files.push(file);
26563 this.selectorEl.dom.value = '';
26565 if(!this.files.length){
26569 if(this.boxes > 0 && this.files.length > this.boxes){
26570 this.files = this.files.slice(0, this.boxes);
26573 this.uploader.show();
26575 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26576 this.uploader.hide();
26585 Roo.each(this.files, function(file){
26587 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26588 var f = this.renderPreview(file);
26593 if(file.type.indexOf('image') != -1){
26594 this.delegates.push(
26596 _this.process(file);
26597 }).createDelegate(this)
26605 _this.process(file);
26606 }).createDelegate(this)
26611 this.files = files;
26613 this.delegates = this.delegates.concat(docs);
26615 if(!this.delegates.length){
26620 this.progressBar.aria_valuemax = this.delegates.length;
26627 arrange : function()
26629 if(!this.delegates.length){
26630 this.progressDialog.hide();
26635 var delegate = this.delegates.shift();
26637 this.progressDialog.show();
26639 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26641 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26646 refresh : function()
26648 this.uploader.show();
26650 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26651 this.uploader.hide();
26654 Roo.isTouch ? this.closable(false) : this.closable(true);
26656 this.fireEvent('refresh', this);
26659 onRemove : function(e, el, o)
26661 e.preventDefault();
26663 this.fireEvent('remove', this, o);
26667 remove : function(o)
26671 Roo.each(this.files, function(file){
26672 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26681 this.files = files;
26688 Roo.each(this.files, function(file){
26693 file.target.remove();
26702 onClick : function(e, el, o)
26704 e.preventDefault();
26706 this.fireEvent('click', this, o);
26710 closable : function(closable)
26712 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26714 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26726 xhrOnLoad : function(xhr)
26728 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26732 if (xhr.readyState !== 4) {
26734 this.fireEvent('exception', this, xhr);
26738 var response = Roo.decode(xhr.responseText);
26740 if(!response.success){
26742 this.fireEvent('exception', this, xhr);
26746 var file = this.renderPreview(response.data);
26748 this.files.push(file);
26754 xhrOnError : function()
26756 Roo.log('xhr on error');
26758 var response = Roo.decode(xhr.responseText);
26765 process : function(file)
26767 if(this.fireEvent('process', this, file) !== false){
26768 if(this.editable && file.type.indexOf('image') != -1){
26769 this.fireEvent('edit', this, file);
26773 this.uploadStart(file, false);
26780 uploadStart : function(file, crop)
26782 this.xhr = new XMLHttpRequest();
26784 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26789 file.xhr = this.xhr;
26791 this.managerEl.createChild({
26793 cls : 'roo-document-manager-loading',
26797 tooltip : file.name,
26798 cls : 'roo-document-manager-thumb',
26799 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26805 this.xhr.open(this.method, this.url, true);
26808 "Accept": "application/json",
26809 "Cache-Control": "no-cache",
26810 "X-Requested-With": "XMLHttpRequest"
26813 for (var headerName in headers) {
26814 var headerValue = headers[headerName];
26816 this.xhr.setRequestHeader(headerName, headerValue);
26822 this.xhr.onload = function()
26824 _this.xhrOnLoad(_this.xhr);
26827 this.xhr.onerror = function()
26829 _this.xhrOnError(_this.xhr);
26832 var formData = new FormData();
26834 formData.append('returnHTML', 'NO');
26837 formData.append('crop', crop);
26840 formData.append(this.paramName, file, file.name);
26842 if(this.fireEvent('prepare', this, formData) != false){
26843 this.xhr.send(formData);
26847 uploadCancel : function()
26854 this.delegates = [];
26856 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26863 renderPreview : function(file)
26865 if(typeof(file.target) != 'undefined' && file.target){
26869 var previewEl = this.managerEl.createChild({
26871 cls : 'roo-document-manager-preview',
26875 tooltip : file.filename,
26876 cls : 'roo-document-manager-thumb',
26877 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26882 html : '<i class="fa fa-times-circle"></i>'
26887 var close = previewEl.select('button.close', true).first();
26889 close.on('click', this.onRemove, this, file);
26891 file.target = previewEl;
26893 var image = previewEl.select('img', true).first();
26897 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26899 image.on('click', this.onClick, this, file);
26905 onPreviewLoad : function(file, image)
26907 if(typeof(file.target) == 'undefined' || !file.target){
26911 var width = image.dom.naturalWidth || image.dom.width;
26912 var height = image.dom.naturalHeight || image.dom.height;
26914 if(width > height){
26915 file.target.addClass('wide');
26919 file.target.addClass('tall');
26924 uploadFromSource : function(file, crop)
26926 this.xhr = new XMLHttpRequest();
26928 this.managerEl.createChild({
26930 cls : 'roo-document-manager-loading',
26934 tooltip : file.name,
26935 cls : 'roo-document-manager-thumb',
26936 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26942 this.xhr.open(this.method, this.url, true);
26945 "Accept": "application/json",
26946 "Cache-Control": "no-cache",
26947 "X-Requested-With": "XMLHttpRequest"
26950 for (var headerName in headers) {
26951 var headerValue = headers[headerName];
26953 this.xhr.setRequestHeader(headerName, headerValue);
26959 this.xhr.onload = function()
26961 _this.xhrOnLoad(_this.xhr);
26964 this.xhr.onerror = function()
26966 _this.xhrOnError(_this.xhr);
26969 var formData = new FormData();
26971 formData.append('returnHTML', 'NO');
26973 formData.append('crop', crop);
26975 if(typeof(file.filename) != 'undefined'){
26976 formData.append('filename', file.filename);
26979 if(typeof(file.mimetype) != 'undefined'){
26980 formData.append('mimetype', file.mimetype);
26983 if(this.fireEvent('prepare', this, formData) != false){
26984 this.xhr.send(formData);
26994 * @class Roo.bootstrap.DocumentViewer
26995 * @extends Roo.bootstrap.Component
26996 * Bootstrap DocumentViewer class
26999 * Create a new DocumentViewer
27000 * @param {Object} config The config object
27003 Roo.bootstrap.DocumentViewer = function(config){
27004 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27009 * Fire after initEvent
27010 * @param {Roo.bootstrap.DocumentViewer} this
27016 * @param {Roo.bootstrap.DocumentViewer} this
27021 * Fire after trash button
27022 * @param {Roo.bootstrap.DocumentViewer} this
27029 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
27031 getAutoCreate : function()
27035 cls : 'roo-document-viewer',
27039 cls : 'roo-document-viewer-body',
27043 cls : 'roo-document-viewer-thumb',
27047 cls : 'roo-document-viewer-image'
27055 cls : 'roo-document-viewer-footer',
27058 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27066 cls : 'btn btn-default roo-document-viewer-trash',
27067 html : '<i class="fa fa-trash"></i>'
27080 initEvents : function()
27083 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27084 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27086 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27087 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27089 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27090 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27092 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27093 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27095 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27096 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27098 this.bodyEl.on('click', this.onClick, this);
27100 this.trashBtn.on('click', this.onTrash, this);
27104 initial : function()
27106 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27109 this.fireEvent('initial', this);
27113 onClick : function(e)
27115 e.preventDefault();
27117 this.fireEvent('click', this);
27120 onTrash : function(e)
27122 e.preventDefault();
27124 this.fireEvent('trash', this);
27136 * @class Roo.bootstrap.NavProgressBar
27137 * @extends Roo.bootstrap.Component
27138 * Bootstrap NavProgressBar class
27141 * Create a new nav progress bar
27142 * @param {Object} config The config object
27145 Roo.bootstrap.NavProgressBar = function(config){
27146 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27148 this.bullets = this.bullets || [];
27150 // Roo.bootstrap.NavProgressBar.register(this);
27154 * Fires when the active item changes
27155 * @param {Roo.bootstrap.NavProgressBar} this
27156 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27157 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
27164 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
27169 getAutoCreate : function()
27171 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27175 cls : 'roo-navigation-bar-group',
27179 cls : 'roo-navigation-top-bar'
27183 cls : 'roo-navigation-bullets-bar',
27187 cls : 'roo-navigation-bar'
27194 cls : 'roo-navigation-bottom-bar'
27204 initEvents: function()
27209 onRender : function(ct, position)
27211 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27213 if(this.bullets.length){
27214 Roo.each(this.bullets, function(b){
27223 addItem : function(cfg)
27225 var item = new Roo.bootstrap.NavProgressItem(cfg);
27227 item.parentId = this.id;
27228 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27231 var top = new Roo.bootstrap.Element({
27233 cls : 'roo-navigation-bar-text'
27236 var bottom = new Roo.bootstrap.Element({
27238 cls : 'roo-navigation-bar-text'
27241 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27242 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27244 var topText = new Roo.bootstrap.Element({
27246 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27249 var bottomText = new Roo.bootstrap.Element({
27251 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27254 topText.onRender(top.el, null);
27255 bottomText.onRender(bottom.el, null);
27258 item.bottomEl = bottom;
27261 this.barItems.push(item);
27266 getActive : function()
27268 var active = false;
27270 Roo.each(this.barItems, function(v){
27272 if (!v.isActive()) {
27284 setActiveItem : function(item)
27288 Roo.each(this.barItems, function(v){
27289 if (v.rid == item.rid) {
27293 if (v.isActive()) {
27294 v.setActive(false);
27299 item.setActive(true);
27301 this.fireEvent('changed', this, item, prev);
27304 getBarItem: function(rid)
27308 Roo.each(this.barItems, function(e) {
27309 if (e.rid != rid) {
27320 indexOfItem : function(item)
27324 Roo.each(this.barItems, function(v, i){
27326 if (v.rid != item.rid) {
27337 setActiveNext : function()
27339 var i = this.indexOfItem(this.getActive());
27341 if (i > this.barItems.length) {
27345 this.setActiveItem(this.barItems[i+1]);
27348 setActivePrev : function()
27350 var i = this.indexOfItem(this.getActive());
27356 this.setActiveItem(this.barItems[i-1]);
27359 format : function()
27361 if(!this.barItems.length){
27365 var width = 100 / this.barItems.length;
27367 Roo.each(this.barItems, function(i){
27368 i.el.setStyle('width', width + '%');
27369 i.topEl.el.setStyle('width', width + '%');
27370 i.bottomEl.el.setStyle('width', width + '%');
27379 * Nav Progress Item
27384 * @class Roo.bootstrap.NavProgressItem
27385 * @extends Roo.bootstrap.Component
27386 * Bootstrap NavProgressItem class
27387 * @cfg {String} rid the reference id
27388 * @cfg {Boolean} active (true|false) Is item active default false
27389 * @cfg {Boolean} disabled (true|false) Is item active default false
27390 * @cfg {String} html
27391 * @cfg {String} position (top|bottom) text position default bottom
27392 * @cfg {String} icon show icon instead of number
27395 * Create a new NavProgressItem
27396 * @param {Object} config The config object
27398 Roo.bootstrap.NavProgressItem = function(config){
27399 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27404 * The raw click event for the entire grid.
27405 * @param {Roo.bootstrap.NavProgressItem} this
27406 * @param {Roo.EventObject} e
27413 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
27419 position : 'bottom',
27422 getAutoCreate : function()
27424 var iconCls = 'roo-navigation-bar-item-icon';
27426 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27430 cls: 'roo-navigation-bar-item',
27440 cfg.cls += ' active';
27443 cfg.cls += ' disabled';
27449 disable : function()
27451 this.setDisabled(true);
27454 enable : function()
27456 this.setDisabled(false);
27459 initEvents: function()
27461 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27463 this.iconEl.on('click', this.onClick, this);
27466 onClick : function(e)
27468 e.preventDefault();
27474 if(this.fireEvent('click', this, e) === false){
27478 this.parent().setActiveItem(this);
27481 isActive: function ()
27483 return this.active;
27486 setActive : function(state)
27488 if(this.active == state){
27492 this.active = state;
27495 this.el.addClass('active');
27499 this.el.removeClass('active');
27504 setDisabled : function(state)
27506 if(this.disabled == state){
27510 this.disabled = state;
27513 this.el.addClass('disabled');
27517 this.el.removeClass('disabled');
27520 tooltipEl : function()
27522 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27535 * @class Roo.bootstrap.FieldLabel
27536 * @extends Roo.bootstrap.Component
27537 * Bootstrap FieldLabel class
27538 * @cfg {String} html contents of the element
27539 * @cfg {String} tag tag of the element default label
27540 * @cfg {String} cls class of the element
27541 * @cfg {String} target label target
27542 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27543 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27544 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27545 * @cfg {String} iconTooltip default "This field is required"
27548 * Create a new FieldLabel
27549 * @param {Object} config The config object
27552 Roo.bootstrap.FieldLabel = function(config){
27553 Roo.bootstrap.Element.superclass.constructor.call(this, config);
27558 * Fires after the field has been marked as invalid.
27559 * @param {Roo.form.FieldLabel} this
27560 * @param {String} msg The validation message
27565 * Fires after the field has been validated with no errors.
27566 * @param {Roo.form.FieldLabel} this
27572 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
27579 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27580 validClass : 'text-success fa fa-lg fa-check',
27581 iconTooltip : 'This field is required',
27583 getAutoCreate : function(){
27587 cls : 'roo-bootstrap-field-label ' + this.cls,
27593 tooltip : this.iconTooltip
27605 initEvents: function()
27607 Roo.bootstrap.Element.superclass.initEvents.call(this);
27609 this.iconEl = this.el.select('i', true).first();
27611 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27613 Roo.bootstrap.FieldLabel.register(this);
27617 * Mark this field as valid
27619 markValid : function()
27621 this.iconEl.show();
27623 this.iconEl.removeClass(this.invalidClass);
27625 this.iconEl.addClass(this.validClass);
27627 this.fireEvent('valid', this);
27631 * Mark this field as invalid
27632 * @param {String} msg The validation message
27634 markInvalid : function(msg)
27636 this.iconEl.show();
27638 this.iconEl.removeClass(this.validClass);
27640 this.iconEl.addClass(this.invalidClass);
27642 this.fireEvent('invalid', this, msg);
27648 Roo.apply(Roo.bootstrap.FieldLabel, {
27653 * register a FieldLabel Group
27654 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27656 register : function(label)
27658 if(this.groups.hasOwnProperty(label.target)){
27662 this.groups[label.target] = label;
27666 * fetch a FieldLabel Group based on the target
27667 * @param {string} target
27668 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27670 get: function(target) {
27671 if (typeof(this.groups[target]) == 'undefined') {
27675 return this.groups[target] ;
27684 * page DateSplitField.
27690 * @class Roo.bootstrap.DateSplitField
27691 * @extends Roo.bootstrap.Component
27692 * Bootstrap DateSplitField class
27693 * @cfg {string} fieldLabel - the label associated
27694 * @cfg {Number} labelWidth set the width of label (0-12)
27695 * @cfg {String} labelAlign (top|left)
27696 * @cfg {Boolean} dayAllowBlank (true|false) default false
27697 * @cfg {Boolean} monthAllowBlank (true|false) default false
27698 * @cfg {Boolean} yearAllowBlank (true|false) default false
27699 * @cfg {string} dayPlaceholder
27700 * @cfg {string} monthPlaceholder
27701 * @cfg {string} yearPlaceholder
27702 * @cfg {string} dayFormat default 'd'
27703 * @cfg {string} monthFormat default 'm'
27704 * @cfg {string} yearFormat default 'Y'
27708 * Create a new DateSplitField
27709 * @param {Object} config The config object
27712 Roo.bootstrap.DateSplitField = function(config){
27713 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27719 * getting the data of years
27720 * @param {Roo.bootstrap.DateSplitField} this
27721 * @param {Object} years
27726 * getting the data of days
27727 * @param {Roo.bootstrap.DateSplitField} this
27728 * @param {Object} days
27733 * Fires after the field has been marked as invalid.
27734 * @param {Roo.form.Field} this
27735 * @param {String} msg The validation message
27740 * Fires after the field has been validated with no errors.
27741 * @param {Roo.form.Field} this
27747 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
27750 labelAlign : 'top',
27752 dayAllowBlank : false,
27753 monthAllowBlank : false,
27754 yearAllowBlank : false,
27755 dayPlaceholder : '',
27756 monthPlaceholder : '',
27757 yearPlaceholder : '',
27761 isFormField : true,
27763 getAutoCreate : function()
27767 cls : 'row roo-date-split-field-group',
27772 cls : 'form-hidden-field roo-date-split-field-group-value',
27778 if(this.fieldLabel){
27781 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27785 html : this.fieldLabel
27791 Roo.each(['day', 'month', 'year'], function(t){
27794 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27801 inputEl: function ()
27803 return this.el.select('.roo-date-split-field-group-value', true).first();
27806 onRender : function(ct, position)
27810 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27812 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27814 this.dayField = new Roo.bootstrap.ComboBox({
27815 allowBlank : this.dayAllowBlank,
27816 alwaysQuery : true,
27817 displayField : 'value',
27820 forceSelection : true,
27822 placeholder : this.dayPlaceholder,
27823 selectOnFocus : true,
27824 tpl : '<div class="select2-result"><b>{value}</b></div>',
27825 triggerAction : 'all',
27827 valueField : 'value',
27828 store : new Roo.data.SimpleStore({
27829 data : (function() {
27831 _this.fireEvent('days', _this, days);
27834 fields : [ 'value' ]
27837 select : function (_self, record, index)
27839 _this.setValue(_this.getValue());
27844 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27846 this.monthField = new Roo.bootstrap.MonthField({
27847 after : '<i class=\"fa fa-calendar\"></i>',
27848 allowBlank : this.monthAllowBlank,
27849 placeholder : this.monthPlaceholder,
27852 render : function (_self)
27854 this.el.select('span.input-group-addon', true).first().on('click', function(e){
27855 e.preventDefault();
27859 select : function (_self, oldvalue, newvalue)
27861 _this.setValue(_this.getValue());
27866 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27868 this.yearField = new Roo.bootstrap.ComboBox({
27869 allowBlank : this.yearAllowBlank,
27870 alwaysQuery : true,
27871 displayField : 'value',
27874 forceSelection : true,
27876 placeholder : this.yearPlaceholder,
27877 selectOnFocus : true,
27878 tpl : '<div class="select2-result"><b>{value}</b></div>',
27879 triggerAction : 'all',
27881 valueField : 'value',
27882 store : new Roo.data.SimpleStore({
27883 data : (function() {
27885 _this.fireEvent('years', _this, years);
27888 fields : [ 'value' ]
27891 select : function (_self, record, index)
27893 _this.setValue(_this.getValue());
27898 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
27901 setValue : function(v, format)
27903 this.inputEl.dom.value = v;
27905 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
27907 var d = Date.parseDate(v, f);
27914 this.setDay(d.format(this.dayFormat));
27915 this.setMonth(d.format(this.monthFormat));
27916 this.setYear(d.format(this.yearFormat));
27923 setDay : function(v)
27925 this.dayField.setValue(v);
27926 this.inputEl.dom.value = this.getValue();
27931 setMonth : function(v)
27933 this.monthField.setValue(v, true);
27934 this.inputEl.dom.value = this.getValue();
27939 setYear : function(v)
27941 this.yearField.setValue(v);
27942 this.inputEl.dom.value = this.getValue();
27947 getDay : function()
27949 return this.dayField.getValue();
27952 getMonth : function()
27954 return this.monthField.getValue();
27957 getYear : function()
27959 return this.yearField.getValue();
27962 getValue : function()
27964 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
27966 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
27976 this.inputEl.dom.value = '';
27981 validate : function()
27983 var d = this.dayField.validate();
27984 var m = this.monthField.validate();
27985 var y = this.yearField.validate();
27990 (!this.dayAllowBlank && !d) ||
27991 (!this.monthAllowBlank && !m) ||
27992 (!this.yearAllowBlank && !y)
27997 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28006 this.markInvalid();
28011 markValid : function()
28014 var label = this.el.select('label', true).first();
28015 var icon = this.el.select('i.fa-star', true).first();
28021 this.fireEvent('valid', this);
28025 * Mark this field as invalid
28026 * @param {String} msg The validation message
28028 markInvalid : function(msg)
28031 var label = this.el.select('label', true).first();
28032 var icon = this.el.select('i.fa-star', true).first();
28034 if(label && !icon){
28035 this.el.select('.roo-date-split-field-label', true).createChild({
28037 cls : 'text-danger fa fa-lg fa-star',
28038 tooltip : 'This field is required',
28039 style : 'margin-right:5px;'
28043 this.fireEvent('invalid', this, msg);
28046 clearInvalid : function()
28048 var label = this.el.select('label', true).first();
28049 var icon = this.el.select('i.fa-star', true).first();
28055 this.fireEvent('valid', this);
28058 getName: function()