4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
23 * Do not use directly - it does not do anything..
24 * @param {Object} config The config object
29 Roo.bootstrap.Component = function(config){
30 Roo.bootstrap.Component.superclass.constructor.call(this, config);
34 * @event childrenrendered
35 * Fires when the children have been rendered..
36 * @param {Roo.bootstrap.Component} this
38 "childrenrendered" : true
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
50 allowDomMove : false, // to stop relocations in parent onRender...
60 * Initialize Events for the element
62 initEvents : function() { },
68 can_build_overlaid : true,
70 container_method : false,
77 // returns the parent component..
78 return Roo.ComponentMgr.get(this.parentId)
84 onRender : function(ct, position)
86 // Roo.log("Call onRender: " + this.xtype);
88 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
91 if (this.el.attr('xtype')) {
92 this.el.attr('xtypex', this.el.attr('xtype'));
93 this.el.dom.removeAttribute('xtype');
103 var cfg = Roo.apply({}, this.getAutoCreate());
104 cfg.id = this.id || Roo.id();
106 // fill in the extra attributes
107 if (this.xattr && typeof(this.xattr) =='object') {
108 for (var i in this.xattr) {
109 cfg[i] = this.xattr[i];
114 cfg.dataId = this.dataId;
118 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121 if (this.style) { // fixme needs to support more complex style data.
122 cfg.style = this.style;
126 cfg.name = this.name;
129 this.el = ct.createChild(cfg, position);
132 this.tooltipEl().attr('tooltip', this.tooltip);
135 if(this.tabIndex !== undefined){
136 this.el.dom.setAttribute('tabIndex', this.tabIndex);
143 * Fetch the element to add children to
144 * @return {Roo.Element} defaults to this.el
146 getChildContainer : function()
151 * Fetch the element to display the tooltip on.
152 * @return {Roo.Element} defaults to this.el
154 tooltipEl : function()
159 addxtype : function(tree,cntr)
163 cn = Roo.factory(tree);
165 cn.parentType = this.xtype; //??
166 cn.parentId = this.id;
168 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
169 if (typeof(cn.container_method) == 'string') {
170 cntr = cn.container_method;
174 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
176 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
178 var build_from_html = Roo.XComponent.build_from_html;
180 var is_body = (tree.xtype == 'Body') ;
182 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
184 var self_cntr_el = Roo.get(this[cntr](false));
186 // do not try and build conditional elements
187 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
191 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
192 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
193 return this.addxtypeChild(tree,cntr);
196 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202 Roo.log('skipping render');
208 if (!build_from_html) {
212 // this i think handles overlaying multiple children of the same type
213 // with the sam eelement.. - which might be buggy..
215 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
221 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
225 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
230 addxtypeChild : function (tree, cntr)
232 Roo.debug && Roo.log('addxtypeChild:' + cntr);
234 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
237 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
238 (typeof(tree['flexy:foreach']) != 'undefined');
242 skip_children = false;
243 // render the element if it's not BODY.
244 if (tree.xtype != 'Body') {
246 cn = Roo.factory(tree);
248 cn.parentType = this.xtype; //??
249 cn.parentId = this.id;
251 var build_from_html = Roo.XComponent.build_from_html;
254 // does the container contain child eleemnts with 'xtype' attributes.
255 // that match this xtype..
256 // note - when we render we create these as well..
257 // so we should check to see if body has xtype set.
258 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
260 var self_cntr_el = Roo.get(this[cntr](false));
261 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
263 //Roo.log(Roo.XComponent.build_from_html);
264 //Roo.log("got echild:");
267 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
268 // and are not displayed -this causes this to use up the wrong element when matching.
269 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
272 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
273 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
279 //echild.dom.removeAttribute('xtype');
281 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
282 Roo.debug && Roo.log(self_cntr_el);
283 Roo.debug && Roo.log(echild);
284 Roo.debug && Roo.log(cn);
290 // if object has flexy:if - then it may or may not be rendered.
291 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
292 // skip a flexy if element.
293 Roo.debug && Roo.log('skipping render');
294 Roo.debug && Roo.log(tree);
296 Roo.debug && Roo.log('skipping all children');
297 skip_children = true;
302 // actually if flexy:foreach is found, we really want to create
303 // multiple copies here...
305 //Roo.log(this[cntr]());
306 cn.render(this[cntr](true));
308 // then add the element..
316 if (typeof (tree.menu) != 'undefined') {
317 tree.menu.parentType = cn.xtype;
318 tree.menu.triggerEl = cn.el;
319 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
323 if (!tree.items || !tree.items.length) {
327 var items = tree.items;
330 //Roo.log(items.length);
332 if (!skip_children) {
333 for(var i =0;i < items.length;i++) {
334 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
340 this.fireEvent('childrenrendered', this);
345 * Show a component - removes 'hidden' class
350 this.el.removeClass('hidden');
354 * Hide a component - adds 'hidden' class
358 if (this.el && !this.el.hasClass('hidden')) {
359 this.el.addClass('hidden');
373 * @class Roo.bootstrap.Body
374 * @extends Roo.bootstrap.Component
375 * Bootstrap Body class
379 * @param {Object} config The config object
382 Roo.bootstrap.Body = function(config){
383 Roo.bootstrap.Body.superclass.constructor.call(this, config);
384 this.el = Roo.get(document.body);
385 if (this.cls && this.cls.length) {
386 Roo.get(document.body).addClass(this.cls);
390 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
395 onRender : function(ct, position)
397 /* Roo.log("Roo.bootstrap.Body - onRender");
398 if (this.cls && this.cls.length) {
399 Roo.get(document.body).addClass(this.cls);
419 * @class Roo.bootstrap.ButtonGroup
420 * @extends Roo.bootstrap.Component
421 * Bootstrap ButtonGroup class
422 * @cfg {String} size lg | sm | xs (default empty normal)
423 * @cfg {String} align vertical | justified (default none)
424 * @cfg {String} direction up | down (default down)
425 * @cfg {Boolean} toolbar false | true
426 * @cfg {Boolean} btn true | false
431 * @param {Object} config The config object
434 Roo.bootstrap.ButtonGroup = function(config){
435 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
438 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
446 getAutoCreate : function(){
452 cfg.html = this.html || cfg.html;
463 if (['vertical','justified'].indexOf(this.align)!==-1) {
464 cfg.cls = 'btn-group-' + this.align;
466 if (this.align == 'justified') {
467 console.log(this.items);
471 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
472 cfg.cls += ' btn-group-' + this.size;
475 if (this.direction == 'up') {
476 cfg.cls += ' dropup' ;
492 * @class Roo.bootstrap.Button
493 * @extends Roo.bootstrap.Component
494 * Bootstrap Button class
495 * @cfg {String} html The button content
496 * @cfg {String} weight ( primary | success | info | warning | danger | link ) default
497 * @cfg {String} size ( lg | sm | xs)
498 * @cfg {String} tag ( a | input | submit)
499 * @cfg {String} href empty or href
500 * @cfg {Boolean} disabled default false;
501 * @cfg {Boolean} isClose default false;
502 * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
503 * @cfg {String} badge text for badge
504 * @cfg {String} theme default
505 * @cfg {Boolean} inverse
506 * @cfg {Boolean} toggle
507 * @cfg {String} ontext text for on toggle state
508 * @cfg {String} offtext text for off toggle state
509 * @cfg {Boolean} defaulton
510 * @cfg {Boolean} preventDefault default true
511 * @cfg {Boolean} removeClass remove the standard class..
512 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
515 * Create a new button
516 * @param {Object} config The config object
520 Roo.bootstrap.Button = function(config){
521 Roo.bootstrap.Button.superclass.constructor.call(this, config);
526 * When a butotn is pressed
527 * @param {Roo.bootstrap.Button} this
528 * @param {Roo.EventObject} e
533 * After the button has been toggles
534 * @param {Roo.EventObject} e
535 * @param {boolean} pressed (also available as button.pressed)
541 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
559 preventDefault: true,
568 getAutoCreate : function(){
576 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
577 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
582 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
584 if (this.toggle == true) {
587 cls: 'slider-frame roo-button',
592 'data-off-text':'OFF',
593 cls: 'slider-button',
599 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
600 cfg.cls += ' '+this.weight;
609 cfg["aria-hidden"] = true;
611 cfg.html = "×";
617 if (this.theme==='default') {
618 cfg.cls = 'btn roo-button';
620 //if (this.parentType != 'Navbar') {
621 this.weight = this.weight.length ? this.weight : 'default';
623 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
625 cfg.cls += ' btn-' + this.weight;
627 } else if (this.theme==='glow') {
630 cfg.cls = 'btn-glow roo-button';
632 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
634 cfg.cls += ' ' + this.weight;
640 this.cls += ' inverse';
645 cfg.cls += ' active';
649 cfg.disabled = 'disabled';
653 Roo.log('changing to ul' );
655 this.glyphicon = 'caret';
658 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
660 //gsRoo.log(this.parentType);
661 if (this.parentType === 'Navbar' && !this.parent().bar) {
662 Roo.log('changing to li?');
671 href : this.href || '#'
674 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
675 cfg.cls += ' dropdown';
682 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
684 if (this.glyphicon) {
685 cfg.html = ' ' + cfg.html;
690 cls: 'glyphicon glyphicon-' + this.glyphicon
700 // cfg.cls='btn roo-button';
704 var value = cfg.html;
709 cls: 'glyphicon glyphicon-' + this.glyphicon,
728 cfg.cls += ' dropdown';
729 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
732 if (cfg.tag !== 'a' && this.href !== '') {
733 throw "Tag must be a to set href.";
734 } else if (this.href.length > 0) {
735 cfg.href = this.href;
738 if(this.removeClass){
743 cfg.target = this.target;
748 initEvents: function() {
749 // Roo.log('init events?');
750 // Roo.log(this.el.dom);
753 if (typeof (this.menu) != 'undefined') {
754 this.menu.parentType = this.xtype;
755 this.menu.triggerEl = this.el;
756 this.addxtype(Roo.apply({}, this.menu));
760 if (this.el.hasClass('roo-button')) {
761 this.el.on('click', this.onClick, this);
763 this.el.select('.roo-button').on('click', this.onClick, this);
766 if(this.removeClass){
767 this.el.on('click', this.onClick, this);
770 this.el.enableDisplayMode();
773 onClick : function(e)
780 Roo.log('button on click ');
781 if(this.preventDefault){
784 if (this.pressed === true || this.pressed === false) {
785 this.pressed = !this.pressed;
786 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
787 this.fireEvent('toggle', this, e, this.pressed);
791 this.fireEvent('click', this, e);
795 * Enables this button
799 this.disabled = false;
800 this.el.removeClass('disabled');
804 * Disable this button
808 this.disabled = true;
809 this.el.addClass('disabled');
812 * sets the active state on/off,
813 * @param {Boolean} state (optional) Force a particular state
815 setActive : function(v) {
817 this.el[v ? 'addClass' : 'removeClass']('active');
820 * toggles the current active state
822 toggleActive : function()
824 var active = this.el.hasClass('active');
825 this.setActive(!active);
829 setText : function(str)
831 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
835 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
858 * @class Roo.bootstrap.Column
859 * @extends Roo.bootstrap.Component
860 * Bootstrap Column class
861 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
862 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
863 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
864 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
865 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
866 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
867 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
868 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
871 * @cfg {Boolean} hidden (true|false) hide the element
872 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
873 * @cfg {String} fa (ban|check|...) font awesome icon
874 * @cfg {Number} fasize (1|2|....) font awsome size
876 * @cfg {String} icon (info-sign|check|...) glyphicon name
878 * @cfg {String} html content of column.
881 * Create a new Column
882 * @param {Object} config The config object
885 Roo.bootstrap.Column = function(config){
886 Roo.bootstrap.Column.superclass.constructor.call(this, config);
889 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
907 getAutoCreate : function(){
908 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
916 ['xs','sm','md','lg'].map(function(size){
917 //Roo.log( size + ':' + settings[size]);
919 if (settings[size+'off'] !== false) {
920 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
923 if (settings[size] === false) {
927 if (!settings[size]) { // 0 = hidden
928 cfg.cls += ' hidden-' + size;
931 cfg.cls += ' col-' + size + '-' + settings[size];
936 cfg.cls += ' hidden';
939 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
940 cfg.cls +=' alert alert-' + this.alert;
944 if (this.html.length) {
945 cfg.html = this.html;
949 if (this.fasize > 1) {
950 fasize = ' fa-' + this.fasize + 'x';
952 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
957 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
976 * @class Roo.bootstrap.Container
977 * @extends Roo.bootstrap.Component
978 * Bootstrap Container class
979 * @cfg {Boolean} jumbotron is it a jumbotron element
980 * @cfg {String} html content of element
981 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
982 * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
983 * @cfg {String} header content of header (for panel)
984 * @cfg {String} footer content of footer (for panel)
985 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
986 * @cfg {String} tag (header|aside|section) type of HTML tag.
987 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
988 * @cfg {String} fa font awesome icon
989 * @cfg {String} icon (info-sign|check|...) glyphicon name
990 * @cfg {Boolean} hidden (true|false) hide the element
991 * @cfg {Boolean} expandable (true|false) default false
992 * @cfg {Boolean} expanded (true|false) default true
993 * @cfg {String} rheader contet on the right of header
994 * @cfg {Boolean} clickable (true|false) default false
998 * Create a new Container
999 * @param {Object} config The config object
1002 Roo.bootstrap.Container = function(config){
1003 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1009 * After the panel has been expand
1011 * @param {Roo.bootstrap.Container} this
1016 * After the panel has been collapsed
1018 * @param {Roo.bootstrap.Container} this
1023 * When a element is chick
1024 * @param {Roo.bootstrap.Container} this
1025 * @param {Roo.EventObject} e
1031 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1049 getChildContainer : function() {
1055 if (this.panel.length) {
1056 return this.el.select('.panel-body',true).first();
1063 getAutoCreate : function(){
1066 tag : this.tag || 'div',
1070 if (this.jumbotron) {
1071 cfg.cls = 'jumbotron';
1076 // - this is applied by the parent..
1078 // cfg.cls = this.cls + '';
1081 if (this.sticky.length) {
1083 var bd = Roo.get(document.body);
1084 if (!bd.hasClass('bootstrap-sticky')) {
1085 bd.addClass('bootstrap-sticky');
1086 Roo.select('html',true).setStyle('height', '100%');
1089 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1093 if (this.well.length) {
1094 switch (this.well) {
1097 cfg.cls +=' well well-' +this.well;
1106 cfg.cls += ' hidden';
1110 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1111 cfg.cls +=' alert alert-' + this.alert;
1116 if (this.panel.length) {
1117 cfg.cls += ' panel panel-' + this.panel;
1119 if (this.header.length) {
1123 if(this.expandable){
1125 cfg.cls = cfg.cls + ' expandable';
1129 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1137 cls : 'panel-title',
1138 html : (this.expandable ? ' ' : '') + this.header
1142 cls: 'panel-header-right',
1148 cls : 'panel-heading',
1149 style : this.expandable ? 'cursor: pointer' : '',
1157 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1162 if (this.footer.length) {
1164 cls : 'panel-footer',
1173 body.html = this.html || cfg.html;
1174 // prefix with the icons..
1176 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1179 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1184 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1185 cfg.cls = 'container';
1191 initEvents: function()
1193 if(this.expandable){
1194 var headerEl = this.headerEl();
1197 headerEl.on('click', this.onToggleClick, this);
1202 this.el.on('click', this.onClick, this);
1207 onToggleClick : function()
1209 var headerEl = this.headerEl();
1225 if(this.fireEvent('expand', this)) {
1227 this.expanded = true;
1229 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1231 this.el.select('.panel-body',true).first().removeClass('hide');
1233 var toggleEl = this.toggleEl();
1239 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1244 collapse : function()
1246 if(this.fireEvent('collapse', this)) {
1248 this.expanded = false;
1250 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1251 this.el.select('.panel-body',true).first().addClass('hide');
1253 var toggleEl = this.toggleEl();
1259 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1263 toggleEl : function()
1265 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1269 return this.el.select('.panel-heading .fa',true).first();
1272 headerEl : function()
1274 if(!this.el || !this.panel.length || !this.header.length){
1278 return this.el.select('.panel-heading',true).first()
1281 titleEl : function()
1283 if(!this.el || !this.panel.length || !this.header.length){
1287 return this.el.select('.panel-title',true).first();
1290 setTitle : function(v)
1292 var titleEl = this.titleEl();
1298 titleEl.dom.innerHTML = v;
1301 getTitle : function()
1304 var titleEl = this.titleEl();
1310 return titleEl.dom.innerHTML;
1313 setRightTitle : function(v)
1315 var t = this.el.select('.panel-header-right',true).first();
1321 t.dom.innerHTML = v;
1324 onClick : function(e)
1328 this.fireEvent('click', this, e);
1342 * @class Roo.bootstrap.Img
1343 * @extends Roo.bootstrap.Component
1344 * Bootstrap Img class
1345 * @cfg {Boolean} imgResponsive false | true
1346 * @cfg {String} border rounded | circle | thumbnail
1347 * @cfg {String} src image source
1348 * @cfg {String} alt image alternative text
1349 * @cfg {String} href a tag href
1350 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1351 * @cfg {String} xsUrl xs image source
1352 * @cfg {String} smUrl sm image source
1353 * @cfg {String} mdUrl md image source
1354 * @cfg {String} lgUrl lg image source
1357 * Create a new Input
1358 * @param {Object} config The config object
1361 Roo.bootstrap.Img = function(config){
1362 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1368 * The img click event for the img.
1369 * @param {Roo.EventObject} e
1375 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1377 imgResponsive: true,
1387 getAutoCreate : function()
1389 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1390 return this.createSingleImg();
1395 cls: 'roo-image-responsive-group',
1400 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1402 if(!_this[size + 'Url']){
1408 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1409 html: _this.html || cfg.html,
1410 src: _this[size + 'Url']
1413 img.cls += ' roo-image-responsive-' + size;
1415 var s = ['xs', 'sm', 'md', 'lg'];
1417 s.splice(s.indexOf(size), 1);
1419 Roo.each(s, function(ss){
1420 img.cls += ' hidden-' + ss;
1423 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1424 cfg.cls += ' img-' + _this.border;
1428 cfg.alt = _this.alt;
1441 a.target = _this.target;
1445 cfg.cn.push((_this.href) ? a : img);
1452 createSingleImg : function()
1456 cls: (this.imgResponsive) ? 'img-responsive' : '',
1458 src : 'about:blank' // just incase src get's set to undefined?!?
1461 cfg.html = this.html || cfg.html;
1463 cfg.src = this.src || cfg.src;
1465 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1466 cfg.cls += ' img-' + this.border;
1483 a.target = this.target;
1488 return (this.href) ? a : cfg;
1491 initEvents: function()
1494 this.el.on('click', this.onClick, this);
1499 onClick : function(e)
1501 Roo.log('img onclick');
1502 this.fireEvent('click', this, e);
1516 * @class Roo.bootstrap.Link
1517 * @extends Roo.bootstrap.Component
1518 * Bootstrap Link Class
1519 * @cfg {String} alt image alternative text
1520 * @cfg {String} href a tag href
1521 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1522 * @cfg {String} html the content of the link.
1523 * @cfg {String} anchor name for the anchor link
1525 * @cfg {Boolean} preventDefault (true | false) default false
1529 * Create a new Input
1530 * @param {Object} config The config object
1533 Roo.bootstrap.Link = function(config){
1534 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1540 * The img click event for the img.
1541 * @param {Roo.EventObject} e
1547 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1551 preventDefault: false,
1555 getAutoCreate : function()
1561 // anchor's do not require html/href...
1562 if (this.anchor === false) {
1563 cfg.html = this.html || '';
1564 cfg.href = this.href || '#';
1566 cfg.name = this.anchor;
1567 if (this.html !== false) {
1568 cfg.html = this.html;
1570 if (this.href !== false) {
1571 cfg.href = this.href;
1575 if(this.alt !== false){
1580 if(this.target !== false) {
1581 cfg.target = this.target;
1587 initEvents: function() {
1589 if(!this.href || this.preventDefault){
1590 this.el.on('click', this.onClick, this);
1594 onClick : function(e)
1596 if(this.preventDefault){
1599 //Roo.log('img onclick');
1600 this.fireEvent('click', this, e);
1613 * @class Roo.bootstrap.Header
1614 * @extends Roo.bootstrap.Component
1615 * Bootstrap Header class
1616 * @cfg {String} html content of header
1617 * @cfg {Number} level (1|2|3|4|5|6) default 1
1620 * Create a new Header
1621 * @param {Object} config The config object
1625 Roo.bootstrap.Header = function(config){
1626 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1629 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1637 getAutoCreate : function(){
1642 tag: 'h' + (1 *this.level),
1643 html: this.html || ''
1655 * Ext JS Library 1.1.1
1656 * Copyright(c) 2006-2007, Ext JS, LLC.
1658 * Originally Released Under LGPL - original licence link has changed is not relivant.
1661 * <script type="text/javascript">
1665 * @class Roo.bootstrap.MenuMgr
1666 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1669 Roo.bootstrap.MenuMgr = function(){
1670 var menus, active, groups = {}, attached = false, lastShow = new Date();
1672 // private - called when first menu is created
1675 active = new Roo.util.MixedCollection();
1676 Roo.get(document).addKeyListener(27, function(){
1677 if(active.length > 0){
1685 if(active && active.length > 0){
1686 var c = active.clone();
1696 if(active.length < 1){
1697 Roo.get(document).un("mouseup", onMouseDown);
1705 var last = active.last();
1706 lastShow = new Date();
1709 Roo.get(document).on("mouseup", onMouseDown);
1714 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1715 m.parentMenu.activeChild = m;
1716 }else if(last && last.isVisible()){
1717 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1722 function onBeforeHide(m){
1724 m.activeChild.hide();
1726 if(m.autoHideTimer){
1727 clearTimeout(m.autoHideTimer);
1728 delete m.autoHideTimer;
1733 function onBeforeShow(m){
1734 var pm = m.parentMenu;
1735 if(!pm && !m.allowOtherMenus){
1737 }else if(pm && pm.activeChild && active != m){
1738 pm.activeChild.hide();
1742 // private this should really trigger on mouseup..
1743 function onMouseDown(e){
1744 Roo.log("on Mouse Up");
1745 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1755 function onBeforeCheck(mi, state){
1757 var g = groups[mi.group];
1758 for(var i = 0, l = g.length; i < l; i++){
1760 g[i].setChecked(false);
1769 * Hides all menus that are currently visible
1771 hideAll : function(){
1776 register : function(menu){
1780 menus[menu.id] = menu;
1781 menu.on("beforehide", onBeforeHide);
1782 menu.on("hide", onHide);
1783 menu.on("beforeshow", onBeforeShow);
1784 menu.on("show", onShow);
1786 if(g && menu.events["checkchange"]){
1790 groups[g].push(menu);
1791 menu.on("checkchange", onCheck);
1796 * Returns a {@link Roo.menu.Menu} object
1797 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1798 * be used to generate and return a new Menu instance.
1800 get : function(menu){
1801 if(typeof menu == "string"){ // menu id
1803 }else if(menu.events){ // menu instance
1806 /*else if(typeof menu.length == 'number'){ // array of menu items?
1807 return new Roo.bootstrap.Menu({items:menu});
1808 }else{ // otherwise, must be a config
1809 return new Roo.bootstrap.Menu(menu);
1816 unregister : function(menu){
1817 delete menus[menu.id];
1818 menu.un("beforehide", onBeforeHide);
1819 menu.un("hide", onHide);
1820 menu.un("beforeshow", onBeforeShow);
1821 menu.un("show", onShow);
1823 if(g && menu.events["checkchange"]){
1824 groups[g].remove(menu);
1825 menu.un("checkchange", onCheck);
1830 registerCheckable : function(menuItem){
1831 var g = menuItem.group;
1836 groups[g].push(menuItem);
1837 menuItem.on("beforecheckchange", onBeforeCheck);
1842 unregisterCheckable : function(menuItem){
1843 var g = menuItem.group;
1845 groups[g].remove(menuItem);
1846 menuItem.un("beforecheckchange", onBeforeCheck);
1858 * @class Roo.bootstrap.Menu
1859 * @extends Roo.bootstrap.Component
1860 * Bootstrap Menu class - container for MenuItems
1861 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1865 * @param {Object} config The config object
1869 Roo.bootstrap.Menu = function(config){
1870 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1871 if (this.registerMenu) {
1872 Roo.bootstrap.MenuMgr.register(this);
1877 * Fires before this menu is displayed
1878 * @param {Roo.menu.Menu} this
1883 * Fires before this menu is hidden
1884 * @param {Roo.menu.Menu} this
1889 * Fires after this menu is displayed
1890 * @param {Roo.menu.Menu} this
1895 * Fires after this menu is hidden
1896 * @param {Roo.menu.Menu} this
1901 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1902 * @param {Roo.menu.Menu} this
1903 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1904 * @param {Roo.EventObject} e
1909 * Fires when the mouse is hovering over this menu
1910 * @param {Roo.menu.Menu} this
1911 * @param {Roo.EventObject} e
1912 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1917 * Fires when the mouse exits this menu
1918 * @param {Roo.menu.Menu} this
1919 * @param {Roo.EventObject} e
1920 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1925 * Fires when a menu item contained in this menu is clicked
1926 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1927 * @param {Roo.EventObject} e
1931 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1934 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1938 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1941 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1943 registerMenu : true,
1945 menuItems :false, // stores the menu items..
1951 getChildContainer : function() {
1955 getAutoCreate : function(){
1957 //if (['right'].indexOf(this.align)!==-1) {
1958 // cfg.cn[1].cls += ' pull-right'
1964 cls : 'dropdown-menu' ,
1965 style : 'z-index:1000'
1969 if (this.type === 'submenu') {
1970 cfg.cls = 'submenu active';
1972 if (this.type === 'treeview') {
1973 cfg.cls = 'treeview-menu';
1978 initEvents : function() {
1980 // Roo.log("ADD event");
1981 // Roo.log(this.triggerEl.dom);
1982 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1984 this.triggerEl.addClass('dropdown-toggle');
1987 this.el.on('touchstart' , this.onTouch, this);
1989 this.el.on('click' , this.onClick, this);
1991 this.el.on("mouseover", this.onMouseOver, this);
1992 this.el.on("mouseout", this.onMouseOut, this);
1996 findTargetItem : function(e)
1998 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2002 //Roo.log(t); Roo.log(t.id);
2004 //Roo.log(this.menuitems);
2005 return this.menuitems.get(t.id);
2007 //return this.items.get(t.menuItemId);
2013 onTouch : function(e)
2015 //e.stopEvent(); this make the user popdown broken
2019 onClick : function(e)
2021 Roo.log("menu.onClick");
2022 var t = this.findTargetItem(e);
2023 if(!t || t.isContainer){
2028 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2029 if(t == this.activeItem && t.shouldDeactivate(e)){
2030 this.activeItem.deactivate();
2031 delete this.activeItem;
2035 this.setActiveItem(t, true);
2043 Roo.log('pass click event');
2047 this.fireEvent("click", this, t, e);
2051 onMouseOver : function(e){
2052 var t = this.findTargetItem(e);
2055 // if(t.canActivate && !t.disabled){
2056 // this.setActiveItem(t, true);
2060 this.fireEvent("mouseover", this, e, t);
2062 isVisible : function(){
2063 return !this.hidden;
2065 onMouseOut : function(e){
2066 var t = this.findTargetItem(e);
2069 // if(t == this.activeItem && t.shouldDeactivate(e)){
2070 // this.activeItem.deactivate();
2071 // delete this.activeItem;
2074 this.fireEvent("mouseout", this, e, t);
2079 * Displays this menu relative to another element
2080 * @param {String/HTMLElement/Roo.Element} element The element to align to
2081 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2082 * the element (defaults to this.defaultAlign)
2083 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2085 show : function(el, pos, parentMenu){
2086 this.parentMenu = parentMenu;
2090 this.fireEvent("beforeshow", this);
2091 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2094 * Displays this menu at a specific xy position
2095 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2096 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2098 showAt : function(xy, parentMenu, /* private: */_e){
2099 this.parentMenu = parentMenu;
2104 this.fireEvent("beforeshow", this);
2105 //xy = this.el.adjustForConstraints(xy);
2109 this.hideMenuItems();
2110 this.hidden = false;
2111 this.triggerEl.addClass('open');
2113 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2114 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2119 this.fireEvent("show", this);
2125 this.doFocus.defer(50, this);
2129 doFocus : function(){
2131 this.focusEl.focus();
2136 * Hides this menu and optionally all parent menus
2137 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2139 hide : function(deep){
2141 this.hideMenuItems();
2142 if(this.el && this.isVisible()){
2143 this.fireEvent("beforehide", this);
2144 if(this.activeItem){
2145 this.activeItem.deactivate();
2146 this.activeItem = null;
2148 this.triggerEl.removeClass('open');;
2150 this.fireEvent("hide", this);
2152 if(deep === true && this.parentMenu){
2153 this.parentMenu.hide(true);
2157 onTriggerPress : function(e)
2160 Roo.log('trigger press');
2161 //Roo.log(e.getTarget());
2162 // Roo.log(this.triggerEl.dom);
2163 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2167 if (this.isVisible()) {
2172 this.show(this.triggerEl, false, false);
2181 hideMenuItems : function()
2183 //$(backdrop).remove()
2184 Roo.select('.open',true).each(function(aa) {
2186 aa.removeClass('open');
2187 //var parent = getParent($(this))
2188 //var relatedTarget = { relatedTarget: this }
2190 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2191 //if (e.isDefaultPrevented()) return
2192 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2195 addxtypeChild : function (tree, cntr) {
2196 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2198 this.menuitems.add(comp);
2219 * @class Roo.bootstrap.MenuItem
2220 * @extends Roo.bootstrap.Component
2221 * Bootstrap MenuItem class
2222 * @cfg {String} html the menu label
2223 * @cfg {String} href the link
2224 * @cfg {Boolean} preventDefault (true | false) default true
2225 * @cfg {Boolean} isContainer (true | false) default false
2229 * Create a new MenuItem
2230 * @param {Object} config The config object
2234 Roo.bootstrap.MenuItem = function(config){
2235 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2240 * The raw click event for the entire grid.
2241 * @param {Roo.bootstrap.MenuItem} this
2242 * @param {Roo.EventObject} e
2248 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2252 preventDefault: true,
2253 isContainer : false,
2255 getAutoCreate : function(){
2257 if(this.isContainer){
2260 cls: 'dropdown-menu-item'
2266 cls: 'dropdown-menu-item',
2275 if (this.parent().type == 'treeview') {
2276 cfg.cls = 'treeview-menu';
2279 cfg.cn[0].href = this.href || cfg.cn[0].href ;
2280 cfg.cn[0].html = this.html || cfg.cn[0].html ;
2284 initEvents: function() {
2286 //this.el.select('a').on('click', this.onClick, this);
2289 onClick : function(e)
2291 Roo.log('item on click ');
2292 //if(this.preventDefault){
2293 // e.preventDefault();
2295 //this.parent().hideMenuItems();
2297 this.fireEvent('click', this, e);
2316 * @class Roo.bootstrap.MenuSeparator
2317 * @extends Roo.bootstrap.Component
2318 * Bootstrap MenuSeparator class
2321 * Create a new MenuItem
2322 * @param {Object} config The config object
2326 Roo.bootstrap.MenuSeparator = function(config){
2327 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2330 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2332 getAutoCreate : function(){
2351 * @class Roo.bootstrap.Modal
2352 * @extends Roo.bootstrap.Component
2353 * Bootstrap Modal class
2354 * @cfg {String} title Title of dialog
2355 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2356 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2357 * @cfg {Boolean} specificTitle default false
2358 * @cfg {Array} buttons Array of buttons or standard button set..
2359 * @cfg {String} buttonPosition (left|right|center) default right
2360 * @cfg {Boolean} animate default true
2361 * @cfg {Boolean} allow_close default true
2364 * Create a new Modal Dialog
2365 * @param {Object} config The config object
2368 Roo.bootstrap.Modal = function(config){
2369 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2374 * The raw btnclick event for the button
2375 * @param {Roo.EventObject} e
2379 this.buttons = this.buttons || [];
2382 this.tmpl = Roo.factory(this.tmpl);
2387 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2389 title : 'test dialog',
2399 specificTitle: false,
2401 buttonPosition: 'right',
2415 onRender : function(ct, position)
2417 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2420 var cfg = Roo.apply({}, this.getAutoCreate());
2423 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2425 //if (!cfg.name.length) {
2429 cfg.cls += ' ' + this.cls;
2432 cfg.style = this.style;
2434 this.el = Roo.get(document.body).createChild(cfg, position);
2436 //var type = this.el.dom.type;
2439 if(this.tabIndex !== undefined){
2440 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2444 this.bodyEl = this.el.select('.modal-body',true).first();
2445 this.closeEl = this.el.select('.modal-header .close', true).first();
2446 this.footerEl = this.el.select('.modal-footer',true).first();
2447 this.titleEl = this.el.select('.modal-title',true).first();
2451 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2452 this.maskEl.enableDisplayMode("block");
2454 //this.el.addClass("x-dlg-modal");
2456 if (this.buttons.length) {
2457 Roo.each(this.buttons, function(bb) {
2458 var b = Roo.apply({}, bb);
2459 b.xns = b.xns || Roo.bootstrap;
2460 b.xtype = b.xtype || 'Button';
2461 if (typeof(b.listeners) == 'undefined') {
2462 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2465 var btn = Roo.factory(b);
2467 btn.onRender(this.el.select('.modal-footer div').first());
2471 // render the children.
2474 if(typeof(this.items) != 'undefined'){
2475 var items = this.items;
2478 for(var i =0;i < items.length;i++) {
2479 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2483 this.items = nitems;
2485 // where are these used - they used to be body/close/footer
2489 //this.el.addClass([this.fieldClass, this.cls]);
2493 getAutoCreate : function(){
2498 html : this.html || ''
2503 cls : 'modal-title',
2507 if(this.specificTitle){
2513 if (this.allow_close) {
2524 style : 'display: none',
2527 cls: "modal-dialog",
2530 cls : "modal-content",
2533 cls : 'modal-header',
2538 cls : 'modal-footer',
2542 cls: 'btn-' + this.buttonPosition
2559 modal.cls += ' fade';
2565 getChildContainer : function() {
2570 getButtonContainer : function() {
2571 return this.el.select('.modal-footer div',true).first();
2574 initEvents : function()
2576 if (this.allow_close) {
2577 this.closeEl.on('click', this.hide, this);
2582 window.addEventListener("resize", function() { _this.resize(); } );
2588 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2593 if (!this.rendered) {
2597 this.el.setStyle('display', 'block');
2599 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2602 this.el.addClass('in');
2605 this.el.addClass('in');
2609 // not sure how we can show data in here..
2611 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2614 Roo.get(document.body).addClass("x-body-masked");
2615 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2617 this.el.setStyle('zIndex', '10001');
2619 this.fireEvent('show', this);
2627 Roo.get(document.body).removeClass("x-body-masked");
2628 this.el.removeClass('in');
2629 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2631 if(this.animate){ // why
2633 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2635 this.el.setStyle('display', 'none');
2638 this.fireEvent('hide', this);
2641 addButton : function(str, cb)
2645 var b = Roo.apply({}, { html : str } );
2646 b.xns = b.xns || Roo.bootstrap;
2647 b.xtype = b.xtype || 'Button';
2648 if (typeof(b.listeners) == 'undefined') {
2649 b.listeners = { click : cb.createDelegate(this) };
2652 var btn = Roo.factory(b);
2654 btn.onRender(this.el.select('.modal-footer div').first());
2660 setDefaultButton : function(btn)
2662 //this.el.select('.modal-footer').()
2664 resizeTo: function(w,h)
2668 setContentSize : function(w, h)
2672 onButtonClick: function(btn,e)
2675 this.fireEvent('btnclick', btn.name, e);
2678 * Set the title of the Dialog
2679 * @param {String} str new Title
2681 setTitle: function(str) {
2682 this.titleEl.dom.innerHTML = str;
2685 * Set the body of the Dialog
2686 * @param {String} str new Title
2688 setBody: function(str) {
2689 this.bodyEl.dom.innerHTML = str;
2692 * Set the body of the Dialog using the template
2693 * @param {Obj} data - apply this data to the template and replace the body contents.
2695 applyBody: function(obj)
2698 Roo.log("Error - using apply Body without a template");
2701 this.tmpl.overwrite(this.bodyEl, obj);
2707 Roo.apply(Roo.bootstrap.Modal, {
2709 * Button config that displays a single OK button
2718 * Button config that displays Yes and No buttons
2734 * Button config that displays OK and Cancel buttons
2749 * Button config that displays Yes, No and Cancel buttons
2772 * messagebox - can be used as a replace
2776 * @class Roo.MessageBox
2777 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2781 Roo.Msg.alert('Status', 'Changes saved successfully.');
2783 // Prompt for user data:
2784 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2786 // process text value...
2790 // Show a dialog using config options:
2792 title:'Save Changes?',
2793 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2794 buttons: Roo.Msg.YESNOCANCEL,
2801 Roo.bootstrap.MessageBox = function(){
2802 var dlg, opt, mask, waitTimer;
2803 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2804 var buttons, activeTextEl, bwidth;
2808 var handleButton = function(button){
2810 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2814 var handleHide = function(){
2816 dlg.el.removeClass(opt.cls);
2819 // Roo.TaskMgr.stop(waitTimer);
2820 // waitTimer = null;
2825 var updateButtons = function(b){
2828 buttons["ok"].hide();
2829 buttons["cancel"].hide();
2830 buttons["yes"].hide();
2831 buttons["no"].hide();
2832 //dlg.footer.dom.style.display = 'none';
2835 dlg.footerEl.dom.style.display = '';
2836 for(var k in buttons){
2837 if(typeof buttons[k] != "function"){
2840 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2841 width += buttons[k].el.getWidth()+15;
2851 var handleEsc = function(d, k, e){
2852 if(opt && opt.closable !== false){
2862 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2863 * @return {Roo.BasicDialog} The BasicDialog element
2865 getDialog : function(){
2867 dlg = new Roo.bootstrap.Modal( {
2870 //constraintoviewport:false,
2872 //collapsible : false,
2877 //buttonAlign:"center",
2878 closeClick : function(){
2879 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2882 handleButton("cancel");
2887 dlg.on("hide", handleHide);
2889 //dlg.addKeyListener(27, handleEsc);
2891 this.buttons = buttons;
2892 var bt = this.buttonText;
2893 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2894 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2895 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2896 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2898 bodyEl = dlg.bodyEl.createChild({
2900 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2901 '<textarea class="roo-mb-textarea"></textarea>' +
2902 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2904 msgEl = bodyEl.dom.firstChild;
2905 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2906 textboxEl.enableDisplayMode();
2907 textboxEl.addKeyListener([10,13], function(){
2908 if(dlg.isVisible() && opt && opt.buttons){
2911 }else if(opt.buttons.yes){
2912 handleButton("yes");
2916 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2917 textareaEl.enableDisplayMode();
2918 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2919 progressEl.enableDisplayMode();
2920 var pf = progressEl.dom.firstChild;
2922 pp = Roo.get(pf.firstChild);
2923 pp.setHeight(pf.offsetHeight);
2931 * Updates the message box body text
2932 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2933 * the XHTML-compliant non-breaking space character '&#160;')
2934 * @return {Roo.MessageBox} This message box
2936 updateText : function(text){
2937 if(!dlg.isVisible() && !opt.width){
2938 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2940 msgEl.innerHTML = text || ' ';
2942 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2943 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2945 Math.min(opt.width || cw , this.maxWidth),
2946 Math.max(opt.minWidth || this.minWidth, bwidth)
2949 activeTextEl.setWidth(w);
2951 if(dlg.isVisible()){
2952 dlg.fixedcenter = false;
2954 // to big, make it scroll. = But as usual stupid IE does not support
2957 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2958 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2959 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2961 bodyEl.dom.style.height = '';
2962 bodyEl.dom.style.overflowY = '';
2965 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2967 bodyEl.dom.style.overflowX = '';
2970 dlg.setContentSize(w, bodyEl.getHeight());
2971 if(dlg.isVisible()){
2972 dlg.fixedcenter = true;
2978 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
2979 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2980 * @param {Number} value Any number between 0 and 1 (e.g., .5)
2981 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2982 * @return {Roo.MessageBox} This message box
2984 updateProgress : function(value, text){
2986 this.updateText(text);
2988 if (pp) { // weird bug on my firefox - for some reason this is not defined
2989 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2995 * Returns true if the message box is currently displayed
2996 * @return {Boolean} True if the message box is visible, else false
2998 isVisible : function(){
2999 return dlg && dlg.isVisible();
3003 * Hides the message box if it is displayed
3006 if(this.isVisible()){
3012 * Displays a new message box, or reinitializes an existing message box, based on the config options
3013 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3014 * The following config object properties are supported:
3016 Property Type Description
3017 ---------- --------------- ------------------------------------------------------------------------------------
3018 animEl String/Element An id or Element from which the message box should animate as it opens and
3019 closes (defaults to undefined)
3020 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3021 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3022 closable Boolean False to hide the top-right close button (defaults to true). Note that
3023 progress and wait dialogs will ignore this property and always hide the
3024 close button as they can only be closed programmatically.
3025 cls String A custom CSS class to apply to the message box element
3026 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3027 displayed (defaults to 75)
3028 fn Function A callback function to execute after closing the dialog. The arguments to the
3029 function will be btn (the name of the button that was clicked, if applicable,
3030 e.g. "ok"), and text (the value of the active text field, if applicable).
3031 Progress and wait dialogs will ignore this option since they do not respond to
3032 user actions and can only be closed programmatically, so any required function
3033 should be called by the same code after it closes the dialog.
3034 icon String A CSS class that provides a background image to be used as an icon for
3035 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3036 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3037 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3038 modal Boolean False to allow user interaction with the page while the message box is
3039 displayed (defaults to true)
3040 msg String A string that will replace the existing message box body text (defaults
3041 to the XHTML-compliant non-breaking space character ' ')
3042 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3043 progress Boolean True to display a progress bar (defaults to false)
3044 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3045 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3046 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3047 title String The title text
3048 value String The string value to set into the active textbox element if displayed
3049 wait Boolean True to display a progress bar (defaults to false)
3050 width Number The width of the dialog in pixels
3057 msg: 'Please enter your address:',
3059 buttons: Roo.MessageBox.OKCANCEL,
3062 animEl: 'addAddressBtn'
3065 * @param {Object} config Configuration options
3066 * @return {Roo.MessageBox} This message box
3068 show : function(options)
3071 // this causes nightmares if you show one dialog after another
3072 // especially on callbacks..
3074 if(this.isVisible()){
3077 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3078 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3079 Roo.log("New Dialog Message:" + options.msg )
3080 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3081 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3084 var d = this.getDialog();
3086 d.setTitle(opt.title || " ");
3087 d.closeEl.setDisplayed(opt.closable !== false);
3088 activeTextEl = textboxEl;
3089 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3094 textareaEl.setHeight(typeof opt.multiline == "number" ?
3095 opt.multiline : this.defaultTextHeight);
3096 activeTextEl = textareaEl;
3105 progressEl.setDisplayed(opt.progress === true);
3106 this.updateProgress(0);
3107 activeTextEl.dom.value = opt.value || "";
3109 dlg.setDefaultButton(activeTextEl);
3111 var bs = opt.buttons;
3115 }else if(bs && bs.yes){
3116 db = buttons["yes"];
3118 dlg.setDefaultButton(db);
3120 bwidth = updateButtons(opt.buttons);
3121 this.updateText(opt.msg);
3123 d.el.addClass(opt.cls);
3125 d.proxyDrag = opt.proxyDrag === true;
3126 d.modal = opt.modal !== false;
3127 d.mask = opt.modal !== false ? mask : false;
3129 // force it to the end of the z-index stack so it gets a cursor in FF
3130 document.body.appendChild(dlg.el.dom);
3131 d.animateTarget = null;
3132 d.show(options.animEl);
3138 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3139 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3140 * and closing the message box when the process is complete.
3141 * @param {String} title The title bar text
3142 * @param {String} msg The message box body text
3143 * @return {Roo.MessageBox} This message box
3145 progress : function(title, msg){
3152 minWidth: this.minProgressWidth,
3159 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3160 * If a callback function is passed it will be called after the user clicks the button, and the
3161 * id of the button that was clicked will be passed as the only parameter to the callback
3162 * (could also be the top-right close button).
3163 * @param {String} title The title bar text
3164 * @param {String} msg The message box body text
3165 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3166 * @param {Object} scope (optional) The scope of the callback function
3167 * @return {Roo.MessageBox} This message box
3169 alert : function(title, msg, fn, scope){
3182 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3183 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3184 * You are responsible for closing the message box when the process is complete.
3185 * @param {String} msg The message box body text
3186 * @param {String} title (optional) The title bar text
3187 * @return {Roo.MessageBox} This message box
3189 wait : function(msg, title){
3200 waitTimer = Roo.TaskMgr.start({
3202 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3210 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3211 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3212 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3213 * @param {String} title The title bar text
3214 * @param {String} msg The message box body text
3215 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3216 * @param {Object} scope (optional) The scope of the callback function
3217 * @return {Roo.MessageBox} This message box
3219 confirm : function(title, msg, fn, scope){
3223 buttons: this.YESNO,
3232 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3233 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3234 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3235 * (could also be the top-right close button) and the text that was entered will be passed as the two
3236 * parameters to the callback.
3237 * @param {String} title The title bar text
3238 * @param {String} msg The message box body text
3239 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3240 * @param {Object} scope (optional) The scope of the callback function
3241 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3242 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3243 * @return {Roo.MessageBox} This message box
3245 prompt : function(title, msg, fn, scope, multiline){
3249 buttons: this.OKCANCEL,
3254 multiline: multiline,
3261 * Button config that displays a single OK button
3266 * Button config that displays Yes and No buttons
3269 YESNO : {yes:true, no:true},
3271 * Button config that displays OK and Cancel buttons
3274 OKCANCEL : {ok:true, cancel:true},
3276 * Button config that displays Yes, No and Cancel buttons
3279 YESNOCANCEL : {yes:true, no:true, cancel:true},
3282 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3285 defaultTextHeight : 75,
3287 * The maximum width in pixels of the message box (defaults to 600)
3292 * The minimum width in pixels of the message box (defaults to 100)
3297 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3298 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3301 minProgressWidth : 250,
3303 * An object containing the default button text strings that can be overriden for localized language support.
3304 * Supported properties are: ok, cancel, yes and no.
3305 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3318 * Shorthand for {@link Roo.MessageBox}
3320 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3321 Roo.Msg = Roo.Msg || Roo.MessageBox;
3330 * @class Roo.bootstrap.Navbar
3331 * @extends Roo.bootstrap.Component
3332 * Bootstrap Navbar class
3335 * Create a new Navbar
3336 * @param {Object} config The config object
3340 Roo.bootstrap.Navbar = function(config){
3341 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3345 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3354 getAutoCreate : function(){
3357 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3361 initEvents :function ()
3363 //Roo.log(this.el.select('.navbar-toggle',true));
3364 this.el.select('.navbar-toggle',true).on('click', function() {
3365 // Roo.log('click');
3366 this.el.select('.navbar-collapse',true).toggleClass('in');
3374 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3376 var size = this.el.getSize();
3377 this.maskEl.setSize(size.width, size.height);
3378 this.maskEl.enableDisplayMode("block");
3387 getChildContainer : function()
3389 if (this.el.select('.collapse').getCount()) {
3390 return this.el.select('.collapse',true).first();
3423 * @class Roo.bootstrap.NavSimplebar
3424 * @extends Roo.bootstrap.Navbar
3425 * Bootstrap Sidebar class
3427 * @cfg {Boolean} inverse is inverted color
3429 * @cfg {String} type (nav | pills | tabs)
3430 * @cfg {Boolean} arrangement stacked | justified
3431 * @cfg {String} align (left | right) alignment
3433 * @cfg {Boolean} main (true|false) main nav bar? default false
3434 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3436 * @cfg {String} tag (header|footer|nav|div) default is nav
3442 * Create a new Sidebar
3443 * @param {Object} config The config object
3447 Roo.bootstrap.NavSimplebar = function(config){
3448 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3451 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3467 getAutoCreate : function(){
3471 tag : this.tag || 'div',
3484 this.type = this.type || 'nav';
3485 if (['tabs','pills'].indexOf(this.type)!==-1) {
3486 cfg.cn[0].cls += ' nav-' + this.type
3490 if (this.type!=='nav') {
3491 Roo.log('nav type must be nav/tabs/pills')
3493 cfg.cn[0].cls += ' navbar-nav'
3499 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3500 cfg.cn[0].cls += ' nav-' + this.arrangement;
3504 if (this.align === 'right') {
3505 cfg.cn[0].cls += ' navbar-right';
3509 cfg.cls += ' navbar-inverse';
3536 * @class Roo.bootstrap.NavHeaderbar
3537 * @extends Roo.bootstrap.NavSimplebar
3538 * Bootstrap Sidebar class
3540 * @cfg {String} brand what is brand
3541 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3542 * @cfg {String} brand_href href of the brand
3543 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3544 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3545 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3546 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3549 * Create a new Sidebar
3550 * @param {Object} config The config object
3554 Roo.bootstrap.NavHeaderbar = function(config){
3555 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3559 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3566 desktopCenter : false,
3569 getAutoCreate : function(){
3572 tag: this.nav || 'nav',
3579 if (this.desktopCenter) {
3580 cn.push({cls : 'container', cn : []});
3587 cls: 'navbar-header',
3592 cls: 'navbar-toggle',
3593 'data-toggle': 'collapse',
3598 html: 'Toggle navigation'
3620 cls: 'collapse navbar-collapse',
3624 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3626 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3627 cfg.cls += ' navbar-' + this.position;
3629 // tag can override this..
3631 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3634 if (this.brand !== '') {
3637 href: this.brand_href ? this.brand_href : '#',
3638 cls: 'navbar-brand',
3646 cfg.cls += ' main-nav';
3654 getHeaderChildContainer : function()
3656 if (this.el.select('.navbar-header').getCount()) {
3657 return this.el.select('.navbar-header',true).first();
3660 return this.getChildContainer();
3664 initEvents : function()
3666 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3668 if (this.autohide) {
3673 Roo.get(document).on('scroll',function(e) {
3674 var ns = Roo.get(document).getScroll().top;
3675 var os = prevScroll;
3679 ft.removeClass('slideDown');
3680 ft.addClass('slideUp');
3683 ft.removeClass('slideUp');
3684 ft.addClass('slideDown');
3705 * @class Roo.bootstrap.NavSidebar
3706 * @extends Roo.bootstrap.Navbar
3707 * Bootstrap Sidebar class
3710 * Create a new Sidebar
3711 * @param {Object} config The config object
3715 Roo.bootstrap.NavSidebar = function(config){
3716 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3719 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3721 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3723 getAutoCreate : function(){
3728 cls: 'sidebar sidebar-nav'
3750 * @class Roo.bootstrap.NavGroup
3751 * @extends Roo.bootstrap.Component
3752 * Bootstrap NavGroup class
3753 * @cfg {String} align (left|right)
3754 * @cfg {Boolean} inverse
3755 * @cfg {String} type (nav|pills|tab) default nav
3756 * @cfg {String} navId - reference Id for navbar.
3760 * Create a new nav group
3761 * @param {Object} config The config object
3764 Roo.bootstrap.NavGroup = function(config){
3765 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3768 Roo.bootstrap.NavGroup.register(this);
3772 * Fires when the active item changes
3773 * @param {Roo.bootstrap.NavGroup} this
3774 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3775 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3782 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3793 getAutoCreate : function()
3795 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3802 if (['tabs','pills'].indexOf(this.type)!==-1) {
3803 cfg.cls += ' nav-' + this.type
3805 if (this.type!=='nav') {
3806 Roo.log('nav type must be nav/tabs/pills')
3808 cfg.cls += ' navbar-nav'
3811 if (this.parent().sidebar) {
3814 cls: 'dashboard-menu sidebar-menu'
3820 if (this.form === true) {
3826 if (this.align === 'right') {
3827 cfg.cls += ' navbar-right';
3829 cfg.cls += ' navbar-left';
3833 if (this.align === 'right') {
3834 cfg.cls += ' navbar-right';
3838 cfg.cls += ' navbar-inverse';
3846 * sets the active Navigation item
3847 * @param {Roo.bootstrap.NavItem} the new current navitem
3849 setActiveItem : function(item)
3852 Roo.each(this.navItems, function(v){
3857 v.setActive(false, true);
3864 item.setActive(true, true);
3865 this.fireEvent('changed', this, item, prev);
3870 * gets the active Navigation item
3871 * @return {Roo.bootstrap.NavItem} the current navitem
3873 getActive : function()
3877 Roo.each(this.navItems, function(v){
3888 indexOfNav : function()
3892 Roo.each(this.navItems, function(v,i){
3903 * adds a Navigation item
3904 * @param {Roo.bootstrap.NavItem} the navitem to add
3906 addItem : function(cfg)
3908 var cn = new Roo.bootstrap.NavItem(cfg);
3910 cn.parentId = this.id;
3911 cn.onRender(this.el, null);
3915 * register a Navigation item
3916 * @param {Roo.bootstrap.NavItem} the navitem to add
3918 register : function(item)
3920 this.navItems.push( item);
3921 item.navId = this.navId;
3926 * clear all the Navigation item
3929 clearAll : function()
3932 this.el.dom.innerHTML = '';
3935 getNavItem: function(tabId)
3938 Roo.each(this.navItems, function(e) {
3939 if (e.tabId == tabId) {
3949 setActiveNext : function()
3951 var i = this.indexOfNav(this.getActive());
3952 if (i > this.navItems.length) {
3955 this.setActiveItem(this.navItems[i+1]);
3957 setActivePrev : function()
3959 var i = this.indexOfNav(this.getActive());
3963 this.setActiveItem(this.navItems[i-1]);
3965 clearWasActive : function(except) {
3966 Roo.each(this.navItems, function(e) {
3967 if (e.tabId != except.tabId && e.was_active) {
3968 e.was_active = false;
3975 getWasActive : function ()
3978 Roo.each(this.navItems, function(e) {
3993 Roo.apply(Roo.bootstrap.NavGroup, {
3997 * register a Navigation Group
3998 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4000 register : function(navgrp)
4002 this.groups[navgrp.navId] = navgrp;
4006 * fetch a Navigation Group based on the navigation ID
4007 * @param {string} the navgroup to add
4008 * @returns {Roo.bootstrap.NavGroup} the navgroup
4010 get: function(navId) {
4011 if (typeof(this.groups[navId]) == 'undefined') {
4013 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4015 return this.groups[navId] ;
4030 * @class Roo.bootstrap.NavItem
4031 * @extends Roo.bootstrap.Component
4032 * Bootstrap Navbar.NavItem class
4033 * @cfg {String} href link to
4034 * @cfg {String} html content of button
4035 * @cfg {String} badge text inside badge
4036 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4037 * @cfg {String} glyphicon name of glyphicon
4038 * @cfg {String} icon name of font awesome icon
4039 * @cfg {Boolean} active Is item active
4040 * @cfg {Boolean} disabled Is item disabled
4042 * @cfg {Boolean} preventDefault (true | false) default false
4043 * @cfg {String} tabId the tab that this item activates.
4044 * @cfg {String} tagtype (a|span) render as a href or span?
4045 * @cfg {Boolean} animateRef (true|false) link to element default false
4048 * Create a new Navbar Item
4049 * @param {Object} config The config object
4051 Roo.bootstrap.NavItem = function(config){
4052 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4057 * The raw click event for the entire grid.
4058 * @param {Roo.EventObject} e
4063 * Fires when the active item active state changes
4064 * @param {Roo.bootstrap.NavItem} this
4065 * @param {boolean} state the new state
4071 * Fires when scroll to element
4072 * @param {Roo.bootstrap.NavItem} this
4073 * @param {Object} options
4074 * @param {Roo.EventObject} e
4082 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4090 preventDefault : false,
4097 getAutoCreate : function(){
4106 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4108 if (this.disabled) {
4109 cfg.cls += ' disabled';
4112 if (this.href || this.html || this.glyphicon || this.icon) {
4116 href : this.href || "#",
4117 html: this.html || ''
4122 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4125 if(this.glyphicon) {
4126 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4131 cfg.cn[0].html += " <span class='caret'></span>";
4135 if (this.badge !== '') {
4137 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4145 initEvents: function()
4147 if (typeof (this.menu) != 'undefined') {
4148 this.menu.parentType = this.xtype;
4149 this.menu.triggerEl = this.el;
4150 this.menu = this.addxtype(Roo.apply({}, this.menu));
4153 this.el.select('a',true).on('click', this.onClick, this);
4155 if(this.tagtype == 'span'){
4156 this.el.select('span',true).on('click', this.onClick, this);
4159 // at this point parent should be available..
4160 this.parent().register(this);
4163 onClick : function(e)
4166 this.preventDefault ||
4173 if (this.disabled) {
4177 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4178 if (tg && tg.transition) {
4179 Roo.log("waiting for the transitionend");
4185 //Roo.log("fire event clicked");
4186 if(this.fireEvent('click', this, e) === false){
4190 if(this.tagtype == 'span'){
4194 //Roo.log(this.href);
4195 var ael = this.el.select('a',true).first();
4198 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4199 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4200 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4201 return; // ignore... - it's a 'hash' to another page.
4205 this.scrollToElement(e);
4209 var p = this.parent();
4211 if (['tabs','pills'].indexOf(p.type)!==-1) {
4212 if (typeof(p.setActiveItem) !== 'undefined') {
4213 p.setActiveItem(this);
4217 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4218 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4219 // remove the collapsed menu expand...
4220 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4224 isActive: function () {
4227 setActive : function(state, fire, is_was_active)
4229 if (this.active && !state && this.navId) {
4230 this.was_active = true;
4231 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4233 nv.clearWasActive(this);
4237 this.active = state;
4240 this.el.removeClass('active');
4241 } else if (!this.el.hasClass('active')) {
4242 this.el.addClass('active');
4245 this.fireEvent('changed', this, state);
4248 // show a panel if it's registered and related..
4250 if (!this.navId || !this.tabId || !state || is_was_active) {
4254 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4258 var pan = tg.getPanelByName(this.tabId);
4262 // if we can not flip to new panel - go back to old nav highlight..
4263 if (false == tg.showPanel(pan)) {
4264 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4266 var onav = nv.getWasActive();
4268 onav.setActive(true, false, true);
4277 // this should not be here...
4278 setDisabled : function(state)
4280 this.disabled = state;
4282 this.el.removeClass('disabled');
4283 } else if (!this.el.hasClass('disabled')) {
4284 this.el.addClass('disabled');
4290 * Fetch the element to display the tooltip on.
4291 * @return {Roo.Element} defaults to this.el
4293 tooltipEl : function()
4295 return this.el.select('' + this.tagtype + '', true).first();
4298 scrollToElement : function(e)
4300 var c = document.body;
4303 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4305 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4306 c = document.documentElement;
4309 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4315 var o = target.calcOffsetsTo(c);
4322 this.fireEvent('scrollto', this, options, e);
4324 Roo.get(c).scrollTo('top', options.value, true);
4337 * <span> icon </span>
4338 * <span> text </span>
4339 * <span>badge </span>
4343 * @class Roo.bootstrap.NavSidebarItem
4344 * @extends Roo.bootstrap.NavItem
4345 * Bootstrap Navbar.NavSidebarItem class
4346 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4348 * Create a new Navbar Button
4349 * @param {Object} config The config object
4351 Roo.bootstrap.NavSidebarItem = function(config){
4352 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4357 * The raw click event for the entire grid.
4358 * @param {Roo.EventObject} e
4363 * Fires when the active item active state changes
4364 * @param {Roo.bootstrap.NavSidebarItem} this
4365 * @param {boolean} state the new state
4373 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4375 badgeWeight : 'default',
4377 getAutoCreate : function(){
4382 href : this.href || '#',
4394 html : this.html || ''
4399 cfg.cls += ' active';
4402 if (this.disabled) {
4403 cfg.cls += ' disabled';
4407 if (this.glyphicon || this.icon) {
4408 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4409 a.cn.push({ tag : 'i', cls : c }) ;
4414 if (this.badge !== '') {
4416 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4420 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4421 a.cls += 'dropdown-toggle treeview' ;
4432 initEvents : function()
4434 this.el.on('click', this.onClick, this);
4437 if(this.badge !== ''){
4439 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4444 onClick : function(e)
4451 if(this.preventDefault){
4455 this.fireEvent('click', this);
4458 disable : function()
4460 this.setDisabled(true);
4465 this.setDisabled(false);
4468 setDisabled : function(state)
4470 if(this.disabled == state){
4474 this.disabled = state;
4477 this.el.addClass('disabled');
4481 this.el.removeClass('disabled');
4486 setActive : function(state)
4488 if(this.active == state){
4492 this.active = state;
4495 this.el.addClass('active');
4499 this.el.removeClass('active');
4504 isActive: function ()
4509 setBadge : function(str)
4515 this.badgeEl.dom.innerHTML = str;
4532 * @class Roo.bootstrap.Row
4533 * @extends Roo.bootstrap.Component
4534 * Bootstrap Row class (contains columns...)
4538 * @param {Object} config The config object
4541 Roo.bootstrap.Row = function(config){
4542 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4545 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4547 getAutoCreate : function(){
4566 * @class Roo.bootstrap.Element
4567 * @extends Roo.bootstrap.Component
4568 * Bootstrap Element class
4569 * @cfg {String} html contents of the element
4570 * @cfg {String} tag tag of the element
4571 * @cfg {String} cls class of the element
4572 * @cfg {Boolean} preventDefault (true|false) default false
4573 * @cfg {Boolean} clickable (true|false) default false
4576 * Create a new Element
4577 * @param {Object} config The config object
4580 Roo.bootstrap.Element = function(config){
4581 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4587 * When a element is chick
4588 * @param {Roo.bootstrap.Element} this
4589 * @param {Roo.EventObject} e
4595 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4600 preventDefault: false,
4603 getAutoCreate : function(){
4614 initEvents: function()
4616 Roo.bootstrap.Element.superclass.initEvents.call(this);
4619 this.el.on('click', this.onClick, this);
4624 onClick : function(e)
4626 if(this.preventDefault){
4630 this.fireEvent('click', this, e);
4633 getValue : function()
4635 return this.el.dom.innerHTML;
4638 setValue : function(value)
4640 this.el.dom.innerHTML = value;
4655 * @class Roo.bootstrap.Pagination
4656 * @extends Roo.bootstrap.Component
4657 * Bootstrap Pagination class
4658 * @cfg {String} size xs | sm | md | lg
4659 * @cfg {Boolean} inverse false | true
4662 * Create a new Pagination
4663 * @param {Object} config The config object
4666 Roo.bootstrap.Pagination = function(config){
4667 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4670 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4676 getAutoCreate : function(){
4682 cfg.cls += ' inverse';
4688 cfg.cls += " " + this.cls;
4706 * @class Roo.bootstrap.PaginationItem
4707 * @extends Roo.bootstrap.Component
4708 * Bootstrap PaginationItem class
4709 * @cfg {String} html text
4710 * @cfg {String} href the link
4711 * @cfg {Boolean} preventDefault (true | false) default true
4712 * @cfg {Boolean} active (true | false) default false
4713 * @cfg {Boolean} disabled default false
4717 * Create a new PaginationItem
4718 * @param {Object} config The config object
4722 Roo.bootstrap.PaginationItem = function(config){
4723 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4728 * The raw click event for the entire grid.
4729 * @param {Roo.EventObject} e
4735 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4739 preventDefault: true,
4744 getAutoCreate : function(){
4750 href : this.href ? this.href : '#',
4751 html : this.html ? this.html : ''
4761 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4765 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4771 initEvents: function() {
4773 this.el.on('click', this.onClick, this);
4776 onClick : function(e)
4778 Roo.log('PaginationItem on click ');
4779 if(this.preventDefault){
4787 this.fireEvent('click', this, e);
4803 * @class Roo.bootstrap.Slider
4804 * @extends Roo.bootstrap.Component
4805 * Bootstrap Slider class
4808 * Create a new Slider
4809 * @param {Object} config The config object
4812 Roo.bootstrap.Slider = function(config){
4813 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4816 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4818 getAutoCreate : function(){
4822 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4826 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4838 * Ext JS Library 1.1.1
4839 * Copyright(c) 2006-2007, Ext JS, LLC.
4841 * Originally Released Under LGPL - original licence link has changed is not relivant.
4844 * <script type="text/javascript">
4849 * @class Roo.grid.ColumnModel
4850 * @extends Roo.util.Observable
4851 * This is the default implementation of a ColumnModel used by the Grid. It defines
4852 * the columns in the grid.
4855 var colModel = new Roo.grid.ColumnModel([
4856 {header: "Ticker", width: 60, sortable: true, locked: true},
4857 {header: "Company Name", width: 150, sortable: true},
4858 {header: "Market Cap.", width: 100, sortable: true},
4859 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4860 {header: "Employees", width: 100, sortable: true, resizable: false}
4865 * The config options listed for this class are options which may appear in each
4866 * individual column definition.
4867 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4869 * @param {Object} config An Array of column config objects. See this class's
4870 * config objects for details.
4872 Roo.grid.ColumnModel = function(config){
4874 * The config passed into the constructor
4876 this.config = config;
4879 // if no id, create one
4880 // if the column does not have a dataIndex mapping,
4881 // map it to the order it is in the config
4882 for(var i = 0, len = config.length; i < len; i++){
4884 if(typeof c.dataIndex == "undefined"){
4887 if(typeof c.renderer == "string"){
4888 c.renderer = Roo.util.Format[c.renderer];
4890 if(typeof c.id == "undefined"){
4893 if(c.editor && c.editor.xtype){
4894 c.editor = Roo.factory(c.editor, Roo.grid);
4896 if(c.editor && c.editor.isFormField){
4897 c.editor = new Roo.grid.GridEditor(c.editor);
4899 this.lookup[c.id] = c;
4903 * The width of columns which have no width specified (defaults to 100)
4906 this.defaultWidth = 100;
4909 * Default sortable of columns which have no sortable specified (defaults to false)
4912 this.defaultSortable = false;
4916 * @event widthchange
4917 * Fires when the width of a column changes.
4918 * @param {ColumnModel} this
4919 * @param {Number} columnIndex The column index
4920 * @param {Number} newWidth The new width
4922 "widthchange": true,
4924 * @event headerchange
4925 * Fires when the text of a header changes.
4926 * @param {ColumnModel} this
4927 * @param {Number} columnIndex The column index
4928 * @param {Number} newText The new header text
4930 "headerchange": true,
4932 * @event hiddenchange
4933 * Fires when a column is hidden or "unhidden".
4934 * @param {ColumnModel} this
4935 * @param {Number} columnIndex The column index
4936 * @param {Boolean} hidden true if hidden, false otherwise
4938 "hiddenchange": true,
4940 * @event columnmoved
4941 * Fires when a column is moved.
4942 * @param {ColumnModel} this
4943 * @param {Number} oldIndex
4944 * @param {Number} newIndex
4946 "columnmoved" : true,
4948 * @event columlockchange
4949 * Fires when a column's locked state is changed
4950 * @param {ColumnModel} this
4951 * @param {Number} colIndex
4952 * @param {Boolean} locked true if locked
4954 "columnlockchange" : true
4956 Roo.grid.ColumnModel.superclass.constructor.call(this);
4958 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4960 * @cfg {String} header The header text to display in the Grid view.
4963 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4964 * {@link Roo.data.Record} definition from which to draw the column's value. If not
4965 * specified, the column's index is used as an index into the Record's data Array.
4968 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4969 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4972 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4973 * Defaults to the value of the {@link #defaultSortable} property.
4974 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4977 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
4980 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
4983 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4986 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4989 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4990 * given the cell's data value. See {@link #setRenderer}. If not specified, the
4991 * default renderer uses the raw data value. If an object is returned (bootstrap only)
4992 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4995 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
4998 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5001 * @cfg {String} cursor (Optional)
5004 * @cfg {String} tooltip (Optional)
5007 * @cfg {Number} xs (Optional)
5010 * @cfg {Number} sm (Optional)
5013 * @cfg {Number} md (Optional)
5016 * @cfg {Number} lg (Optional)
5019 * Returns the id of the column at the specified index.
5020 * @param {Number} index The column index
5021 * @return {String} the id
5023 getColumnId : function(index){
5024 return this.config[index].id;
5028 * Returns the column for a specified id.
5029 * @param {String} id The column id
5030 * @return {Object} the column
5032 getColumnById : function(id){
5033 return this.lookup[id];
5038 * Returns the column for a specified dataIndex.
5039 * @param {String} dataIndex The column dataIndex
5040 * @return {Object|Boolean} the column or false if not found
5042 getColumnByDataIndex: function(dataIndex){
5043 var index = this.findColumnIndex(dataIndex);
5044 return index > -1 ? this.config[index] : false;
5048 * Returns the index for a specified column id.
5049 * @param {String} id The column id
5050 * @return {Number} the index, or -1 if not found
5052 getIndexById : function(id){
5053 for(var i = 0, len = this.config.length; i < len; i++){
5054 if(this.config[i].id == id){
5062 * Returns the index for a specified column dataIndex.
5063 * @param {String} dataIndex The column dataIndex
5064 * @return {Number} the index, or -1 if not found
5067 findColumnIndex : function(dataIndex){
5068 for(var i = 0, len = this.config.length; i < len; i++){
5069 if(this.config[i].dataIndex == dataIndex){
5077 moveColumn : function(oldIndex, newIndex){
5078 var c = this.config[oldIndex];
5079 this.config.splice(oldIndex, 1);
5080 this.config.splice(newIndex, 0, c);
5081 this.dataMap = null;
5082 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5085 isLocked : function(colIndex){
5086 return this.config[colIndex].locked === true;
5089 setLocked : function(colIndex, value, suppressEvent){
5090 if(this.isLocked(colIndex) == value){
5093 this.config[colIndex].locked = value;
5095 this.fireEvent("columnlockchange", this, colIndex, value);
5099 getTotalLockedWidth : function(){
5101 for(var i = 0; i < this.config.length; i++){
5102 if(this.isLocked(i) && !this.isHidden(i)){
5103 this.totalWidth += this.getColumnWidth(i);
5109 getLockedCount : function(){
5110 for(var i = 0, len = this.config.length; i < len; i++){
5111 if(!this.isLocked(i)){
5118 * Returns the number of columns.
5121 getColumnCount : function(visibleOnly){
5122 if(visibleOnly === true){
5124 for(var i = 0, len = this.config.length; i < len; i++){
5125 if(!this.isHidden(i)){
5131 return this.config.length;
5135 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5136 * @param {Function} fn
5137 * @param {Object} scope (optional)
5138 * @return {Array} result
5140 getColumnsBy : function(fn, scope){
5142 for(var i = 0, len = this.config.length; i < len; i++){
5143 var c = this.config[i];
5144 if(fn.call(scope||this, c, i) === true){
5152 * Returns true if the specified column is sortable.
5153 * @param {Number} col The column index
5156 isSortable : function(col){
5157 if(typeof this.config[col].sortable == "undefined"){
5158 return this.defaultSortable;
5160 return this.config[col].sortable;
5164 * Returns the rendering (formatting) function defined for the column.
5165 * @param {Number} col The column index.
5166 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5168 getRenderer : function(col){
5169 if(!this.config[col].renderer){
5170 return Roo.grid.ColumnModel.defaultRenderer;
5172 return this.config[col].renderer;
5176 * Sets the rendering (formatting) function for a column.
5177 * @param {Number} col The column index
5178 * @param {Function} fn The function to use to process the cell's raw data
5179 * to return HTML markup for the grid view. The render function is called with
5180 * the following parameters:<ul>
5181 * <li>Data value.</li>
5182 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5183 * <li>css A CSS style string to apply to the table cell.</li>
5184 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5185 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5186 * <li>Row index</li>
5187 * <li>Column index</li>
5188 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5190 setRenderer : function(col, fn){
5191 this.config[col].renderer = fn;
5195 * Returns the width for the specified column.
5196 * @param {Number} col The column index
5199 getColumnWidth : function(col){
5200 return this.config[col].width * 1 || this.defaultWidth;
5204 * Sets the width for a column.
5205 * @param {Number} col The column index
5206 * @param {Number} width The new width
5208 setColumnWidth : function(col, width, suppressEvent){
5209 this.config[col].width = width;
5210 this.totalWidth = null;
5212 this.fireEvent("widthchange", this, col, width);
5217 * Returns the total width of all columns.
5218 * @param {Boolean} includeHidden True to include hidden column widths
5221 getTotalWidth : function(includeHidden){
5222 if(!this.totalWidth){
5223 this.totalWidth = 0;
5224 for(var i = 0, len = this.config.length; i < len; i++){
5225 if(includeHidden || !this.isHidden(i)){
5226 this.totalWidth += this.getColumnWidth(i);
5230 return this.totalWidth;
5234 * Returns the header for the specified column.
5235 * @param {Number} col The column index
5238 getColumnHeader : function(col){
5239 return this.config[col].header;
5243 * Sets the header for a column.
5244 * @param {Number} col The column index
5245 * @param {String} header The new header
5247 setColumnHeader : function(col, header){
5248 this.config[col].header = header;
5249 this.fireEvent("headerchange", this, col, header);
5253 * Returns the tooltip for the specified column.
5254 * @param {Number} col The column index
5257 getColumnTooltip : function(col){
5258 return this.config[col].tooltip;
5261 * Sets the tooltip for a column.
5262 * @param {Number} col The column index
5263 * @param {String} tooltip The new tooltip
5265 setColumnTooltip : function(col, tooltip){
5266 this.config[col].tooltip = tooltip;
5270 * Returns the dataIndex for the specified column.
5271 * @param {Number} col The column index
5274 getDataIndex : function(col){
5275 return this.config[col].dataIndex;
5279 * Sets the dataIndex for a column.
5280 * @param {Number} col The column index
5281 * @param {Number} dataIndex The new dataIndex
5283 setDataIndex : function(col, dataIndex){
5284 this.config[col].dataIndex = dataIndex;
5290 * Returns true if the cell is editable.
5291 * @param {Number} colIndex The column index
5292 * @param {Number} rowIndex The row index - this is nto actually used..?
5295 isCellEditable : function(colIndex, rowIndex){
5296 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5300 * Returns the editor defined for the cell/column.
5301 * return false or null to disable editing.
5302 * @param {Number} colIndex The column index
5303 * @param {Number} rowIndex The row index
5306 getCellEditor : function(colIndex, rowIndex){
5307 return this.config[colIndex].editor;
5311 * Sets if a column is editable.
5312 * @param {Number} col The column index
5313 * @param {Boolean} editable True if the column is editable
5315 setEditable : function(col, editable){
5316 this.config[col].editable = editable;
5321 * Returns true if the column is hidden.
5322 * @param {Number} colIndex The column index
5325 isHidden : function(colIndex){
5326 return this.config[colIndex].hidden;
5331 * Returns true if the column width cannot be changed
5333 isFixed : function(colIndex){
5334 return this.config[colIndex].fixed;
5338 * Returns true if the column can be resized
5341 isResizable : function(colIndex){
5342 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5345 * Sets if a column is hidden.
5346 * @param {Number} colIndex The column index
5347 * @param {Boolean} hidden True if the column is hidden
5349 setHidden : function(colIndex, hidden){
5350 this.config[colIndex].hidden = hidden;
5351 this.totalWidth = null;
5352 this.fireEvent("hiddenchange", this, colIndex, hidden);
5356 * Sets the editor for a column.
5357 * @param {Number} col The column index
5358 * @param {Object} editor The editor object
5360 setEditor : function(col, editor){
5361 this.config[col].editor = editor;
5365 Roo.grid.ColumnModel.defaultRenderer = function(value){
5366 if(typeof value == "string" && value.length < 1){
5372 // Alias for backwards compatibility
5373 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5376 * Ext JS Library 1.1.1
5377 * Copyright(c) 2006-2007, Ext JS, LLC.
5379 * Originally Released Under LGPL - original licence link has changed is not relivant.
5382 * <script type="text/javascript">
5386 * @class Roo.LoadMask
5387 * A simple utility class for generically masking elements while loading data. If the element being masked has
5388 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5389 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5390 * element's UpdateManager load indicator and will be destroyed after the initial load.
5392 * Create a new LoadMask
5393 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5394 * @param {Object} config The config object
5396 Roo.LoadMask = function(el, config){
5397 this.el = Roo.get(el);
5398 Roo.apply(this, config);
5400 this.store.on('beforeload', this.onBeforeLoad, this);
5401 this.store.on('load', this.onLoad, this);
5402 this.store.on('loadexception', this.onLoadException, this);
5403 this.removeMask = false;
5405 var um = this.el.getUpdateManager();
5406 um.showLoadIndicator = false; // disable the default indicator
5407 um.on('beforeupdate', this.onBeforeLoad, this);
5408 um.on('update', this.onLoad, this);
5409 um.on('failure', this.onLoad, this);
5410 this.removeMask = true;
5414 Roo.LoadMask.prototype = {
5416 * @cfg {Boolean} removeMask
5417 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5418 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5422 * The text to display in a centered loading message box (defaults to 'Loading...')
5426 * @cfg {String} msgCls
5427 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5429 msgCls : 'x-mask-loading',
5432 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5438 * Disables the mask to prevent it from being displayed
5440 disable : function(){
5441 this.disabled = true;
5445 * Enables the mask so that it can be displayed
5447 enable : function(){
5448 this.disabled = false;
5451 onLoadException : function()
5455 if (typeof(arguments[3]) != 'undefined') {
5456 Roo.MessageBox.alert("Error loading",arguments[3]);
5460 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5461 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5470 this.el.unmask(this.removeMask);
5475 this.el.unmask(this.removeMask);
5479 onBeforeLoad : function(){
5481 this.el.mask(this.msg, this.msgCls);
5486 destroy : function(){
5488 this.store.un('beforeload', this.onBeforeLoad, this);
5489 this.store.un('load', this.onLoad, this);
5490 this.store.un('loadexception', this.onLoadException, this);
5492 var um = this.el.getUpdateManager();
5493 um.un('beforeupdate', this.onBeforeLoad, this);
5494 um.un('update', this.onLoad, this);
5495 um.un('failure', this.onLoad, this);
5506 * @class Roo.bootstrap.Table
5507 * @extends Roo.bootstrap.Component
5508 * Bootstrap Table class
5509 * @cfg {String} cls table class
5510 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5511 * @cfg {String} bgcolor Specifies the background color for a table
5512 * @cfg {Number} border Specifies whether the table cells should have borders or not
5513 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5514 * @cfg {Number} cellspacing Specifies the space between cells
5515 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5516 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5517 * @cfg {String} sortable Specifies that the table should be sortable
5518 * @cfg {String} summary Specifies a summary of the content of a table
5519 * @cfg {Number} width Specifies the width of a table
5520 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5522 * @cfg {boolean} striped Should the rows be alternative striped
5523 * @cfg {boolean} bordered Add borders to the table
5524 * @cfg {boolean} hover Add hover highlighting
5525 * @cfg {boolean} condensed Format condensed
5526 * @cfg {boolean} responsive Format condensed
5527 * @cfg {Boolean} loadMask (true|false) default false
5528 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5529 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5530 * @cfg {Boolean} rowSelection (true|false) default false
5531 * @cfg {Boolean} cellSelection (true|false) default false
5532 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5536 * Create a new Table
5537 * @param {Object} config The config object
5540 Roo.bootstrap.Table = function(config){
5541 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5544 this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5545 this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5546 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5547 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5551 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5552 this.sm = this.selModel;
5553 this.sm.xmodule = this.xmodule || false;
5555 if (this.cm && typeof(this.cm.config) == 'undefined') {
5556 this.colModel = new Roo.grid.ColumnModel(this.cm);
5557 this.cm = this.colModel;
5558 this.cm.xmodule = this.xmodule || false;
5561 this.store= Roo.factory(this.store, Roo.data);
5562 this.ds = this.store;
5563 this.ds.xmodule = this.xmodule || false;
5566 if (this.footer && this.store) {
5567 this.footer.dataSource = this.ds;
5568 this.footer = Roo.factory(this.footer);
5575 * Fires when a cell is clicked
5576 * @param {Roo.bootstrap.Table} this
5577 * @param {Roo.Element} el
5578 * @param {Number} rowIndex
5579 * @param {Number} columnIndex
5580 * @param {Roo.EventObject} e
5584 * @event celldblclick
5585 * Fires when a cell is double clicked
5586 * @param {Roo.bootstrap.Table} this
5587 * @param {Roo.Element} el
5588 * @param {Number} rowIndex
5589 * @param {Number} columnIndex
5590 * @param {Roo.EventObject} e
5592 "celldblclick" : true,
5595 * Fires when a row is clicked
5596 * @param {Roo.bootstrap.Table} this
5597 * @param {Roo.Element} el
5598 * @param {Number} rowIndex
5599 * @param {Roo.EventObject} e
5603 * @event rowdblclick
5604 * Fires when a row is double clicked
5605 * @param {Roo.bootstrap.Table} this
5606 * @param {Roo.Element} el
5607 * @param {Number} rowIndex
5608 * @param {Roo.EventObject} e
5610 "rowdblclick" : true,
5613 * Fires when a mouseover occur
5614 * @param {Roo.bootstrap.Table} this
5615 * @param {Roo.Element} el
5616 * @param {Number} rowIndex
5617 * @param {Number} columnIndex
5618 * @param {Roo.EventObject} e
5623 * Fires when a mouseout occur
5624 * @param {Roo.bootstrap.Table} this
5625 * @param {Roo.Element} el
5626 * @param {Number} rowIndex
5627 * @param {Number} columnIndex
5628 * @param {Roo.EventObject} e
5633 * Fires when a row is rendered, so you can change add a style to it.
5634 * @param {Roo.bootstrap.Table} this
5635 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5639 * @event rowsrendered
5640 * Fires when all the rows have been rendered
5641 * @param {Roo.bootstrap.Table} this
5643 'rowsrendered' : true
5648 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5673 rowSelection : false,
5674 cellSelection : false,
5677 // Roo.Element - the tbody
5680 getAutoCreate : function(){
5681 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5690 cfg.cls += ' table-striped';
5694 cfg.cls += ' table-hover';
5696 if (this.bordered) {
5697 cfg.cls += ' table-bordered';
5699 if (this.condensed) {
5700 cfg.cls += ' table-condensed';
5702 if (this.responsive) {
5703 cfg.cls += ' table-responsive';
5707 cfg.cls+= ' ' +this.cls;
5710 // this lot should be simplifed...
5713 cfg.align=this.align;
5716 cfg.bgcolor=this.bgcolor;
5719 cfg.border=this.border;
5721 if (this.cellpadding) {
5722 cfg.cellpadding=this.cellpadding;
5724 if (this.cellspacing) {
5725 cfg.cellspacing=this.cellspacing;
5728 cfg.frame=this.frame;
5731 cfg.rules=this.rules;
5733 if (this.sortable) {
5734 cfg.sortable=this.sortable;
5737 cfg.summary=this.summary;
5740 cfg.width=this.width;
5743 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5746 if(this.store || this.cm){
5747 if(this.headerShow){
5748 cfg.cn.push(this.renderHeader());
5751 cfg.cn.push(this.renderBody());
5753 if(this.footerShow){
5754 cfg.cn.push(this.renderFooter());
5757 cfg.cls+= ' TableGrid';
5760 return { cn : [ cfg ] };
5763 initEvents : function()
5765 if(!this.store || !this.cm){
5769 //Roo.log('initEvents with ds!!!!');
5771 this.mainBody = this.el.select('tbody', true).first();
5776 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5777 e.on('click', _this.sort, _this);
5780 this.el.on("click", this.onClick, this);
5781 this.el.on("dblclick", this.onDblClick, this);
5783 // why is this done????? = it breaks dialogs??
5784 //this.parent().el.setStyle('position', 'relative');
5788 this.footer.parentId = this.id;
5789 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5792 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5794 this.store.on('load', this.onLoad, this);
5795 this.store.on('beforeload', this.onBeforeLoad, this);
5796 this.store.on('update', this.onUpdate, this);
5797 this.store.on('add', this.onAdd, this);
5801 onMouseover : function(e, el)
5803 var cell = Roo.get(el);
5809 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5810 cell = cell.findParent('td', false, true);
5813 var row = cell.findParent('tr', false, true);
5814 var cellIndex = cell.dom.cellIndex;
5815 var rowIndex = row.dom.rowIndex - 1; // start from 0
5817 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5821 onMouseout : function(e, el)
5823 var cell = Roo.get(el);
5829 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5830 cell = cell.findParent('td', false, true);
5833 var row = cell.findParent('tr', false, true);
5834 var cellIndex = cell.dom.cellIndex;
5835 var rowIndex = row.dom.rowIndex - 1; // start from 0
5837 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5841 onClick : function(e, el)
5843 var cell = Roo.get(el);
5845 if(!cell || (!this.cellSelection && !this.rowSelection)){
5849 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5850 cell = cell.findParent('td', false, true);
5853 if(!cell || typeof(cell) == 'undefined'){
5857 var row = cell.findParent('tr', false, true);
5859 if(!row || typeof(row) == 'undefined'){
5863 var cellIndex = cell.dom.cellIndex;
5864 var rowIndex = this.getRowIndex(row);
5866 // why??? - should these not be based on SelectionModel?
5867 if(this.cellSelection){
5868 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5871 if(this.rowSelection){
5872 this.fireEvent('rowclick', this, row, rowIndex, e);
5878 onDblClick : function(e,el)
5880 var cell = Roo.get(el);
5882 if(!cell || (!this.CellSelection && !this.RowSelection)){
5886 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5887 cell = cell.findParent('td', false, true);
5890 if(!cell || typeof(cell) == 'undefined'){
5894 var row = cell.findParent('tr', false, true);
5896 if(!row || typeof(row) == 'undefined'){
5900 var cellIndex = cell.dom.cellIndex;
5901 var rowIndex = this.getRowIndex(row);
5903 if(this.CellSelection){
5904 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5907 if(this.RowSelection){
5908 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5912 sort : function(e,el)
5914 var col = Roo.get(el);
5916 if(!col.hasClass('sortable')){
5920 var sort = col.attr('sort');
5923 if(col.hasClass('glyphicon-arrow-up')){
5927 this.store.sortInfo = {field : sort, direction : dir};
5930 Roo.log("calling footer first");
5931 this.footer.onClick('first');
5934 this.store.load({ params : { start : 0 } });
5938 renderHeader : function()
5947 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5949 var config = cm.config[i];
5954 html: cm.getColumnHeader(i)
5959 if(typeof(config.lgHeader) != 'undefined'){
5960 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5963 if(typeof(config.mdHeader) != 'undefined'){
5964 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5967 if(typeof(config.smHeader) != 'undefined'){
5968 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5971 if(typeof(config.xsHeader) != 'undefined'){
5972 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5979 if(typeof(config.tooltip) != 'undefined'){
5980 c.tooltip = config.tooltip;
5983 if(typeof(config.colspan) != 'undefined'){
5984 c.colspan = config.colspan;
5987 if(typeof(config.hidden) != 'undefined' && config.hidden){
5988 c.style += ' display:none;';
5991 if(typeof(config.dataIndex) != 'undefined'){
5992 c.sort = config.dataIndex;
5995 if(typeof(config.sortable) != 'undefined' && config.sortable){
5999 if(typeof(config.align) != 'undefined' && config.align.length){
6000 c.style += ' text-align:' + config.align + ';';
6003 if(typeof(config.width) != 'undefined'){
6004 c.style += ' width:' + config.width + 'px;';
6007 if(typeof(config.cls) != 'undefined'){
6008 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6011 ['xs','sm','md','lg'].map(function(size){
6013 if(typeof(config[size]) == 'undefined'){
6017 if (!config[size]) { // 0 = hidden
6018 c.cls += ' hidden-' + size;
6022 c.cls += ' col-' + size + '-' + config[size];
6032 renderBody : function()
6042 colspan : this.cm.getColumnCount()
6052 renderFooter : function()
6062 colspan : this.cm.getColumnCount()
6076 Roo.log('ds onload');
6081 var ds = this.store;
6083 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6084 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6086 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6087 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6090 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6091 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6095 var tbody = this.mainBody;
6097 if(ds.getCount() > 0){
6098 ds.data.each(function(d,rowIndex){
6099 var row = this.renderRow(cm, ds, rowIndex);
6101 tbody.createChild(row);
6105 if(row.cellObjects.length){
6106 Roo.each(row.cellObjects, function(r){
6107 _this.renderCellObject(r);
6114 Roo.each(this.el.select('tbody td', true).elements, function(e){
6115 e.on('mouseover', _this.onMouseover, _this);
6118 Roo.each(this.el.select('tbody td', true).elements, function(e){
6119 e.on('mouseout', _this.onMouseout, _this);
6121 this.fireEvent('rowsrendered', this);
6122 //if(this.loadMask){
6123 // this.maskEl.hide();
6128 onUpdate : function(ds,record)
6130 this.refreshRow(record);
6133 onRemove : function(ds, record, index, isUpdate){
6134 if(isUpdate !== true){
6135 this.fireEvent("beforerowremoved", this, index, record);
6137 var bt = this.mainBody.dom;
6139 var rows = this.el.select('tbody > tr', true).elements;
6141 if(typeof(rows[index]) != 'undefined'){
6142 bt.removeChild(rows[index].dom);
6145 // if(bt.rows[index]){
6146 // bt.removeChild(bt.rows[index]);
6149 if(isUpdate !== true){
6150 //this.stripeRows(index);
6151 //this.syncRowHeights(index, index);
6153 this.fireEvent("rowremoved", this, index, record);
6157 onAdd : function(ds, records, rowIndex)
6159 //Roo.log('on Add called');
6160 // - note this does not handle multiple adding very well..
6161 var bt = this.mainBody.dom;
6162 for (var i =0 ; i < records.length;i++) {
6163 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6164 //Roo.log(records[i]);
6165 //Roo.log(this.store.getAt(rowIndex+i));
6166 this.insertRow(this.store, rowIndex + i, false);
6173 refreshRow : function(record){
6174 var ds = this.store, index;
6175 if(typeof record == 'number'){
6177 record = ds.getAt(index);
6179 index = ds.indexOf(record);
6181 this.insertRow(ds, index, true);
6182 this.onRemove(ds, record, index+1, true);
6183 //this.syncRowHeights(index, index);
6185 this.fireEvent("rowupdated", this, index, record);
6188 insertRow : function(dm, rowIndex, isUpdate){
6191 this.fireEvent("beforerowsinserted", this, rowIndex);
6193 //var s = this.getScrollState();
6194 var row = this.renderRow(this.cm, this.store, rowIndex);
6195 // insert before rowIndex..
6196 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6200 if(row.cellObjects.length){
6201 Roo.each(row.cellObjects, function(r){
6202 _this.renderCellObject(r);
6207 this.fireEvent("rowsinserted", this, rowIndex);
6208 //this.syncRowHeights(firstRow, lastRow);
6209 //this.stripeRows(firstRow);
6216 getRowDom : function(rowIndex)
6218 var rows = this.el.select('tbody > tr', true).elements;
6220 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6223 // returns the object tree for a tr..
6226 renderRow : function(cm, ds, rowIndex)
6229 var d = ds.getAt(rowIndex);
6236 var cellObjects = [];
6238 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6239 var config = cm.config[i];
6241 var renderer = cm.getRenderer(i);
6245 if(typeof(renderer) !== 'undefined'){
6246 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6248 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6249 // and are rendered into the cells after the row is rendered - using the id for the element.
6251 if(typeof(value) === 'object'){
6261 rowIndex : rowIndex,
6266 this.fireEvent('rowclass', this, rowcfg);
6270 cls : rowcfg.rowClass,
6272 html: (typeof(value) === 'object') ? '' : value
6279 if(typeof(config.colspan) != 'undefined'){
6280 td.colspan = config.colspan;
6283 if(typeof(config.hidden) != 'undefined' && config.hidden){
6284 td.style += ' display:none;';
6287 if(typeof(config.align) != 'undefined' && config.align.length){
6288 td.style += ' text-align:' + config.align + ';';
6291 if(typeof(config.width) != 'undefined'){
6292 td.style += ' width:' + config.width + 'px;';
6295 if(typeof(config.cursor) != 'undefined'){
6296 td.style += ' cursor:' + config.cursor + ';';
6299 if(typeof(config.cls) != 'undefined'){
6300 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6303 ['xs','sm','md','lg'].map(function(size){
6305 if(typeof(config[size]) == 'undefined'){
6309 if (!config[size]) { // 0 = hidden
6310 td.cls += ' hidden-' + size;
6314 td.cls += ' col-' + size + '-' + config[size];
6322 row.cellObjects = cellObjects;
6330 onBeforeLoad : function()
6332 //Roo.log('ds onBeforeLoad');
6336 //if(this.loadMask){
6337 // this.maskEl.show();
6345 this.el.select('tbody', true).first().dom.innerHTML = '';
6348 * Show or hide a row.
6349 * @param {Number} rowIndex to show or hide
6350 * @param {Boolean} state hide
6352 setRowVisibility : function(rowIndex, state)
6354 var bt = this.mainBody.dom;
6356 var rows = this.el.select('tbody > tr', true).elements;
6358 if(typeof(rows[rowIndex]) == 'undefined'){
6361 rows[rowIndex].dom.style.display = state ? '' : 'none';
6365 getSelectionModel : function(){
6367 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6369 return this.selModel;
6372 * Render the Roo.bootstrap object from renderder
6374 renderCellObject : function(r)
6378 var t = r.cfg.render(r.container);
6381 Roo.each(r.cfg.cn, function(c){
6383 container: t.getChildContainer(),
6386 _this.renderCellObject(child);
6391 getRowIndex : function(row)
6395 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6418 * @class Roo.bootstrap.TableCell
6419 * @extends Roo.bootstrap.Component
6420 * Bootstrap TableCell class
6421 * @cfg {String} html cell contain text
6422 * @cfg {String} cls cell class
6423 * @cfg {String} tag cell tag (td|th) default td
6424 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6425 * @cfg {String} align Aligns the content in a cell
6426 * @cfg {String} axis Categorizes cells
6427 * @cfg {String} bgcolor Specifies the background color of a cell
6428 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6429 * @cfg {Number} colspan Specifies the number of columns a cell should span
6430 * @cfg {String} headers Specifies one or more header cells a cell is related to
6431 * @cfg {Number} height Sets the height of a cell
6432 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6433 * @cfg {Number} rowspan Sets the number of rows a cell should span
6434 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6435 * @cfg {String} valign Vertical aligns the content in a cell
6436 * @cfg {Number} width Specifies the width of a cell
6439 * Create a new TableCell
6440 * @param {Object} config The config object
6443 Roo.bootstrap.TableCell = function(config){
6444 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6447 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6467 getAutoCreate : function(){
6468 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6488 cfg.align=this.align
6494 cfg.bgcolor=this.bgcolor
6497 cfg.charoff=this.charoff
6500 cfg.colspan=this.colspan
6503 cfg.headers=this.headers
6506 cfg.height=this.height
6509 cfg.nowrap=this.nowrap
6512 cfg.rowspan=this.rowspan
6515 cfg.scope=this.scope
6518 cfg.valign=this.valign
6521 cfg.width=this.width
6540 * @class Roo.bootstrap.TableRow
6541 * @extends Roo.bootstrap.Component
6542 * Bootstrap TableRow class
6543 * @cfg {String} cls row class
6544 * @cfg {String} align Aligns the content in a table row
6545 * @cfg {String} bgcolor Specifies a background color for a table row
6546 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6547 * @cfg {String} valign Vertical aligns the content in a table row
6550 * Create a new TableRow
6551 * @param {Object} config The config object
6554 Roo.bootstrap.TableRow = function(config){
6555 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6558 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6566 getAutoCreate : function(){
6567 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6577 cfg.align = this.align;
6580 cfg.bgcolor = this.bgcolor;
6583 cfg.charoff = this.charoff;
6586 cfg.valign = this.valign;
6604 * @class Roo.bootstrap.TableBody
6605 * @extends Roo.bootstrap.Component
6606 * Bootstrap TableBody class
6607 * @cfg {String} cls element class
6608 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6609 * @cfg {String} align Aligns the content inside the element
6610 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6611 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6614 * Create a new TableBody
6615 * @param {Object} config The config object
6618 Roo.bootstrap.TableBody = function(config){
6619 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6622 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6630 getAutoCreate : function(){
6631 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6645 cfg.align = this.align;
6648 cfg.charoff = this.charoff;
6651 cfg.valign = this.valign;
6658 // initEvents : function()
6665 // this.store = Roo.factory(this.store, Roo.data);
6666 // this.store.on('load', this.onLoad, this);
6668 // this.store.load();
6672 // onLoad: function ()
6674 // this.fireEvent('load', this);
6684 * Ext JS Library 1.1.1
6685 * Copyright(c) 2006-2007, Ext JS, LLC.
6687 * Originally Released Under LGPL - original licence link has changed is not relivant.
6690 * <script type="text/javascript">
6693 // as we use this in bootstrap.
6694 Roo.namespace('Roo.form');
6696 * @class Roo.form.Action
6697 * Internal Class used to handle form actions
6699 * @param {Roo.form.BasicForm} el The form element or its id
6700 * @param {Object} config Configuration options
6705 // define the action interface
6706 Roo.form.Action = function(form, options){
6708 this.options = options || {};
6711 * Client Validation Failed
6714 Roo.form.Action.CLIENT_INVALID = 'client';
6716 * Server Validation Failed
6719 Roo.form.Action.SERVER_INVALID = 'server';
6721 * Connect to Server Failed
6724 Roo.form.Action.CONNECT_FAILURE = 'connect';
6726 * Reading Data from Server Failed
6729 Roo.form.Action.LOAD_FAILURE = 'load';
6731 Roo.form.Action.prototype = {
6733 failureType : undefined,
6734 response : undefined,
6738 run : function(options){
6743 success : function(response){
6748 handleResponse : function(response){
6752 // default connection failure
6753 failure : function(response){
6755 this.response = response;
6756 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6757 this.form.afterAction(this, false);
6760 processResponse : function(response){
6761 this.response = response;
6762 if(!response.responseText){
6765 this.result = this.handleResponse(response);
6769 // utility functions used internally
6770 getUrl : function(appendParams){
6771 var url = this.options.url || this.form.url || this.form.el.dom.action;
6773 var p = this.getParams();
6775 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6781 getMethod : function(){
6782 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6785 getParams : function(){
6786 var bp = this.form.baseParams;
6787 var p = this.options.params;
6789 if(typeof p == "object"){
6790 p = Roo.urlEncode(Roo.applyIf(p, bp));
6791 }else if(typeof p == 'string' && bp){
6792 p += '&' + Roo.urlEncode(bp);
6795 p = Roo.urlEncode(bp);
6800 createCallback : function(){
6802 success: this.success,
6803 failure: this.failure,
6805 timeout: (this.form.timeout*1000),
6806 upload: this.form.fileUpload ? this.success : undefined
6811 Roo.form.Action.Submit = function(form, options){
6812 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6815 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6818 haveProgress : false,
6819 uploadComplete : false,
6821 // uploadProgress indicator.
6822 uploadProgress : function()
6824 if (!this.form.progressUrl) {
6828 if (!this.haveProgress) {
6829 Roo.MessageBox.progress("Uploading", "Uploading");
6831 if (this.uploadComplete) {
6832 Roo.MessageBox.hide();
6836 this.haveProgress = true;
6838 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6840 var c = new Roo.data.Connection();
6842 url : this.form.progressUrl,
6847 success : function(req){
6848 //console.log(data);
6852 rdata = Roo.decode(req.responseText)
6854 Roo.log("Invalid data from server..");
6858 if (!rdata || !rdata.success) {
6860 Roo.MessageBox.alert(Roo.encode(rdata));
6863 var data = rdata.data;
6865 if (this.uploadComplete) {
6866 Roo.MessageBox.hide();
6871 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6872 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6875 this.uploadProgress.defer(2000,this);
6878 failure: function(data) {
6879 Roo.log('progress url failed ');
6890 // run get Values on the form, so it syncs any secondary forms.
6891 this.form.getValues();
6893 var o = this.options;
6894 var method = this.getMethod();
6895 var isPost = method == 'POST';
6896 if(o.clientValidation === false || this.form.isValid()){
6898 if (this.form.progressUrl) {
6899 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6900 (new Date() * 1) + '' + Math.random());
6905 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6906 form:this.form.el.dom,
6907 url:this.getUrl(!isPost),
6909 params:isPost ? this.getParams() : null,
6910 isUpload: this.form.fileUpload
6913 this.uploadProgress();
6915 }else if (o.clientValidation !== false){ // client validation failed
6916 this.failureType = Roo.form.Action.CLIENT_INVALID;
6917 this.form.afterAction(this, false);
6921 success : function(response)
6923 this.uploadComplete= true;
6924 if (this.haveProgress) {
6925 Roo.MessageBox.hide();
6929 var result = this.processResponse(response);
6930 if(result === true || result.success){
6931 this.form.afterAction(this, true);
6935 this.form.markInvalid(result.errors);
6936 this.failureType = Roo.form.Action.SERVER_INVALID;
6938 this.form.afterAction(this, false);
6940 failure : function(response)
6942 this.uploadComplete= true;
6943 if (this.haveProgress) {
6944 Roo.MessageBox.hide();
6947 this.response = response;
6948 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6949 this.form.afterAction(this, false);
6952 handleResponse : function(response){
6953 if(this.form.errorReader){
6954 var rs = this.form.errorReader.read(response);
6957 for(var i = 0, len = rs.records.length; i < len; i++) {
6958 var r = rs.records[i];
6962 if(errors.length < 1){
6966 success : rs.success,
6972 ret = Roo.decode(response.responseText);
6976 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6986 Roo.form.Action.Load = function(form, options){
6987 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6988 this.reader = this.form.reader;
6991 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6996 Roo.Ajax.request(Roo.apply(
6997 this.createCallback(), {
6998 method:this.getMethod(),
6999 url:this.getUrl(false),
7000 params:this.getParams()
7004 success : function(response){
7006 var result = this.processResponse(response);
7007 if(result === true || !result.success || !result.data){
7008 this.failureType = Roo.form.Action.LOAD_FAILURE;
7009 this.form.afterAction(this, false);
7012 this.form.clearInvalid();
7013 this.form.setValues(result.data);
7014 this.form.afterAction(this, true);
7017 handleResponse : function(response){
7018 if(this.form.reader){
7019 var rs = this.form.reader.read(response);
7020 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7022 success : rs.success,
7026 return Roo.decode(response.responseText);
7030 Roo.form.Action.ACTION_TYPES = {
7031 'load' : Roo.form.Action.Load,
7032 'submit' : Roo.form.Action.Submit
7041 * @class Roo.bootstrap.Form
7042 * @extends Roo.bootstrap.Component
7043 * Bootstrap Form class
7044 * @cfg {String} method GET | POST (default POST)
7045 * @cfg {String} labelAlign top | left (default top)
7046 * @cfg {String} align left | right - for navbars
7047 * @cfg {Boolean} loadMask load mask when submit (default true)
7052 * @param {Object} config The config object
7056 Roo.bootstrap.Form = function(config){
7057 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7060 * @event clientvalidation
7061 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7062 * @param {Form} this
7063 * @param {Boolean} valid true if the form has passed client-side validation
7065 clientvalidation: true,
7067 * @event beforeaction
7068 * Fires before any action is performed. Return false to cancel the action.
7069 * @param {Form} this
7070 * @param {Action} action The action to be performed
7074 * @event actionfailed
7075 * Fires when an action fails.
7076 * @param {Form} this
7077 * @param {Action} action The action that failed
7079 actionfailed : true,
7081 * @event actioncomplete
7082 * Fires when an action is completed.
7083 * @param {Form} this
7084 * @param {Action} action The action that completed
7086 actioncomplete : true
7091 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7094 * @cfg {String} method
7095 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7100 * The URL to use for form actions if one isn't supplied in the action options.
7103 * @cfg {Boolean} fileUpload
7104 * Set to true if this form is a file upload.
7108 * @cfg {Object} baseParams
7109 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7113 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7117 * @cfg {Sting} align (left|right) for navbar forms
7122 activeAction : null,
7125 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7126 * element by passing it or its id or mask the form itself by passing in true.
7129 waitMsgTarget : false,
7133 getAutoCreate : function(){
7137 method : this.method || 'POST',
7138 id : this.id || Roo.id(),
7141 if (this.parent().xtype.match(/^Nav/)) {
7142 cfg.cls = 'navbar-form navbar-' + this.align;
7146 if (this.labelAlign == 'left' ) {
7147 cfg.cls += ' form-horizontal';
7153 initEvents : function()
7155 this.el.on('submit', this.onSubmit, this);
7156 // this was added as random key presses on the form where triggering form submit.
7157 this.el.on('keypress', function(e) {
7158 if (e.getCharCode() != 13) {
7161 // we might need to allow it for textareas.. and some other items.
7162 // check e.getTarget().
7164 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7168 Roo.log("keypress blocked");
7176 onSubmit : function(e){
7181 * Returns true if client-side validation on the form is successful.
7184 isValid : function(){
7185 var items = this.getItems();
7187 items.each(function(f){
7196 * Returns true if any fields in this form have changed since their original load.
7199 isDirty : function(){
7201 var items = this.getItems();
7202 items.each(function(f){
7212 * Performs a predefined action (submit or load) or custom actions you define on this form.
7213 * @param {String} actionName The name of the action type
7214 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7215 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7216 * accept other config options):
7218 Property Type Description
7219 ---------------- --------------- ----------------------------------------------------------------------------------
7220 url String The url for the action (defaults to the form's url)
7221 method String The form method to use (defaults to the form's method, or POST if not defined)
7222 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7223 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7224 validate the form on the client (defaults to false)
7226 * @return {BasicForm} this
7228 doAction : function(action, options){
7229 if(typeof action == 'string'){
7230 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7232 if(this.fireEvent('beforeaction', this, action) !== false){
7233 this.beforeAction(action);
7234 action.run.defer(100, action);
7240 beforeAction : function(action){
7241 var o = action.options;
7244 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7246 // not really supported yet.. ??
7248 //if(this.waitMsgTarget === true){
7249 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7250 //}else if(this.waitMsgTarget){
7251 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7252 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7254 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7260 afterAction : function(action, success){
7261 this.activeAction = null;
7262 var o = action.options;
7264 //if(this.waitMsgTarget === true){
7266 //}else if(this.waitMsgTarget){
7267 // this.waitMsgTarget.unmask();
7269 // Roo.MessageBox.updateProgress(1);
7270 // Roo.MessageBox.hide();
7277 Roo.callback(o.success, o.scope, [this, action]);
7278 this.fireEvent('actioncomplete', this, action);
7282 // failure condition..
7283 // we have a scenario where updates need confirming.
7284 // eg. if a locking scenario exists..
7285 // we look for { errors : { needs_confirm : true }} in the response.
7287 (typeof(action.result) != 'undefined') &&
7288 (typeof(action.result.errors) != 'undefined') &&
7289 (typeof(action.result.errors.needs_confirm) != 'undefined')
7292 Roo.log("not supported yet");
7295 Roo.MessageBox.confirm(
7296 "Change requires confirmation",
7297 action.result.errorMsg,
7302 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7312 Roo.callback(o.failure, o.scope, [this, action]);
7313 // show an error message if no failed handler is set..
7314 if (!this.hasListener('actionfailed')) {
7315 Roo.log("need to add dialog support");
7317 Roo.MessageBox.alert("Error",
7318 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7319 action.result.errorMsg :
7320 "Saving Failed, please check your entries or try again"
7325 this.fireEvent('actionfailed', this, action);
7330 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7331 * @param {String} id The value to search for
7334 findField : function(id){
7335 var items = this.getItems();
7336 var field = items.get(id);
7338 items.each(function(f){
7339 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7346 return field || null;
7349 * Mark fields in this form invalid in bulk.
7350 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7351 * @return {BasicForm} this
7353 markInvalid : function(errors){
7354 if(errors instanceof Array){
7355 for(var i = 0, len = errors.length; i < len; i++){
7356 var fieldError = errors[i];
7357 var f = this.findField(fieldError.id);
7359 f.markInvalid(fieldError.msg);
7365 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7366 field.markInvalid(errors[id]);
7370 //Roo.each(this.childForms || [], function (f) {
7371 // f.markInvalid(errors);
7378 * Set values for fields in this form in bulk.
7379 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7380 * @return {BasicForm} this
7382 setValues : function(values){
7383 if(values instanceof Array){ // array of objects
7384 for(var i = 0, len = values.length; i < len; i++){
7386 var f = this.findField(v.id);
7388 f.setValue(v.value);
7389 if(this.trackResetOnLoad){
7390 f.originalValue = f.getValue();
7394 }else{ // object hash
7397 if(typeof values[id] != 'function' && (field = this.findField(id))){
7399 if (field.setFromData &&
7401 field.displayField &&
7402 // combos' with local stores can
7403 // be queried via setValue()
7404 // to set their value..
7405 (field.store && !field.store.isLocal)
7409 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7410 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7411 field.setFromData(sd);
7414 field.setValue(values[id]);
7418 if(this.trackResetOnLoad){
7419 field.originalValue = field.getValue();
7425 //Roo.each(this.childForms || [], function (f) {
7426 // f.setValues(values);
7433 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7434 * they are returned as an array.
7435 * @param {Boolean} asString
7438 getValues : function(asString){
7439 //if (this.childForms) {
7440 // copy values from the child forms
7441 // Roo.each(this.childForms, function (f) {
7442 // this.setValues(f.getValues());
7448 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7449 if(asString === true){
7452 return Roo.urlDecode(fs);
7456 * Returns the fields in this form as an object with key/value pairs.
7457 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7460 getFieldValues : function(with_hidden)
7462 var items = this.getItems();
7464 items.each(function(f){
7468 var v = f.getValue();
7469 if (f.inputType =='radio') {
7470 if (typeof(ret[f.getName()]) == 'undefined') {
7471 ret[f.getName()] = ''; // empty..
7474 if (!f.el.dom.checked) {
7482 // not sure if this supported any more..
7483 if ((typeof(v) == 'object') && f.getRawValue) {
7484 v = f.getRawValue() ; // dates..
7486 // combo boxes where name != hiddenName...
7487 if (f.name != f.getName()) {
7488 ret[f.name] = f.getRawValue();
7490 ret[f.getName()] = v;
7497 * Clears all invalid messages in this form.
7498 * @return {BasicForm} this
7500 clearInvalid : function(){
7501 var items = this.getItems();
7503 items.each(function(f){
7514 * @return {BasicForm} this
7517 var items = this.getItems();
7518 items.each(function(f){
7522 Roo.each(this.childForms || [], function (f) {
7529 getItems : function()
7531 var r=new Roo.util.MixedCollection(false, function(o){
7532 return o.id || (o.id = Roo.id());
7534 var iter = function(el) {
7541 Roo.each(el.items,function(e) {
7561 * Ext JS Library 1.1.1
7562 * Copyright(c) 2006-2007, Ext JS, LLC.
7564 * Originally Released Under LGPL - original licence link has changed is not relivant.
7567 * <script type="text/javascript">
7570 * @class Roo.form.VTypes
7571 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7574 Roo.form.VTypes = function(){
7575 // closure these in so they are only created once.
7576 var alpha = /^[a-zA-Z_]+$/;
7577 var alphanum = /^[a-zA-Z0-9_]+$/;
7578 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
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);
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);
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) {
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
8057 if (this.parentType === 'Navbar' && this.parent().bar) {
8058 cfg.cls += ' navbar-form';
8065 * return the real input element.
8067 inputEl: function ()
8069 return this.el.select('input.form-control',true).first();
8072 tooltipEl : function()
8074 return this.inputEl();
8077 setDisabled : function(v)
8079 var i = this.inputEl().dom;
8081 i.removeAttribute('disabled');
8085 i.setAttribute('disabled','true');
8087 initEvents : function()
8090 this.inputEl().on("keydown" , this.fireKey, this);
8091 this.inputEl().on("focus", this.onFocus, this);
8092 this.inputEl().on("blur", this.onBlur, this);
8094 this.inputEl().relayEvent('keyup', this);
8096 // reference to original value for reset
8097 this.originalValue = this.getValue();
8098 //Roo.form.TextField.superclass.initEvents.call(this);
8099 if(this.validationEvent == 'keyup'){
8100 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8101 this.inputEl().on('keyup', this.filterValidation, this);
8103 else if(this.validationEvent !== false){
8104 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8107 if(this.selectOnFocus){
8108 this.on("focus", this.preFocus, this);
8111 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8112 this.inputEl().on("keypress", this.filterKeys, this);
8115 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8116 this.el.on("click", this.autoSize, this);
8119 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8120 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8123 if (typeof(this.before) == 'object') {
8124 this.before.render(this.el.select('.roo-input-before',true).first());
8126 if (typeof(this.after) == 'object') {
8127 this.after.render(this.el.select('.roo-input-after',true).first());
8132 filterValidation : function(e){
8133 if(!e.isNavKeyPress()){
8134 this.validationTask.delay(this.validationDelay);
8138 * Validates the field value
8139 * @return {Boolean} True if the value is valid, else false
8141 validate : function(){
8142 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8143 if(this.disabled || this.validateValue(this.getRawValue())){
8154 * Validates a value according to the field's validation rules and marks the field as invalid
8155 * if the validation fails
8156 * @param {Mixed} value The value to validate
8157 * @return {Boolean} True if the value is valid, else false
8159 validateValue : function(value){
8160 if(value.length < 1) { // if it's blank
8161 if(this.allowBlank){
8167 if(value.length < this.minLength){
8170 if(value.length > this.maxLength){
8174 var vt = Roo.form.VTypes;
8175 if(!vt[this.vtype](value, this)){
8179 if(typeof this.validator == "function"){
8180 var msg = this.validator(value);
8186 if(this.regex && !this.regex.test(value)){
8196 fireKey : function(e){
8197 //Roo.log('field ' + e.getKey());
8198 if(e.isNavKeyPress()){
8199 this.fireEvent("specialkey", this, e);
8202 focus : function (selectText){
8204 this.inputEl().focus();
8205 if(selectText === true){
8206 this.inputEl().dom.select();
8212 onFocus : function(){
8213 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8214 // this.el.addClass(this.focusClass);
8217 this.hasFocus = true;
8218 this.startValue = this.getValue();
8219 this.fireEvent("focus", this);
8223 beforeBlur : Roo.emptyFn,
8227 onBlur : function(){
8229 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8230 //this.el.removeClass(this.focusClass);
8232 this.hasFocus = false;
8233 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8236 var v = this.getValue();
8237 if(String(v) !== String(this.startValue)){
8238 this.fireEvent('change', this, v, this.startValue);
8240 this.fireEvent("blur", this);
8244 * Resets the current field value to the originally loaded value and clears any validation messages
8247 this.setValue(this.originalValue);
8251 * Returns the name of the field
8252 * @return {Mixed} name The name field
8254 getName: function(){
8258 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8259 * @return {Mixed} value The field value
8261 getValue : function(){
8263 var v = this.inputEl().getValue();
8268 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8269 * @return {Mixed} value The field value
8271 getRawValue : function(){
8272 var v = this.inputEl().getValue();
8278 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8279 * @param {Mixed} value The value to set
8281 setRawValue : function(v){
8282 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8285 selectText : function(start, end){
8286 var v = this.getRawValue();
8288 start = start === undefined ? 0 : start;
8289 end = end === undefined ? v.length : end;
8290 var d = this.inputEl().dom;
8291 if(d.setSelectionRange){
8292 d.setSelectionRange(start, end);
8293 }else if(d.createTextRange){
8294 var range = d.createTextRange();
8295 range.moveStart("character", start);
8296 range.moveEnd("character", v.length-end);
8303 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8304 * @param {Mixed} value The value to set
8306 setValue : function(v){
8309 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8315 processValue : function(value){
8316 if(this.stripCharsRe){
8317 var newValue = value.replace(this.stripCharsRe, '');
8318 if(newValue !== value){
8319 this.setRawValue(newValue);
8326 preFocus : function(){
8328 if(this.selectOnFocus){
8329 this.inputEl().dom.select();
8332 filterKeys : function(e){
8334 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8337 var c = e.getCharCode(), cc = String.fromCharCode(c);
8338 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8341 if(!this.maskRe.test(cc)){
8346 * Clear any invalid styles/messages for this field
8348 clearInvalid : function(){
8350 if(!this.el || this.preventMark){ // not rendered
8353 this.el.removeClass(this.invalidClass);
8355 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8357 var feedback = this.el.select('.form-control-feedback', true).first();
8360 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8365 this.fireEvent('valid', this);
8369 * Mark this field as valid
8371 markValid : function()
8373 if(!this.el || this.preventMark){ // not rendered
8377 this.el.removeClass([this.invalidClass, this.validClass]);
8379 var feedback = this.el.select('.form-control-feedback', true).first();
8382 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8385 if(this.disabled || this.allowBlank){
8389 var formGroup = this.el.findParent('.form-group', false, true);
8393 var label = formGroup.select('label', true).first();
8394 var icon = formGroup.select('i.fa-star', true).first();
8401 this.el.addClass(this.validClass);
8403 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8405 var feedback = this.el.select('.form-control-feedback', true).first();
8408 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8409 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8414 this.fireEvent('valid', this);
8418 * Mark this field as invalid
8419 * @param {String} msg The validation message
8421 markInvalid : function(msg)
8423 if(!this.el || this.preventMark){ // not rendered
8427 this.el.removeClass([this.invalidClass, this.validClass]);
8429 var feedback = this.el.select('.form-control-feedback', true).first();
8432 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8435 if(this.disabled || this.allowBlank){
8439 var formGroup = this.el.findParent('.form-group', false, true);
8442 var label = formGroup.select('label', true).first();
8443 var icon = formGroup.select('i.fa-star', true).first();
8445 if(!this.getValue().length && label && !icon){
8446 this.el.findParent('.form-group', false, true).createChild({
8448 cls : 'text-danger fa fa-lg fa-star',
8449 tooltip : 'This field is required',
8450 style : 'margin-right:5px;'
8456 this.el.addClass(this.invalidClass);
8458 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8460 var feedback = this.el.select('.form-control-feedback', true).first();
8463 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8465 if(this.getValue().length || this.forceFeedback){
8466 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8473 this.fireEvent('invalid', this, msg);
8476 SafariOnKeyDown : function(event)
8478 // this is a workaround for a password hang bug on chrome/ webkit.
8480 var isSelectAll = false;
8482 if(this.inputEl().dom.selectionEnd > 0){
8483 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8485 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8486 event.preventDefault();
8491 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8493 event.preventDefault();
8494 // this is very hacky as keydown always get's upper case.
8496 var cc = String.fromCharCode(event.getCharCode());
8497 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8501 adjustWidth : function(tag, w){
8502 tag = tag.toLowerCase();
8503 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8504 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8508 if(tag == 'textarea'){
8511 }else if(Roo.isOpera){
8515 if(tag == 'textarea'){
8534 * @class Roo.bootstrap.TextArea
8535 * @extends Roo.bootstrap.Input
8536 * Bootstrap TextArea class
8537 * @cfg {Number} cols Specifies the visible width of a text area
8538 * @cfg {Number} rows Specifies the visible number of lines in a text area
8539 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8540 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8541 * @cfg {string} html text
8544 * Create a new TextArea
8545 * @param {Object} config The config object
8548 Roo.bootstrap.TextArea = function(config){
8549 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8553 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8563 getAutoCreate : function(){
8565 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8576 value : this.value || '',
8577 html: this.html || '',
8578 cls : 'form-control',
8579 placeholder : this.placeholder || ''
8583 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8584 input.maxLength = this.maxLength;
8588 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8592 input.cols = this.cols;
8595 if (this.readOnly) {
8596 input.readonly = true;
8600 input.name = this.name;
8604 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8608 ['xs','sm','md','lg'].map(function(size){
8609 if (settings[size]) {
8610 cfg.cls += ' col-' + size + '-' + settings[size];
8614 var inputblock = input;
8616 if(this.hasFeedback && !this.allowBlank){
8620 cls: 'glyphicon form-control-feedback'
8624 cls : 'has-feedback',
8633 if (this.before || this.after) {
8636 cls : 'input-group',
8640 inputblock.cn.push({
8642 cls : 'input-group-addon',
8647 inputblock.cn.push(input);
8649 if(this.hasFeedback && !this.allowBlank){
8650 inputblock.cls += ' has-feedback';
8651 inputblock.cn.push(feedback);
8655 inputblock.cn.push({
8657 cls : 'input-group-addon',
8664 if (align ==='left' && this.fieldLabel.length) {
8665 Roo.log("left and has label");
8671 cls : 'control-label col-sm-' + this.labelWidth,
8672 html : this.fieldLabel
8676 cls : "col-sm-" + (12 - this.labelWidth),
8683 } else if ( this.fieldLabel.length) {
8689 //cls : 'input-group-addon',
8690 html : this.fieldLabel
8700 Roo.log(" no label && no align");
8710 if (this.disabled) {
8711 input.disabled=true;
8718 * return the real textarea element.
8720 inputEl: function ()
8722 return this.el.select('textarea.form-control',true).first();
8726 * Clear any invalid styles/messages for this field
8728 clearInvalid : function()
8731 if(!this.el || this.preventMark){ // not rendered
8735 var label = this.el.select('label', true).first();
8736 var icon = this.el.select('i.fa-star', true).first();
8742 this.el.removeClass(this.invalidClass);
8744 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8746 var feedback = this.el.select('.form-control-feedback', true).first();
8749 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8754 this.fireEvent('valid', this);
8758 * Mark this field as valid
8760 markValid : function()
8762 if(!this.el || this.preventMark){ // not rendered
8766 this.el.removeClass([this.invalidClass, this.validClass]);
8768 var feedback = this.el.select('.form-control-feedback', true).first();
8771 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8774 if(this.disabled || this.allowBlank){
8778 var label = this.el.select('label', true).first();
8779 var icon = this.el.select('i.fa-star', true).first();
8785 this.el.addClass(this.validClass);
8787 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8789 var feedback = this.el.select('.form-control-feedback', true).first();
8792 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8793 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8798 this.fireEvent('valid', this);
8802 * Mark this field as invalid
8803 * @param {String} msg The validation message
8805 markInvalid : function(msg)
8807 if(!this.el || this.preventMark){ // not rendered
8811 this.el.removeClass([this.invalidClass, this.validClass]);
8813 var feedback = this.el.select('.form-control-feedback', true).first();
8816 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8819 if(this.disabled || this.allowBlank){
8823 var label = this.el.select('label', true).first();
8824 var icon = this.el.select('i.fa-star', true).first();
8826 if(!this.getValue().length && label && !icon){
8827 this.el.createChild({
8829 cls : 'text-danger fa fa-lg fa-star',
8830 tooltip : 'This field is required',
8831 style : 'margin-right:5px;'
8835 this.el.addClass(this.invalidClass);
8837 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8839 var feedback = this.el.select('.form-control-feedback', true).first();
8842 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8844 if(this.getValue().length || this.forceFeedback){
8845 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8852 this.fireEvent('invalid', this, msg);
8860 * trigger field - base class for combo..
8865 * @class Roo.bootstrap.TriggerField
8866 * @extends Roo.bootstrap.Input
8867 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8868 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8869 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8870 * for which you can provide a custom implementation. For example:
8872 var trigger = new Roo.bootstrap.TriggerField();
8873 trigger.onTriggerClick = myTriggerFn;
8874 trigger.applyTo('my-field');
8877 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8878 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8879 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
8880 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8881 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8884 * Create a new TriggerField.
8885 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8886 * to the base TextField)
8888 Roo.bootstrap.TriggerField = function(config){
8889 this.mimicing = false;
8890 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8893 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
8895 * @cfg {String} triggerClass A CSS class to apply to the trigger
8898 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8903 * @cfg {Boolean} removable (true|false) special filter default false
8907 /** @cfg {Boolean} grow @hide */
8908 /** @cfg {Number} growMin @hide */
8909 /** @cfg {Number} growMax @hide */
8915 autoSize: Roo.emptyFn,
8922 actionMode : 'wrap',
8927 getAutoCreate : function(){
8929 var align = this.labelAlign || this.parentLabelAlign();
8934 cls: 'form-group' //input-group
8941 type : this.inputType,
8942 cls : 'form-control',
8943 autocomplete: 'new-password',
8944 placeholder : this.placeholder || ''
8948 input.name = this.name;
8951 input.cls += ' input-' + this.size;
8954 if (this.disabled) {
8955 input.disabled=true;
8958 var inputblock = input;
8960 if(this.hasFeedback && !this.allowBlank){
8964 cls: 'glyphicon form-control-feedback'
8967 if(this.removable && !this.editable && !this.tickable){
8969 cls : 'has-feedback',
8975 cls : 'roo-combo-removable-btn close'
8982 cls : 'has-feedback',
8991 if(this.removable && !this.editable && !this.tickable){
8993 cls : 'roo-removable',
8999 cls : 'roo-combo-removable-btn close'
9006 if (this.before || this.after) {
9009 cls : 'input-group',
9013 inputblock.cn.push({
9015 cls : 'input-group-addon',
9020 inputblock.cn.push(input);
9022 if(this.hasFeedback && !this.allowBlank){
9023 inputblock.cls += ' has-feedback';
9024 inputblock.cn.push(feedback);
9028 inputblock.cn.push({
9030 cls : 'input-group-addon',
9043 cls: 'form-hidden-field'
9051 // Roo.log('multiple');
9059 cls: 'form-hidden-field'
9063 cls: 'select2-choices',
9067 cls: 'select2-search-field',
9080 cls: 'select2-container input-group',
9085 // cls: 'typeahead typeahead-long dropdown-menu',
9086 // style: 'display:none'
9091 if(!this.multiple && this.showToggleBtn){
9097 if (this.caret != false) {
9100 cls: 'fa fa-' + this.caret
9107 cls : 'input-group-addon btn dropdown-toggle',
9112 cls: 'combobox-clear',
9126 combobox.cls += ' select2-container-multi';
9129 if (align ==='left' && this.fieldLabel.length) {
9131 // Roo.log("left and has label");
9137 cls : 'control-label col-sm-' + this.labelWidth,
9138 html : this.fieldLabel
9142 cls : "col-sm-" + (12 - this.labelWidth),
9149 } else if ( this.fieldLabel.length) {
9150 // Roo.log(" label");
9155 //cls : 'input-group-addon',
9156 html : this.fieldLabel
9166 // Roo.log(" no label && no align");
9173 ['xs','sm','md','lg'].map(function(size){
9174 if (settings[size]) {
9175 cfg.cls += ' col-' + size + '-' + settings[size];
9186 onResize : function(w, h){
9187 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9188 // if(typeof w == 'number'){
9189 // var x = w - this.trigger.getWidth();
9190 // this.inputEl().setWidth(this.adjustWidth('input', x));
9191 // this.trigger.setStyle('left', x+'px');
9196 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9199 getResizeEl : function(){
9200 return this.inputEl();
9204 getPositionEl : function(){
9205 return this.inputEl();
9209 alignErrorIcon : function(){
9210 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9214 initEvents : function(){
9218 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9219 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9220 if(!this.multiple && this.showToggleBtn){
9221 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9222 if(this.hideTrigger){
9223 this.trigger.setDisplayed(false);
9225 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9229 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9232 if(this.removable && !this.editable && !this.tickable){
9233 var close = this.closeTriggerEl();
9236 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9237 close.on('click', this.removeBtnClick, this, close);
9241 //this.trigger.addClassOnOver('x-form-trigger-over');
9242 //this.trigger.addClassOnClick('x-form-trigger-click');
9245 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9249 closeTriggerEl : function()
9251 var close = this.el.select('.roo-combo-removable-btn', true).first();
9252 return close ? close : false;
9255 removeBtnClick : function(e, h, el)
9259 if(this.fireEvent("remove", this) !== false){
9264 createList : function()
9266 this.list = Roo.get(document.body).createChild({
9268 cls: 'typeahead typeahead-long dropdown-menu',
9269 style: 'display:none'
9272 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9277 initTrigger : function(){
9282 onDestroy : function(){
9284 this.trigger.removeAllListeners();
9285 // this.trigger.remove();
9288 // this.wrap.remove();
9290 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9294 onFocus : function(){
9295 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9298 this.wrap.addClass('x-trigger-wrap-focus');
9299 this.mimicing = true;
9300 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9301 if(this.monitorTab){
9302 this.el.on("keydown", this.checkTab, this);
9309 checkTab : function(e){
9310 if(e.getKey() == e.TAB){
9316 onBlur : function(){
9321 mimicBlur : function(e, t){
9323 if(!this.wrap.contains(t) && this.validateBlur()){
9330 triggerBlur : function(){
9331 this.mimicing = false;
9332 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9333 if(this.monitorTab){
9334 this.el.un("keydown", this.checkTab, this);
9336 //this.wrap.removeClass('x-trigger-wrap-focus');
9337 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9341 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9342 validateBlur : function(e, t){
9347 onDisable : function(){
9348 this.inputEl().dom.disabled = true;
9349 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9351 // this.wrap.addClass('x-item-disabled');
9356 onEnable : function(){
9357 this.inputEl().dom.disabled = false;
9358 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9360 // this.el.removeClass('x-item-disabled');
9365 onShow : function(){
9366 var ae = this.getActionEl();
9369 ae.dom.style.display = '';
9370 ae.dom.style.visibility = 'visible';
9376 onHide : function(){
9377 var ae = this.getActionEl();
9378 ae.dom.style.display = 'none';
9382 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9383 * by an implementing function.
9385 * @param {EventObject} e
9387 onTriggerClick : Roo.emptyFn
9391 * Ext JS Library 1.1.1
9392 * Copyright(c) 2006-2007, Ext JS, LLC.
9394 * Originally Released Under LGPL - original licence link has changed is not relivant.
9397 * <script type="text/javascript">
9402 * @class Roo.data.SortTypes
9404 * Defines the default sorting (casting?) comparison functions used when sorting data.
9406 Roo.data.SortTypes = {
9408 * Default sort that does nothing
9409 * @param {Mixed} s The value being converted
9410 * @return {Mixed} The comparison value
9417 * The regular expression used to strip tags
9421 stripTagsRE : /<\/?[^>]+>/gi,
9424 * Strips all HTML tags to sort on text only
9425 * @param {Mixed} s The value being converted
9426 * @return {String} The comparison value
9428 asText : function(s){
9429 return String(s).replace(this.stripTagsRE, "");
9433 * Strips all HTML tags to sort on text only - Case insensitive
9434 * @param {Mixed} s The value being converted
9435 * @return {String} The comparison value
9437 asUCText : function(s){
9438 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9442 * Case insensitive string
9443 * @param {Mixed} s The value being converted
9444 * @return {String} The comparison value
9446 asUCString : function(s) {
9447 return String(s).toUpperCase();
9452 * @param {Mixed} s The value being converted
9453 * @return {Number} The comparison value
9455 asDate : function(s) {
9459 if(s instanceof Date){
9462 return Date.parse(String(s));
9467 * @param {Mixed} s The value being converted
9468 * @return {Float} The comparison value
9470 asFloat : function(s) {
9471 var val = parseFloat(String(s).replace(/,/g, ""));
9480 * @param {Mixed} s The value being converted
9481 * @return {Number} The comparison value
9483 asInt : function(s) {
9484 var val = parseInt(String(s).replace(/,/g, ""));
9492 * Ext JS Library 1.1.1
9493 * Copyright(c) 2006-2007, Ext JS, LLC.
9495 * Originally Released Under LGPL - original licence link has changed is not relivant.
9498 * <script type="text/javascript">
9502 * @class Roo.data.Record
9503 * Instances of this class encapsulate both record <em>definition</em> information, and record
9504 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9505 * to access Records cached in an {@link Roo.data.Store} object.<br>
9507 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9508 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9511 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9513 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9514 * {@link #create}. The parameters are the same.
9515 * @param {Array} data An associative Array of data values keyed by the field name.
9516 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9517 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9518 * not specified an integer id is generated.
9520 Roo.data.Record = function(data, id){
9521 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9526 * Generate a constructor for a specific record layout.
9527 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9528 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9529 * Each field definition object may contain the following properties: <ul>
9530 * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
9531 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9532 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9533 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9534 * is being used, then this is a string containing the javascript expression to reference the data relative to
9535 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9536 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9537 * this may be omitted.</p></li>
9538 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9539 * <ul><li>auto (Default, implies no conversion)</li>
9544 * <li>date</li></ul></p></li>
9545 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9546 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9547 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9548 * by the Reader into an object that will be stored in the Record. It is passed the
9549 * following parameters:<ul>
9550 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9552 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9554 * <br>usage:<br><pre><code>
9555 var TopicRecord = Roo.data.Record.create(
9556 {name: 'title', mapping: 'topic_title'},
9557 {name: 'author', mapping: 'username'},
9558 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9559 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9560 {name: 'lastPoster', mapping: 'user2'},
9561 {name: 'excerpt', mapping: 'post_text'}
9564 var myNewRecord = new TopicRecord({
9565 title: 'Do my job please',
9568 lastPost: new Date(),
9569 lastPoster: 'Animal',
9570 excerpt: 'No way dude!'
9572 myStore.add(myNewRecord);
9577 Roo.data.Record.create = function(o){
9579 f.superclass.constructor.apply(this, arguments);
9581 Roo.extend(f, Roo.data.Record);
9582 var p = f.prototype;
9583 p.fields = new Roo.util.MixedCollection(false, function(field){
9586 for(var i = 0, len = o.length; i < len; i++){
9587 p.fields.add(new Roo.data.Field(o[i]));
9589 f.getField = function(name){
9590 return p.fields.get(name);
9595 Roo.data.Record.AUTO_ID = 1000;
9596 Roo.data.Record.EDIT = 'edit';
9597 Roo.data.Record.REJECT = 'reject';
9598 Roo.data.Record.COMMIT = 'commit';
9600 Roo.data.Record.prototype = {
9602 * Readonly flag - true if this record has been modified.
9611 join : function(store){
9616 * Set the named field to the specified value.
9617 * @param {String} name The name of the field to set.
9618 * @param {Object} value The value to set the field to.
9620 set : function(name, value){
9621 if(this.data[name] == value){
9628 if(typeof this.modified[name] == 'undefined'){
9629 this.modified[name] = this.data[name];
9631 this.data[name] = value;
9632 if(!this.editing && this.store){
9633 this.store.afterEdit(this);
9638 * Get the value of the named field.
9639 * @param {String} name The name of the field to get the value of.
9640 * @return {Object} The value of the field.
9642 get : function(name){
9643 return this.data[name];
9647 beginEdit : function(){
9648 this.editing = true;
9653 cancelEdit : function(){
9654 this.editing = false;
9655 delete this.modified;
9659 endEdit : function(){
9660 this.editing = false;
9661 if(this.dirty && this.store){
9662 this.store.afterEdit(this);
9667 * Usually called by the {@link Roo.data.Store} which owns the Record.
9668 * Rejects all changes made to the Record since either creation, or the last commit operation.
9669 * Modified fields are reverted to their original values.
9671 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9672 * of reject operations.
9674 reject : function(){
9675 var m = this.modified;
9677 if(typeof m[n] != "function"){
9678 this.data[n] = m[n];
9682 delete this.modified;
9683 this.editing = false;
9685 this.store.afterReject(this);
9690 * Usually called by the {@link Roo.data.Store} which owns the Record.
9691 * Commits all changes made to the Record since either creation, or the last commit operation.
9693 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9694 * of commit operations.
9696 commit : function(){
9698 delete this.modified;
9699 this.editing = false;
9701 this.store.afterCommit(this);
9706 hasError : function(){
9707 return this.error != null;
9711 clearError : function(){
9716 * Creates a copy of this record.
9717 * @param {String} id (optional) A new record id if you don't want to use this record's id
9720 copy : function(newId) {
9721 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9725 * Ext JS Library 1.1.1
9726 * Copyright(c) 2006-2007, Ext JS, LLC.
9728 * Originally Released Under LGPL - original licence link has changed is not relivant.
9731 * <script type="text/javascript">
9737 * @class Roo.data.Store
9738 * @extends Roo.util.Observable
9739 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9740 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9742 * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
9743 * has no knowledge of the format of the data returned by the Proxy.<br>
9745 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9746 * instances from the data object. These records are cached and made available through accessor functions.
9748 * Creates a new Store.
9749 * @param {Object} config A config object containing the objects needed for the Store to access data,
9750 * and read the data into Records.
9752 Roo.data.Store = function(config){
9753 this.data = new Roo.util.MixedCollection(false);
9754 this.data.getKey = function(o){
9757 this.baseParams = {};
9764 "multisort" : "_multisort"
9767 if(config && config.data){
9768 this.inlineData = config.data;
9772 Roo.apply(this, config);
9774 if(this.reader){ // reader passed
9775 this.reader = Roo.factory(this.reader, Roo.data);
9776 this.reader.xmodule = this.xmodule || false;
9777 if(!this.recordType){
9778 this.recordType = this.reader.recordType;
9780 if(this.reader.onMetaChange){
9781 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9785 if(this.recordType){
9786 this.fields = this.recordType.prototype.fields;
9792 * @event datachanged
9793 * Fires when the data cache has changed, and a widget which is using this Store
9794 * as a Record cache should refresh its view.
9795 * @param {Store} this
9800 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9801 * @param {Store} this
9802 * @param {Object} meta The JSON metadata
9807 * Fires when Records have been added to the Store
9808 * @param {Store} this
9809 * @param {Roo.data.Record[]} records The array of Records added
9810 * @param {Number} index The index at which the record(s) were added
9815 * Fires when a Record has been removed from the Store
9816 * @param {Store} this
9817 * @param {Roo.data.Record} record The Record that was removed
9818 * @param {Number} index The index at which the record was removed
9823 * Fires when a Record has been updated
9824 * @param {Store} this
9825 * @param {Roo.data.Record} record The Record that was updated
9826 * @param {String} operation The update operation being performed. Value may be one of:
9828 Roo.data.Record.EDIT
9829 Roo.data.Record.REJECT
9830 Roo.data.Record.COMMIT
9836 * Fires when the data cache has been cleared.
9837 * @param {Store} this
9842 * Fires before a request is made for a new data object. If the beforeload handler returns false
9843 * the load action will be canceled.
9844 * @param {Store} this
9845 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9849 * @event beforeloadadd
9850 * Fires after a new set of Records has been loaded.
9851 * @param {Store} this
9852 * @param {Roo.data.Record[]} records The Records that were loaded
9853 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9855 beforeloadadd : true,
9858 * Fires after a new set of Records has been loaded, before they are added to the store.
9859 * @param {Store} this
9860 * @param {Roo.data.Record[]} records The Records that were loaded
9861 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9862 * @params {Object} return from reader
9866 * @event loadexception
9867 * Fires if an exception occurs in the Proxy during loading.
9868 * Called with the signature of the Proxy's "loadexception" event.
9869 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9872 * @param {Object} return from JsonData.reader() - success, totalRecords, records
9873 * @param {Object} load options
9874 * @param {Object} jsonData from your request (normally this contains the Exception)
9876 loadexception : true
9880 this.proxy = Roo.factory(this.proxy, Roo.data);
9881 this.proxy.xmodule = this.xmodule || false;
9882 this.relayEvents(this.proxy, ["loadexception"]);
9884 this.sortToggle = {};
9885 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9887 Roo.data.Store.superclass.constructor.call(this);
9889 if(this.inlineData){
9890 this.loadData(this.inlineData);
9891 delete this.inlineData;
9895 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9897 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
9898 * without a remote query - used by combo/forms at present.
9902 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9905 * @cfg {Array} data Inline data to be loaded when the store is initialized.
9908 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9909 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9912 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9913 * on any HTTP request
9916 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9919 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9923 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9924 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9929 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9930 * loaded or when a record is removed. (defaults to false).
9932 pruneModifiedRecords : false,
9938 * Add Records to the Store and fires the add event.
9939 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9941 add : function(records){
9942 records = [].concat(records);
9943 for(var i = 0, len = records.length; i < len; i++){
9944 records[i].join(this);
9946 var index = this.data.length;
9947 this.data.addAll(records);
9948 this.fireEvent("add", this, records, index);
9952 * Remove a Record from the Store and fires the remove event.
9953 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9955 remove : function(record){
9956 var index = this.data.indexOf(record);
9957 this.data.removeAt(index);
9958 if(this.pruneModifiedRecords){
9959 this.modified.remove(record);
9961 this.fireEvent("remove", this, record, index);
9965 * Remove all Records from the Store and fires the clear event.
9967 removeAll : function(){
9969 if(this.pruneModifiedRecords){
9972 this.fireEvent("clear", this);
9976 * Inserts Records to the Store at the given index and fires the add event.
9977 * @param {Number} index The start index at which to insert the passed Records.
9978 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9980 insert : function(index, records){
9981 records = [].concat(records);
9982 for(var i = 0, len = records.length; i < len; i++){
9983 this.data.insert(index, records[i]);
9984 records[i].join(this);
9986 this.fireEvent("add", this, records, index);
9990 * Get the index within the cache of the passed Record.
9991 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9992 * @return {Number} The index of the passed Record. Returns -1 if not found.
9994 indexOf : function(record){
9995 return this.data.indexOf(record);
9999 * Get the index within the cache of the Record with the passed id.
10000 * @param {String} id The id of the Record to find.
10001 * @return {Number} The index of the Record. Returns -1 if not found.
10003 indexOfId : function(id){
10004 return this.data.indexOfKey(id);
10008 * Get the Record with the specified id.
10009 * @param {String} id The id of the Record to find.
10010 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10012 getById : function(id){
10013 return this.data.key(id);
10017 * Get the Record at the specified index.
10018 * @param {Number} index The index of the Record to find.
10019 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10021 getAt : function(index){
10022 return this.data.itemAt(index);
10026 * Returns a range of Records between specified indices.
10027 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10028 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10029 * @return {Roo.data.Record[]} An array of Records
10031 getRange : function(start, end){
10032 return this.data.getRange(start, end);
10036 storeOptions : function(o){
10037 o = Roo.apply({}, o);
10040 this.lastOptions = o;
10044 * Loads the Record cache from the configured Proxy using the configured Reader.
10046 * If using remote paging, then the first load call must specify the <em>start</em>
10047 * and <em>limit</em> properties in the options.params property to establish the initial
10048 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10050 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10051 * and this call will return before the new data has been loaded. Perform any post-processing
10052 * in a callback function, or in a "load" event handler.</strong>
10054 * @param {Object} options An object containing properties which control loading options:<ul>
10055 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10056 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10057 * passed the following arguments:<ul>
10058 * <li>r : Roo.data.Record[]</li>
10059 * <li>options: Options object from the load call</li>
10060 * <li>success: Boolean success indicator</li></ul></li>
10061 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10062 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10065 load : function(options){
10066 options = options || {};
10067 if(this.fireEvent("beforeload", this, options) !== false){
10068 this.storeOptions(options);
10069 var p = Roo.apply(options.params || {}, this.baseParams);
10070 // if meta was not loaded from remote source.. try requesting it.
10071 if (!this.reader.metaFromRemote) {
10072 p._requestMeta = 1;
10074 if(this.sortInfo && this.remoteSort){
10075 var pn = this.paramNames;
10076 p[pn["sort"]] = this.sortInfo.field;
10077 p[pn["dir"]] = this.sortInfo.direction;
10079 if (this.multiSort) {
10080 var pn = this.paramNames;
10081 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10084 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10089 * Reloads the Record cache from the configured Proxy using the configured Reader and
10090 * the options from the last load operation performed.
10091 * @param {Object} options (optional) An object containing properties which may override the options
10092 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10093 * the most recently used options are reused).
10095 reload : function(options){
10096 this.load(Roo.applyIf(options||{}, this.lastOptions));
10100 // Called as a callback by the Reader during a load operation.
10101 loadRecords : function(o, options, success){
10102 if(!o || success === false){
10103 if(success !== false){
10104 this.fireEvent("load", this, [], options, o);
10106 if(options.callback){
10107 options.callback.call(options.scope || this, [], options, false);
10111 // if data returned failure - throw an exception.
10112 if (o.success === false) {
10113 // show a message if no listener is registered.
10114 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10115 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10117 // loadmask wil be hooked into this..
10118 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10121 var r = o.records, t = o.totalRecords || r.length;
10123 this.fireEvent("beforeloadadd", this, r, options, o);
10125 if(!options || options.add !== true){
10126 if(this.pruneModifiedRecords){
10127 this.modified = [];
10129 for(var i = 0, len = r.length; i < len; i++){
10133 this.data = this.snapshot;
10134 delete this.snapshot;
10137 this.data.addAll(r);
10138 this.totalLength = t;
10140 this.fireEvent("datachanged", this);
10142 this.totalLength = Math.max(t, this.data.length+r.length);
10145 this.fireEvent("load", this, r, options, o);
10146 if(options.callback){
10147 options.callback.call(options.scope || this, r, options, true);
10153 * Loads data from a passed data block. A Reader which understands the format of the data
10154 * must have been configured in the constructor.
10155 * @param {Object} data The data block from which to read the Records. The format of the data expected
10156 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10157 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10159 loadData : function(o, append){
10160 var r = this.reader.readRecords(o);
10161 this.loadRecords(r, {add: append}, true);
10165 * Gets the number of cached records.
10167 * <em>If using paging, this may not be the total size of the dataset. If the data object
10168 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10169 * the data set size</em>
10171 getCount : function(){
10172 return this.data.length || 0;
10176 * Gets the total number of records in the dataset as returned by the server.
10178 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10179 * the dataset size</em>
10181 getTotalCount : function(){
10182 return this.totalLength || 0;
10186 * Returns the sort state of the Store as an object with two properties:
10188 field {String} The name of the field by which the Records are sorted
10189 direction {String} The sort order, "ASC" or "DESC"
10192 getSortState : function(){
10193 return this.sortInfo;
10197 applySort : function(){
10198 if(this.sortInfo && !this.remoteSort){
10199 var s = this.sortInfo, f = s.field;
10200 var st = this.fields.get(f).sortType;
10201 var fn = function(r1, r2){
10202 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10203 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10205 this.data.sort(s.direction, fn);
10206 if(this.snapshot && this.snapshot != this.data){
10207 this.snapshot.sort(s.direction, fn);
10213 * Sets the default sort column and order to be used by the next load operation.
10214 * @param {String} fieldName The name of the field to sort by.
10215 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10217 setDefaultSort : function(field, dir){
10218 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10222 * Sort the Records.
10223 * If remote sorting is used, the sort is performed on the server, and the cache is
10224 * reloaded. If local sorting is used, the cache is sorted internally.
10225 * @param {String} fieldName The name of the field to sort by.
10226 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10228 sort : function(fieldName, dir){
10229 var f = this.fields.get(fieldName);
10231 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10233 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10234 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10239 this.sortToggle[f.name] = dir;
10240 this.sortInfo = {field: f.name, direction: dir};
10241 if(!this.remoteSort){
10243 this.fireEvent("datachanged", this);
10245 this.load(this.lastOptions);
10250 * Calls the specified function for each of the Records in the cache.
10251 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10252 * Returning <em>false</em> aborts and exits the iteration.
10253 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10255 each : function(fn, scope){
10256 this.data.each(fn, scope);
10260 * Gets all records modified since the last commit. Modified records are persisted across load operations
10261 * (e.g., during paging).
10262 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10264 getModifiedRecords : function(){
10265 return this.modified;
10269 createFilterFn : function(property, value, anyMatch){
10270 if(!value.exec){ // not a regex
10271 value = String(value);
10272 if(value.length == 0){
10275 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10277 return function(r){
10278 return value.test(r.data[property]);
10283 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10284 * @param {String} property A field on your records
10285 * @param {Number} start The record index to start at (defaults to 0)
10286 * @param {Number} end The last record index to include (defaults to length - 1)
10287 * @return {Number} The sum
10289 sum : function(property, start, end){
10290 var rs = this.data.items, v = 0;
10291 start = start || 0;
10292 end = (end || end === 0) ? end : rs.length-1;
10294 for(var i = start; i <= end; i++){
10295 v += (rs[i].data[property] || 0);
10301 * Filter the records by a specified property.
10302 * @param {String} field A field on your records
10303 * @param {String/RegExp} value Either a string that the field
10304 * should start with or a RegExp to test against the field
10305 * @param {Boolean} anyMatch True to match any part not just the beginning
10307 filter : function(property, value, anyMatch){
10308 var fn = this.createFilterFn(property, value, anyMatch);
10309 return fn ? this.filterBy(fn) : this.clearFilter();
10313 * Filter by a function. The specified function will be called with each
10314 * record in this data source. If the function returns true the record is included,
10315 * otherwise it is filtered.
10316 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10317 * @param {Object} scope (optional) The scope of the function (defaults to this)
10319 filterBy : function(fn, scope){
10320 this.snapshot = this.snapshot || this.data;
10321 this.data = this.queryBy(fn, scope||this);
10322 this.fireEvent("datachanged", this);
10326 * Query the records by a specified property.
10327 * @param {String} field A field on your records
10328 * @param {String/RegExp} value Either a string that the field
10329 * should start with or a RegExp to test against the field
10330 * @param {Boolean} anyMatch True to match any part not just the beginning
10331 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10333 query : function(property, value, anyMatch){
10334 var fn = this.createFilterFn(property, value, anyMatch);
10335 return fn ? this.queryBy(fn) : this.data.clone();
10339 * Query by a function. The specified function will be called with each
10340 * record in this data source. If the function returns true the record is included
10342 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10343 * @param {Object} scope (optional) The scope of the function (defaults to this)
10344 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10346 queryBy : function(fn, scope){
10347 var data = this.snapshot || this.data;
10348 return data.filterBy(fn, scope||this);
10352 * Collects unique values for a particular dataIndex from this store.
10353 * @param {String} dataIndex The property to collect
10354 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10355 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10356 * @return {Array} An array of the unique values
10358 collect : function(dataIndex, allowNull, bypassFilter){
10359 var d = (bypassFilter === true && this.snapshot) ?
10360 this.snapshot.items : this.data.items;
10361 var v, sv, r = [], l = {};
10362 for(var i = 0, len = d.length; i < len; i++){
10363 v = d[i].data[dataIndex];
10365 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10374 * Revert to a view of the Record cache with no filtering applied.
10375 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10377 clearFilter : function(suppressEvent){
10378 if(this.snapshot && this.snapshot != this.data){
10379 this.data = this.snapshot;
10380 delete this.snapshot;
10381 if(suppressEvent !== true){
10382 this.fireEvent("datachanged", this);
10388 afterEdit : function(record){
10389 if(this.modified.indexOf(record) == -1){
10390 this.modified.push(record);
10392 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10396 afterReject : function(record){
10397 this.modified.remove(record);
10398 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10402 afterCommit : function(record){
10403 this.modified.remove(record);
10404 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10408 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10409 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10411 commitChanges : function(){
10412 var m = this.modified.slice(0);
10413 this.modified = [];
10414 for(var i = 0, len = m.length; i < len; i++){
10420 * Cancel outstanding changes on all changed records.
10422 rejectChanges : function(){
10423 var m = this.modified.slice(0);
10424 this.modified = [];
10425 for(var i = 0, len = m.length; i < len; i++){
10430 onMetaChange : function(meta, rtype, o){
10431 this.recordType = rtype;
10432 this.fields = rtype.prototype.fields;
10433 delete this.snapshot;
10434 this.sortInfo = meta.sortInfo || this.sortInfo;
10435 this.modified = [];
10436 this.fireEvent('metachange', this, this.reader.meta);
10439 moveIndex : function(data, type)
10441 var index = this.indexOf(data);
10443 var newIndex = index + type;
10447 this.insert(newIndex, data);
10452 * Ext JS Library 1.1.1
10453 * Copyright(c) 2006-2007, Ext JS, LLC.
10455 * Originally Released Under LGPL - original licence link has changed is not relivant.
10458 * <script type="text/javascript">
10462 * @class Roo.data.SimpleStore
10463 * @extends Roo.data.Store
10464 * Small helper class to make creating Stores from Array data easier.
10465 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10466 * @cfg {Array} fields An array of field definition objects, or field name strings.
10467 * @cfg {Array} data The multi-dimensional array of data
10469 * @param {Object} config
10471 Roo.data.SimpleStore = function(config){
10472 Roo.data.SimpleStore.superclass.constructor.call(this, {
10474 reader: new Roo.data.ArrayReader({
10477 Roo.data.Record.create(config.fields)
10479 proxy : new Roo.data.MemoryProxy(config.data)
10483 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10485 * Ext JS Library 1.1.1
10486 * Copyright(c) 2006-2007, Ext JS, LLC.
10488 * Originally Released Under LGPL - original licence link has changed is not relivant.
10491 * <script type="text/javascript">
10496 * @extends Roo.data.Store
10497 * @class Roo.data.JsonStore
10498 * Small helper class to make creating Stores for JSON data easier. <br/>
10500 var store = new Roo.data.JsonStore({
10501 url: 'get-images.php',
10503 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10506 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10507 * JsonReader and HttpProxy (unless inline data is provided).</b>
10508 * @cfg {Array} fields An array of field definition objects, or field name strings.
10510 * @param {Object} config
10512 Roo.data.JsonStore = function(c){
10513 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10514 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10515 reader: new Roo.data.JsonReader(c, c.fields)
10518 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10520 * Ext JS Library 1.1.1
10521 * Copyright(c) 2006-2007, Ext JS, LLC.
10523 * Originally Released Under LGPL - original licence link has changed is not relivant.
10526 * <script type="text/javascript">
10530 Roo.data.Field = function(config){
10531 if(typeof config == "string"){
10532 config = {name: config};
10534 Roo.apply(this, config);
10537 this.type = "auto";
10540 var st = Roo.data.SortTypes;
10541 // named sortTypes are supported, here we look them up
10542 if(typeof this.sortType == "string"){
10543 this.sortType = st[this.sortType];
10546 // set default sortType for strings and dates
10547 if(!this.sortType){
10550 this.sortType = st.asUCString;
10553 this.sortType = st.asDate;
10556 this.sortType = st.none;
10561 var stripRe = /[\$,%]/g;
10563 // prebuilt conversion function for this field, instead of
10564 // switching every time we're reading a value
10566 var cv, dateFormat = this.dateFormat;
10571 cv = function(v){ return v; };
10574 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10578 return v !== undefined && v !== null && v !== '' ?
10579 parseInt(String(v).replace(stripRe, ""), 10) : '';
10584 return v !== undefined && v !== null && v !== '' ?
10585 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10590 cv = function(v){ return v === true || v === "true" || v == 1; };
10597 if(v instanceof Date){
10601 if(dateFormat == "timestamp"){
10602 return new Date(v*1000);
10604 return Date.parseDate(v, dateFormat);
10606 var parsed = Date.parse(v);
10607 return parsed ? new Date(parsed) : null;
10616 Roo.data.Field.prototype = {
10624 * Ext JS Library 1.1.1
10625 * Copyright(c) 2006-2007, Ext JS, LLC.
10627 * Originally Released Under LGPL - original licence link has changed is not relivant.
10630 * <script type="text/javascript">
10633 // Base class for reading structured data from a data source. This class is intended to be
10634 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10637 * @class Roo.data.DataReader
10638 * Base class for reading structured data from a data source. This class is intended to be
10639 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10642 Roo.data.DataReader = function(meta, recordType){
10646 this.recordType = recordType instanceof Array ?
10647 Roo.data.Record.create(recordType) : recordType;
10650 Roo.data.DataReader.prototype = {
10652 * Create an empty record
10653 * @param {Object} data (optional) - overlay some values
10654 * @return {Roo.data.Record} record created.
10656 newRow : function(d) {
10658 this.recordType.prototype.fields.each(function(c) {
10660 case 'int' : da[c.name] = 0; break;
10661 case 'date' : da[c.name] = new Date(); break;
10662 case 'float' : da[c.name] = 0.0; break;
10663 case 'boolean' : da[c.name] = false; break;
10664 default : da[c.name] = ""; break;
10668 return new this.recordType(Roo.apply(da, d));
10673 * Ext JS Library 1.1.1
10674 * Copyright(c) 2006-2007, Ext JS, LLC.
10676 * Originally Released Under LGPL - original licence link has changed is not relivant.
10679 * <script type="text/javascript">
10683 * @class Roo.data.DataProxy
10684 * @extends Roo.data.Observable
10685 * This class is an abstract base class for implementations which provide retrieval of
10686 * unformatted data objects.<br>
10688 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10689 * (of the appropriate type which knows how to parse the data object) to provide a block of
10690 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10692 * Custom implementations must implement the load method as described in
10693 * {@link Roo.data.HttpProxy#load}.
10695 Roo.data.DataProxy = function(){
10698 * @event beforeload
10699 * Fires before a network request is made to retrieve a data object.
10700 * @param {Object} This DataProxy object.
10701 * @param {Object} params The params parameter to the load function.
10706 * Fires before the load method's callback is called.
10707 * @param {Object} This DataProxy object.
10708 * @param {Object} o The data object.
10709 * @param {Object} arg The callback argument object passed to the load function.
10713 * @event loadexception
10714 * Fires if an Exception occurs during data retrieval.
10715 * @param {Object} This DataProxy object.
10716 * @param {Object} o The data object.
10717 * @param {Object} arg The callback argument object passed to the load function.
10718 * @param {Object} e The Exception.
10720 loadexception : true
10722 Roo.data.DataProxy.superclass.constructor.call(this);
10725 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10728 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10732 * Ext JS Library 1.1.1
10733 * Copyright(c) 2006-2007, Ext JS, LLC.
10735 * Originally Released Under LGPL - original licence link has changed is not relivant.
10738 * <script type="text/javascript">
10741 * @class Roo.data.MemoryProxy
10742 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10743 * to the Reader when its load method is called.
10745 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10747 Roo.data.MemoryProxy = function(data){
10751 Roo.data.MemoryProxy.superclass.constructor.call(this);
10755 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10757 * Load data from the requested source (in this case an in-memory
10758 * data object passed to the constructor), read the data object into
10759 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10760 * process that block using the passed callback.
10761 * @param {Object} params This parameter is not used by the MemoryProxy class.
10762 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10763 * object into a block of Roo.data.Records.
10764 * @param {Function} callback The function into which to pass the block of Roo.data.records.
10765 * The function must be passed <ul>
10766 * <li>The Record block object</li>
10767 * <li>The "arg" argument from the load function</li>
10768 * <li>A boolean success indicator</li>
10770 * @param {Object} scope The scope in which to call the callback
10771 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10773 load : function(params, reader, callback, scope, arg){
10774 params = params || {};
10777 result = reader.readRecords(this.data);
10779 this.fireEvent("loadexception", this, arg, null, e);
10780 callback.call(scope, null, arg, false);
10783 callback.call(scope, result, arg, true);
10787 update : function(params, records){
10792 * Ext JS Library 1.1.1
10793 * Copyright(c) 2006-2007, Ext JS, LLC.
10795 * Originally Released Under LGPL - original licence link has changed is not relivant.
10798 * <script type="text/javascript">
10801 * @class Roo.data.HttpProxy
10802 * @extends Roo.data.DataProxy
10803 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10804 * configured to reference a certain URL.<br><br>
10806 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10807 * from which the running page was served.<br><br>
10809 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10811 * Be aware that to enable the browser to parse an XML document, the server must set
10812 * the Content-Type header in the HTTP response to "text/xml".
10814 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10815 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
10816 * will be used to make the request.
10818 Roo.data.HttpProxy = function(conn){
10819 Roo.data.HttpProxy.superclass.constructor.call(this);
10820 // is conn a conn config or a real conn?
10822 this.useAjax = !conn || !conn.events;
10826 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10827 // thse are take from connection...
10830 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10833 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10834 * extra parameters to each request made by this object. (defaults to undefined)
10837 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10838 * to each request made by this object. (defaults to undefined)
10841 * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
10844 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10847 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10853 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10857 * Return the {@link Roo.data.Connection} object being used by this Proxy.
10858 * @return {Connection} The Connection object. This object may be used to subscribe to events on
10859 * a finer-grained basis than the DataProxy events.
10861 getConnection : function(){
10862 return this.useAjax ? Roo.Ajax : this.conn;
10866 * Load data from the configured {@link Roo.data.Connection}, read the data object into
10867 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10868 * process that block using the passed callback.
10869 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10870 * for the request to the remote server.
10871 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10872 * object into a block of Roo.data.Records.
10873 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10874 * The function must be passed <ul>
10875 * <li>The Record block object</li>
10876 * <li>The "arg" argument from the load function</li>
10877 * <li>A boolean success indicator</li>
10879 * @param {Object} scope The scope in which to call the callback
10880 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10882 load : function(params, reader, callback, scope, arg){
10883 if(this.fireEvent("beforeload", this, params) !== false){
10885 params : params || {},
10887 callback : callback,
10892 callback : this.loadResponse,
10896 Roo.applyIf(o, this.conn);
10897 if(this.activeRequest){
10898 Roo.Ajax.abort(this.activeRequest);
10900 this.activeRequest = Roo.Ajax.request(o);
10902 this.conn.request(o);
10905 callback.call(scope||this, null, arg, false);
10910 loadResponse : function(o, success, response){
10911 delete this.activeRequest;
10913 this.fireEvent("loadexception", this, o, response);
10914 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10919 result = o.reader.read(response);
10921 this.fireEvent("loadexception", this, o, response, e);
10922 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10926 this.fireEvent("load", this, o, o.request.arg);
10927 o.request.callback.call(o.request.scope, result, o.request.arg, true);
10931 update : function(dataSet){
10936 updateResponse : function(dataSet){
10941 * Ext JS Library 1.1.1
10942 * Copyright(c) 2006-2007, Ext JS, LLC.
10944 * Originally Released Under LGPL - original licence link has changed is not relivant.
10947 * <script type="text/javascript">
10951 * @class Roo.data.ScriptTagProxy
10952 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10953 * other than the originating domain of the running page.<br><br>
10955 * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
10956 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10958 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10959 * source code that is used as the source inside a <script> tag.<br><br>
10961 * In order for the browser to process the returned data, the server must wrap the data object
10962 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10963 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10964 * depending on whether the callback name was passed:
10967 boolean scriptTag = false;
10968 String cb = request.getParameter("callback");
10971 response.setContentType("text/javascript");
10973 response.setContentType("application/x-json");
10975 Writer out = response.getWriter();
10977 out.write(cb + "(");
10979 out.print(dataBlock.toJsonString());
10986 * @param {Object} config A configuration object.
10988 Roo.data.ScriptTagProxy = function(config){
10989 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10990 Roo.apply(this, config);
10991 this.head = document.getElementsByTagName("head")[0];
10994 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10996 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10998 * @cfg {String} url The URL from which to request the data object.
11001 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11005 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11006 * the server the name of the callback function set up by the load call to process the returned data object.
11007 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11008 * javascript output which calls this named function passing the data object as its only parameter.
11010 callbackParam : "callback",
11012 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11013 * name to the request.
11018 * Load data from the configured URL, read the data object into
11019 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11020 * process that block using the passed callback.
11021 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11022 * for the request to the remote server.
11023 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11024 * object into a block of Roo.data.Records.
11025 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11026 * The function must be passed <ul>
11027 * <li>The Record block object</li>
11028 * <li>The "arg" argument from the load function</li>
11029 * <li>A boolean success indicator</li>
11031 * @param {Object} scope The scope in which to call the callback
11032 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11034 load : function(params, reader, callback, scope, arg){
11035 if(this.fireEvent("beforeload", this, params) !== false){
11037 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11039 var url = this.url;
11040 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11042 url += "&_dc=" + (new Date().getTime());
11044 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11047 cb : "stcCallback"+transId,
11048 scriptId : "stcScript"+transId,
11052 callback : callback,
11058 window[trans.cb] = function(o){
11059 conn.handleResponse(o, trans);
11062 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11064 if(this.autoAbort !== false){
11068 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11070 var script = document.createElement("script");
11071 script.setAttribute("src", url);
11072 script.setAttribute("type", "text/javascript");
11073 script.setAttribute("id", trans.scriptId);
11074 this.head.appendChild(script);
11076 this.trans = trans;
11078 callback.call(scope||this, null, arg, false);
11083 isLoading : function(){
11084 return this.trans ? true : false;
11088 * Abort the current server request.
11090 abort : function(){
11091 if(this.isLoading()){
11092 this.destroyTrans(this.trans);
11097 destroyTrans : function(trans, isLoaded){
11098 this.head.removeChild(document.getElementById(trans.scriptId));
11099 clearTimeout(trans.timeoutId);
11101 window[trans.cb] = undefined;
11103 delete window[trans.cb];
11106 // if hasn't been loaded, wait for load to remove it to prevent script error
11107 window[trans.cb] = function(){
11108 window[trans.cb] = undefined;
11110 delete window[trans.cb];
11117 handleResponse : function(o, trans){
11118 this.trans = false;
11119 this.destroyTrans(trans, true);
11122 result = trans.reader.readRecords(o);
11124 this.fireEvent("loadexception", this, o, trans.arg, e);
11125 trans.callback.call(trans.scope||window, null, trans.arg, false);
11128 this.fireEvent("load", this, o, trans.arg);
11129 trans.callback.call(trans.scope||window, result, trans.arg, true);
11133 handleFailure : function(trans){
11134 this.trans = false;
11135 this.destroyTrans(trans, false);
11136 this.fireEvent("loadexception", this, null, trans.arg);
11137 trans.callback.call(trans.scope||window, null, trans.arg, false);
11141 * Ext JS Library 1.1.1
11142 * Copyright(c) 2006-2007, Ext JS, LLC.
11144 * Originally Released Under LGPL - original licence link has changed is not relivant.
11147 * <script type="text/javascript">
11151 * @class Roo.data.JsonReader
11152 * @extends Roo.data.DataReader
11153 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11154 * based on mappings in a provided Roo.data.Record constructor.
11156 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11157 * in the reply previously.
11162 var RecordDef = Roo.data.Record.create([
11163 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11164 {name: 'occupation'} // This field will use "occupation" as the mapping.
11166 var myReader = new Roo.data.JsonReader({
11167 totalProperty: "results", // The property which contains the total dataset size (optional)
11168 root: "rows", // The property which contains an Array of row objects
11169 id: "id" // The property within each row object that provides an ID for the record (optional)
11173 * This would consume a JSON file like this:
11175 { 'results': 2, 'rows': [
11176 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11177 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11180 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11181 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11182 * paged from the remote server.
11183 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11184 * @cfg {String} root name of the property which contains the Array of row objects.
11185 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11186 * @cfg {Array} fields Array of field definition objects
11188 * Create a new JsonReader
11189 * @param {Object} meta Metadata configuration options
11190 * @param {Object} recordType Either an Array of field definition objects,
11191 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11193 Roo.data.JsonReader = function(meta, recordType){
11196 // set some defaults:
11197 Roo.applyIf(meta, {
11198 totalProperty: 'total',
11199 successProperty : 'success',
11204 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11206 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11209 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11210 * Used by Store query builder to append _requestMeta to params.
11213 metaFromRemote : false,
11215 * This method is only used by a DataProxy which has retrieved data from a remote server.
11216 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11217 * @return {Object} data A data block which is used by an Roo.data.Store object as
11218 * a cache of Roo.data.Records.
11220 read : function(response){
11221 var json = response.responseText;
11223 var o = /* eval:var:o */ eval("("+json+")");
11225 throw {message: "JsonReader.read: Json object not found"};
11231 this.metaFromRemote = true;
11232 this.meta = o.metaData;
11233 this.recordType = Roo.data.Record.create(o.metaData.fields);
11234 this.onMetaChange(this.meta, this.recordType, o);
11236 return this.readRecords(o);
11239 // private function a store will implement
11240 onMetaChange : function(meta, recordType, o){
11247 simpleAccess: function(obj, subsc) {
11254 getJsonAccessor: function(){
11256 return function(expr) {
11258 return(re.test(expr))
11259 ? new Function("obj", "return obj." + expr)
11264 return Roo.emptyFn;
11269 * Create a data block containing Roo.data.Records from an XML document.
11270 * @param {Object} o An object which contains an Array of row objects in the property specified
11271 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11272 * which contains the total size of the dataset.
11273 * @return {Object} data A data block which is used by an Roo.data.Store object as
11274 * a cache of Roo.data.Records.
11276 readRecords : function(o){
11278 * After any data loads, the raw JSON data is available for further custom processing.
11282 var s = this.meta, Record = this.recordType,
11283 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11285 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11287 if(s.totalProperty) {
11288 this.getTotal = this.getJsonAccessor(s.totalProperty);
11290 if(s.successProperty) {
11291 this.getSuccess = this.getJsonAccessor(s.successProperty);
11293 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11295 var g = this.getJsonAccessor(s.id);
11296 this.getId = function(rec) {
11298 return (r === undefined || r === "") ? null : r;
11301 this.getId = function(){return null;};
11304 for(var jj = 0; jj < fl; jj++){
11306 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11307 this.ef[jj] = this.getJsonAccessor(map);
11311 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11312 if(s.totalProperty){
11313 var vt = parseInt(this.getTotal(o), 10);
11318 if(s.successProperty){
11319 var vs = this.getSuccess(o);
11320 if(vs === false || vs === 'false'){
11325 for(var i = 0; i < c; i++){
11328 var id = this.getId(n);
11329 for(var j = 0; j < fl; j++){
11331 var v = this.ef[j](n);
11333 Roo.log('missing convert for ' + f.name);
11337 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11339 var record = new Record(values, id);
11341 records[i] = record;
11347 totalRecords : totalRecords
11352 * Ext JS Library 1.1.1
11353 * Copyright(c) 2006-2007, Ext JS, LLC.
11355 * Originally Released Under LGPL - original licence link has changed is not relivant.
11358 * <script type="text/javascript">
11362 * @class Roo.data.ArrayReader
11363 * @extends Roo.data.DataReader
11364 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11365 * Each element of that Array represents a row of data fields. The
11366 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11367 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11371 var RecordDef = Roo.data.Record.create([
11372 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11373 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11375 var myReader = new Roo.data.ArrayReader({
11376 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11380 * This would consume an Array like this:
11382 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11384 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11386 * Create a new JsonReader
11387 * @param {Object} meta Metadata configuration options.
11388 * @param {Object} recordType Either an Array of field definition objects
11389 * as specified to {@link Roo.data.Record#create},
11390 * or an {@link Roo.data.Record} object
11391 * created using {@link Roo.data.Record#create}.
11393 Roo.data.ArrayReader = function(meta, recordType){
11394 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11397 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11399 * Create a data block containing Roo.data.Records from an XML document.
11400 * @param {Object} o An Array of row objects which represents the dataset.
11401 * @return {Object} data A data block which is used by an Roo.data.Store object as
11402 * a cache of Roo.data.Records.
11404 readRecords : function(o){
11405 var sid = this.meta ? this.meta.id : null;
11406 var recordType = this.recordType, fields = recordType.prototype.fields;
11409 for(var i = 0; i < root.length; i++){
11412 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11413 for(var j = 0, jlen = fields.length; j < jlen; j++){
11414 var f = fields.items[j];
11415 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11416 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11418 values[f.name] = v;
11420 var record = new recordType(values, id);
11422 records[records.length] = record;
11426 totalRecords : records.length
11435 * @class Roo.bootstrap.ComboBox
11436 * @extends Roo.bootstrap.TriggerField
11437 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11438 * @cfg {Boolean} append (true|false) default false
11439 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11440 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11441 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11442 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11443 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11444 * @cfg {Boolean} animate default true
11445 * @cfg {Boolean} emptyResultText only for touch device
11446 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11448 * Create a new ComboBox.
11449 * @param {Object} config Configuration options
11451 Roo.bootstrap.ComboBox = function(config){
11452 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11456 * Fires when the dropdown list is expanded
11457 * @param {Roo.bootstrap.ComboBox} combo This combo box
11462 * Fires when the dropdown list is collapsed
11463 * @param {Roo.bootstrap.ComboBox} combo This combo box
11467 * @event beforeselect
11468 * Fires before a list item is selected. Return false to cancel the selection.
11469 * @param {Roo.bootstrap.ComboBox} combo This combo box
11470 * @param {Roo.data.Record} record The data record returned from the underlying store
11471 * @param {Number} index The index of the selected item in the dropdown list
11473 'beforeselect' : true,
11476 * Fires when a list item is selected
11477 * @param {Roo.bootstrap.ComboBox} combo This combo box
11478 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11479 * @param {Number} index The index of the selected item in the dropdown list
11483 * @event beforequery
11484 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11485 * The event object passed has these properties:
11486 * @param {Roo.bootstrap.ComboBox} combo This combo box
11487 * @param {String} query The query
11488 * @param {Boolean} forceAll true to force "all" query
11489 * @param {Boolean} cancel true to cancel the query
11490 * @param {Object} e The query event object
11492 'beforequery': true,
11495 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11496 * @param {Roo.bootstrap.ComboBox} combo This combo box
11501 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11502 * @param {Roo.bootstrap.ComboBox} combo This combo box
11503 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11508 * Fires when the remove value from the combobox array
11509 * @param {Roo.bootstrap.ComboBox} combo This combo box
11513 * @event specialfilter
11514 * Fires when specialfilter
11515 * @param {Roo.bootstrap.ComboBox} combo This combo box
11517 'specialfilter' : true,
11520 * Fires when tick the element
11521 * @param {Roo.bootstrap.ComboBox} combo This combo box
11525 * @event touchviewdisplay
11526 * Fires when touch view require special display (default is using displayField)
11527 * @param {Roo.bootstrap.ComboBox} combo This combo box
11528 * @param {Object} cfg set html .
11530 'touchviewdisplay' : true
11535 this.tickItems = [];
11537 this.selectedIndex = -1;
11538 if(this.mode == 'local'){
11539 if(config.queryDelay === undefined){
11540 this.queryDelay = 10;
11542 if(config.minChars === undefined){
11548 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11551 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11552 * rendering into an Roo.Editor, defaults to false)
11555 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11556 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11559 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11562 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11563 * the dropdown list (defaults to undefined, with no header element)
11567 * @cfg {String/Roo.Template} tpl The template to use to render the output
11571 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11573 listWidth: undefined,
11575 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11576 * mode = 'remote' or 'text' if mode = 'local')
11578 displayField: undefined,
11581 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11582 * mode = 'remote' or 'value' if mode = 'local').
11583 * Note: use of a valueField requires the user make a selection
11584 * in order for a value to be mapped.
11586 valueField: undefined,
11590 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11591 * field's data value (defaults to the underlying DOM element's name)
11593 hiddenName: undefined,
11595 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11599 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11601 selectedClass: 'active',
11604 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11608 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11609 * anchor positions (defaults to 'tl-bl')
11611 listAlign: 'tl-bl?',
11613 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11617 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11618 * query specified by the allQuery config option (defaults to 'query')
11620 triggerAction: 'query',
11622 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11623 * (defaults to 4, does not apply if editable = false)
11627 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11628 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11632 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11633 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11637 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11638 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11642 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11643 * when editable = true (defaults to false)
11645 selectOnFocus:false,
11647 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11649 queryParam: 'query',
11651 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
11652 * when mode = 'remote' (defaults to 'Loading...')
11654 loadingText: 'Loading...',
11656 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11660 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11664 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11665 * traditional select (defaults to true)
11669 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11673 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11677 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11678 * listWidth has a higher value)
11682 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11683 * allow the user to set arbitrary text into the field (defaults to false)
11685 forceSelection:false,
11687 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11688 * if typeAhead = true (defaults to 250)
11690 typeAheadDelay : 250,
11692 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11693 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11695 valueNotFoundText : undefined,
11697 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11699 blockFocus : false,
11702 * @cfg {Boolean} disableClear Disable showing of clear button.
11704 disableClear : false,
11706 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
11708 alwaysQuery : false,
11711 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
11716 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11718 invalidClass : "has-warning",
11721 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11723 validClass : "has-success",
11726 * @cfg {Boolean} specialFilter (true|false) special filter default false
11728 specialFilter : false,
11731 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11733 mobileTouchView : true,
11745 btnPosition : 'right',
11746 triggerList : true,
11747 showToggleBtn : true,
11749 emptyResultText: 'Empty',
11750 triggerText : 'Select',
11752 // element that contains real text value.. (when hidden is used..)
11754 getAutoCreate : function()
11762 if(Roo.isTouch && this.mobileTouchView){
11763 cfg = this.getAutoCreateTouchView();
11770 if(!this.tickable){
11771 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11776 * ComboBox with tickable selections
11779 var align = this.labelAlign || this.parentLabelAlign();
11782 cls : 'form-group roo-combobox-tickable' //input-group
11787 cls : 'tickable-buttons',
11792 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11793 html : this.triggerText
11799 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11806 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11813 buttons.cn.unshift({
11815 cls: 'select2-search-field-input'
11821 Roo.each(buttons.cn, function(c){
11823 c.cls += ' btn-' + _this.size;
11826 if (_this.disabled) {
11837 cls: 'form-hidden-field'
11841 cls: 'select2-choices',
11845 cls: 'select2-search-field',
11857 cls: 'select2-container input-group select2-container-multi',
11862 // cls: 'typeahead typeahead-long dropdown-menu',
11863 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
11868 if(this.hasFeedback && !this.allowBlank){
11872 cls: 'glyphicon form-control-feedback'
11875 combobox.cn.push(feedback);
11878 if (align ==='left' && this.fieldLabel.length) {
11880 Roo.log("left and has label");
11886 cls : 'control-label col-sm-' + this.labelWidth,
11887 html : this.fieldLabel
11891 cls : "col-sm-" + (12 - this.labelWidth),
11898 } else if ( this.fieldLabel.length) {
11904 //cls : 'input-group-addon',
11905 html : this.fieldLabel
11915 Roo.log(" no label && no align");
11922 ['xs','sm','md','lg'].map(function(size){
11923 if (settings[size]) {
11924 cfg.cls += ' col-' + size + '-' + settings[size];
11932 _initEventsCalled : false,
11935 initEvents: function()
11938 if (this._initEventsCalled) { // as we call render... prevent looping...
11941 this._initEventsCalled = true;
11944 throw "can not find store for combo";
11947 this.store = Roo.factory(this.store, Roo.data);
11949 // if we are building from html. then this element is so complex, that we can not really
11950 // use the rendered HTML.
11951 // so we have to trash and replace the previous code.
11952 if (Roo.XComponent.build_from_html) {
11954 // remove this element....
11955 var e = this.el.dom, k=0;
11956 while (e ) { e = e.previousSibling; ++k;}
11961 this.rendered = false;
11963 this.render(this.parent().getChildContainer(true), k);
11974 if(Roo.isTouch && this.mobileTouchView){
11975 this.initTouchView();
11980 this.initTickableEvents();
11984 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11986 if(this.hiddenName){
11988 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11990 this.hiddenField.dom.value =
11991 this.hiddenValue !== undefined ? this.hiddenValue :
11992 this.value !== undefined ? this.value : '';
11994 // prevent input submission
11995 this.el.dom.removeAttribute('name');
11996 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12001 // this.el.dom.setAttribute('autocomplete', 'off');
12004 var cls = 'x-combo-list';
12006 //this.list = new Roo.Layer({
12007 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12013 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12014 _this.list.setWidth(lw);
12017 this.list.on('mouseover', this.onViewOver, this);
12018 this.list.on('mousemove', this.onViewMove, this);
12020 this.list.on('scroll', this.onViewScroll, this);
12023 this.list.swallowEvent('mousewheel');
12024 this.assetHeight = 0;
12027 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12028 this.assetHeight += this.header.getHeight();
12031 this.innerList = this.list.createChild({cls:cls+'-inner'});
12032 this.innerList.on('mouseover', this.onViewOver, this);
12033 this.innerList.on('mousemove', this.onViewMove, this);
12034 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12036 if(this.allowBlank && !this.pageSize && !this.disableClear){
12037 this.footer = this.list.createChild({cls:cls+'-ft'});
12038 this.pageTb = new Roo.Toolbar(this.footer);
12042 this.footer = this.list.createChild({cls:cls+'-ft'});
12043 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12044 {pageSize: this.pageSize});
12048 if (this.pageTb && this.allowBlank && !this.disableClear) {
12050 this.pageTb.add(new Roo.Toolbar.Fill(), {
12051 cls: 'x-btn-icon x-btn-clear',
12053 handler: function()
12056 _this.clearValue();
12057 _this.onSelect(false, -1);
12062 this.assetHeight += this.footer.getHeight();
12067 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12070 this.view = new Roo.View(this.list, this.tpl, {
12071 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12073 //this.view.wrapEl.setDisplayed(false);
12074 this.view.on('click', this.onViewClick, this);
12078 this.store.on('beforeload', this.onBeforeLoad, this);
12079 this.store.on('load', this.onLoad, this);
12080 this.store.on('loadexception', this.onLoadException, this);
12082 if(this.resizable){
12083 this.resizer = new Roo.Resizable(this.list, {
12084 pinned:true, handles:'se'
12086 this.resizer.on('resize', function(r, w, h){
12087 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12088 this.listWidth = w;
12089 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12090 this.restrictHeight();
12092 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12095 if(!this.editable){
12096 this.editable = true;
12097 this.setEditable(false);
12102 if (typeof(this.events.add.listeners) != 'undefined') {
12104 this.addicon = this.wrap.createChild(
12105 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12107 this.addicon.on('click', function(e) {
12108 this.fireEvent('add', this);
12111 if (typeof(this.events.edit.listeners) != 'undefined') {
12113 this.editicon = this.wrap.createChild(
12114 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12115 if (this.addicon) {
12116 this.editicon.setStyle('margin-left', '40px');
12118 this.editicon.on('click', function(e) {
12120 // we fire even if inothing is selected..
12121 this.fireEvent('edit', this, this.lastData );
12127 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12128 "up" : function(e){
12129 this.inKeyMode = true;
12133 "down" : function(e){
12134 if(!this.isExpanded()){
12135 this.onTriggerClick();
12137 this.inKeyMode = true;
12142 "enter" : function(e){
12143 // this.onViewClick();
12147 if(this.fireEvent("specialkey", this, e)){
12148 this.onViewClick(false);
12154 "esc" : function(e){
12158 "tab" : function(e){
12161 if(this.fireEvent("specialkey", this, e)){
12162 this.onViewClick(false);
12170 doRelay : function(foo, bar, hname){
12171 if(hname == 'down' || this.scope.isExpanded()){
12172 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12181 this.queryDelay = Math.max(this.queryDelay || 10,
12182 this.mode == 'local' ? 10 : 250);
12185 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12187 if(this.typeAhead){
12188 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12190 if(this.editable !== false){
12191 this.inputEl().on("keyup", this.onKeyUp, this);
12193 if(this.forceSelection){
12194 this.inputEl().on('blur', this.doForce, this);
12198 this.choices = this.el.select('ul.select2-choices', true).first();
12199 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12203 initTickableEvents: function()
12207 if(this.hiddenName){
12209 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12211 this.hiddenField.dom.value =
12212 this.hiddenValue !== undefined ? this.hiddenValue :
12213 this.value !== undefined ? this.value : '';
12215 // prevent input submission
12216 this.el.dom.removeAttribute('name');
12217 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12222 // this.list = this.el.select('ul.dropdown-menu',true).first();
12224 this.choices = this.el.select('ul.select2-choices', true).first();
12225 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12226 if(this.triggerList){
12227 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12230 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12231 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12233 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12234 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12236 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12237 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12239 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12240 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12241 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12244 this.cancelBtn.hide();
12249 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12250 _this.list.setWidth(lw);
12253 this.list.on('mouseover', this.onViewOver, this);
12254 this.list.on('mousemove', this.onViewMove, this);
12256 this.list.on('scroll', this.onViewScroll, this);
12259 this.tpl = '<li class="select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
12262 this.view = new Roo.View(this.list, this.tpl, {
12263 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12266 //this.view.wrapEl.setDisplayed(false);
12267 this.view.on('click', this.onViewClick, this);
12271 this.store.on('beforeload', this.onBeforeLoad, this);
12272 this.store.on('load', this.onLoad, this);
12273 this.store.on('loadexception', this.onLoadException, this);
12276 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12277 "up" : function(e){
12278 this.inKeyMode = true;
12282 "down" : function(e){
12283 this.inKeyMode = true;
12287 "enter" : function(e){
12288 if(this.fireEvent("specialkey", this, e)){
12289 this.onViewClick(false);
12295 "esc" : function(e){
12296 this.onTickableFooterButtonClick(e, false, false);
12299 "tab" : function(e){
12300 this.fireEvent("specialkey", this, e);
12302 this.onTickableFooterButtonClick(e, false, false);
12309 doRelay : function(e, fn, key){
12310 if(this.scope.isExpanded()){
12311 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12320 this.queryDelay = Math.max(this.queryDelay || 10,
12321 this.mode == 'local' ? 10 : 250);
12324 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12326 if(this.typeAhead){
12327 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12330 if(this.editable !== false){
12331 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12336 onDestroy : function(){
12338 this.view.setStore(null);
12339 this.view.el.removeAllListeners();
12340 this.view.el.remove();
12341 this.view.purgeListeners();
12344 this.list.dom.innerHTML = '';
12348 this.store.un('beforeload', this.onBeforeLoad, this);
12349 this.store.un('load', this.onLoad, this);
12350 this.store.un('loadexception', this.onLoadException, this);
12352 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12356 fireKey : function(e){
12357 if(e.isNavKeyPress() && !this.list.isVisible()){
12358 this.fireEvent("specialkey", this, e);
12363 onResize: function(w, h){
12364 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12366 // if(typeof w != 'number'){
12367 // // we do not handle it!?!?
12370 // var tw = this.trigger.getWidth();
12371 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12372 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12374 // this.inputEl().setWidth( this.adjustWidth('input', x));
12376 // //this.trigger.setStyle('left', x+'px');
12378 // if(this.list && this.listWidth === undefined){
12379 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12380 // this.list.setWidth(lw);
12381 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12389 * Allow or prevent the user from directly editing the field text. If false is passed,
12390 * the user will only be able to select from the items defined in the dropdown list. This method
12391 * is the runtime equivalent of setting the 'editable' config option at config time.
12392 * @param {Boolean} value True to allow the user to directly edit the field text
12394 setEditable : function(value){
12395 if(value == this.editable){
12398 this.editable = value;
12400 this.inputEl().dom.setAttribute('readOnly', true);
12401 this.inputEl().on('mousedown', this.onTriggerClick, this);
12402 this.inputEl().addClass('x-combo-noedit');
12404 this.inputEl().dom.setAttribute('readOnly', false);
12405 this.inputEl().un('mousedown', this.onTriggerClick, this);
12406 this.inputEl().removeClass('x-combo-noedit');
12412 onBeforeLoad : function(combo,opts){
12413 if(!this.hasFocus){
12417 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12419 this.restrictHeight();
12420 this.selectedIndex = -1;
12424 onLoad : function(){
12426 this.hasQuery = false;
12428 if(!this.hasFocus){
12432 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12433 this.loading.hide();
12436 if(this.store.getCount() > 0){
12438 this.restrictHeight();
12439 if(this.lastQuery == this.allQuery){
12440 if(this.editable && !this.tickable){
12441 this.inputEl().dom.select();
12445 !this.selectByValue(this.value, true) &&
12448 !this.store.lastOptions ||
12449 typeof(this.store.lastOptions.add) == 'undefined' ||
12450 this.store.lastOptions.add != true
12453 this.select(0, true);
12456 if(this.autoFocus){
12459 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12460 this.taTask.delay(this.typeAheadDelay);
12464 this.onEmptyResults();
12470 onLoadException : function()
12472 this.hasQuery = false;
12474 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12475 this.loading.hide();
12478 if(this.tickable && this.editable){
12483 // only causes errors at present
12484 //Roo.log(this.store.reader.jsonData);
12485 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12487 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12493 onTypeAhead : function(){
12494 if(this.store.getCount() > 0){
12495 var r = this.store.getAt(0);
12496 var newValue = r.data[this.displayField];
12497 var len = newValue.length;
12498 var selStart = this.getRawValue().length;
12500 if(selStart != len){
12501 this.setRawValue(newValue);
12502 this.selectText(selStart, newValue.length);
12508 onSelect : function(record, index){
12510 if(this.fireEvent('beforeselect', this, record, index) !== false){
12512 this.setFromData(index > -1 ? record.data : false);
12515 this.fireEvent('select', this, record, index);
12520 * Returns the currently selected field value or empty string if no value is set.
12521 * @return {String} value The selected value
12523 getValue : function(){
12526 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12529 if(this.valueField){
12530 return typeof this.value != 'undefined' ? this.value : '';
12532 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12537 * Clears any text/value currently set in the field
12539 clearValue : function(){
12540 if(this.hiddenField){
12541 this.hiddenField.dom.value = '';
12544 this.setRawValue('');
12545 this.lastSelectionText = '';
12546 this.lastData = false;
12548 var close = this.closeTriggerEl();
12557 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12558 * will be displayed in the field. If the value does not match the data value of an existing item,
12559 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12560 * Otherwise the field will be blank (although the value will still be set).
12561 * @param {String} value The value to match
12563 setValue : function(v){
12570 if(this.valueField){
12571 var r = this.findRecord(this.valueField, v);
12573 text = r.data[this.displayField];
12574 }else if(this.valueNotFoundText !== undefined){
12575 text = this.valueNotFoundText;
12578 this.lastSelectionText = text;
12579 if(this.hiddenField){
12580 this.hiddenField.dom.value = v;
12582 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12585 var close = this.closeTriggerEl();
12588 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12592 * @property {Object} the last set data for the element
12597 * Sets the value of the field based on a object which is related to the record format for the store.
12598 * @param {Object} value the value to set as. or false on reset?
12600 setFromData : function(o){
12607 var dv = ''; // display value
12608 var vv = ''; // value value..
12610 if (this.displayField) {
12611 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12613 // this is an error condition!!!
12614 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12617 if(this.valueField){
12618 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12621 var close = this.closeTriggerEl();
12624 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12627 if(this.hiddenField){
12628 this.hiddenField.dom.value = vv;
12630 this.lastSelectionText = dv;
12631 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12635 // no hidden field.. - we store the value in 'value', but still display
12636 // display field!!!!
12637 this.lastSelectionText = dv;
12638 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12645 reset : function(){
12646 // overridden so that last data is reset..
12653 this.setValue(this.originalValue);
12654 this.clearInvalid();
12655 this.lastData = false;
12657 this.view.clearSelections();
12661 findRecord : function(prop, value){
12663 if(this.store.getCount() > 0){
12664 this.store.each(function(r){
12665 if(r.data[prop] == value){
12675 getName: function()
12677 // returns hidden if it's set..
12678 if (!this.rendered) {return ''};
12679 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
12683 onViewMove : function(e, t){
12684 this.inKeyMode = false;
12688 onViewOver : function(e, t){
12689 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12692 var item = this.view.findItemFromChild(t);
12695 var index = this.view.indexOf(item);
12696 this.select(index, false);
12701 onViewClick : function(view, doFocus, el, e)
12703 var index = this.view.getSelectedIndexes()[0];
12705 var r = this.store.getAt(index);
12709 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12716 Roo.each(this.tickItems, function(v,k){
12718 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12720 _this.tickItems.splice(k, 1);
12722 if(typeof(e) == 'undefined' && view == false){
12723 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12735 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
12736 this.tickItems.push(r.data);
12739 if(typeof(e) == 'undefined' && view == false){
12740 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12747 this.onSelect(r, index);
12749 if(doFocus !== false && !this.blockFocus){
12750 this.inputEl().focus();
12755 restrictHeight : function(){
12756 //this.innerList.dom.style.height = '';
12757 //var inner = this.innerList.dom;
12758 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12759 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12760 //this.list.beginUpdate();
12761 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12762 this.list.alignTo(this.inputEl(), this.listAlign);
12763 this.list.alignTo(this.inputEl(), this.listAlign);
12764 //this.list.endUpdate();
12768 onEmptyResults : function(){
12770 if(this.tickable && this.editable){
12771 this.restrictHeight();
12779 * Returns true if the dropdown list is expanded, else false.
12781 isExpanded : function(){
12782 return this.list.isVisible();
12786 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12787 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12788 * @param {String} value The data value of the item to select
12789 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12790 * selected item if it is not currently in view (defaults to true)
12791 * @return {Boolean} True if the value matched an item in the list, else false
12793 selectByValue : function(v, scrollIntoView){
12794 if(v !== undefined && v !== null){
12795 var r = this.findRecord(this.valueField || this.displayField, v);
12797 this.select(this.store.indexOf(r), scrollIntoView);
12805 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12806 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12807 * @param {Number} index The zero-based index of the list item to select
12808 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12809 * selected item if it is not currently in view (defaults to true)
12811 select : function(index, scrollIntoView){
12812 this.selectedIndex = index;
12813 this.view.select(index);
12814 if(scrollIntoView !== false){
12815 var el = this.view.getNode(index);
12817 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12820 this.list.scrollChildIntoView(el, false);
12826 selectNext : function(){
12827 var ct = this.store.getCount();
12829 if(this.selectedIndex == -1){
12831 }else if(this.selectedIndex < ct-1){
12832 this.select(this.selectedIndex+1);
12838 selectPrev : function(){
12839 var ct = this.store.getCount();
12841 if(this.selectedIndex == -1){
12843 }else if(this.selectedIndex != 0){
12844 this.select(this.selectedIndex-1);
12850 onKeyUp : function(e){
12851 if(this.editable !== false && !e.isSpecialKey()){
12852 this.lastKey = e.getKey();
12853 this.dqTask.delay(this.queryDelay);
12858 validateBlur : function(){
12859 return !this.list || !this.list.isVisible();
12863 initQuery : function(){
12865 var v = this.getRawValue();
12867 if(this.tickable && this.editable){
12868 v = this.tickableInputEl().getValue();
12875 doForce : function(){
12876 if(this.inputEl().dom.value.length > 0){
12877 this.inputEl().dom.value =
12878 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12884 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
12885 * query allowing the query action to be canceled if needed.
12886 * @param {String} query The SQL query to execute
12887 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12888 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
12889 * saved in the current store (defaults to false)
12891 doQuery : function(q, forceAll){
12893 if(q === undefined || q === null){
12898 forceAll: forceAll,
12902 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12907 forceAll = qe.forceAll;
12908 if(forceAll === true || (q.length >= this.minChars)){
12910 this.hasQuery = true;
12912 if(this.lastQuery != q || this.alwaysQuery){
12913 this.lastQuery = q;
12914 if(this.mode == 'local'){
12915 this.selectedIndex = -1;
12917 this.store.clearFilter();
12920 if(this.specialFilter){
12921 this.fireEvent('specialfilter', this);
12926 this.store.filter(this.displayField, q);
12929 this.store.fireEvent("datachanged", this.store);
12936 this.store.baseParams[this.queryParam] = q;
12938 var options = {params : this.getParams(q)};
12941 options.add = true;
12942 options.params.start = this.page * this.pageSize;
12945 this.store.load(options);
12948 * this code will make the page width larger, at the beginning, the list not align correctly,
12949 * we should expand the list on onLoad
12950 * so command out it
12955 this.selectedIndex = -1;
12960 this.loadNext = false;
12964 getParams : function(q){
12966 //p[this.queryParam] = q;
12970 p.limit = this.pageSize;
12976 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12978 collapse : function(){
12979 if(!this.isExpanded()){
12986 this.hasFocus = false;
12988 this.cancelBtn.hide();
12989 this.trigger.show();
12992 this.tickableInputEl().dom.value = '';
12993 this.tickableInputEl().blur();
12998 Roo.get(document).un('mousedown', this.collapseIf, this);
12999 Roo.get(document).un('mousewheel', this.collapseIf, this);
13000 if (!this.editable) {
13001 Roo.get(document).un('keydown', this.listKeyPress, this);
13003 this.fireEvent('collapse', this);
13007 collapseIf : function(e){
13008 var in_combo = e.within(this.el);
13009 var in_list = e.within(this.list);
13010 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13012 if (in_combo || in_list || is_list) {
13013 //e.stopPropagation();
13018 this.onTickableFooterButtonClick(e, false, false);
13026 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13028 expand : function(){
13030 if(this.isExpanded() || !this.hasFocus){
13034 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13035 this.list.setWidth(lw);
13042 this.restrictHeight();
13046 this.tickItems = Roo.apply([], this.item);
13049 this.cancelBtn.show();
13050 this.trigger.hide();
13053 this.tickableInputEl().focus();
13058 Roo.get(document).on('mousedown', this.collapseIf, this);
13059 Roo.get(document).on('mousewheel', this.collapseIf, this);
13060 if (!this.editable) {
13061 Roo.get(document).on('keydown', this.listKeyPress, this);
13064 this.fireEvent('expand', this);
13068 // Implements the default empty TriggerField.onTriggerClick function
13069 onTriggerClick : function(e)
13071 Roo.log('trigger click');
13073 if(this.disabled || !this.triggerList){
13078 this.loadNext = false;
13080 if(this.isExpanded()){
13082 if (!this.blockFocus) {
13083 this.inputEl().focus();
13087 this.hasFocus = true;
13088 if(this.triggerAction == 'all') {
13089 this.doQuery(this.allQuery, true);
13091 this.doQuery(this.getRawValue());
13093 if (!this.blockFocus) {
13094 this.inputEl().focus();
13099 onTickableTriggerClick : function(e)
13106 this.loadNext = false;
13107 this.hasFocus = true;
13109 if(this.triggerAction == 'all') {
13110 this.doQuery(this.allQuery, true);
13112 this.doQuery(this.getRawValue());
13116 onSearchFieldClick : function(e)
13118 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13119 this.onTickableFooterButtonClick(e, false, false);
13123 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13128 this.loadNext = false;
13129 this.hasFocus = true;
13131 if(this.triggerAction == 'all') {
13132 this.doQuery(this.allQuery, true);
13134 this.doQuery(this.getRawValue());
13138 listKeyPress : function(e)
13140 //Roo.log('listkeypress');
13141 // scroll to first matching element based on key pres..
13142 if (e.isSpecialKey()) {
13145 var k = String.fromCharCode(e.getKey()).toUpperCase();
13148 var csel = this.view.getSelectedNodes();
13149 var cselitem = false;
13151 var ix = this.view.indexOf(csel[0]);
13152 cselitem = this.store.getAt(ix);
13153 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13159 this.store.each(function(v) {
13161 // start at existing selection.
13162 if (cselitem.id == v.id) {
13168 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13169 match = this.store.indexOf(v);
13175 if (match === false) {
13176 return true; // no more action?
13179 this.view.select(match);
13180 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13181 sn.scrollIntoView(sn.dom.parentNode, false);
13184 onViewScroll : function(e, t){
13186 if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
13190 this.hasQuery = true;
13192 this.loading = this.list.select('.loading', true).first();
13194 if(this.loading === null){
13195 this.list.createChild({
13197 cls: 'loading select2-more-results select2-active',
13198 html: 'Loading more results...'
13201 this.loading = this.list.select('.loading', true).first();
13203 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13205 this.loading.hide();
13208 this.loading.show();
13213 this.loadNext = true;
13215 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13220 addItem : function(o)
13222 var dv = ''; // display value
13224 if (this.displayField) {
13225 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13227 // this is an error condition!!!
13228 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13235 var choice = this.choices.createChild({
13237 cls: 'select2-search-choice',
13246 cls: 'select2-search-choice-close',
13251 }, this.searchField);
13253 var close = choice.select('a.select2-search-choice-close', true).first();
13255 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13263 this.inputEl().dom.value = '';
13268 onRemoveItem : function(e, _self, o)
13270 e.preventDefault();
13272 this.lastItem = Roo.apply([], this.item);
13274 var index = this.item.indexOf(o.data) * 1;
13277 Roo.log('not this item?!');
13281 this.item.splice(index, 1);
13286 this.fireEvent('remove', this, e);
13292 syncValue : function()
13294 if(!this.item.length){
13301 Roo.each(this.item, function(i){
13302 if(_this.valueField){
13303 value.push(i[_this.valueField]);
13310 this.value = value.join(',');
13312 if(this.hiddenField){
13313 this.hiddenField.dom.value = this.value;
13316 this.store.fireEvent("datachanged", this.store);
13319 clearItem : function()
13321 if(!this.multiple){
13327 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13335 if(this.tickable && !Roo.isTouch){
13336 this.view.refresh();
13340 inputEl: function ()
13342 if(Roo.isTouch && this.mobileTouchView){
13343 return this.el.select('input.form-control',true).first();
13347 return this.searchField;
13350 return this.el.select('input.form-control',true).first();
13354 onTickableFooterButtonClick : function(e, btn, el)
13356 e.preventDefault();
13358 this.lastItem = Roo.apply([], this.item);
13360 if(btn && btn.name == 'cancel'){
13361 this.tickItems = Roo.apply([], this.item);
13370 Roo.each(this.tickItems, function(o){
13378 validate : function()
13380 var v = this.getRawValue();
13383 v = this.getValue();
13386 if(this.disabled || this.allowBlank || v.length){
13391 this.markInvalid();
13395 tickableInputEl : function()
13397 if(!this.tickable || !this.editable){
13398 return this.inputEl();
13401 return this.inputEl().select('.select2-search-field-input', true).first();
13405 getAutoCreateTouchView : function()
13410 cls: 'form-group' //input-group
13416 type : this.inputType,
13417 cls : 'form-control x-combo-noedit',
13418 autocomplete: 'new-password',
13419 placeholder : this.placeholder || '',
13424 input.name = this.name;
13428 input.cls += ' input-' + this.size;
13431 if (this.disabled) {
13432 input.disabled = true;
13443 inputblock.cls += ' input-group';
13445 inputblock.cn.unshift({
13447 cls : 'input-group-addon',
13452 if(this.removable && !this.multiple){
13453 inputblock.cls += ' roo-removable';
13455 inputblock.cn.push({
13458 cls : 'roo-combo-removable-btn close'
13462 if(this.hasFeedback && !this.allowBlank){
13464 inputblock.cls += ' has-feedback';
13466 inputblock.cn.push({
13468 cls: 'glyphicon form-control-feedback'
13475 inputblock.cls += (this.before) ? '' : ' input-group';
13477 inputblock.cn.push({
13479 cls : 'input-group-addon',
13490 cls: 'form-hidden-field'
13504 cls: 'form-hidden-field'
13508 cls: 'select2-choices',
13512 cls: 'select2-search-field',
13525 cls: 'select2-container input-group',
13532 combobox.cls += ' select2-container-multi';
13535 var align = this.labelAlign || this.parentLabelAlign();
13539 if(this.fieldLabel.length){
13541 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13542 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13547 cls : 'control-label ' + lw,
13548 html : this.fieldLabel
13560 var settings = this;
13562 ['xs','sm','md','lg'].map(function(size){
13563 if (settings[size]) {
13564 cfg.cls += ' col-' + size + '-' + settings[size];
13571 initTouchView : function()
13573 this.renderTouchView();
13575 this.touchViewEl.on('scroll', function(){
13576 this.el.dom.scrollTop = 0;
13579 this.originalValue = this.getValue();
13581 this.inputEl().on("click", this.showTouchView, this);
13583 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13584 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13586 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13588 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13589 this.store.on('load', this.onTouchViewLoad, this);
13590 this.store.on('loadexception', this.onTouchViewLoadException, this);
13592 if(this.hiddenName){
13594 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13596 this.hiddenField.dom.value =
13597 this.hiddenValue !== undefined ? this.hiddenValue :
13598 this.value !== undefined ? this.value : '';
13600 this.el.dom.removeAttribute('name');
13601 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13605 this.choices = this.el.select('ul.select2-choices', true).first();
13606 this.searchField = this.el.select('ul li.select2-search-field', true).first();
13609 if(this.removable && !this.multiple){
13610 var close = this.closeTriggerEl();
13612 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13613 close.on('click', this.removeBtnClick, this, close);
13617 * fix the bug in Safari iOS8
13619 this.inputEl().on("focus", function(e){
13620 document.activeElement.blur();
13628 renderTouchView : function()
13630 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13631 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13633 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13634 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13636 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13637 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13638 this.touchViewBodyEl.setStyle('overflow', 'auto');
13640 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13641 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13643 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13644 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13648 showTouchView : function()
13654 this.touchViewHeaderEl.hide();
13656 if(this.fieldLabel.length){
13657 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13658 this.touchViewHeaderEl.show();
13661 this.touchViewEl.show();
13663 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13664 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13666 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13668 if(this.fieldLabel.length){
13669 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13672 this.touchViewBodyEl.setHeight(bodyHeight);
13676 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13678 this.touchViewEl.addClass('in');
13681 this.doTouchViewQuery();
13685 hideTouchView : function()
13687 this.touchViewEl.removeClass('in');
13691 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13693 this.touchViewEl.setStyle('display', 'none');
13698 setTouchViewValue : function()
13705 Roo.each(this.tickItems, function(o){
13710 this.hideTouchView();
13713 doTouchViewQuery : function()
13722 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13726 if(!this.alwaysQuery || this.mode == 'local'){
13727 this.onTouchViewLoad();
13734 onTouchViewBeforeLoad : function(combo,opts)
13740 onTouchViewLoad : function()
13742 if(this.store.getCount() < 1){
13743 this.onTouchViewEmptyResults();
13747 this.clearTouchView();
13749 var rawValue = this.getRawValue();
13751 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13753 this.tickItems = [];
13755 this.store.data.each(function(d, rowIndex){
13756 var row = this.touchViewListGroup.createChild(template);
13758 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13761 html : d.data[this.displayField]
13764 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
13765 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
13769 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13770 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13773 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13774 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13775 this.tickItems.push(d.data);
13778 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13782 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13784 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13786 if(this.fieldLabel.length){
13787 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13790 var listHeight = this.touchViewListGroup.getHeight();
13794 if(firstChecked && listHeight > bodyHeight){
13795 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13800 onTouchViewLoadException : function()
13802 this.hideTouchView();
13805 onTouchViewEmptyResults : function()
13807 this.clearTouchView();
13809 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13811 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13815 clearTouchView : function()
13817 this.touchViewListGroup.dom.innerHTML = '';
13820 onTouchViewClick : function(e, el, o)
13822 e.preventDefault();
13825 var rowIndex = o.rowIndex;
13827 var r = this.store.getAt(rowIndex);
13829 if(!this.multiple){
13830 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13831 c.dom.removeAttribute('checked');
13834 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13836 this.setFromData(r.data);
13838 var close = this.closeTriggerEl();
13844 this.hideTouchView();
13846 this.fireEvent('select', this, r, rowIndex);
13851 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13852 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13853 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13857 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13858 this.addItem(r.data);
13859 this.tickItems.push(r.data);
13865 * @cfg {Boolean} grow
13869 * @cfg {Number} growMin
13873 * @cfg {Number} growMax
13882 Roo.apply(Roo.bootstrap.ComboBox, {
13886 cls: 'modal-header',
13908 cls: 'list-group-item',
13912 cls: 'roo-combobox-list-group-item-value'
13916 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13930 listItemCheckbox : {
13932 cls: 'list-group-item',
13936 cls: 'roo-combobox-list-group-item-value'
13940 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13956 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13961 cls: 'modal-footer',
13969 cls: 'col-xs-6 text-left',
13972 cls: 'btn btn-danger roo-touch-view-cancel',
13978 cls: 'col-xs-6 text-right',
13981 cls: 'btn btn-success roo-touch-view-ok',
13992 Roo.apply(Roo.bootstrap.ComboBox, {
13994 touchViewTemplate : {
13996 cls: 'modal fade roo-combobox-touch-view',
14000 cls: 'modal-dialog',
14001 style : 'position:fixed', // we have to fix position....
14005 cls: 'modal-content',
14007 Roo.bootstrap.ComboBox.header,
14008 Roo.bootstrap.ComboBox.body,
14009 Roo.bootstrap.ComboBox.footer
14018 * Ext JS Library 1.1.1
14019 * Copyright(c) 2006-2007, Ext JS, LLC.
14021 * Originally Released Under LGPL - original licence link has changed is not relivant.
14024 * <script type="text/javascript">
14029 * @extends Roo.util.Observable
14030 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14031 * This class also supports single and multi selection modes. <br>
14032 * Create a data model bound view:
14034 var store = new Roo.data.Store(...);
14036 var view = new Roo.View({
14038 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14040 singleSelect: true,
14041 selectedClass: "ydataview-selected",
14045 // listen for node click?
14046 view.on("click", function(vw, index, node, e){
14047 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14051 dataModel.load("foobar.xml");
14053 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14055 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14056 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14058 * Note: old style constructor is still suported (container, template, config)
14061 * Create a new View
14062 * @param {Object} config The config object
14065 Roo.View = function(config, depreciated_tpl, depreciated_config){
14067 this.parent = false;
14069 if (typeof(depreciated_tpl) == 'undefined') {
14070 // new way.. - universal constructor.
14071 Roo.apply(this, config);
14072 this.el = Roo.get(this.el);
14075 this.el = Roo.get(config);
14076 this.tpl = depreciated_tpl;
14077 Roo.apply(this, depreciated_config);
14079 this.wrapEl = this.el.wrap().wrap();
14080 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14083 if(typeof(this.tpl) == "string"){
14084 this.tpl = new Roo.Template(this.tpl);
14086 // support xtype ctors..
14087 this.tpl = new Roo.factory(this.tpl, Roo);
14091 this.tpl.compile();
14096 * @event beforeclick
14097 * Fires before a click is processed. Returns false to cancel the default action.
14098 * @param {Roo.View} this
14099 * @param {Number} index The index of the target node
14100 * @param {HTMLElement} node The target node
14101 * @param {Roo.EventObject} e The raw event object
14103 "beforeclick" : true,
14106 * Fires when a template node is clicked.
14107 * @param {Roo.View} this
14108 * @param {Number} index The index of the target node
14109 * @param {HTMLElement} node The target node
14110 * @param {Roo.EventObject} e The raw event object
14115 * Fires when a template node is double clicked.
14116 * @param {Roo.View} this
14117 * @param {Number} index The index of the target node
14118 * @param {HTMLElement} node The target node
14119 * @param {Roo.EventObject} e The raw event object
14123 * @event contextmenu
14124 * Fires when a template node is right clicked.
14125 * @param {Roo.View} this
14126 * @param {Number} index The index of the target node
14127 * @param {HTMLElement} node The target node
14128 * @param {Roo.EventObject} e The raw event object
14130 "contextmenu" : true,
14132 * @event selectionchange
14133 * Fires when the selected nodes change.
14134 * @param {Roo.View} this
14135 * @param {Array} selections Array of the selected nodes
14137 "selectionchange" : true,
14140 * @event beforeselect
14141 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14142 * @param {Roo.View} this
14143 * @param {HTMLElement} node The node to be selected
14144 * @param {Array} selections Array of currently selected nodes
14146 "beforeselect" : true,
14148 * @event preparedata
14149 * Fires on every row to render, to allow you to change the data.
14150 * @param {Roo.View} this
14151 * @param {Object} data to be rendered (change this)
14153 "preparedata" : true
14161 "click": this.onClick,
14162 "dblclick": this.onDblClick,
14163 "contextmenu": this.onContextMenu,
14167 this.selections = [];
14169 this.cmp = new Roo.CompositeElementLite([]);
14171 this.store = Roo.factory(this.store, Roo.data);
14172 this.setStore(this.store, true);
14175 if ( this.footer && this.footer.xtype) {
14177 var fctr = this.wrapEl.appendChild(document.createElement("div"));
14179 this.footer.dataSource = this.store;
14180 this.footer.container = fctr;
14181 this.footer = Roo.factory(this.footer, Roo);
14182 fctr.insertFirst(this.el);
14184 // this is a bit insane - as the paging toolbar seems to detach the el..
14185 // dom.parentNode.parentNode.parentNode
14186 // they get detached?
14190 Roo.View.superclass.constructor.call(this);
14195 Roo.extend(Roo.View, Roo.util.Observable, {
14198 * @cfg {Roo.data.Store} store Data store to load data from.
14203 * @cfg {String|Roo.Element} el The container element.
14208 * @cfg {String|Roo.Template} tpl The template used by this View
14212 * @cfg {String} dataName the named area of the template to use as the data area
14213 * Works with domtemplates roo-name="name"
14217 * @cfg {String} selectedClass The css class to add to selected nodes
14219 selectedClass : "x-view-selected",
14221 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14226 * @cfg {String} text to display on mask (default Loading)
14230 * @cfg {Boolean} multiSelect Allow multiple selection
14232 multiSelect : false,
14234 * @cfg {Boolean} singleSelect Allow single selection
14236 singleSelect: false,
14239 * @cfg {Boolean} toggleSelect - selecting
14241 toggleSelect : false,
14244 * @cfg {Boolean} tickable - selecting
14249 * Returns the element this view is bound to.
14250 * @return {Roo.Element}
14252 getEl : function(){
14253 return this.wrapEl;
14259 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14261 refresh : function(){
14262 //Roo.log('refresh');
14265 // if we are using something like 'domtemplate', then
14266 // the what gets used is:
14267 // t.applySubtemplate(NAME, data, wrapping data..)
14268 // the outer template then get' applied with
14269 // the store 'extra data'
14270 // and the body get's added to the
14271 // roo-name="data" node?
14272 // <span class='roo-tpl-{name}'></span> ?????
14276 this.clearSelections();
14277 this.el.update("");
14279 var records = this.store.getRange();
14280 if(records.length < 1) {
14282 // is this valid?? = should it render a template??
14284 this.el.update(this.emptyText);
14288 if (this.dataName) {
14289 this.el.update(t.apply(this.store.meta)); //????
14290 el = this.el.child('.roo-tpl-' + this.dataName);
14293 for(var i = 0, len = records.length; i < len; i++){
14294 var data = this.prepareData(records[i].data, i, records[i]);
14295 this.fireEvent("preparedata", this, data, i, records[i]);
14297 var d = Roo.apply({}, data);
14300 Roo.apply(d, {'roo-id' : Roo.id()});
14304 Roo.each(this.parent.item, function(item){
14305 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14308 Roo.apply(d, {'roo-data-checked' : 'checked'});
14312 html[html.length] = Roo.util.Format.trim(
14314 t.applySubtemplate(this.dataName, d, this.store.meta) :
14321 el.update(html.join(""));
14322 this.nodes = el.dom.childNodes;
14323 this.updateIndexes(0);
14328 * Function to override to reformat the data that is sent to
14329 * the template for each node.
14330 * DEPRICATED - use the preparedata event handler.
14331 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14332 * a JSON object for an UpdateManager bound view).
14334 prepareData : function(data, index, record)
14336 this.fireEvent("preparedata", this, data, index, record);
14340 onUpdate : function(ds, record){
14341 // Roo.log('on update');
14342 this.clearSelections();
14343 var index = this.store.indexOf(record);
14344 var n = this.nodes[index];
14345 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14346 n.parentNode.removeChild(n);
14347 this.updateIndexes(index, index);
14353 onAdd : function(ds, records, index)
14355 //Roo.log(['on Add', ds, records, index] );
14356 this.clearSelections();
14357 if(this.nodes.length == 0){
14361 var n = this.nodes[index];
14362 for(var i = 0, len = records.length; i < len; i++){
14363 var d = this.prepareData(records[i].data, i, records[i]);
14365 this.tpl.insertBefore(n, d);
14368 this.tpl.append(this.el, d);
14371 this.updateIndexes(index);
14374 onRemove : function(ds, record, index){
14375 // Roo.log('onRemove');
14376 this.clearSelections();
14377 var el = this.dataName ?
14378 this.el.child('.roo-tpl-' + this.dataName) :
14381 el.dom.removeChild(this.nodes[index]);
14382 this.updateIndexes(index);
14386 * Refresh an individual node.
14387 * @param {Number} index
14389 refreshNode : function(index){
14390 this.onUpdate(this.store, this.store.getAt(index));
14393 updateIndexes : function(startIndex, endIndex){
14394 var ns = this.nodes;
14395 startIndex = startIndex || 0;
14396 endIndex = endIndex || ns.length - 1;
14397 for(var i = startIndex; i <= endIndex; i++){
14398 ns[i].nodeIndex = i;
14403 * Changes the data store this view uses and refresh the view.
14404 * @param {Store} store
14406 setStore : function(store, initial){
14407 if(!initial && this.store){
14408 this.store.un("datachanged", this.refresh);
14409 this.store.un("add", this.onAdd);
14410 this.store.un("remove", this.onRemove);
14411 this.store.un("update", this.onUpdate);
14412 this.store.un("clear", this.refresh);
14413 this.store.un("beforeload", this.onBeforeLoad);
14414 this.store.un("load", this.onLoad);
14415 this.store.un("loadexception", this.onLoad);
14419 store.on("datachanged", this.refresh, this);
14420 store.on("add", this.onAdd, this);
14421 store.on("remove", this.onRemove, this);
14422 store.on("update", this.onUpdate, this);
14423 store.on("clear", this.refresh, this);
14424 store.on("beforeload", this.onBeforeLoad, this);
14425 store.on("load", this.onLoad, this);
14426 store.on("loadexception", this.onLoad, this);
14434 * onbeforeLoad - masks the loading area.
14437 onBeforeLoad : function(store,opts)
14439 //Roo.log('onBeforeLoad');
14441 this.el.update("");
14443 this.el.mask(this.mask ? this.mask : "Loading" );
14445 onLoad : function ()
14452 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14453 * @param {HTMLElement} node
14454 * @return {HTMLElement} The template node
14456 findItemFromChild : function(node){
14457 var el = this.dataName ?
14458 this.el.child('.roo-tpl-' + this.dataName,true) :
14461 if(!node || node.parentNode == el){
14464 var p = node.parentNode;
14465 while(p && p != el){
14466 if(p.parentNode == el){
14475 onClick : function(e){
14476 var item = this.findItemFromChild(e.getTarget());
14478 var index = this.indexOf(item);
14479 if(this.onItemClick(item, index, e) !== false){
14480 this.fireEvent("click", this, index, item, e);
14483 this.clearSelections();
14488 onContextMenu : function(e){
14489 var item = this.findItemFromChild(e.getTarget());
14491 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14496 onDblClick : function(e){
14497 var item = this.findItemFromChild(e.getTarget());
14499 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14503 onItemClick : function(item, index, e)
14505 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14508 if (this.toggleSelect) {
14509 var m = this.isSelected(item) ? 'unselect' : 'select';
14512 _t[m](item, true, false);
14515 if(this.multiSelect || this.singleSelect){
14516 if(this.multiSelect && e.shiftKey && this.lastSelection){
14517 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14519 this.select(item, this.multiSelect && e.ctrlKey);
14520 this.lastSelection = item;
14523 if(!this.tickable){
14524 e.preventDefault();
14532 * Get the number of selected nodes.
14535 getSelectionCount : function(){
14536 return this.selections.length;
14540 * Get the currently selected nodes.
14541 * @return {Array} An array of HTMLElements
14543 getSelectedNodes : function(){
14544 return this.selections;
14548 * Get the indexes of the selected nodes.
14551 getSelectedIndexes : function(){
14552 var indexes = [], s = this.selections;
14553 for(var i = 0, len = s.length; i < len; i++){
14554 indexes.push(s[i].nodeIndex);
14560 * Clear all selections
14561 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14563 clearSelections : function(suppressEvent){
14564 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14565 this.cmp.elements = this.selections;
14566 this.cmp.removeClass(this.selectedClass);
14567 this.selections = [];
14568 if(!suppressEvent){
14569 this.fireEvent("selectionchange", this, this.selections);
14575 * Returns true if the passed node is selected
14576 * @param {HTMLElement/Number} node The node or node index
14577 * @return {Boolean}
14579 isSelected : function(node){
14580 var s = this.selections;
14584 node = this.getNode(node);
14585 return s.indexOf(node) !== -1;
14590 * @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
14591 * @param {Boolean} keepExisting (optional) true to keep existing selections
14592 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14594 select : function(nodeInfo, keepExisting, suppressEvent){
14595 if(nodeInfo instanceof Array){
14597 this.clearSelections(true);
14599 for(var i = 0, len = nodeInfo.length; i < len; i++){
14600 this.select(nodeInfo[i], true, true);
14604 var node = this.getNode(nodeInfo);
14605 if(!node || this.isSelected(node)){
14606 return; // already selected.
14609 this.clearSelections(true);
14612 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14613 Roo.fly(node).addClass(this.selectedClass);
14614 this.selections.push(node);
14615 if(!suppressEvent){
14616 this.fireEvent("selectionchange", this, this.selections);
14624 * @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
14625 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14626 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14628 unselect : function(nodeInfo, keepExisting, suppressEvent)
14630 if(nodeInfo instanceof Array){
14631 Roo.each(this.selections, function(s) {
14632 this.unselect(s, nodeInfo);
14636 var node = this.getNode(nodeInfo);
14637 if(!node || !this.isSelected(node)){
14638 //Roo.log("not selected");
14639 return; // not selected.
14643 Roo.each(this.selections, function(s) {
14645 Roo.fly(node).removeClass(this.selectedClass);
14652 this.selections= ns;
14653 this.fireEvent("selectionchange", this, this.selections);
14657 * Gets a template node.
14658 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14659 * @return {HTMLElement} The node or null if it wasn't found
14661 getNode : function(nodeInfo){
14662 if(typeof nodeInfo == "string"){
14663 return document.getElementById(nodeInfo);
14664 }else if(typeof nodeInfo == "number"){
14665 return this.nodes[nodeInfo];
14671 * Gets a range template nodes.
14672 * @param {Number} startIndex
14673 * @param {Number} endIndex
14674 * @return {Array} An array of nodes
14676 getNodes : function(start, end){
14677 var ns = this.nodes;
14678 start = start || 0;
14679 end = typeof end == "undefined" ? ns.length - 1 : end;
14682 for(var i = start; i <= end; i++){
14686 for(var i = start; i >= end; i--){
14694 * Finds the index of the passed node
14695 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14696 * @return {Number} The index of the node or -1
14698 indexOf : function(node){
14699 node = this.getNode(node);
14700 if(typeof node.nodeIndex == "number"){
14701 return node.nodeIndex;
14703 var ns = this.nodes;
14704 for(var i = 0, len = ns.length; i < len; i++){
14715 * based on jquery fullcalendar
14719 Roo.bootstrap = Roo.bootstrap || {};
14721 * @class Roo.bootstrap.Calendar
14722 * @extends Roo.bootstrap.Component
14723 * Bootstrap Calendar class
14724 * @cfg {Boolean} loadMask (true|false) default false
14725 * @cfg {Object} header generate the user specific header of the calendar, default false
14728 * Create a new Container
14729 * @param {Object} config The config object
14734 Roo.bootstrap.Calendar = function(config){
14735 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14739 * Fires when a date is selected
14740 * @param {DatePicker} this
14741 * @param {Date} date The selected date
14745 * @event monthchange
14746 * Fires when the displayed month changes
14747 * @param {DatePicker} this
14748 * @param {Date} date The selected month
14750 'monthchange': true,
14752 * @event evententer
14753 * Fires when mouse over an event
14754 * @param {Calendar} this
14755 * @param {event} Event
14757 'evententer': true,
14759 * @event eventleave
14760 * Fires when the mouse leaves an
14761 * @param {Calendar} this
14764 'eventleave': true,
14766 * @event eventclick
14767 * Fires when the mouse click an
14768 * @param {Calendar} this
14777 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
14780 * @cfg {Number} startDay
14781 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14789 getAutoCreate : function(){
14792 var fc_button = function(name, corner, style, content ) {
14793 return Roo.apply({},{
14795 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
14797 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14800 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14811 style : 'width:100%',
14818 cls : 'fc-header-left',
14820 fc_button('prev', 'left', 'arrow', '‹' ),
14821 fc_button('next', 'right', 'arrow', '›' ),
14822 { tag: 'span', cls: 'fc-header-space' },
14823 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
14831 cls : 'fc-header-center',
14835 cls: 'fc-header-title',
14838 html : 'month / year'
14846 cls : 'fc-header-right',
14848 /* fc_button('month', 'left', '', 'month' ),
14849 fc_button('week', '', '', 'week' ),
14850 fc_button('day', 'right', '', 'day' )
14862 header = this.header;
14865 var cal_heads = function() {
14867 // fixme - handle this.
14869 for (var i =0; i < Date.dayNames.length; i++) {
14870 var d = Date.dayNames[i];
14873 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14874 html : d.substring(0,3)
14878 ret[0].cls += ' fc-first';
14879 ret[6].cls += ' fc-last';
14882 var cal_cell = function(n) {
14885 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14890 cls: 'fc-day-number',
14894 cls: 'fc-day-content',
14898 style: 'position: relative;' // height: 17px;
14910 var cal_rows = function() {
14913 for (var r = 0; r < 6; r++) {
14920 for (var i =0; i < Date.dayNames.length; i++) {
14921 var d = Date.dayNames[i];
14922 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14925 row.cn[0].cls+=' fc-first';
14926 row.cn[0].cn[0].style = 'min-height:90px';
14927 row.cn[6].cls+=' fc-last';
14931 ret[0].cls += ' fc-first';
14932 ret[4].cls += ' fc-prev-last';
14933 ret[5].cls += ' fc-last';
14940 cls: 'fc-border-separate',
14941 style : 'width:100%',
14949 cls : 'fc-first fc-last',
14967 cls : 'fc-content',
14968 style : "position: relative;",
14971 cls : 'fc-view fc-view-month fc-grid',
14972 style : 'position: relative',
14973 unselectable : 'on',
14976 cls : 'fc-event-container',
14977 style : 'position:absolute;z-index:8;top:0;left:0;'
14995 initEvents : function()
14998 throw "can not find store for calendar";
15004 style: "text-align:center",
15008 style: "background-color:white;width:50%;margin:250 auto",
15012 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15023 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15025 var size = this.el.select('.fc-content', true).first().getSize();
15026 this.maskEl.setSize(size.width, size.height);
15027 this.maskEl.enableDisplayMode("block");
15028 if(!this.loadMask){
15029 this.maskEl.hide();
15032 this.store = Roo.factory(this.store, Roo.data);
15033 this.store.on('load', this.onLoad, this);
15034 this.store.on('beforeload', this.onBeforeLoad, this);
15038 this.cells = this.el.select('.fc-day',true);
15039 //Roo.log(this.cells);
15040 this.textNodes = this.el.query('.fc-day-number');
15041 this.cells.addClassOnOver('fc-state-hover');
15043 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15044 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15045 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15046 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15048 this.on('monthchange', this.onMonthChange, this);
15050 this.update(new Date().clearTime());
15053 resize : function() {
15054 var sz = this.el.getSize();
15056 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15057 this.el.select('.fc-day-content div',true).setHeight(34);
15062 showPrevMonth : function(e){
15063 this.update(this.activeDate.add("mo", -1));
15065 showToday : function(e){
15066 this.update(new Date().clearTime());
15069 showNextMonth : function(e){
15070 this.update(this.activeDate.add("mo", 1));
15074 showPrevYear : function(){
15075 this.update(this.activeDate.add("y", -1));
15079 showNextYear : function(){
15080 this.update(this.activeDate.add("y", 1));
15085 update : function(date)
15087 var vd = this.activeDate;
15088 this.activeDate = date;
15089 // if(vd && this.el){
15090 // var t = date.getTime();
15091 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15092 // Roo.log('using add remove');
15094 // this.fireEvent('monthchange', this, date);
15096 // this.cells.removeClass("fc-state-highlight");
15097 // this.cells.each(function(c){
15098 // if(c.dateValue == t){
15099 // c.addClass("fc-state-highlight");
15100 // setTimeout(function(){
15101 // try{c.dom.firstChild.focus();}catch(e){}
15111 var days = date.getDaysInMonth();
15113 var firstOfMonth = date.getFirstDateOfMonth();
15114 var startingPos = firstOfMonth.getDay()-this.startDay;
15116 if(startingPos < this.startDay){
15120 var pm = date.add(Date.MONTH, -1);
15121 var prevStart = pm.getDaysInMonth()-startingPos;
15123 this.cells = this.el.select('.fc-day',true);
15124 this.textNodes = this.el.query('.fc-day-number');
15125 this.cells.addClassOnOver('fc-state-hover');
15127 var cells = this.cells.elements;
15128 var textEls = this.textNodes;
15130 Roo.each(cells, function(cell){
15131 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15134 days += startingPos;
15136 // convert everything to numbers so it's fast
15137 var day = 86400000;
15138 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15141 //Roo.log(prevStart);
15143 var today = new Date().clearTime().getTime();
15144 var sel = date.clearTime().getTime();
15145 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15146 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15147 var ddMatch = this.disabledDatesRE;
15148 var ddText = this.disabledDatesText;
15149 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15150 var ddaysText = this.disabledDaysText;
15151 var format = this.format;
15153 var setCellClass = function(cal, cell){
15157 //Roo.log('set Cell Class');
15159 var t = d.getTime();
15163 cell.dateValue = t;
15165 cell.className += " fc-today";
15166 cell.className += " fc-state-highlight";
15167 cell.title = cal.todayText;
15170 // disable highlight in other month..
15171 //cell.className += " fc-state-highlight";
15176 cell.className = " fc-state-disabled";
15177 cell.title = cal.minText;
15181 cell.className = " fc-state-disabled";
15182 cell.title = cal.maxText;
15186 if(ddays.indexOf(d.getDay()) != -1){
15187 cell.title = ddaysText;
15188 cell.className = " fc-state-disabled";
15191 if(ddMatch && format){
15192 var fvalue = d.dateFormat(format);
15193 if(ddMatch.test(fvalue)){
15194 cell.title = ddText.replace("%0", fvalue);
15195 cell.className = " fc-state-disabled";
15199 if (!cell.initialClassName) {
15200 cell.initialClassName = cell.dom.className;
15203 cell.dom.className = cell.initialClassName + ' ' + cell.className;
15208 for(; i < startingPos; i++) {
15209 textEls[i].innerHTML = (++prevStart);
15210 d.setDate(d.getDate()+1);
15212 cells[i].className = "fc-past fc-other-month";
15213 setCellClass(this, cells[i]);
15218 for(; i < days; i++){
15219 intDay = i - startingPos + 1;
15220 textEls[i].innerHTML = (intDay);
15221 d.setDate(d.getDate()+1);
15223 cells[i].className = ''; // "x-date-active";
15224 setCellClass(this, cells[i]);
15228 for(; i < 42; i++) {
15229 textEls[i].innerHTML = (++extraDays);
15230 d.setDate(d.getDate()+1);
15232 cells[i].className = "fc-future fc-other-month";
15233 setCellClass(this, cells[i]);
15236 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15238 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15240 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15241 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15243 if(totalRows != 6){
15244 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15245 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15248 this.fireEvent('monthchange', this, date);
15252 if(!this.internalRender){
15253 var main = this.el.dom.firstChild;
15254 var w = main.offsetWidth;
15255 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15256 Roo.fly(main).setWidth(w);
15257 this.internalRender = true;
15258 // opera does not respect the auto grow header center column
15259 // then, after it gets a width opera refuses to recalculate
15260 // without a second pass
15261 if(Roo.isOpera && !this.secondPass){
15262 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15263 this.secondPass = true;
15264 this.update.defer(10, this, [date]);
15271 findCell : function(dt) {
15272 dt = dt.clearTime().getTime();
15274 this.cells.each(function(c){
15275 //Roo.log("check " +c.dateValue + '?=' + dt);
15276 if(c.dateValue == dt){
15286 findCells : function(ev) {
15287 var s = ev.start.clone().clearTime().getTime();
15289 var e= ev.end.clone().clearTime().getTime();
15292 this.cells.each(function(c){
15293 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15295 if(c.dateValue > e){
15298 if(c.dateValue < s){
15307 // findBestRow: function(cells)
15311 // for (var i =0 ; i < cells.length;i++) {
15312 // ret = Math.max(cells[i].rows || 0,ret);
15319 addItem : function(ev)
15321 // look for vertical location slot in
15322 var cells = this.findCells(ev);
15324 // ev.row = this.findBestRow(cells);
15326 // work out the location.
15330 for(var i =0; i < cells.length; i++) {
15332 cells[i].row = cells[0].row;
15335 cells[i].row = cells[i].row + 1;
15345 if (crow.start.getY() == cells[i].getY()) {
15347 crow.end = cells[i];
15364 cells[0].events.push(ev);
15366 this.calevents.push(ev);
15369 clearEvents: function() {
15371 if(!this.calevents){
15375 Roo.each(this.cells.elements, function(c){
15381 Roo.each(this.calevents, function(e) {
15382 Roo.each(e.els, function(el) {
15383 el.un('mouseenter' ,this.onEventEnter, this);
15384 el.un('mouseleave' ,this.onEventLeave, this);
15389 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15395 renderEvents: function()
15399 this.cells.each(function(c) {
15408 if(c.row != c.events.length){
15409 r = 4 - (4 - (c.row - c.events.length));
15412 c.events = ev.slice(0, r);
15413 c.more = ev.slice(r);
15415 if(c.more.length && c.more.length == 1){
15416 c.events.push(c.more.pop());
15419 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15423 this.cells.each(function(c) {
15425 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15428 for (var e = 0; e < c.events.length; e++){
15429 var ev = c.events[e];
15430 var rows = ev.rows;
15432 for(var i = 0; i < rows.length; i++) {
15434 // how many rows should it span..
15437 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15438 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15440 unselectable : "on",
15443 cls: 'fc-event-inner',
15447 // cls: 'fc-event-time',
15448 // html : cells.length > 1 ? '' : ev.time
15452 cls: 'fc-event-title',
15453 html : String.format('{0}', ev.title)
15460 cls: 'ui-resizable-handle ui-resizable-e',
15461 html : '  '
15468 cfg.cls += ' fc-event-start';
15470 if ((i+1) == rows.length) {
15471 cfg.cls += ' fc-event-end';
15474 var ctr = _this.el.select('.fc-event-container',true).first();
15475 var cg = ctr.createChild(cfg);
15477 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15478 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15480 var r = (c.more.length) ? 1 : 0;
15481 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15482 cg.setWidth(ebox.right - sbox.x -2);
15484 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15485 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15486 cg.on('click', _this.onEventClick, _this, ev);
15497 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15498 style : 'position: absolute',
15499 unselectable : "on",
15502 cls: 'fc-event-inner',
15506 cls: 'fc-event-title',
15514 cls: 'ui-resizable-handle ui-resizable-e',
15515 html : '  '
15521 var ctr = _this.el.select('.fc-event-container',true).first();
15522 var cg = ctr.createChild(cfg);
15524 var sbox = c.select('.fc-day-content',true).first().getBox();
15525 var ebox = c.select('.fc-day-content',true).first().getBox();
15527 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15528 cg.setWidth(ebox.right - sbox.x -2);
15530 cg.on('click', _this.onMoreEventClick, _this, c.more);
15540 onEventEnter: function (e, el,event,d) {
15541 this.fireEvent('evententer', this, el, event);
15544 onEventLeave: function (e, el,event,d) {
15545 this.fireEvent('eventleave', this, el, event);
15548 onEventClick: function (e, el,event,d) {
15549 this.fireEvent('eventclick', this, el, event);
15552 onMonthChange: function () {
15556 onMoreEventClick: function(e, el, more)
15560 this.calpopover.placement = 'right';
15561 this.calpopover.setTitle('More');
15563 this.calpopover.setContent('');
15565 var ctr = this.calpopover.el.select('.popover-content', true).first();
15567 Roo.each(more, function(m){
15569 cls : 'fc-event-hori fc-event-draggable',
15572 var cg = ctr.createChild(cfg);
15574 cg.on('click', _this.onEventClick, _this, m);
15577 this.calpopover.show(el);
15582 onLoad: function ()
15584 this.calevents = [];
15587 if(this.store.getCount() > 0){
15588 this.store.data.each(function(d){
15591 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15592 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15593 time : d.data.start_time,
15594 title : d.data.title,
15595 description : d.data.description,
15596 venue : d.data.venue
15601 this.renderEvents();
15603 if(this.calevents.length && this.loadMask){
15604 this.maskEl.hide();
15608 onBeforeLoad: function()
15610 this.clearEvents();
15612 this.maskEl.show();
15626 * @class Roo.bootstrap.Popover
15627 * @extends Roo.bootstrap.Component
15628 * Bootstrap Popover class
15629 * @cfg {String} html contents of the popover (or false to use children..)
15630 * @cfg {String} title of popover (or false to hide)
15631 * @cfg {String} placement how it is placed
15632 * @cfg {String} trigger click || hover (or false to trigger manually)
15633 * @cfg {String} over what (parent or false to trigger manually.)
15634 * @cfg {Number} delay - delay before showing
15637 * Create a new Popover
15638 * @param {Object} config The config object
15641 Roo.bootstrap.Popover = function(config){
15642 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15645 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
15647 title: 'Fill in a title',
15650 placement : 'right',
15651 trigger : 'hover', // hover
15657 can_build_overlaid : false,
15659 getChildContainer : function()
15661 return this.el.select('.popover-content',true).first();
15664 getAutoCreate : function(){
15667 cls : 'popover roo-dynamic',
15668 style: 'display:block',
15674 cls : 'popover-inner',
15678 cls: 'popover-title',
15682 cls : 'popover-content',
15693 setTitle: function(str)
15696 this.el.select('.popover-title',true).first().dom.innerHTML = str;
15698 setContent: function(str)
15701 this.el.select('.popover-content',true).first().dom.innerHTML = str;
15703 // as it get's added to the bottom of the page.
15704 onRender : function(ct, position)
15706 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15708 var cfg = Roo.apply({}, this.getAutoCreate());
15712 cfg.cls += ' ' + this.cls;
15715 cfg.style = this.style;
15717 //Roo.log("adding to ");
15718 this.el = Roo.get(document.body).createChild(cfg, position);
15719 // Roo.log(this.el);
15724 initEvents : function()
15726 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15727 this.el.enableDisplayMode('block');
15729 if (this.over === false) {
15732 if (this.triggers === false) {
15735 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15736 var triggers = this.trigger ? this.trigger.split(' ') : [];
15737 Roo.each(triggers, function(trigger) {
15739 if (trigger == 'click') {
15740 on_el.on('click', this.toggle, this);
15741 } else if (trigger != 'manual') {
15742 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
15743 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15745 on_el.on(eventIn ,this.enter, this);
15746 on_el.on(eventOut, this.leave, this);
15757 toggle : function () {
15758 this.hoverState == 'in' ? this.leave() : this.enter();
15761 enter : function () {
15764 clearTimeout(this.timeout);
15766 this.hoverState = 'in';
15768 if (!this.delay || !this.delay.show) {
15773 this.timeout = setTimeout(function () {
15774 if (_t.hoverState == 'in') {
15777 }, this.delay.show)
15779 leave : function() {
15780 clearTimeout(this.timeout);
15782 this.hoverState = 'out';
15784 if (!this.delay || !this.delay.hide) {
15789 this.timeout = setTimeout(function () {
15790 if (_t.hoverState == 'out') {
15793 }, this.delay.hide)
15796 show : function (on_el)
15799 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15802 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15803 if (this.html !== false) {
15804 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15806 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15807 if (!this.title.length) {
15808 this.el.select('.popover-title',true).hide();
15811 var placement = typeof this.placement == 'function' ?
15812 this.placement.call(this, this.el, on_el) :
15815 var autoToken = /\s?auto?\s?/i;
15816 var autoPlace = autoToken.test(placement);
15818 placement = placement.replace(autoToken, '') || 'top';
15822 //this.el.setXY([0,0]);
15824 this.el.dom.style.display='block';
15825 this.el.addClass(placement);
15827 //this.el.appendTo(on_el);
15829 var p = this.getPosition();
15830 var box = this.el.getBox();
15835 var align = Roo.bootstrap.Popover.alignment[placement];
15836 this.el.alignTo(on_el, align[0],align[1]);
15837 //var arrow = this.el.select('.arrow',true).first();
15838 //arrow.set(align[2],
15840 this.el.addClass('in');
15843 if (this.el.hasClass('fade')) {
15850 this.el.setXY([0,0]);
15851 this.el.removeClass('in');
15853 this.hoverState = null;
15859 Roo.bootstrap.Popover.alignment = {
15860 'left' : ['r-l', [-10,0], 'right'],
15861 'right' : ['l-r', [10,0], 'left'],
15862 'bottom' : ['t-b', [0,10], 'top'],
15863 'top' : [ 'b-t', [0,-10], 'bottom']
15874 * @class Roo.bootstrap.Progress
15875 * @extends Roo.bootstrap.Component
15876 * Bootstrap Progress class
15877 * @cfg {Boolean} striped striped of the progress bar
15878 * @cfg {Boolean} active animated of the progress bar
15882 * Create a new Progress
15883 * @param {Object} config The config object
15886 Roo.bootstrap.Progress = function(config){
15887 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15890 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
15895 getAutoCreate : function(){
15903 cfg.cls += ' progress-striped';
15907 cfg.cls += ' active';
15926 * @class Roo.bootstrap.ProgressBar
15927 * @extends Roo.bootstrap.Component
15928 * Bootstrap ProgressBar class
15929 * @cfg {Number} aria_valuenow aria-value now
15930 * @cfg {Number} aria_valuemin aria-value min
15931 * @cfg {Number} aria_valuemax aria-value max
15932 * @cfg {String} label label for the progress bar
15933 * @cfg {String} panel (success | info | warning | danger )
15934 * @cfg {String} role role of the progress bar
15935 * @cfg {String} sr_only text
15939 * Create a new ProgressBar
15940 * @param {Object} config The config object
15943 Roo.bootstrap.ProgressBar = function(config){
15944 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15947 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
15951 aria_valuemax : 100,
15957 getAutoCreate : function()
15962 cls: 'progress-bar',
15963 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15975 cfg.role = this.role;
15978 if(this.aria_valuenow){
15979 cfg['aria-valuenow'] = this.aria_valuenow;
15982 if(this.aria_valuemin){
15983 cfg['aria-valuemin'] = this.aria_valuemin;
15986 if(this.aria_valuemax){
15987 cfg['aria-valuemax'] = this.aria_valuemax;
15990 if(this.label && !this.sr_only){
15991 cfg.html = this.label;
15995 cfg.cls += ' progress-bar-' + this.panel;
16001 update : function(aria_valuenow)
16003 this.aria_valuenow = aria_valuenow;
16005 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16020 * @class Roo.bootstrap.TabGroup
16021 * @extends Roo.bootstrap.Column
16022 * Bootstrap Column class
16023 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16024 * @cfg {Boolean} carousel true to make the group behave like a carousel
16025 * @cfg {Boolean} bullets show bullets for the panels
16026 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16027 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16028 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16031 * Create a new TabGroup
16032 * @param {Object} config The config object
16035 Roo.bootstrap.TabGroup = function(config){
16036 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16038 this.navId = Roo.id();
16041 Roo.bootstrap.TabGroup.register(this);
16045 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16048 transition : false,
16053 slideOnTouch : false,
16055 getAutoCreate : function()
16057 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16059 cfg.cls += ' tab-content';
16061 if (this.carousel) {
16062 cfg.cls += ' carousel slide';
16065 cls : 'carousel-inner'
16068 if(this.bullets && !Roo.isTouch){
16071 cls : 'carousel-bullets',
16075 if(this.bullets_cls){
16076 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16079 for (var i = 0; i < this.bullets; i++){
16081 cls : 'bullet bullet-' + i
16089 cfg.cn[0].cn = bullets;
16096 initEvents: function()
16098 if(Roo.isTouch && this.slideOnTouch){
16099 this.el.on("touchstart", this.onTouchStart, this);
16102 if(this.autoslide){
16105 this.slideFn = window.setInterval(function() {
16106 _this.showPanelNext();
16112 onTouchStart : function(e, el, o)
16114 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16118 this.showPanelNext();
16121 getChildContainer : function()
16123 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16127 * register a Navigation item
16128 * @param {Roo.bootstrap.NavItem} the navitem to add
16130 register : function(item)
16132 this.tabs.push( item);
16133 item.navId = this.navId; // not really needed..
16138 getActivePanel : function()
16141 Roo.each(this.tabs, function(t) {
16151 getPanelByName : function(n)
16154 Roo.each(this.tabs, function(t) {
16155 if (t.tabId == n) {
16163 indexOfPanel : function(p)
16166 Roo.each(this.tabs, function(t,i) {
16167 if (t.tabId == p.tabId) {
16176 * show a specific panel
16177 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16178 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16180 showPanel : function (pan)
16182 if(this.transition || typeof(pan) == 'undefined'){
16183 Roo.log("waiting for the transitionend");
16187 if (typeof(pan) == 'number') {
16188 pan = this.tabs[pan];
16191 if (typeof(pan) == 'string') {
16192 pan = this.getPanelByName(pan);
16195 var cur = this.getActivePanel();
16198 Roo.log('pan or acitve pan is undefined');
16202 if (pan.tabId == this.getActivePanel().tabId) {
16206 if (false === cur.fireEvent('beforedeactivate')) {
16210 if(this.bullets > 0 && !Roo.isTouch){
16211 this.setActiveBullet(this.indexOfPanel(pan));
16214 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16216 this.transition = true;
16217 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16218 var lr = dir == 'next' ? 'left' : 'right';
16219 pan.el.addClass(dir); // or prev
16220 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16221 cur.el.addClass(lr); // or right
16222 pan.el.addClass(lr);
16225 cur.el.on('transitionend', function() {
16226 Roo.log("trans end?");
16228 pan.el.removeClass([lr,dir]);
16229 pan.setActive(true);
16231 cur.el.removeClass([lr]);
16232 cur.setActive(false);
16234 _this.transition = false;
16236 }, this, { single: true } );
16241 cur.setActive(false);
16242 pan.setActive(true);
16247 showPanelNext : function()
16249 var i = this.indexOfPanel(this.getActivePanel());
16251 if (i >= this.tabs.length - 1 && !this.autoslide) {
16255 if (i >= this.tabs.length - 1 && this.autoslide) {
16259 this.showPanel(this.tabs[i+1]);
16262 showPanelPrev : function()
16264 var i = this.indexOfPanel(this.getActivePanel());
16266 if (i < 1 && !this.autoslide) {
16270 if (i < 1 && this.autoslide) {
16271 i = this.tabs.length;
16274 this.showPanel(this.tabs[i-1]);
16278 addBullet: function()
16280 if(!this.bullets || Roo.isTouch){
16283 var ctr = this.el.select('.carousel-bullets',true).first();
16284 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16285 var bullet = ctr.createChild({
16286 cls : 'bullet bullet-' + i
16287 },ctr.dom.lastChild);
16292 bullet.on('click', (function(e, el, o, ii, t){
16294 e.preventDefault();
16296 this.showPanel(ii);
16298 if(this.autoslide && this.slideFn){
16299 clearInterval(this.slideFn);
16300 this.slideFn = window.setInterval(function() {
16301 _this.showPanelNext();
16305 }).createDelegate(this, [i, bullet], true));
16310 setActiveBullet : function(i)
16316 Roo.each(this.el.select('.bullet', true).elements, function(el){
16317 el.removeClass('selected');
16320 var bullet = this.el.select('.bullet-' + i, true).first();
16326 bullet.addClass('selected');
16337 Roo.apply(Roo.bootstrap.TabGroup, {
16341 * register a Navigation Group
16342 * @param {Roo.bootstrap.NavGroup} the navgroup to add
16344 register : function(navgrp)
16346 this.groups[navgrp.navId] = navgrp;
16350 * fetch a Navigation Group based on the navigation ID
16351 * if one does not exist , it will get created.
16352 * @param {string} the navgroup to add
16353 * @returns {Roo.bootstrap.NavGroup} the navgroup
16355 get: function(navId) {
16356 if (typeof(this.groups[navId]) == 'undefined') {
16357 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16359 return this.groups[navId] ;
16374 * @class Roo.bootstrap.TabPanel
16375 * @extends Roo.bootstrap.Component
16376 * Bootstrap TabPanel class
16377 * @cfg {Boolean} active panel active
16378 * @cfg {String} html panel content
16379 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16380 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16384 * Create a new TabPanel
16385 * @param {Object} config The config object
16388 Roo.bootstrap.TabPanel = function(config){
16389 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16393 * Fires when the active status changes
16394 * @param {Roo.bootstrap.TabPanel} this
16395 * @param {Boolean} state the new state
16400 * @event beforedeactivate
16401 * Fires before a tab is de-activated - can be used to do validation on a form.
16402 * @param {Roo.bootstrap.TabPanel} this
16403 * @return {Boolean} false if there is an error
16406 'beforedeactivate': true
16409 this.tabId = this.tabId || Roo.id();
16413 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16420 getAutoCreate : function(){
16423 // item is needed for carousel - not sure if it has any effect otherwise
16424 cls: 'tab-pane item',
16425 html: this.html || ''
16429 cfg.cls += ' active';
16433 cfg.tabId = this.tabId;
16440 initEvents: function()
16442 var p = this.parent();
16443 this.navId = this.navId || p.navId;
16445 if (typeof(this.navId) != 'undefined') {
16446 // not really needed.. but just in case.. parent should be a NavGroup.
16447 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16451 var i = tg.tabs.length - 1;
16453 if(this.active && tg.bullets > 0 && i < tg.bullets){
16454 tg.setActiveBullet(i);
16461 onRender : function(ct, position)
16463 // Roo.log("Call onRender: " + this.xtype);
16465 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16473 setActive: function(state)
16475 Roo.log("panel - set active " + this.tabId + "=" + state);
16477 this.active = state;
16479 this.el.removeClass('active');
16481 } else if (!this.el.hasClass('active')) {
16482 this.el.addClass('active');
16485 this.fireEvent('changed', this, state);
16502 * @class Roo.bootstrap.DateField
16503 * @extends Roo.bootstrap.Input
16504 * Bootstrap DateField class
16505 * @cfg {Number} weekStart default 0
16506 * @cfg {String} viewMode default empty, (months|years)
16507 * @cfg {String} minViewMode default empty, (months|years)
16508 * @cfg {Number} startDate default -Infinity
16509 * @cfg {Number} endDate default Infinity
16510 * @cfg {Boolean} todayHighlight default false
16511 * @cfg {Boolean} todayBtn default false
16512 * @cfg {Boolean} calendarWeeks default false
16513 * @cfg {Object} daysOfWeekDisabled default empty
16514 * @cfg {Boolean} singleMode default false (true | false)
16516 * @cfg {Boolean} keyboardNavigation default true
16517 * @cfg {String} language default en
16520 * Create a new DateField
16521 * @param {Object} config The config object
16524 Roo.bootstrap.DateField = function(config){
16525 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16529 * Fires when this field show.
16530 * @param {Roo.bootstrap.DateField} this
16531 * @param {Mixed} date The date value
16536 * Fires when this field hide.
16537 * @param {Roo.bootstrap.DateField} this
16538 * @param {Mixed} date The date value
16543 * Fires when select a date.
16544 * @param {Roo.bootstrap.DateField} this
16545 * @param {Mixed} date The date value
16549 * @event beforeselect
16550 * Fires when before select a date.
16551 * @param {Roo.bootstrap.DateField} this
16552 * @param {Mixed} date The date value
16554 beforeselect : true
16558 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16561 * @cfg {String} format
16562 * The default date format string which can be overriden for localization support. The format must be
16563 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16567 * @cfg {String} altFormats
16568 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16569 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16571 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16579 todayHighlight : false,
16585 keyboardNavigation: true,
16587 calendarWeeks: false,
16589 startDate: -Infinity,
16593 daysOfWeekDisabled: [],
16597 singleMode : false,
16599 UTCDate: function()
16601 return new Date(Date.UTC.apply(Date, arguments));
16604 UTCToday: function()
16606 var today = new Date();
16607 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16610 getDate: function() {
16611 var d = this.getUTCDate();
16612 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16615 getUTCDate: function() {
16619 setDate: function(d) {
16620 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16623 setUTCDate: function(d) {
16625 this.setValue(this.formatDate(this.date));
16628 onRender: function(ct, position)
16631 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16633 this.language = this.language || 'en';
16634 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16635 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16637 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16638 this.format = this.format || 'm/d/y';
16639 this.isInline = false;
16640 this.isInput = true;
16641 this.component = this.el.select('.add-on', true).first() || false;
16642 this.component = (this.component && this.component.length === 0) ? false : this.component;
16643 this.hasInput = this.component && this.inputEL().length;
16645 if (typeof(this.minViewMode === 'string')) {
16646 switch (this.minViewMode) {
16648 this.minViewMode = 1;
16651 this.minViewMode = 2;
16654 this.minViewMode = 0;
16659 if (typeof(this.viewMode === 'string')) {
16660 switch (this.viewMode) {
16673 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16675 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16677 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16679 this.picker().on('mousedown', this.onMousedown, this);
16680 this.picker().on('click', this.onClick, this);
16682 this.picker().addClass('datepicker-dropdown');
16684 this.startViewMode = this.viewMode;
16686 if(this.singleMode){
16687 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16688 v.setVisibilityMode(Roo.Element.DISPLAY);
16692 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16693 v.setStyle('width', '189px');
16697 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16698 if(!this.calendarWeeks){
16703 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16704 v.attr('colspan', function(i, val){
16705 return parseInt(val) + 1;
16710 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16712 this.setStartDate(this.startDate);
16713 this.setEndDate(this.endDate);
16715 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16722 if(this.isInline) {
16727 picker : function()
16729 return this.pickerEl;
16730 // return this.el.select('.datepicker', true).first();
16733 fillDow: function()
16735 var dowCnt = this.weekStart;
16744 if(this.calendarWeeks){
16752 while (dowCnt < this.weekStart + 7) {
16756 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16760 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16763 fillMonths: function()
16766 var months = this.picker().select('>.datepicker-months td', true).first();
16768 months.dom.innerHTML = '';
16774 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16777 months.createChild(month);
16784 this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
16786 if (this.date < this.startDate) {
16787 this.viewDate = new Date(this.startDate);
16788 } else if (this.date > this.endDate) {
16789 this.viewDate = new Date(this.endDate);
16791 this.viewDate = new Date(this.date);
16799 var d = new Date(this.viewDate),
16800 year = d.getUTCFullYear(),
16801 month = d.getUTCMonth(),
16802 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16803 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16804 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16805 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16806 currentDate = this.date && this.date.valueOf(),
16807 today = this.UTCToday();
16809 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16811 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16813 // this.picker.select('>tfoot th.today').
16814 // .text(dates[this.language].today)
16815 // .toggle(this.todayBtn !== false);
16817 this.updateNavArrows();
16820 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16822 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16824 prevMonth.setUTCDate(day);
16826 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16828 var nextMonth = new Date(prevMonth);
16830 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16832 nextMonth = nextMonth.valueOf();
16834 var fillMonths = false;
16836 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16838 while(prevMonth.valueOf() < nextMonth) {
16841 if (prevMonth.getUTCDay() === this.weekStart) {
16843 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16851 if(this.calendarWeeks){
16852 // ISO 8601: First week contains first thursday.
16853 // ISO also states week starts on Monday, but we can be more abstract here.
16855 // Start of current week: based on weekstart/current date
16856 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16857 // Thursday of this week
16858 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16859 // First Thursday of year, year from thursday
16860 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16861 // Calendar week: ms between thursdays, div ms per day, div 7 days
16862 calWeek = (th - yth) / 864e5 / 7 + 1;
16864 fillMonths.cn.push({
16872 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16874 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16877 if (this.todayHighlight &&
16878 prevMonth.getUTCFullYear() == today.getFullYear() &&
16879 prevMonth.getUTCMonth() == today.getMonth() &&
16880 prevMonth.getUTCDate() == today.getDate()) {
16881 clsName += ' today';
16884 if (currentDate && prevMonth.valueOf() === currentDate) {
16885 clsName += ' active';
16888 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16889 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16890 clsName += ' disabled';
16893 fillMonths.cn.push({
16895 cls: 'day ' + clsName,
16896 html: prevMonth.getDate()
16899 prevMonth.setDate(prevMonth.getDate()+1);
16902 var currentYear = this.date && this.date.getUTCFullYear();
16903 var currentMonth = this.date && this.date.getUTCMonth();
16905 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16907 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16908 v.removeClass('active');
16910 if(currentYear === year && k === currentMonth){
16911 v.addClass('active');
16914 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16915 v.addClass('disabled');
16921 year = parseInt(year/10, 10) * 10;
16923 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16925 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16928 for (var i = -1; i < 11; i++) {
16929 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16931 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16939 showMode: function(dir)
16942 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16945 Roo.each(this.picker().select('>div',true).elements, function(v){
16946 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16949 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16954 if(this.isInline) {
16958 this.picker().removeClass(['bottom', 'top']);
16960 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16962 * place to the top of element!
16966 this.picker().addClass('top');
16967 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16972 this.picker().addClass('bottom');
16974 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16977 parseDate : function(value)
16979 if(!value || value instanceof Date){
16982 var v = Date.parseDate(value, this.format);
16983 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16984 v = Date.parseDate(value, 'Y-m-d');
16986 if(!v && this.altFormats){
16987 if(!this.altFormatsArray){
16988 this.altFormatsArray = this.altFormats.split("|");
16990 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16991 v = Date.parseDate(value, this.altFormatsArray[i]);
16997 formatDate : function(date, fmt)
16999 return (!date || !(date instanceof Date)) ?
17000 date : date.dateFormat(fmt || this.format);
17003 onFocus : function()
17005 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17009 onBlur : function()
17011 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17013 var d = this.inputEl().getValue();
17022 this.picker().show();
17026 this.fireEvent('show', this, this.date);
17031 if(this.isInline) {
17034 this.picker().hide();
17035 this.viewMode = this.startViewMode;
17038 this.fireEvent('hide', this, this.date);
17042 onMousedown: function(e)
17044 e.stopPropagation();
17045 e.preventDefault();
17050 Roo.bootstrap.DateField.superclass.keyup.call(this);
17054 setValue: function(v)
17056 if(this.fireEvent('beforeselect', this, v) !== false){
17057 var d = new Date(this.parseDate(v) ).clearTime();
17059 if(isNaN(d.getTime())){
17060 this.date = this.viewDate = '';
17061 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17065 v = this.formatDate(d);
17067 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17069 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17073 this.fireEvent('select', this, this.date);
17077 getValue: function()
17079 return this.formatDate(this.date);
17082 fireKey: function(e)
17084 if (!this.picker().isVisible()){
17085 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17091 var dateChanged = false,
17093 newDate, newViewDate;
17098 e.preventDefault();
17102 if (!this.keyboardNavigation) {
17105 dir = e.keyCode == 37 ? -1 : 1;
17108 newDate = this.moveYear(this.date, dir);
17109 newViewDate = this.moveYear(this.viewDate, dir);
17110 } else if (e.shiftKey){
17111 newDate = this.moveMonth(this.date, dir);
17112 newViewDate = this.moveMonth(this.viewDate, dir);
17114 newDate = new Date(this.date);
17115 newDate.setUTCDate(this.date.getUTCDate() + dir);
17116 newViewDate = new Date(this.viewDate);
17117 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17119 if (this.dateWithinRange(newDate)){
17120 this.date = newDate;
17121 this.viewDate = newViewDate;
17122 this.setValue(this.formatDate(this.date));
17124 e.preventDefault();
17125 dateChanged = true;
17130 if (!this.keyboardNavigation) {
17133 dir = e.keyCode == 38 ? -1 : 1;
17135 newDate = this.moveYear(this.date, dir);
17136 newViewDate = this.moveYear(this.viewDate, dir);
17137 } else if (e.shiftKey){
17138 newDate = this.moveMonth(this.date, dir);
17139 newViewDate = this.moveMonth(this.viewDate, dir);
17141 newDate = new Date(this.date);
17142 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17143 newViewDate = new Date(this.viewDate);
17144 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17146 if (this.dateWithinRange(newDate)){
17147 this.date = newDate;
17148 this.viewDate = newViewDate;
17149 this.setValue(this.formatDate(this.date));
17151 e.preventDefault();
17152 dateChanged = true;
17156 this.setValue(this.formatDate(this.date));
17158 e.preventDefault();
17161 this.setValue(this.formatDate(this.date));
17175 onClick: function(e)
17177 e.stopPropagation();
17178 e.preventDefault();
17180 var target = e.getTarget();
17182 if(target.nodeName.toLowerCase() === 'i'){
17183 target = Roo.get(target).dom.parentNode;
17186 var nodeName = target.nodeName;
17187 var className = target.className;
17188 var html = target.innerHTML;
17189 //Roo.log(nodeName);
17191 switch(nodeName.toLowerCase()) {
17193 switch(className) {
17199 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17200 switch(this.viewMode){
17202 this.viewDate = this.moveMonth(this.viewDate, dir);
17206 this.viewDate = this.moveYear(this.viewDate, dir);
17212 var date = new Date();
17213 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17215 this.setValue(this.formatDate(this.date));
17222 if (className.indexOf('disabled') < 0) {
17223 this.viewDate.setUTCDate(1);
17224 if (className.indexOf('month') > -1) {
17225 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17227 var year = parseInt(html, 10) || 0;
17228 this.viewDate.setUTCFullYear(year);
17232 if(this.singleMode){
17233 this.setValue(this.formatDate(this.viewDate));
17244 //Roo.log(className);
17245 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17246 var day = parseInt(html, 10) || 1;
17247 var year = this.viewDate.getUTCFullYear(),
17248 month = this.viewDate.getUTCMonth();
17250 if (className.indexOf('old') > -1) {
17257 } else if (className.indexOf('new') > -1) {
17265 //Roo.log([year,month,day]);
17266 this.date = this.UTCDate(year, month, day,0,0,0,0);
17267 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17269 //Roo.log(this.formatDate(this.date));
17270 this.setValue(this.formatDate(this.date));
17277 setStartDate: function(startDate)
17279 this.startDate = startDate || -Infinity;
17280 if (this.startDate !== -Infinity) {
17281 this.startDate = this.parseDate(this.startDate);
17284 this.updateNavArrows();
17287 setEndDate: function(endDate)
17289 this.endDate = endDate || Infinity;
17290 if (this.endDate !== Infinity) {
17291 this.endDate = this.parseDate(this.endDate);
17294 this.updateNavArrows();
17297 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17299 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17300 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17301 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17303 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17304 return parseInt(d, 10);
17307 this.updateNavArrows();
17310 updateNavArrows: function()
17312 if(this.singleMode){
17316 var d = new Date(this.viewDate),
17317 year = d.getUTCFullYear(),
17318 month = d.getUTCMonth();
17320 Roo.each(this.picker().select('.prev', true).elements, function(v){
17322 switch (this.viewMode) {
17325 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17331 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17338 Roo.each(this.picker().select('.next', true).elements, function(v){
17340 switch (this.viewMode) {
17343 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17349 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17357 moveMonth: function(date, dir)
17362 var new_date = new Date(date.valueOf()),
17363 day = new_date.getUTCDate(),
17364 month = new_date.getUTCMonth(),
17365 mag = Math.abs(dir),
17367 dir = dir > 0 ? 1 : -1;
17370 // If going back one month, make sure month is not current month
17371 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17373 return new_date.getUTCMonth() == month;
17375 // If going forward one month, make sure month is as expected
17376 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17378 return new_date.getUTCMonth() != new_month;
17380 new_month = month + dir;
17381 new_date.setUTCMonth(new_month);
17382 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17383 if (new_month < 0 || new_month > 11) {
17384 new_month = (new_month + 12) % 12;
17387 // For magnitudes >1, move one month at a time...
17388 for (var i=0; i<mag; i++) {
17389 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17390 new_date = this.moveMonth(new_date, dir);
17392 // ...then reset the day, keeping it in the new month
17393 new_month = new_date.getUTCMonth();
17394 new_date.setUTCDate(day);
17396 return new_month != new_date.getUTCMonth();
17399 // Common date-resetting loop -- if date is beyond end of month, make it
17402 new_date.setUTCDate(--day);
17403 new_date.setUTCMonth(new_month);
17408 moveYear: function(date, dir)
17410 return this.moveMonth(date, dir*12);
17413 dateWithinRange: function(date)
17415 return date >= this.startDate && date <= this.endDate;
17421 this.picker().remove();
17426 Roo.apply(Roo.bootstrap.DateField, {
17437 html: '<i class="fa fa-arrow-left"/>'
17447 html: '<i class="fa fa-arrow-right"/>'
17489 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17490 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17491 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17492 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17493 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17506 navFnc: 'FullYear',
17511 navFnc: 'FullYear',
17516 Roo.apply(Roo.bootstrap.DateField, {
17520 cls: 'datepicker dropdown-menu roo-dynamic',
17524 cls: 'datepicker-days',
17528 cls: 'table-condensed',
17530 Roo.bootstrap.DateField.head,
17534 Roo.bootstrap.DateField.footer
17541 cls: 'datepicker-months',
17545 cls: 'table-condensed',
17547 Roo.bootstrap.DateField.head,
17548 Roo.bootstrap.DateField.content,
17549 Roo.bootstrap.DateField.footer
17556 cls: 'datepicker-years',
17560 cls: 'table-condensed',
17562 Roo.bootstrap.DateField.head,
17563 Roo.bootstrap.DateField.content,
17564 Roo.bootstrap.DateField.footer
17583 * @class Roo.bootstrap.TimeField
17584 * @extends Roo.bootstrap.Input
17585 * Bootstrap DateField class
17589 * Create a new TimeField
17590 * @param {Object} config The config object
17593 Roo.bootstrap.TimeField = function(config){
17594 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17598 * Fires when this field show.
17599 * @param {Roo.bootstrap.DateField} thisthis
17600 * @param {Mixed} date The date value
17605 * Fires when this field hide.
17606 * @param {Roo.bootstrap.DateField} this
17607 * @param {Mixed} date The date value
17612 * Fires when select a date.
17613 * @param {Roo.bootstrap.DateField} this
17614 * @param {Mixed} date The date value
17620 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17623 * @cfg {String} format
17624 * The default time format string which can be overriden for localization support. The format must be
17625 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17629 onRender: function(ct, position)
17632 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17634 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17636 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17638 this.pop = this.picker().select('>.datepicker-time',true).first();
17639 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17641 this.picker().on('mousedown', this.onMousedown, this);
17642 this.picker().on('click', this.onClick, this);
17644 this.picker().addClass('datepicker-dropdown');
17649 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17650 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17651 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17652 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17653 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17654 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17658 fireKey: function(e){
17659 if (!this.picker().isVisible()){
17660 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17666 e.preventDefault();
17674 this.onTogglePeriod();
17677 this.onIncrementMinutes();
17680 this.onDecrementMinutes();
17689 onClick: function(e) {
17690 e.stopPropagation();
17691 e.preventDefault();
17694 picker : function()
17696 return this.el.select('.datepicker', true).first();
17699 fillTime: function()
17701 var time = this.pop.select('tbody', true).first();
17703 time.dom.innerHTML = '';
17718 cls: 'hours-up glyphicon glyphicon-chevron-up'
17738 cls: 'minutes-up glyphicon glyphicon-chevron-up'
17759 cls: 'timepicker-hour',
17774 cls: 'timepicker-minute',
17789 cls: 'btn btn-primary period',
17811 cls: 'hours-down glyphicon glyphicon-chevron-down'
17831 cls: 'minutes-down glyphicon glyphicon-chevron-down'
17849 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17856 var hours = this.time.getHours();
17857 var minutes = this.time.getMinutes();
17870 hours = hours - 12;
17874 hours = '0' + hours;
17878 minutes = '0' + minutes;
17881 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17882 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17883 this.pop.select('button', true).first().dom.innerHTML = period;
17889 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17891 var cls = ['bottom'];
17893 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17900 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17905 this.picker().addClass(cls.join('-'));
17909 Roo.each(cls, function(c){
17911 _this.picker().setTop(_this.inputEl().getHeight());
17915 _this.picker().setTop(0 - _this.picker().getHeight());
17920 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17924 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17931 onFocus : function()
17933 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17937 onBlur : function()
17939 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17945 this.picker().show();
17950 this.fireEvent('show', this, this.date);
17955 this.picker().hide();
17958 this.fireEvent('hide', this, this.date);
17961 setTime : function()
17964 this.setValue(this.time.format(this.format));
17966 this.fireEvent('select', this, this.date);
17971 onMousedown: function(e){
17972 e.stopPropagation();
17973 e.preventDefault();
17976 onIncrementHours: function()
17978 Roo.log('onIncrementHours');
17979 this.time = this.time.add(Date.HOUR, 1);
17984 onDecrementHours: function()
17986 Roo.log('onDecrementHours');
17987 this.time = this.time.add(Date.HOUR, -1);
17991 onIncrementMinutes: function()
17993 Roo.log('onIncrementMinutes');
17994 this.time = this.time.add(Date.MINUTE, 1);
17998 onDecrementMinutes: function()
18000 Roo.log('onDecrementMinutes');
18001 this.time = this.time.add(Date.MINUTE, -1);
18005 onTogglePeriod: function()
18007 Roo.log('onTogglePeriod');
18008 this.time = this.time.add(Date.HOUR, 12);
18015 Roo.apply(Roo.bootstrap.TimeField, {
18045 cls: 'btn btn-info ok',
18057 Roo.apply(Roo.bootstrap.TimeField, {
18061 cls: 'datepicker dropdown-menu',
18065 cls: 'datepicker-time',
18069 cls: 'table-condensed',
18071 Roo.bootstrap.TimeField.content,
18072 Roo.bootstrap.TimeField.footer
18091 * @class Roo.bootstrap.MonthField
18092 * @extends Roo.bootstrap.Input
18093 * Bootstrap MonthField class
18095 * @cfg {String} language default en
18098 * Create a new MonthField
18099 * @param {Object} config The config object
18102 Roo.bootstrap.MonthField = function(config){
18103 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18108 * Fires when this field show.
18109 * @param {Roo.bootstrap.MonthField} this
18110 * @param {Mixed} date The date value
18115 * Fires when this field hide.
18116 * @param {Roo.bootstrap.MonthField} this
18117 * @param {Mixed} date The date value
18122 * Fires when select a date.
18123 * @param {Roo.bootstrap.MonthField} this
18124 * @param {String} oldvalue The old value
18125 * @param {String} newvalue The new value
18131 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
18133 onRender: function(ct, position)
18136 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18138 this.language = this.language || 'en';
18139 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18140 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18142 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18143 this.isInline = false;
18144 this.isInput = true;
18145 this.component = this.el.select('.add-on', true).first() || false;
18146 this.component = (this.component && this.component.length === 0) ? false : this.component;
18147 this.hasInput = this.component && this.inputEL().length;
18149 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18151 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18153 this.picker().on('mousedown', this.onMousedown, this);
18154 this.picker().on('click', this.onClick, this);
18156 this.picker().addClass('datepicker-dropdown');
18158 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18159 v.setStyle('width', '189px');
18166 if(this.isInline) {
18172 setValue: function(v, suppressEvent)
18174 var o = this.getValue();
18176 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18180 if(suppressEvent !== true){
18181 this.fireEvent('select', this, o, v);
18186 getValue: function()
18191 onClick: function(e)
18193 e.stopPropagation();
18194 e.preventDefault();
18196 var target = e.getTarget();
18198 if(target.nodeName.toLowerCase() === 'i'){
18199 target = Roo.get(target).dom.parentNode;
18202 var nodeName = target.nodeName;
18203 var className = target.className;
18204 var html = target.innerHTML;
18206 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18210 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18212 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18218 picker : function()
18220 return this.pickerEl;
18223 fillMonths: function()
18226 var months = this.picker().select('>.datepicker-months td', true).first();
18228 months.dom.innerHTML = '';
18234 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18237 months.createChild(month);
18246 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18247 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18250 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18251 e.removeClass('active');
18253 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18254 e.addClass('active');
18261 if(this.isInline) {
18265 this.picker().removeClass(['bottom', 'top']);
18267 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18269 * place to the top of element!
18273 this.picker().addClass('top');
18274 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18279 this.picker().addClass('bottom');
18281 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18284 onFocus : function()
18286 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18290 onBlur : function()
18292 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18294 var d = this.inputEl().getValue();
18303 this.picker().show();
18304 this.picker().select('>.datepicker-months', true).first().show();
18308 this.fireEvent('show', this, this.date);
18313 if(this.isInline) {
18316 this.picker().hide();
18317 this.fireEvent('hide', this, this.date);
18321 onMousedown: function(e)
18323 e.stopPropagation();
18324 e.preventDefault();
18329 Roo.bootstrap.MonthField.superclass.keyup.call(this);
18333 fireKey: function(e)
18335 if (!this.picker().isVisible()){
18336 if (e.keyCode == 27) {// allow escape to hide and re-show picker
18347 e.preventDefault();
18351 dir = e.keyCode == 37 ? -1 : 1;
18353 this.vIndex = this.vIndex + dir;
18355 if(this.vIndex < 0){
18359 if(this.vIndex > 11){
18363 if(isNaN(this.vIndex)){
18367 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18373 dir = e.keyCode == 38 ? -1 : 1;
18375 this.vIndex = this.vIndex + dir * 4;
18377 if(this.vIndex < 0){
18381 if(this.vIndex > 11){
18385 if(isNaN(this.vIndex)){
18389 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18394 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18395 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18399 e.preventDefault();
18402 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18403 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18419 this.picker().remove();
18424 Roo.apply(Roo.bootstrap.MonthField, {
18443 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18444 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18449 Roo.apply(Roo.bootstrap.MonthField, {
18453 cls: 'datepicker dropdown-menu roo-dynamic',
18457 cls: 'datepicker-months',
18461 cls: 'table-condensed',
18463 Roo.bootstrap.DateField.content
18483 * @class Roo.bootstrap.CheckBox
18484 * @extends Roo.bootstrap.Input
18485 * Bootstrap CheckBox class
18487 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18488 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18489 * @cfg {String} boxLabel The text that appears beside the checkbox
18490 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18491 * @cfg {Boolean} checked initnal the element
18492 * @cfg {Boolean} inline inline the element (default false)
18493 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18496 * Create a new CheckBox
18497 * @param {Object} config The config object
18500 Roo.bootstrap.CheckBox = function(config){
18501 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18506 * Fires when the element is checked or unchecked.
18507 * @param {Roo.bootstrap.CheckBox} this This input
18508 * @param {Boolean} checked The new checked value
18515 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18517 inputType: 'checkbox',
18525 getAutoCreate : function()
18527 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18533 cfg.cls = 'form-group ' + this.inputType; //input-group
18536 cfg.cls += ' ' + this.inputType + '-inline';
18542 type : this.inputType,
18543 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18544 cls : 'roo-' + this.inputType, //'form-box',
18545 placeholder : this.placeholder || ''
18549 if (this.weight) { // Validity check?
18550 cfg.cls += " " + this.inputType + "-" + this.weight;
18553 if (this.disabled) {
18554 input.disabled=true;
18558 input.checked = this.checked;
18562 input.name = this.name;
18566 input.cls += ' input-' + this.size;
18571 ['xs','sm','md','lg'].map(function(size){
18572 if (settings[size]) {
18573 cfg.cls += ' col-' + size + '-' + settings[size];
18577 var inputblock = input;
18579 if (this.before || this.after) {
18582 cls : 'input-group',
18587 inputblock.cn.push({
18589 cls : 'input-group-addon',
18594 inputblock.cn.push(input);
18597 inputblock.cn.push({
18599 cls : 'input-group-addon',
18606 if (align ==='left' && this.fieldLabel.length) {
18607 Roo.log("left and has label");
18613 cls : 'control-label col-md-' + this.labelWidth,
18614 html : this.fieldLabel
18618 cls : "col-md-" + (12 - this.labelWidth),
18625 } else if ( this.fieldLabel.length) {
18630 tag: this.boxLabel ? 'span' : 'label',
18632 cls: 'control-label box-input-label',
18633 //cls : 'input-group-addon',
18634 html : this.fieldLabel
18644 Roo.log(" no label && no align");
18645 cfg.cn = [ inputblock ] ;
18650 var boxLabelCfg = {
18652 //'for': id, // box label is handled by onclick - so no for...
18654 html: this.boxLabel
18658 boxLabelCfg.tooltip = this.tooltip;
18661 cfg.cn.push(boxLabelCfg);
18671 * return the real input element.
18673 inputEl: function ()
18675 return this.el.select('input.roo-' + this.inputType,true).first();
18678 labelEl: function()
18680 return this.el.select('label.control-label',true).first();
18682 /* depricated... */
18686 return this.labelEl();
18689 initEvents : function()
18691 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18693 this.inputEl().on('click', this.onClick, this);
18695 if (this.boxLabel) {
18696 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18699 this.startValue = this.getValue();
18702 Roo.bootstrap.CheckBox.register(this);
18706 onClick : function()
18708 this.setChecked(!this.checked);
18711 setChecked : function(state,suppressEvent)
18713 this.startValue = this.getValue();
18715 if(this.inputType == 'radio'){
18717 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18718 e.dom.checked = false;
18721 this.inputEl().dom.checked = true;
18723 this.inputEl().dom.value = this.inputValue;
18725 if(suppressEvent !== true){
18726 this.fireEvent('check', this, true);
18734 this.checked = state;
18736 this.inputEl().dom.checked = state;
18738 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18740 if(suppressEvent !== true){
18741 this.fireEvent('check', this, state);
18747 getValue : function()
18749 if(this.inputType == 'radio'){
18750 return this.getGroupValue();
18753 return this.inputEl().getValue();
18757 getGroupValue : function()
18759 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18763 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18766 setValue : function(v,suppressEvent)
18768 if(this.inputType == 'radio'){
18769 this.setGroupValue(v, suppressEvent);
18773 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18778 setGroupValue : function(v, suppressEvent)
18780 this.startValue = this.getValue();
18782 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18783 e.dom.checked = false;
18785 if(e.dom.value == v){
18786 e.dom.checked = true;
18790 if(suppressEvent !== true){
18791 this.fireEvent('check', this, true);
18799 validate : function()
18803 (this.inputType == 'radio' && this.validateRadio()) ||
18804 (this.inputType == 'checkbox' && this.validateCheckbox())
18810 this.markInvalid();
18814 validateRadio : function()
18818 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18819 if(!e.dom.checked){
18831 validateCheckbox : function()
18834 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18837 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18845 for(var i in group){
18850 r = (group[i].getValue() == group[i].inputValue) ? true : false;
18857 * Mark this field as valid
18859 markValid : function()
18861 if(this.allowBlank){
18867 this.fireEvent('valid', this);
18869 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18872 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18879 if(this.inputType == 'radio'){
18880 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18881 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18882 e.findParent('.form-group', false, true).addClass(_this.validClass);
18889 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18890 this.el.findParent('.form-group', false, true).addClass(this.validClass);
18894 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18900 for(var i in group){
18901 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18902 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18907 * Mark this field as invalid
18908 * @param {String} msg The validation message
18910 markInvalid : function(msg)
18912 if(this.allowBlank){
18918 this.fireEvent('invalid', this, msg);
18920 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18923 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18927 label.markInvalid();
18930 if(this.inputType == 'radio'){
18931 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18932 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18933 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18940 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18941 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18945 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18951 for(var i in group){
18952 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18953 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18960 Roo.apply(Roo.bootstrap.CheckBox, {
18965 * register a CheckBox Group
18966 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18968 register : function(checkbox)
18970 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18971 this.groups[checkbox.groupId] = {};
18974 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18978 this.groups[checkbox.groupId][checkbox.name] = checkbox;
18982 * fetch a CheckBox Group based on the group ID
18983 * @param {string} the group ID
18984 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18986 get: function(groupId) {
18987 if (typeof(this.groups[groupId]) == 'undefined') {
18991 return this.groups[groupId] ;
19003 *<div class="radio">
19005 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19006 Option one is this and that—be sure to include why it's great
19013 *<label class="radio-inline">fieldLabel</label>
19014 *<label class="radio-inline">
19015 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19023 * @class Roo.bootstrap.Radio
19024 * @extends Roo.bootstrap.CheckBox
19025 * Bootstrap Radio class
19028 * Create a new Radio
19029 * @param {Object} config The config object
19032 Roo.bootstrap.Radio = function(config){
19033 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19037 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
19039 inputType: 'radio',
19043 getAutoCreate : function()
19045 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19046 align = align || 'left'; // default...
19053 tag : this.inline ? 'span' : 'div',
19058 var inline = this.inline ? ' radio-inline' : '';
19062 // does not need for, as we wrap the input with it..
19064 cls : 'control-label box-label' + inline,
19067 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19071 //cls : 'control-label' + inline,
19072 html : this.fieldLabel,
19073 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19082 type : this.inputType,
19083 //value : (!this.checked) ? this.valueOff : this.inputValue,
19084 value : this.inputValue,
19086 placeholder : this.placeholder || '' // ?? needed????
19089 if (this.weight) { // Validity check?
19090 input.cls += " radio-" + this.weight;
19092 if (this.disabled) {
19093 input.disabled=true;
19097 input.checked = this.checked;
19101 input.name = this.name;
19105 input.cls += ' input-' + this.size;
19108 //?? can span's inline have a width??
19111 ['xs','sm','md','lg'].map(function(size){
19112 if (settings[size]) {
19113 cfg.cls += ' col-' + size + '-' + settings[size];
19117 var inputblock = input;
19119 if (this.before || this.after) {
19122 cls : 'input-group',
19127 inputblock.cn.push({
19129 cls : 'input-group-addon',
19133 inputblock.cn.push(input);
19135 inputblock.cn.push({
19137 cls : 'input-group-addon',
19145 if (this.fieldLabel && this.fieldLabel.length) {
19146 cfg.cn.push(fieldLabel);
19149 // normal bootstrap puts the input inside the label.
19150 // however with our styled version - it has to go after the input.
19152 //lbl.cn.push(inputblock);
19156 cls: 'radio' + inline,
19163 cfg.cn.push( lblwrap);
19168 html: this.boxLabel
19177 initEvents : function()
19179 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19181 this.inputEl().on('click', this.onClick, this);
19182 if (this.boxLabel) {
19183 //Roo.log('find label');
19184 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
19189 inputEl: function ()
19191 return this.el.select('input.roo-radio',true).first();
19193 onClick : function()
19196 this.setChecked(true);
19199 setChecked : function(state,suppressEvent)
19202 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19203 v.dom.checked = false;
19206 Roo.log(this.inputEl().dom);
19207 this.checked = state;
19208 this.inputEl().dom.checked = state;
19210 if(suppressEvent !== true){
19211 this.fireEvent('check', this, state);
19214 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19218 getGroupValue : function()
19221 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19222 if(v.dom.checked == true){
19223 value = v.dom.value;
19231 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
19232 * @return {Mixed} value The field value
19234 getValue : function(){
19235 return this.getGroupValue();
19241 //<script type="text/javascript">
19244 * Based Ext JS Library 1.1.1
19245 * Copyright(c) 2006-2007, Ext JS, LLC.
19251 * @class Roo.HtmlEditorCore
19252 * @extends Roo.Component
19253 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19255 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19258 Roo.HtmlEditorCore = function(config){
19261 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19266 * @event initialize
19267 * Fires when the editor is fully initialized (including the iframe)
19268 * @param {Roo.HtmlEditorCore} this
19273 * Fires when the editor is first receives the focus. Any insertion must wait
19274 * until after this event.
19275 * @param {Roo.HtmlEditorCore} this
19279 * @event beforesync
19280 * Fires before the textarea is updated with content from the editor iframe. Return false
19281 * to cancel the sync.
19282 * @param {Roo.HtmlEditorCore} this
19283 * @param {String} html
19287 * @event beforepush
19288 * Fires before the iframe editor is updated with content from the textarea. Return false
19289 * to cancel the push.
19290 * @param {Roo.HtmlEditorCore} this
19291 * @param {String} html
19296 * Fires when the textarea is updated with content from the editor iframe.
19297 * @param {Roo.HtmlEditorCore} this
19298 * @param {String} html
19303 * Fires when the iframe editor is updated with content from the textarea.
19304 * @param {Roo.HtmlEditorCore} this
19305 * @param {String} html
19310 * @event editorevent
19311 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19312 * @param {Roo.HtmlEditorCore} this
19318 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19320 // defaults : white / black...
19321 this.applyBlacklists();
19328 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
19332 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
19338 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
19343 * @cfg {Number} height (in pixels)
19347 * @cfg {Number} width (in pixels)
19352 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19355 stylesheets: false,
19360 // private properties
19361 validationEvent : false,
19363 initialized : false,
19365 sourceEditMode : false,
19366 onFocus : Roo.emptyFn,
19368 hideMode:'offsets',
19372 // blacklist + whitelisted elements..
19379 * Protected method that will not generally be called directly. It
19380 * is called when the editor initializes the iframe with HTML contents. Override this method if you
19381 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19383 getDocMarkup : function(){
19387 // inherit styels from page...??
19388 if (this.stylesheets === false) {
19390 Roo.get(document.head).select('style').each(function(node) {
19391 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19394 Roo.get(document.head).select('link').each(function(node) {
19395 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19398 } else if (!this.stylesheets.length) {
19400 st = '<style type="text/css">' +
19401 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19407 st += '<style type="text/css">' +
19408 'IMG { cursor: pointer } ' +
19412 return '<html><head>' + st +
19413 //<style type="text/css">' +
19414 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19416 ' </head><body class="roo-htmleditor-body"></body></html>';
19420 onRender : function(ct, position)
19423 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19424 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19427 this.el.dom.style.border = '0 none';
19428 this.el.dom.setAttribute('tabIndex', -1);
19429 this.el.addClass('x-hidden hide');
19433 if(Roo.isIE){ // fix IE 1px bogus margin
19434 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19438 this.frameId = Roo.id();
19442 var iframe = this.owner.wrap.createChild({
19444 cls: 'form-control', // bootstrap..
19446 name: this.frameId,
19447 frameBorder : 'no',
19448 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19453 this.iframe = iframe.dom;
19455 this.assignDocWin();
19457 this.doc.designMode = 'on';
19460 this.doc.write(this.getDocMarkup());
19464 var task = { // must defer to wait for browser to be ready
19466 //console.log("run task?" + this.doc.readyState);
19467 this.assignDocWin();
19468 if(this.doc.body || this.doc.readyState == 'complete'){
19470 this.doc.designMode="on";
19474 Roo.TaskMgr.stop(task);
19475 this.initEditor.defer(10, this);
19482 Roo.TaskMgr.start(task);
19487 onResize : function(w, h)
19489 Roo.log('resize: ' +w + ',' + h );
19490 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19494 if(typeof w == 'number'){
19496 this.iframe.style.width = w + 'px';
19498 if(typeof h == 'number'){
19500 this.iframe.style.height = h + 'px';
19502 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19509 * Toggles the editor between standard and source edit mode.
19510 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19512 toggleSourceEdit : function(sourceEditMode){
19514 this.sourceEditMode = sourceEditMode === true;
19516 if(this.sourceEditMode){
19518 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19521 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19522 //this.iframe.className = '';
19525 //this.setSize(this.owner.wrap.getSize());
19526 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19533 * Protected method that will not generally be called directly. If you need/want
19534 * custom HTML cleanup, this is the method you should override.
19535 * @param {String} html The HTML to be cleaned
19536 * return {String} The cleaned HTML
19538 cleanHtml : function(html){
19539 html = String(html);
19540 if(html.length > 5){
19541 if(Roo.isSafari){ // strip safari nonsense
19542 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19545 if(html == ' '){
19552 * HTML Editor -> Textarea
19553 * Protected method that will not generally be called directly. Syncs the contents
19554 * of the editor iframe with the textarea.
19556 syncValue : function(){
19557 if(this.initialized){
19558 var bd = (this.doc.body || this.doc.documentElement);
19559 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19560 var html = bd.innerHTML;
19562 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19563 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19565 html = '<div style="'+m[0]+'">' + html + '</div>';
19568 html = this.cleanHtml(html);
19569 // fix up the special chars.. normaly like back quotes in word...
19570 // however we do not want to do this with chinese..
19571 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19572 var cc = b.charCodeAt();
19574 (cc >= 0x4E00 && cc < 0xA000 ) ||
19575 (cc >= 0x3400 && cc < 0x4E00 ) ||
19576 (cc >= 0xf900 && cc < 0xfb00 )
19582 if(this.owner.fireEvent('beforesync', this, html) !== false){
19583 this.el.dom.value = html;
19584 this.owner.fireEvent('sync', this, html);
19590 * Protected method that will not generally be called directly. Pushes the value of the textarea
19591 * into the iframe editor.
19593 pushValue : function(){
19594 if(this.initialized){
19595 var v = this.el.dom.value.trim();
19597 // if(v.length < 1){
19601 if(this.owner.fireEvent('beforepush', this, v) !== false){
19602 var d = (this.doc.body || this.doc.documentElement);
19604 this.cleanUpPaste();
19605 this.el.dom.value = d.innerHTML;
19606 this.owner.fireEvent('push', this, v);
19612 deferFocus : function(){
19613 this.focus.defer(10, this);
19617 focus : function(){
19618 if(this.win && !this.sourceEditMode){
19625 assignDocWin: function()
19627 var iframe = this.iframe;
19630 this.doc = iframe.contentWindow.document;
19631 this.win = iframe.contentWindow;
19633 // if (!Roo.get(this.frameId)) {
19636 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19637 // this.win = Roo.get(this.frameId).dom.contentWindow;
19639 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19643 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19644 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19649 initEditor : function(){
19650 //console.log("INIT EDITOR");
19651 this.assignDocWin();
19655 this.doc.designMode="on";
19657 this.doc.write(this.getDocMarkup());
19660 var dbody = (this.doc.body || this.doc.documentElement);
19661 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19662 // this copies styles from the containing element into thsi one..
19663 // not sure why we need all of this..
19664 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19666 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19667 //ss['background-attachment'] = 'fixed'; // w3c
19668 dbody.bgProperties = 'fixed'; // ie
19669 //Roo.DomHelper.applyStyles(dbody, ss);
19670 Roo.EventManager.on(this.doc, {
19671 //'mousedown': this.onEditorEvent,
19672 'mouseup': this.onEditorEvent,
19673 'dblclick': this.onEditorEvent,
19674 'click': this.onEditorEvent,
19675 'keyup': this.onEditorEvent,
19680 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19682 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19683 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19685 this.initialized = true;
19687 this.owner.fireEvent('initialize', this);
19692 onDestroy : function(){
19698 //for (var i =0; i < this.toolbars.length;i++) {
19699 // // fixme - ask toolbars for heights?
19700 // this.toolbars[i].onDestroy();
19703 //this.wrap.dom.innerHTML = '';
19704 //this.wrap.remove();
19709 onFirstFocus : function(){
19711 this.assignDocWin();
19714 this.activated = true;
19717 if(Roo.isGecko){ // prevent silly gecko errors
19719 var s = this.win.getSelection();
19720 if(!s.focusNode || s.focusNode.nodeType != 3){
19721 var r = s.getRangeAt(0);
19722 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19727 this.execCmd('useCSS', true);
19728 this.execCmd('styleWithCSS', false);
19731 this.owner.fireEvent('activate', this);
19735 adjustFont: function(btn){
19736 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19737 //if(Roo.isSafari){ // safari
19740 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19741 if(Roo.isSafari){ // safari
19742 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19743 v = (v < 10) ? 10 : v;
19744 v = (v > 48) ? 48 : v;
19745 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19750 v = Math.max(1, v+adjust);
19752 this.execCmd('FontSize', v );
19755 onEditorEvent : function(e)
19757 this.owner.fireEvent('editorevent', this, e);
19758 // this.updateToolbar();
19759 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19762 insertTag : function(tg)
19764 // could be a bit smarter... -> wrap the current selected tRoo..
19765 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19767 range = this.createRange(this.getSelection());
19768 var wrappingNode = this.doc.createElement(tg.toLowerCase());
19769 wrappingNode.appendChild(range.extractContents());
19770 range.insertNode(wrappingNode);
19777 this.execCmd("formatblock", tg);
19781 insertText : function(txt)
19785 var range = this.createRange();
19786 range.deleteContents();
19787 //alert(Sender.getAttribute('label'));
19789 range.insertNode(this.doc.createTextNode(txt));
19795 * Executes a Midas editor command on the editor document and performs necessary focus and
19796 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19797 * @param {String} cmd The Midas command
19798 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19800 relayCmd : function(cmd, value){
19802 this.execCmd(cmd, value);
19803 this.owner.fireEvent('editorevent', this);
19804 //this.updateToolbar();
19805 this.owner.deferFocus();
19809 * Executes a Midas editor command directly on the editor document.
19810 * For visual commands, you should use {@link #relayCmd} instead.
19811 * <b>This should only be called after the editor is initialized.</b>
19812 * @param {String} cmd The Midas command
19813 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19815 execCmd : function(cmd, value){
19816 this.doc.execCommand(cmd, false, value === undefined ? null : value);
19823 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19825 * @param {String} text | dom node..
19827 insertAtCursor : function(text)
19832 if(!this.activated){
19838 var r = this.doc.selection.createRange();
19849 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19853 // from jquery ui (MIT licenced)
19855 var win = this.win;
19857 if (win.getSelection && win.getSelection().getRangeAt) {
19858 range = win.getSelection().getRangeAt(0);
19859 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19860 range.insertNode(node);
19861 } else if (win.document.selection && win.document.selection.createRange) {
19862 // no firefox support
19863 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19864 win.document.selection.createRange().pasteHTML(txt);
19866 // no firefox support
19867 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19868 this.execCmd('InsertHTML', txt);
19877 mozKeyPress : function(e){
19879 var c = e.getCharCode(), cmd;
19882 c = String.fromCharCode(c).toLowerCase();
19896 this.cleanUpPaste.defer(100, this);
19904 e.preventDefault();
19912 fixKeys : function(){ // load time branching for fastest keydown performance
19914 return function(e){
19915 var k = e.getKey(), r;
19918 r = this.doc.selection.createRange();
19921 r.pasteHTML('    ');
19928 r = this.doc.selection.createRange();
19930 var target = r.parentElement();
19931 if(!target || target.tagName.toLowerCase() != 'li'){
19933 r.pasteHTML('<br />');
19939 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19940 this.cleanUpPaste.defer(100, this);
19946 }else if(Roo.isOpera){
19947 return function(e){
19948 var k = e.getKey();
19952 this.execCmd('InsertHTML','    ');
19955 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19956 this.cleanUpPaste.defer(100, this);
19961 }else if(Roo.isSafari){
19962 return function(e){
19963 var k = e.getKey();
19967 this.execCmd('InsertText','\t');
19971 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19972 this.cleanUpPaste.defer(100, this);
19980 getAllAncestors: function()
19982 var p = this.getSelectedNode();
19985 a.push(p); // push blank onto stack..
19986 p = this.getParentElement();
19990 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19994 a.push(this.doc.body);
19998 lastSelNode : false,
20001 getSelection : function()
20003 this.assignDocWin();
20004 return Roo.isIE ? this.doc.selection : this.win.getSelection();
20007 getSelectedNode: function()
20009 // this may only work on Gecko!!!
20011 // should we cache this!!!!
20016 var range = this.createRange(this.getSelection()).cloneRange();
20019 var parent = range.parentElement();
20021 var testRange = range.duplicate();
20022 testRange.moveToElementText(parent);
20023 if (testRange.inRange(range)) {
20026 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20029 parent = parent.parentElement;
20034 // is ancestor a text element.
20035 var ac = range.commonAncestorContainer;
20036 if (ac.nodeType == 3) {
20037 ac = ac.parentNode;
20040 var ar = ac.childNodes;
20043 var other_nodes = [];
20044 var has_other_nodes = false;
20045 for (var i=0;i<ar.length;i++) {
20046 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
20049 // fullly contained node.
20051 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20056 // probably selected..
20057 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20058 other_nodes.push(ar[i]);
20062 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
20067 has_other_nodes = true;
20069 if (!nodes.length && other_nodes.length) {
20070 nodes= other_nodes;
20072 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20078 createRange: function(sel)
20080 // this has strange effects when using with
20081 // top toolbar - not sure if it's a great idea.
20082 //this.editor.contentWindow.focus();
20083 if (typeof sel != "undefined") {
20085 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20087 return this.doc.createRange();
20090 return this.doc.createRange();
20093 getParentElement: function()
20096 this.assignDocWin();
20097 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20099 var range = this.createRange(sel);
20102 var p = range.commonAncestorContainer;
20103 while (p.nodeType == 3) { // text node
20114 * Range intersection.. the hard stuff...
20118 * [ -- selected range --- ]
20122 * if end is before start or hits it. fail.
20123 * if start is after end or hits it fail.
20125 * if either hits (but other is outside. - then it's not
20131 // @see http://www.thismuchiknow.co.uk/?p=64.
20132 rangeIntersectsNode : function(range, node)
20134 var nodeRange = node.ownerDocument.createRange();
20136 nodeRange.selectNode(node);
20138 nodeRange.selectNodeContents(node);
20141 var rangeStartRange = range.cloneRange();
20142 rangeStartRange.collapse(true);
20144 var rangeEndRange = range.cloneRange();
20145 rangeEndRange.collapse(false);
20147 var nodeStartRange = nodeRange.cloneRange();
20148 nodeStartRange.collapse(true);
20150 var nodeEndRange = nodeRange.cloneRange();
20151 nodeEndRange.collapse(false);
20153 return rangeStartRange.compareBoundaryPoints(
20154 Range.START_TO_START, nodeEndRange) == -1 &&
20155 rangeEndRange.compareBoundaryPoints(
20156 Range.START_TO_START, nodeStartRange) == 1;
20160 rangeCompareNode : function(range, node)
20162 var nodeRange = node.ownerDocument.createRange();
20164 nodeRange.selectNode(node);
20166 nodeRange.selectNodeContents(node);
20170 range.collapse(true);
20172 nodeRange.collapse(true);
20174 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20175 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
20177 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20179 var nodeIsBefore = ss == 1;
20180 var nodeIsAfter = ee == -1;
20182 if (nodeIsBefore && nodeIsAfter) {
20185 if (!nodeIsBefore && nodeIsAfter) {
20186 return 1; //right trailed.
20189 if (nodeIsBefore && !nodeIsAfter) {
20190 return 2; // left trailed.
20196 // private? - in a new class?
20197 cleanUpPaste : function()
20199 // cleans up the whole document..
20200 Roo.log('cleanuppaste');
20202 this.cleanUpChildren(this.doc.body);
20203 var clean = this.cleanWordChars(this.doc.body.innerHTML);
20204 if (clean != this.doc.body.innerHTML) {
20205 this.doc.body.innerHTML = clean;
20210 cleanWordChars : function(input) {// change the chars to hex code
20211 var he = Roo.HtmlEditorCore;
20213 var output = input;
20214 Roo.each(he.swapCodes, function(sw) {
20215 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20217 output = output.replace(swapper, sw[1]);
20224 cleanUpChildren : function (n)
20226 if (!n.childNodes.length) {
20229 for (var i = n.childNodes.length-1; i > -1 ; i--) {
20230 this.cleanUpChild(n.childNodes[i]);
20237 cleanUpChild : function (node)
20240 //console.log(node);
20241 if (node.nodeName == "#text") {
20242 // clean up silly Windows -- stuff?
20245 if (node.nodeName == "#comment") {
20246 node.parentNode.removeChild(node);
20247 // clean up silly Windows -- stuff?
20250 var lcname = node.tagName.toLowerCase();
20251 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20252 // whitelist of tags..
20254 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20256 node.parentNode.removeChild(node);
20261 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20263 // remove <a name=....> as rendering on yahoo mailer is borked with this.
20264 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20266 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20267 // remove_keep_children = true;
20270 if (remove_keep_children) {
20271 this.cleanUpChildren(node);
20272 // inserts everything just before this node...
20273 while (node.childNodes.length) {
20274 var cn = node.childNodes[0];
20275 node.removeChild(cn);
20276 node.parentNode.insertBefore(cn, node);
20278 node.parentNode.removeChild(node);
20282 if (!node.attributes || !node.attributes.length) {
20283 this.cleanUpChildren(node);
20287 function cleanAttr(n,v)
20290 if (v.match(/^\./) || v.match(/^\//)) {
20293 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20296 if (v.match(/^#/)) {
20299 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20300 node.removeAttribute(n);
20304 var cwhite = this.cwhite;
20305 var cblack = this.cblack;
20307 function cleanStyle(n,v)
20309 if (v.match(/expression/)) { //XSS?? should we even bother..
20310 node.removeAttribute(n);
20314 var parts = v.split(/;/);
20317 Roo.each(parts, function(p) {
20318 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20322 var l = p.split(':').shift().replace(/\s+/g,'');
20323 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20325 if ( cwhite.length && cblack.indexOf(l) > -1) {
20326 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20327 //node.removeAttribute(n);
20331 // only allow 'c whitelisted system attributes'
20332 if ( cwhite.length && cwhite.indexOf(l) < 0) {
20333 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20334 //node.removeAttribute(n);
20344 if (clean.length) {
20345 node.setAttribute(n, clean.join(';'));
20347 node.removeAttribute(n);
20353 for (var i = node.attributes.length-1; i > -1 ; i--) {
20354 var a = node.attributes[i];
20357 if (a.name.toLowerCase().substr(0,2)=='on') {
20358 node.removeAttribute(a.name);
20361 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20362 node.removeAttribute(a.name);
20365 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20366 cleanAttr(a.name,a.value); // fixme..
20369 if (a.name == 'style') {
20370 cleanStyle(a.name,a.value);
20373 /// clean up MS crap..
20374 // tecnically this should be a list of valid class'es..
20377 if (a.name == 'class') {
20378 if (a.value.match(/^Mso/)) {
20379 node.className = '';
20382 if (a.value.match(/body/)) {
20383 node.className = '';
20394 this.cleanUpChildren(node);
20400 * Clean up MS wordisms...
20402 cleanWord : function(node)
20407 this.cleanWord(this.doc.body);
20410 if (node.nodeName == "#text") {
20411 // clean up silly Windows -- stuff?
20414 if (node.nodeName == "#comment") {
20415 node.parentNode.removeChild(node);
20416 // clean up silly Windows -- stuff?
20420 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20421 node.parentNode.removeChild(node);
20425 // remove - but keep children..
20426 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20427 while (node.childNodes.length) {
20428 var cn = node.childNodes[0];
20429 node.removeChild(cn);
20430 node.parentNode.insertBefore(cn, node);
20432 node.parentNode.removeChild(node);
20433 this.iterateChildren(node, this.cleanWord);
20437 if (node.className.length) {
20439 var cn = node.className.split(/\W+/);
20441 Roo.each(cn, function(cls) {
20442 if (cls.match(/Mso[a-zA-Z]+/)) {
20447 node.className = cna.length ? cna.join(' ') : '';
20449 node.removeAttribute("class");
20453 if (node.hasAttribute("lang")) {
20454 node.removeAttribute("lang");
20457 if (node.hasAttribute("style")) {
20459 var styles = node.getAttribute("style").split(";");
20461 Roo.each(styles, function(s) {
20462 if (!s.match(/:/)) {
20465 var kv = s.split(":");
20466 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20469 // what ever is left... we allow.
20472 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20473 if (!nstyle.length) {
20474 node.removeAttribute('style');
20477 this.iterateChildren(node, this.cleanWord);
20483 * iterateChildren of a Node, calling fn each time, using this as the scole..
20484 * @param {DomNode} node node to iterate children of.
20485 * @param {Function} fn method of this class to call on each item.
20487 iterateChildren : function(node, fn)
20489 if (!node.childNodes.length) {
20492 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20493 fn.call(this, node.childNodes[i])
20499 * cleanTableWidths.
20501 * Quite often pasting from word etc.. results in tables with column and widths.
20502 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20505 cleanTableWidths : function(node)
20510 this.cleanTableWidths(this.doc.body);
20515 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20518 Roo.log(node.tagName);
20519 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20520 this.iterateChildren(node, this.cleanTableWidths);
20523 if (node.hasAttribute('width')) {
20524 node.removeAttribute('width');
20528 if (node.hasAttribute("style")) {
20531 var styles = node.getAttribute("style").split(";");
20533 Roo.each(styles, function(s) {
20534 if (!s.match(/:/)) {
20537 var kv = s.split(":");
20538 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20541 // what ever is left... we allow.
20544 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20545 if (!nstyle.length) {
20546 node.removeAttribute('style');
20550 this.iterateChildren(node, this.cleanTableWidths);
20558 domToHTML : function(currentElement, depth, nopadtext) {
20560 depth = depth || 0;
20561 nopadtext = nopadtext || false;
20563 if (!currentElement) {
20564 return this.domToHTML(this.doc.body);
20567 //Roo.log(currentElement);
20569 var allText = false;
20570 var nodeName = currentElement.nodeName;
20571 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20573 if (nodeName == '#text') {
20575 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20580 if (nodeName != 'BODY') {
20583 // Prints the node tagName, such as <A>, <IMG>, etc
20586 for(i = 0; i < currentElement.attributes.length;i++) {
20588 var aname = currentElement.attributes.item(i).name;
20589 if (!currentElement.attributes.item(i).value.length) {
20592 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20595 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20604 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20607 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20612 // Traverse the tree
20614 var currentElementChild = currentElement.childNodes.item(i);
20615 var allText = true;
20616 var innerHTML = '';
20618 while (currentElementChild) {
20619 // Formatting code (indent the tree so it looks nice on the screen)
20620 var nopad = nopadtext;
20621 if (lastnode == 'SPAN') {
20625 if (currentElementChild.nodeName == '#text') {
20626 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20627 toadd = nopadtext ? toadd : toadd.trim();
20628 if (!nopad && toadd.length > 80) {
20629 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20631 innerHTML += toadd;
20634 currentElementChild = currentElement.childNodes.item(i);
20640 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20642 // Recursively traverse the tree structure of the child node
20643 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20644 lastnode = currentElementChild.nodeName;
20646 currentElementChild=currentElement.childNodes.item(i);
20652 // The remaining code is mostly for formatting the tree
20653 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20658 ret+= "</"+tagName+">";
20664 applyBlacklists : function()
20666 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20667 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20671 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20672 if (b.indexOf(tag) > -1) {
20675 this.white.push(tag);
20679 Roo.each(w, function(tag) {
20680 if (b.indexOf(tag) > -1) {
20683 if (this.white.indexOf(tag) > -1) {
20686 this.white.push(tag);
20691 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20692 if (w.indexOf(tag) > -1) {
20695 this.black.push(tag);
20699 Roo.each(b, function(tag) {
20700 if (w.indexOf(tag) > -1) {
20703 if (this.black.indexOf(tag) > -1) {
20706 this.black.push(tag);
20711 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20712 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20716 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20717 if (b.indexOf(tag) > -1) {
20720 this.cwhite.push(tag);
20724 Roo.each(w, function(tag) {
20725 if (b.indexOf(tag) > -1) {
20728 if (this.cwhite.indexOf(tag) > -1) {
20731 this.cwhite.push(tag);
20736 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20737 if (w.indexOf(tag) > -1) {
20740 this.cblack.push(tag);
20744 Roo.each(b, function(tag) {
20745 if (w.indexOf(tag) > -1) {
20748 if (this.cblack.indexOf(tag) > -1) {
20751 this.cblack.push(tag);
20756 setStylesheets : function(stylesheets)
20758 if(typeof(stylesheets) == 'string'){
20759 Roo.get(this.iframe.contentDocument.head).createChild({
20761 rel : 'stylesheet',
20770 Roo.each(stylesheets, function(s) {
20775 Roo.get(_this.iframe.contentDocument.head).createChild({
20777 rel : 'stylesheet',
20786 removeStylesheets : function()
20790 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20795 // hide stuff that is not compatible
20809 * @event specialkey
20813 * @cfg {String} fieldClass @hide
20816 * @cfg {String} focusClass @hide
20819 * @cfg {String} autoCreate @hide
20822 * @cfg {String} inputType @hide
20825 * @cfg {String} invalidClass @hide
20828 * @cfg {String} invalidText @hide
20831 * @cfg {String} msgFx @hide
20834 * @cfg {String} validateOnBlur @hide
20838 Roo.HtmlEditorCore.white = [
20839 'area', 'br', 'img', 'input', 'hr', 'wbr',
20841 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
20842 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
20843 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
20844 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
20845 'table', 'ul', 'xmp',
20847 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
20850 'dir', 'menu', 'ol', 'ul', 'dl',
20856 Roo.HtmlEditorCore.black = [
20857 // 'embed', 'object', // enable - backend responsiblity to clean thiese
20859 'base', 'basefont', 'bgsound', 'blink', 'body',
20860 'frame', 'frameset', 'head', 'html', 'ilayer',
20861 'iframe', 'layer', 'link', 'meta', 'object',
20862 'script', 'style' ,'title', 'xml' // clean later..
20864 Roo.HtmlEditorCore.clean = [
20865 'script', 'style', 'title', 'xml'
20867 Roo.HtmlEditorCore.remove = [
20872 Roo.HtmlEditorCore.ablack = [
20876 Roo.HtmlEditorCore.aclean = [
20877 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
20881 Roo.HtmlEditorCore.pwhite= [
20882 'http', 'https', 'mailto'
20885 // white listed style attributes.
20886 Roo.HtmlEditorCore.cwhite= [
20887 // 'text-align', /// default is to allow most things..
20893 // black listed style attributes.
20894 Roo.HtmlEditorCore.cblack= [
20895 // 'font-size' -- this can be set by the project
20899 Roo.HtmlEditorCore.swapCodes =[
20918 * @class Roo.bootstrap.HtmlEditor
20919 * @extends Roo.bootstrap.TextArea
20920 * Bootstrap HtmlEditor class
20923 * Create a new HtmlEditor
20924 * @param {Object} config The config object
20927 Roo.bootstrap.HtmlEditor = function(config){
20928 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20929 if (!this.toolbars) {
20930 this.toolbars = [];
20932 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20935 * @event initialize
20936 * Fires when the editor is fully initialized (including the iframe)
20937 * @param {HtmlEditor} this
20942 * Fires when the editor is first receives the focus. Any insertion must wait
20943 * until after this event.
20944 * @param {HtmlEditor} this
20948 * @event beforesync
20949 * Fires before the textarea is updated with content from the editor iframe. Return false
20950 * to cancel the sync.
20951 * @param {HtmlEditor} this
20952 * @param {String} html
20956 * @event beforepush
20957 * Fires before the iframe editor is updated with content from the textarea. Return false
20958 * to cancel the push.
20959 * @param {HtmlEditor} this
20960 * @param {String} html
20965 * Fires when the textarea is updated with content from the editor iframe.
20966 * @param {HtmlEditor} this
20967 * @param {String} html
20972 * Fires when the iframe editor is updated with content from the textarea.
20973 * @param {HtmlEditor} this
20974 * @param {String} html
20978 * @event editmodechange
20979 * Fires when the editor switches edit modes
20980 * @param {HtmlEditor} this
20981 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20983 editmodechange: true,
20985 * @event editorevent
20986 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20987 * @param {HtmlEditor} this
20991 * @event firstfocus
20992 * Fires when on first focus - needed by toolbars..
20993 * @param {HtmlEditor} this
20998 * Auto save the htmlEditor value as a file into Events
20999 * @param {HtmlEditor} this
21003 * @event savedpreview
21004 * preview the saved version of htmlEditor
21005 * @param {HtmlEditor} this
21012 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
21016 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21021 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21026 * @cfg {Number} height (in pixels)
21030 * @cfg {Number} width (in pixels)
21035 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21038 stylesheets: false,
21043 // private properties
21044 validationEvent : false,
21046 initialized : false,
21049 onFocus : Roo.emptyFn,
21051 hideMode:'offsets',
21054 tbContainer : false,
21056 toolbarContainer :function() {
21057 return this.wrap.select('.x-html-editor-tb',true).first();
21061 * Protected method that will not generally be called directly. It
21062 * is called when the editor creates its toolbar. Override this method if you need to
21063 * add custom toolbar buttons.
21064 * @param {HtmlEditor} editor
21066 createToolbar : function(){
21068 Roo.log("create toolbars");
21070 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21071 this.toolbars[0].render(this.toolbarContainer());
21075 // if (!editor.toolbars || !editor.toolbars.length) {
21076 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21079 // for (var i =0 ; i < editor.toolbars.length;i++) {
21080 // editor.toolbars[i] = Roo.factory(
21081 // typeof(editor.toolbars[i]) == 'string' ?
21082 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
21083 // Roo.bootstrap.HtmlEditor);
21084 // editor.toolbars[i].init(editor);
21090 onRender : function(ct, position)
21092 // Roo.log("Call onRender: " + this.xtype);
21094 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21096 this.wrap = this.inputEl().wrap({
21097 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21100 this.editorcore.onRender(ct, position);
21102 if (this.resizable) {
21103 this.resizeEl = new Roo.Resizable(this.wrap, {
21107 minHeight : this.height,
21108 height: this.height,
21109 handles : this.resizable,
21112 resize : function(r, w, h) {
21113 _t.onResize(w,h); // -something
21119 this.createToolbar(this);
21122 if(!this.width && this.resizable){
21123 this.setSize(this.wrap.getSize());
21125 if (this.resizeEl) {
21126 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21127 // should trigger onReize..
21133 onResize : function(w, h)
21135 Roo.log('resize: ' +w + ',' + h );
21136 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21140 if(this.inputEl() ){
21141 if(typeof w == 'number'){
21142 var aw = w - this.wrap.getFrameWidth('lr');
21143 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21146 if(typeof h == 'number'){
21147 var tbh = -11; // fixme it needs to tool bar size!
21148 for (var i =0; i < this.toolbars.length;i++) {
21149 // fixme - ask toolbars for heights?
21150 tbh += this.toolbars[i].el.getHeight();
21151 //if (this.toolbars[i].footer) {
21152 // tbh += this.toolbars[i].footer.el.getHeight();
21160 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21161 ah -= 5; // knock a few pixes off for look..
21162 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21166 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21167 this.editorcore.onResize(ew,eh);
21172 * Toggles the editor between standard and source edit mode.
21173 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21175 toggleSourceEdit : function(sourceEditMode)
21177 this.editorcore.toggleSourceEdit(sourceEditMode);
21179 if(this.editorcore.sourceEditMode){
21180 Roo.log('editor - showing textarea');
21183 // Roo.log(this.syncValue());
21185 this.inputEl().removeClass(['hide', 'x-hidden']);
21186 this.inputEl().dom.removeAttribute('tabIndex');
21187 this.inputEl().focus();
21189 Roo.log('editor - hiding textarea');
21191 // Roo.log(this.pushValue());
21194 this.inputEl().addClass(['hide', 'x-hidden']);
21195 this.inputEl().dom.setAttribute('tabIndex', -1);
21196 //this.deferFocus();
21199 if(this.resizable){
21200 this.setSize(this.wrap.getSize());
21203 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21206 // private (for BoxComponent)
21207 adjustSize : Roo.BoxComponent.prototype.adjustSize,
21209 // private (for BoxComponent)
21210 getResizeEl : function(){
21214 // private (for BoxComponent)
21215 getPositionEl : function(){
21220 initEvents : function(){
21221 this.originalValue = this.getValue();
21225 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21228 // markInvalid : Roo.emptyFn,
21230 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21233 // clearInvalid : Roo.emptyFn,
21235 setValue : function(v){
21236 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21237 this.editorcore.pushValue();
21242 deferFocus : function(){
21243 this.focus.defer(10, this);
21247 focus : function(){
21248 this.editorcore.focus();
21254 onDestroy : function(){
21260 for (var i =0; i < this.toolbars.length;i++) {
21261 // fixme - ask toolbars for heights?
21262 this.toolbars[i].onDestroy();
21265 this.wrap.dom.innerHTML = '';
21266 this.wrap.remove();
21271 onFirstFocus : function(){
21272 //Roo.log("onFirstFocus");
21273 this.editorcore.onFirstFocus();
21274 for (var i =0; i < this.toolbars.length;i++) {
21275 this.toolbars[i].onFirstFocus();
21281 syncValue : function()
21283 this.editorcore.syncValue();
21286 pushValue : function()
21288 this.editorcore.pushValue();
21292 // hide stuff that is not compatible
21306 * @event specialkey
21310 * @cfg {String} fieldClass @hide
21313 * @cfg {String} focusClass @hide
21316 * @cfg {String} autoCreate @hide
21319 * @cfg {String} inputType @hide
21322 * @cfg {String} invalidClass @hide
21325 * @cfg {String} invalidText @hide
21328 * @cfg {String} msgFx @hide
21331 * @cfg {String} validateOnBlur @hide
21340 Roo.namespace('Roo.bootstrap.htmleditor');
21342 * @class Roo.bootstrap.HtmlEditorToolbar1
21347 new Roo.bootstrap.HtmlEditor({
21350 new Roo.bootstrap.HtmlEditorToolbar1({
21351 disable : { fonts: 1 , format: 1, ..., ... , ...],
21357 * @cfg {Object} disable List of elements to disable..
21358 * @cfg {Array} btns List of additional buttons.
21362 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21365 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21368 Roo.apply(this, config);
21370 // default disabled, based on 'good practice'..
21371 this.disable = this.disable || {};
21372 Roo.applyIf(this.disable, {
21375 specialElements : true
21377 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21379 this.editor = config.editor;
21380 this.editorcore = config.editor.editorcore;
21382 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21384 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21385 // dont call parent... till later.
21387 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
21392 editorcore : false,
21397 "h1","h2","h3","h4","h5","h6",
21399 "abbr", "acronym", "address", "cite", "samp", "var",
21403 onRender : function(ct, position)
21405 // Roo.log("Call onRender: " + this.xtype);
21407 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21409 this.el.dom.style.marginBottom = '0';
21411 var editorcore = this.editorcore;
21412 var editor= this.editor;
21415 var btn = function(id,cmd , toggle, handler){
21417 var event = toggle ? 'toggle' : 'click';
21422 xns: Roo.bootstrap,
21425 enableToggle:toggle !== false,
21427 pressed : toggle ? false : null,
21430 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21431 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21440 xns: Roo.bootstrap,
21441 glyphicon : 'font',
21445 xns: Roo.bootstrap,
21449 Roo.each(this.formats, function(f) {
21450 style.menu.items.push({
21452 xns: Roo.bootstrap,
21453 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21458 editorcore.insertTag(this.tagname);
21465 children.push(style);
21468 btn('bold',false,true);
21469 btn('italic',false,true);
21470 btn('align-left', 'justifyleft',true);
21471 btn('align-center', 'justifycenter',true);
21472 btn('align-right' , 'justifyright',true);
21473 btn('link', false, false, function(btn) {
21474 //Roo.log("create link?");
21475 var url = prompt(this.createLinkText, this.defaultLinkValue);
21476 if(url && url != 'http:/'+'/'){
21477 this.editorcore.relayCmd('createlink', url);
21480 btn('list','insertunorderedlist',true);
21481 btn('pencil', false,true, function(btn){
21484 this.toggleSourceEdit(btn.pressed);
21490 xns: Roo.bootstrap,
21495 xns: Roo.bootstrap,
21500 cog.menu.items.push({
21502 xns: Roo.bootstrap,
21503 html : Clean styles,
21508 editorcore.insertTag(this.tagname);
21517 this.xtype = 'NavSimplebar';
21519 for(var i=0;i< children.length;i++) {
21521 this.buttons.add(this.addxtypeChild(children[i]));
21525 editor.on('editorevent', this.updateToolbar, this);
21527 onBtnClick : function(id)
21529 this.editorcore.relayCmd(id);
21530 this.editorcore.focus();
21534 * Protected method that will not generally be called directly. It triggers
21535 * a toolbar update by reading the markup state of the current selection in the editor.
21537 updateToolbar: function(){
21539 if(!this.editorcore.activated){
21540 this.editor.onFirstFocus(); // is this neeed?
21544 var btns = this.buttons;
21545 var doc = this.editorcore.doc;
21546 btns.get('bold').setActive(doc.queryCommandState('bold'));
21547 btns.get('italic').setActive(doc.queryCommandState('italic'));
21548 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21550 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21551 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21552 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21554 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21555 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21558 var ans = this.editorcore.getAllAncestors();
21559 if (this.formatCombo) {
21562 var store = this.formatCombo.store;
21563 this.formatCombo.setValue("");
21564 for (var i =0; i < ans.length;i++) {
21565 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21567 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21575 // hides menus... - so this cant be on a menu...
21576 Roo.bootstrap.MenuMgr.hideAll();
21578 Roo.bootstrap.MenuMgr.hideAll();
21579 //this.editorsyncValue();
21581 onFirstFocus: function() {
21582 this.buttons.each(function(item){
21586 toggleSourceEdit : function(sourceEditMode){
21589 if(sourceEditMode){
21590 Roo.log("disabling buttons");
21591 this.buttons.each( function(item){
21592 if(item.cmd != 'pencil'){
21598 Roo.log("enabling buttons");
21599 if(this.editorcore.initialized){
21600 this.buttons.each( function(item){
21606 Roo.log("calling toggole on editor");
21607 // tell the editor that it's been pressed..
21608 this.editor.toggleSourceEdit(sourceEditMode);
21618 * @class Roo.bootstrap.Table.AbstractSelectionModel
21619 * @extends Roo.util.Observable
21620 * Abstract base class for grid SelectionModels. It provides the interface that should be
21621 * implemented by descendant classes. This class should not be directly instantiated.
21624 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21625 this.locked = false;
21626 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21630 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21631 /** @ignore Called by the grid automatically. Do not call directly. */
21632 init : function(grid){
21638 * Locks the selections.
21641 this.locked = true;
21645 * Unlocks the selections.
21647 unlock : function(){
21648 this.locked = false;
21652 * Returns true if the selections are locked.
21653 * @return {Boolean}
21655 isLocked : function(){
21656 return this.locked;
21660 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21661 * @class Roo.bootstrap.Table.RowSelectionModel
21662 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21663 * It supports multiple selections and keyboard selection/navigation.
21665 * @param {Object} config
21668 Roo.bootstrap.Table.RowSelectionModel = function(config){
21669 Roo.apply(this, config);
21670 this.selections = new Roo.util.MixedCollection(false, function(o){
21675 this.lastActive = false;
21679 * @event selectionchange
21680 * Fires when the selection changes
21681 * @param {SelectionModel} this
21683 "selectionchange" : true,
21685 * @event afterselectionchange
21686 * Fires after the selection changes (eg. by key press or clicking)
21687 * @param {SelectionModel} this
21689 "afterselectionchange" : true,
21691 * @event beforerowselect
21692 * Fires when a row is selected being selected, return false to cancel.
21693 * @param {SelectionModel} this
21694 * @param {Number} rowIndex The selected index
21695 * @param {Boolean} keepExisting False if other selections will be cleared
21697 "beforerowselect" : true,
21700 * Fires when a row is selected.
21701 * @param {SelectionModel} this
21702 * @param {Number} rowIndex The selected index
21703 * @param {Roo.data.Record} r The record
21705 "rowselect" : true,
21707 * @event rowdeselect
21708 * Fires when a row is deselected.
21709 * @param {SelectionModel} this
21710 * @param {Number} rowIndex The selected index
21712 "rowdeselect" : true
21714 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21715 this.locked = false;
21718 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21720 * @cfg {Boolean} singleSelect
21721 * True to allow selection of only one row at a time (defaults to false)
21723 singleSelect : false,
21726 initEvents : function(){
21728 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21729 this.grid.on("mousedown", this.handleMouseDown, this);
21730 }else{ // allow click to work like normal
21731 this.grid.on("rowclick", this.handleDragableRowClick, this);
21734 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21735 "up" : function(e){
21737 this.selectPrevious(e.shiftKey);
21738 }else if(this.last !== false && this.lastActive !== false){
21739 var last = this.last;
21740 this.selectRange(this.last, this.lastActive-1);
21741 this.grid.getView().focusRow(this.lastActive);
21742 if(last !== false){
21746 this.selectFirstRow();
21748 this.fireEvent("afterselectionchange", this);
21750 "down" : function(e){
21752 this.selectNext(e.shiftKey);
21753 }else if(this.last !== false && this.lastActive !== false){
21754 var last = this.last;
21755 this.selectRange(this.last, this.lastActive+1);
21756 this.grid.getView().focusRow(this.lastActive);
21757 if(last !== false){
21761 this.selectFirstRow();
21763 this.fireEvent("afterselectionchange", this);
21768 var view = this.grid.view;
21769 view.on("refresh", this.onRefresh, this);
21770 view.on("rowupdated", this.onRowUpdated, this);
21771 view.on("rowremoved", this.onRemove, this);
21775 onRefresh : function(){
21776 var ds = this.grid.dataSource, i, v = this.grid.view;
21777 var s = this.selections;
21778 s.each(function(r){
21779 if((i = ds.indexOfId(r.id)) != -1){
21788 onRemove : function(v, index, r){
21789 this.selections.remove(r);
21793 onRowUpdated : function(v, index, r){
21794 if(this.isSelected(r)){
21795 v.onRowSelect(index);
21801 * @param {Array} records The records to select
21802 * @param {Boolean} keepExisting (optional) True to keep existing selections
21804 selectRecords : function(records, keepExisting){
21806 this.clearSelections();
21808 var ds = this.grid.dataSource;
21809 for(var i = 0, len = records.length; i < len; i++){
21810 this.selectRow(ds.indexOf(records[i]), true);
21815 * Gets the number of selected rows.
21818 getCount : function(){
21819 return this.selections.length;
21823 * Selects the first row in the grid.
21825 selectFirstRow : function(){
21830 * Select the last row.
21831 * @param {Boolean} keepExisting (optional) True to keep existing selections
21833 selectLastRow : function(keepExisting){
21834 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21838 * Selects the row immediately following the last selected row.
21839 * @param {Boolean} keepExisting (optional) True to keep existing selections
21841 selectNext : function(keepExisting){
21842 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21843 this.selectRow(this.last+1, keepExisting);
21844 this.grid.getView().focusRow(this.last);
21849 * Selects the row that precedes the last selected row.
21850 * @param {Boolean} keepExisting (optional) True to keep existing selections
21852 selectPrevious : function(keepExisting){
21854 this.selectRow(this.last-1, keepExisting);
21855 this.grid.getView().focusRow(this.last);
21860 * Returns the selected records
21861 * @return {Array} Array of selected records
21863 getSelections : function(){
21864 return [].concat(this.selections.items);
21868 * Returns the first selected record.
21871 getSelected : function(){
21872 return this.selections.itemAt(0);
21877 * Clears all selections.
21879 clearSelections : function(fast){
21884 var ds = this.grid.dataSource;
21885 var s = this.selections;
21886 s.each(function(r){
21887 this.deselectRow(ds.indexOfId(r.id));
21891 this.selections.clear();
21898 * Selects all rows.
21900 selectAll : function(){
21904 this.selections.clear();
21905 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21906 this.selectRow(i, true);
21911 * Returns True if there is a selection.
21912 * @return {Boolean}
21914 hasSelection : function(){
21915 return this.selections.length > 0;
21919 * Returns True if the specified row is selected.
21920 * @param {Number/Record} record The record or index of the record to check
21921 * @return {Boolean}
21923 isSelected : function(index){
21924 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21925 return (r && this.selections.key(r.id) ? true : false);
21929 * Returns True if the specified record id is selected.
21930 * @param {String} id The id of record to check
21931 * @return {Boolean}
21933 isIdSelected : function(id){
21934 return (this.selections.key(id) ? true : false);
21938 handleMouseDown : function(e, t){
21939 var view = this.grid.getView(), rowIndex;
21940 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21943 if(e.shiftKey && this.last !== false){
21944 var last = this.last;
21945 this.selectRange(last, rowIndex, e.ctrlKey);
21946 this.last = last; // reset the last
21947 view.focusRow(rowIndex);
21949 var isSelected = this.isSelected(rowIndex);
21950 if(e.button !== 0 && isSelected){
21951 view.focusRow(rowIndex);
21952 }else if(e.ctrlKey && isSelected){
21953 this.deselectRow(rowIndex);
21954 }else if(!isSelected){
21955 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21956 view.focusRow(rowIndex);
21959 this.fireEvent("afterselectionchange", this);
21962 handleDragableRowClick : function(grid, rowIndex, e)
21964 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21965 this.selectRow(rowIndex, false);
21966 grid.view.focusRow(rowIndex);
21967 this.fireEvent("afterselectionchange", this);
21972 * Selects multiple rows.
21973 * @param {Array} rows Array of the indexes of the row to select
21974 * @param {Boolean} keepExisting (optional) True to keep existing selections
21976 selectRows : function(rows, keepExisting){
21978 this.clearSelections();
21980 for(var i = 0, len = rows.length; i < len; i++){
21981 this.selectRow(rows[i], true);
21986 * Selects a range of rows. All rows in between startRow and endRow are also selected.
21987 * @param {Number} startRow The index of the first row in the range
21988 * @param {Number} endRow The index of the last row in the range
21989 * @param {Boolean} keepExisting (optional) True to retain existing selections
21991 selectRange : function(startRow, endRow, keepExisting){
21996 this.clearSelections();
21998 if(startRow <= endRow){
21999 for(var i = startRow; i <= endRow; i++){
22000 this.selectRow(i, true);
22003 for(var i = startRow; i >= endRow; i--){
22004 this.selectRow(i, true);
22010 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22011 * @param {Number} startRow The index of the first row in the range
22012 * @param {Number} endRow The index of the last row in the range
22014 deselectRange : function(startRow, endRow, preventViewNotify){
22018 for(var i = startRow; i <= endRow; i++){
22019 this.deselectRow(i, preventViewNotify);
22025 * @param {Number} row The index of the row to select
22026 * @param {Boolean} keepExisting (optional) True to keep existing selections
22028 selectRow : function(index, keepExisting, preventViewNotify){
22029 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22032 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22033 if(!keepExisting || this.singleSelect){
22034 this.clearSelections();
22036 var r = this.grid.dataSource.getAt(index);
22037 this.selections.add(r);
22038 this.last = this.lastActive = index;
22039 if(!preventViewNotify){
22040 this.grid.getView().onRowSelect(index);
22042 this.fireEvent("rowselect", this, index, r);
22043 this.fireEvent("selectionchange", this);
22049 * @param {Number} row The index of the row to deselect
22051 deselectRow : function(index, preventViewNotify){
22055 if(this.last == index){
22058 if(this.lastActive == index){
22059 this.lastActive = false;
22061 var r = this.grid.dataSource.getAt(index);
22062 this.selections.remove(r);
22063 if(!preventViewNotify){
22064 this.grid.getView().onRowDeselect(index);
22066 this.fireEvent("rowdeselect", this, index);
22067 this.fireEvent("selectionchange", this);
22071 restoreLast : function(){
22073 this.last = this._last;
22078 acceptsNav : function(row, col, cm){
22079 return !cm.isHidden(col) && cm.isCellEditable(col, row);
22083 onEditorKey : function(field, e){
22084 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22089 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22091 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22093 }else if(k == e.ENTER && !e.ctrlKey){
22097 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22099 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22101 }else if(k == e.ESC){
22105 g.startEditing(newCell[0], newCell[1]);
22110 * Ext JS Library 1.1.1
22111 * Copyright(c) 2006-2007, Ext JS, LLC.
22113 * Originally Released Under LGPL - original licence link has changed is not relivant.
22116 * <script type="text/javascript">
22120 * @class Roo.bootstrap.PagingToolbar
22121 * @extends Roo.bootstrap.NavSimplebar
22122 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22124 * Create a new PagingToolbar
22125 * @param {Object} config The config object
22126 * @param {Roo.data.Store} store
22128 Roo.bootstrap.PagingToolbar = function(config)
22130 // old args format still supported... - xtype is prefered..
22131 // created from xtype...
22133 this.ds = config.dataSource;
22135 if (config.store && !this.ds) {
22136 this.store= Roo.factory(config.store, Roo.data);
22137 this.ds = this.store;
22138 this.ds.xmodule = this.xmodule || false;
22141 this.toolbarItems = [];
22142 if (config.items) {
22143 this.toolbarItems = config.items;
22146 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22151 this.bind(this.ds);
22154 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22158 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22160 * @cfg {Roo.data.Store} dataSource
22161 * The underlying data store providing the paged data
22164 * @cfg {String/HTMLElement/Element} container
22165 * container The id or element that will contain the toolbar
22168 * @cfg {Boolean} displayInfo
22169 * True to display the displayMsg (defaults to false)
22172 * @cfg {Number} pageSize
22173 * The number of records to display per page (defaults to 20)
22177 * @cfg {String} displayMsg
22178 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22180 displayMsg : 'Displaying {0} - {1} of {2}',
22182 * @cfg {String} emptyMsg
22183 * The message to display when no records are found (defaults to "No data to display")
22185 emptyMsg : 'No data to display',
22187 * Customizable piece of the default paging text (defaults to "Page")
22190 beforePageText : "Page",
22192 * Customizable piece of the default paging text (defaults to "of %0")
22195 afterPageText : "of {0}",
22197 * Customizable piece of the default paging text (defaults to "First Page")
22200 firstText : "First Page",
22202 * Customizable piece of the default paging text (defaults to "Previous Page")
22205 prevText : "Previous Page",
22207 * Customizable piece of the default paging text (defaults to "Next Page")
22210 nextText : "Next Page",
22212 * Customizable piece of the default paging text (defaults to "Last Page")
22215 lastText : "Last Page",
22217 * Customizable piece of the default paging text (defaults to "Refresh")
22220 refreshText : "Refresh",
22224 onRender : function(ct, position)
22226 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22227 this.navgroup.parentId = this.id;
22228 this.navgroup.onRender(this.el, null);
22229 // add the buttons to the navgroup
22231 if(this.displayInfo){
22232 Roo.log(this.el.select('ul.navbar-nav',true).first());
22233 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22234 this.displayEl = this.el.select('.x-paging-info', true).first();
22235 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22236 // this.displayEl = navel.el.select('span',true).first();
22242 Roo.each(_this.buttons, function(e){ // this might need to use render????
22243 Roo.factory(e).onRender(_this.el, null);
22247 Roo.each(_this.toolbarItems, function(e) {
22248 _this.navgroup.addItem(e);
22252 this.first = this.navgroup.addItem({
22253 tooltip: this.firstText,
22255 icon : 'fa fa-backward',
22257 preventDefault: true,
22258 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22261 this.prev = this.navgroup.addItem({
22262 tooltip: this.prevText,
22264 icon : 'fa fa-step-backward',
22266 preventDefault: true,
22267 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
22269 //this.addSeparator();
22272 var field = this.navgroup.addItem( {
22274 cls : 'x-paging-position',
22276 html : this.beforePageText +
22277 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22278 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
22281 this.field = field.el.select('input', true).first();
22282 this.field.on("keydown", this.onPagingKeydown, this);
22283 this.field.on("focus", function(){this.dom.select();});
22286 this.afterTextEl = field.el.select('.x-paging-after',true).first();
22287 //this.field.setHeight(18);
22288 //this.addSeparator();
22289 this.next = this.navgroup.addItem({
22290 tooltip: this.nextText,
22292 html : ' <i class="fa fa-step-forward">',
22294 preventDefault: true,
22295 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
22297 this.last = this.navgroup.addItem({
22298 tooltip: this.lastText,
22299 icon : 'fa fa-forward',
22302 preventDefault: true,
22303 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
22305 //this.addSeparator();
22306 this.loading = this.navgroup.addItem({
22307 tooltip: this.refreshText,
22308 icon: 'fa fa-refresh',
22309 preventDefault: true,
22310 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22316 updateInfo : function(){
22317 if(this.displayEl){
22318 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22319 var msg = count == 0 ?
22323 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
22325 this.displayEl.update(msg);
22330 onLoad : function(ds, r, o){
22331 this.cursor = o.params ? o.params.start : 0;
22332 var d = this.getPageData(),
22336 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22337 this.field.dom.value = ap;
22338 this.first.setDisabled(ap == 1);
22339 this.prev.setDisabled(ap == 1);
22340 this.next.setDisabled(ap == ps);
22341 this.last.setDisabled(ap == ps);
22342 this.loading.enable();
22347 getPageData : function(){
22348 var total = this.ds.getTotalCount();
22351 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22352 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22357 onLoadError : function(){
22358 this.loading.enable();
22362 onPagingKeydown : function(e){
22363 var k = e.getKey();
22364 var d = this.getPageData();
22366 var v = this.field.dom.value, pageNum;
22367 if(!v || isNaN(pageNum = parseInt(v, 10))){
22368 this.field.dom.value = d.activePage;
22371 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22372 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22375 else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
22377 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22378 this.field.dom.value = pageNum;
22379 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22382 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22384 var v = this.field.dom.value, pageNum;
22385 var increment = (e.shiftKey) ? 10 : 1;
22386 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22389 if(!v || isNaN(pageNum = parseInt(v, 10))) {
22390 this.field.dom.value = d.activePage;
22393 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22395 this.field.dom.value = parseInt(v, 10) + increment;
22396 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22397 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22404 beforeLoad : function(){
22406 this.loading.disable();
22411 onClick : function(which){
22420 ds.load({params:{start: 0, limit: this.pageSize}});
22423 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22426 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22429 var total = ds.getTotalCount();
22430 var extra = total % this.pageSize;
22431 var lastStart = extra ? (total - extra) : total-this.pageSize;
22432 ds.load({params:{start: lastStart, limit: this.pageSize}});
22435 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22441 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22442 * @param {Roo.data.Store} store The data store to unbind
22444 unbind : function(ds){
22445 ds.un("beforeload", this.beforeLoad, this);
22446 ds.un("load", this.onLoad, this);
22447 ds.un("loadexception", this.onLoadError, this);
22448 ds.un("remove", this.updateInfo, this);
22449 ds.un("add", this.updateInfo, this);
22450 this.ds = undefined;
22454 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22455 * @param {Roo.data.Store} store The data store to bind
22457 bind : function(ds){
22458 ds.on("beforeload", this.beforeLoad, this);
22459 ds.on("load", this.onLoad, this);
22460 ds.on("loadexception", this.onLoadError, this);
22461 ds.on("remove", this.updateInfo, this);
22462 ds.on("add", this.updateInfo, this);
22473 * @class Roo.bootstrap.MessageBar
22474 * @extends Roo.bootstrap.Component
22475 * Bootstrap MessageBar class
22476 * @cfg {String} html contents of the MessageBar
22477 * @cfg {String} weight (info | success | warning | danger) default info
22478 * @cfg {String} beforeClass insert the bar before the given class
22479 * @cfg {Boolean} closable (true | false) default false
22480 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22483 * Create a new Element
22484 * @param {Object} config The config object
22487 Roo.bootstrap.MessageBar = function(config){
22488 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22491 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22497 beforeClass: 'bootstrap-sticky-wrap',
22499 getAutoCreate : function(){
22503 cls: 'alert alert-dismissable alert-' + this.weight,
22508 html: this.html || ''
22514 cfg.cls += ' alert-messages-fixed';
22528 onRender : function(ct, position)
22530 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22533 var cfg = Roo.apply({}, this.getAutoCreate());
22537 cfg.cls += ' ' + this.cls;
22540 cfg.style = this.style;
22542 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22544 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22547 this.el.select('>button.close').on('click', this.hide, this);
22553 if (!this.rendered) {
22559 this.fireEvent('show', this);
22565 if (!this.rendered) {
22571 this.fireEvent('hide', this);
22574 update : function()
22576 // var e = this.el.dom.firstChild;
22578 // if(this.closable){
22579 // e = e.nextSibling;
22582 // e.data = this.html || '';
22584 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22600 * @class Roo.bootstrap.Graph
22601 * @extends Roo.bootstrap.Component
22602 * Bootstrap Graph class
22606 @cfg {String} graphtype bar | vbar | pie
22607 @cfg {number} g_x coodinator | centre x (pie)
22608 @cfg {number} g_y coodinator | centre y (pie)
22609 @cfg {number} g_r radius (pie)
22610 @cfg {number} g_height height of the chart (respected by all elements in the set)
22611 @cfg {number} g_width width of the chart (respected by all elements in the set)
22612 @cfg {Object} title The title of the chart
22615 -opts (object) options for the chart
22617 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22618 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22620 o colors (array) colors be used repeatedly to plot the bars. If multicolumn bar is used each sequence of bars with use a different color.
22621 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22623 o stretch (boolean)
22625 -opts (object) options for the pie
22628 o startAngle (number)
22629 o endAngle (number)
22633 * Create a new Input
22634 * @param {Object} config The config object
22637 Roo.bootstrap.Graph = function(config){
22638 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22644 * The img click event for the img.
22645 * @param {Roo.EventObject} e
22651 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22662 //g_colors: this.colors,
22669 getAutoCreate : function(){
22680 onRender : function(ct,position){
22681 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22682 this.raphael = Raphael(this.el.dom);
22684 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22685 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22686 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22687 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22689 r.text(160, 10, "Single Series Chart").attr(txtattr);
22690 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22691 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22692 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22694 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22695 r.barchart(330, 10, 300, 220, data1);
22696 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22697 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22700 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22701 // r.barchart(30, 30, 560, 250, xdata, {
22702 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22703 // axis : "0 0 1 1",
22704 // axisxlabels : xdata
22705 // //yvalues : cols,
22708 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22710 // this.load(null,xdata,{
22711 // axis : "0 0 1 1",
22712 // axisxlabels : xdata
22717 load : function(graphtype,xdata,opts){
22718 this.raphael.clear();
22720 graphtype = this.graphtype;
22725 var r = this.raphael,
22726 fin = function () {
22727 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22729 fout = function () {
22730 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22732 pfin = function() {
22733 this.sector.stop();
22734 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22737 this.label[0].stop();
22738 this.label[0].attr({ r: 7.5 });
22739 this.label[1].attr({ "font-weight": 800 });
22742 pfout = function() {
22743 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22746 this.label[0].animate({ r: 5 }, 500, "bounce");
22747 this.label[1].attr({ "font-weight": 400 });
22753 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22756 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22759 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
22760 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22762 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22769 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22774 setTitle: function(o)
22779 initEvents: function() {
22782 this.el.on('click', this.onClick, this);
22786 onClick : function(e)
22788 Roo.log('img onclick');
22789 this.fireEvent('click', this, e);
22801 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22804 * @class Roo.bootstrap.dash.NumberBox
22805 * @extends Roo.bootstrap.Component
22806 * Bootstrap NumberBox class
22807 * @cfg {String} headline Box headline
22808 * @cfg {String} content Box content
22809 * @cfg {String} icon Box icon
22810 * @cfg {String} footer Footer text
22811 * @cfg {String} fhref Footer href
22814 * Create a new NumberBox
22815 * @param {Object} config The config object
22819 Roo.bootstrap.dash.NumberBox = function(config){
22820 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22824 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
22833 getAutoCreate : function(){
22837 cls : 'small-box ',
22845 cls : 'roo-headline',
22846 html : this.headline
22850 cls : 'roo-content',
22851 html : this.content
22865 cls : 'ion ' + this.icon
22874 cls : 'small-box-footer',
22875 href : this.fhref || '#',
22879 cfg.cn.push(footer);
22886 onRender : function(ct,position){
22887 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22894 setHeadline: function (value)
22896 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22899 setFooter: function (value, href)
22901 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22904 this.el.select('a.small-box-footer',true).first().attr('href', href);
22909 setContent: function (value)
22911 this.el.select('.roo-content',true).first().dom.innerHTML = value;
22914 initEvents: function()
22928 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22931 * @class Roo.bootstrap.dash.TabBox
22932 * @extends Roo.bootstrap.Component
22933 * Bootstrap TabBox class
22934 * @cfg {String} title Title of the TabBox
22935 * @cfg {String} icon Icon of the TabBox
22936 * @cfg {Boolean} showtabs (true|false) show the tabs default true
22937 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22940 * Create a new TabBox
22941 * @param {Object} config The config object
22945 Roo.bootstrap.dash.TabBox = function(config){
22946 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22951 * When a pane is added
22952 * @param {Roo.bootstrap.dash.TabPane} pane
22956 * @event activatepane
22957 * When a pane is activated
22958 * @param {Roo.bootstrap.dash.TabPane} pane
22960 "activatepane" : true
22968 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
22973 tabScrollable : false,
22975 getChildContainer : function()
22977 return this.el.select('.tab-content', true).first();
22980 getAutoCreate : function(){
22984 cls: 'pull-left header',
22992 cls: 'fa ' + this.icon
22998 cls: 'nav nav-tabs pull-right',
23004 if(this.tabScrollable){
23011 cls: 'nav nav-tabs pull-right',
23022 cls: 'nav-tabs-custom',
23027 cls: 'tab-content no-padding',
23035 initEvents : function()
23037 //Roo.log('add add pane handler');
23038 this.on('addpane', this.onAddPane, this);
23041 * Updates the box title
23042 * @param {String} html to set the title to.
23044 setTitle : function(value)
23046 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23048 onAddPane : function(pane)
23050 this.panes.push(pane);
23051 //Roo.log('addpane');
23053 // tabs are rendere left to right..
23054 if(!this.showtabs){
23058 var ctr = this.el.select('.nav-tabs', true).first();
23061 var existing = ctr.select('.nav-tab',true);
23062 var qty = existing.getCount();;
23065 var tab = ctr.createChild({
23067 cls : 'nav-tab' + (qty ? '' : ' active'),
23075 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23078 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23080 pane.el.addClass('active');
23085 onTabClick : function(ev,un,ob,pane)
23087 //Roo.log('tab - prev default');
23088 ev.preventDefault();
23091 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23092 pane.tab.addClass('active');
23093 //Roo.log(pane.title);
23094 this.getChildContainer().select('.tab-pane',true).removeClass('active');
23095 // technically we should have a deactivate event.. but maybe add later.
23096 // and it should not de-activate the selected tab...
23097 this.fireEvent('activatepane', pane);
23098 pane.el.addClass('active');
23099 pane.fireEvent('activate');
23104 getActivePane : function()
23107 Roo.each(this.panes, function(p) {
23108 if(p.el.hasClass('active')){
23129 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23131 * @class Roo.bootstrap.TabPane
23132 * @extends Roo.bootstrap.Component
23133 * Bootstrap TabPane class
23134 * @cfg {Boolean} active (false | true) Default false
23135 * @cfg {String} title title of panel
23139 * Create a new TabPane
23140 * @param {Object} config The config object
23143 Roo.bootstrap.dash.TabPane = function(config){
23144 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23150 * When a pane is activated
23151 * @param {Roo.bootstrap.dash.TabPane} pane
23158 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
23163 // the tabBox that this is attached to.
23166 getAutoCreate : function()
23174 cfg.cls += ' active';
23179 initEvents : function()
23181 //Roo.log('trigger add pane handler');
23182 this.parent().fireEvent('addpane', this)
23186 * Updates the tab title
23187 * @param {String} html to set the title to.
23189 setTitle: function(str)
23195 this.tab.select('a', true).first().dom.innerHTML = str;
23212 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23215 * @class Roo.bootstrap.menu.Menu
23216 * @extends Roo.bootstrap.Component
23217 * Bootstrap Menu class - container for Menu
23218 * @cfg {String} html Text of the menu
23219 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23220 * @cfg {String} icon Font awesome icon
23221 * @cfg {String} pos Menu align to (top | bottom) default bottom
23225 * Create a new Menu
23226 * @param {Object} config The config object
23230 Roo.bootstrap.menu.Menu = function(config){
23231 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23235 * @event beforeshow
23236 * Fires before this menu is displayed
23237 * @param {Roo.bootstrap.menu.Menu} this
23241 * @event beforehide
23242 * Fires before this menu is hidden
23243 * @param {Roo.bootstrap.menu.Menu} this
23248 * Fires after this menu is displayed
23249 * @param {Roo.bootstrap.menu.Menu} this
23254 * Fires after this menu is hidden
23255 * @param {Roo.bootstrap.menu.Menu} this
23260 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23261 * @param {Roo.bootstrap.menu.Menu} this
23262 * @param {Roo.EventObject} e
23269 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
23273 weight : 'default',
23278 getChildContainer : function() {
23279 if(this.isSubMenu){
23283 return this.el.select('ul.dropdown-menu', true).first();
23286 getAutoCreate : function()
23291 cls : 'roo-menu-text',
23299 cls : 'fa ' + this.icon
23310 cls : 'dropdown-button btn btn-' + this.weight,
23315 cls : 'dropdown-toggle btn btn-' + this.weight,
23325 cls : 'dropdown-menu'
23331 if(this.pos == 'top'){
23332 cfg.cls += ' dropup';
23335 if(this.isSubMenu){
23338 cls : 'dropdown-menu'
23345 onRender : function(ct, position)
23347 this.isSubMenu = ct.hasClass('dropdown-submenu');
23349 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23352 initEvents : function()
23354 if(this.isSubMenu){
23358 this.hidden = true;
23360 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23361 this.triggerEl.on('click', this.onTriggerPress, this);
23363 this.buttonEl = this.el.select('button.dropdown-button', true).first();
23364 this.buttonEl.on('click', this.onClick, this);
23370 if(this.isSubMenu){
23374 return this.el.select('ul.dropdown-menu', true).first();
23377 onClick : function(e)
23379 this.fireEvent("click", this, e);
23382 onTriggerPress : function(e)
23384 if (this.isVisible()) {
23391 isVisible : function(){
23392 return !this.hidden;
23397 this.fireEvent("beforeshow", this);
23399 this.hidden = false;
23400 this.el.addClass('open');
23402 Roo.get(document).on("mouseup", this.onMouseUp, this);
23404 this.fireEvent("show", this);
23411 this.fireEvent("beforehide", this);
23413 this.hidden = true;
23414 this.el.removeClass('open');
23416 Roo.get(document).un("mouseup", this.onMouseUp);
23418 this.fireEvent("hide", this);
23421 onMouseUp : function()
23435 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23438 * @class Roo.bootstrap.menu.Item
23439 * @extends Roo.bootstrap.Component
23440 * Bootstrap MenuItem class
23441 * @cfg {Boolean} submenu (true | false) default false
23442 * @cfg {String} html text of the item
23443 * @cfg {String} href the link
23444 * @cfg {Boolean} disable (true | false) default false
23445 * @cfg {Boolean} preventDefault (true | false) default true
23446 * @cfg {String} icon Font awesome icon
23447 * @cfg {String} pos Submenu align to (left | right) default right
23451 * Create a new Item
23452 * @param {Object} config The config object
23456 Roo.bootstrap.menu.Item = function(config){
23457 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23461 * Fires when the mouse is hovering over this menu
23462 * @param {Roo.bootstrap.menu.Item} this
23463 * @param {Roo.EventObject} e
23468 * Fires when the mouse exits this menu
23469 * @param {Roo.bootstrap.menu.Item} this
23470 * @param {Roo.EventObject} e
23476 * The raw click event for the entire grid.
23477 * @param {Roo.EventObject} e
23483 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23488 preventDefault: true,
23493 getAutoCreate : function()
23498 cls : 'roo-menu-item-text',
23506 cls : 'fa ' + this.icon
23515 href : this.href || '#',
23522 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23526 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23528 if(this.pos == 'left'){
23529 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23536 initEvents : function()
23538 this.el.on('mouseover', this.onMouseOver, this);
23539 this.el.on('mouseout', this.onMouseOut, this);
23541 this.el.select('a', true).first().on('click', this.onClick, this);
23545 onClick : function(e)
23547 if(this.preventDefault){
23548 e.preventDefault();
23551 this.fireEvent("click", this, e);
23554 onMouseOver : function(e)
23556 if(this.submenu && this.pos == 'left'){
23557 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23560 this.fireEvent("mouseover", this, e);
23563 onMouseOut : function(e)
23565 this.fireEvent("mouseout", this, e);
23577 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23580 * @class Roo.bootstrap.menu.Separator
23581 * @extends Roo.bootstrap.Component
23582 * Bootstrap Separator class
23585 * Create a new Separator
23586 * @param {Object} config The config object
23590 Roo.bootstrap.menu.Separator = function(config){
23591 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23594 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23596 getAutoCreate : function(){
23617 * @class Roo.bootstrap.Tooltip
23618 * Bootstrap Tooltip class
23619 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23620 * to determine which dom element triggers the tooltip.
23622 * It needs to add support for additional attributes like tooltip-position
23625 * Create a new Toolti
23626 * @param {Object} config The config object
23629 Roo.bootstrap.Tooltip = function(config){
23630 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23633 Roo.apply(Roo.bootstrap.Tooltip, {
23635 * @function init initialize tooltip monitoring.
23639 currentTip : false,
23640 currentRegion : false,
23646 Roo.get(document).on('mouseover', this.enter ,this);
23647 Roo.get(document).on('mouseout', this.leave, this);
23650 this.currentTip = new Roo.bootstrap.Tooltip();
23653 enter : function(ev)
23655 var dom = ev.getTarget();
23657 //Roo.log(['enter',dom]);
23658 var el = Roo.fly(dom);
23659 if (this.currentEl) {
23661 //Roo.log(this.currentEl);
23662 //Roo.log(this.currentEl.contains(dom));
23663 if (this.currentEl == el) {
23666 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23672 if (this.currentTip.el) {
23673 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23678 // you can not look for children, as if el is the body.. then everythign is the child..
23679 if (!el.attr('tooltip')) { //
23680 if (!el.select("[tooltip]").elements.length) {
23683 // is the mouse over this child...?
23684 bindEl = el.select("[tooltip]").first();
23685 var xy = ev.getXY();
23686 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23687 //Roo.log("not in region.");
23690 //Roo.log("child element over..");
23693 this.currentEl = bindEl;
23694 this.currentTip.bind(bindEl);
23695 this.currentRegion = Roo.lib.Region.getRegion(dom);
23696 this.currentTip.enter();
23699 leave : function(ev)
23701 var dom = ev.getTarget();
23702 //Roo.log(['leave',dom]);
23703 if (!this.currentEl) {
23708 if (dom != this.currentEl.dom) {
23711 var xy = ev.getXY();
23712 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23715 // only activate leave if mouse cursor is outside... bounding box..
23720 if (this.currentTip) {
23721 this.currentTip.leave();
23723 //Roo.log('clear currentEl');
23724 this.currentEl = false;
23729 'left' : ['r-l', [-2,0], 'right'],
23730 'right' : ['l-r', [2,0], 'left'],
23731 'bottom' : ['t-b', [0,2], 'top'],
23732 'top' : [ 'b-t', [0,-2], 'bottom']
23738 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
23743 delay : null, // can be { show : 300 , hide: 500}
23747 hoverState : null, //???
23749 placement : 'bottom',
23751 getAutoCreate : function(){
23758 cls : 'tooltip-arrow'
23761 cls : 'tooltip-inner'
23768 bind : function(el)
23774 enter : function () {
23776 if (this.timeout != null) {
23777 clearTimeout(this.timeout);
23780 this.hoverState = 'in';
23781 //Roo.log("enter - show");
23782 if (!this.delay || !this.delay.show) {
23787 this.timeout = setTimeout(function () {
23788 if (_t.hoverState == 'in') {
23791 }, this.delay.show);
23795 clearTimeout(this.timeout);
23797 this.hoverState = 'out';
23798 if (!this.delay || !this.delay.hide) {
23804 this.timeout = setTimeout(function () {
23805 //Roo.log("leave - timeout");
23807 if (_t.hoverState == 'out') {
23809 Roo.bootstrap.Tooltip.currentEl = false;
23817 this.render(document.body);
23820 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23822 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23824 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23826 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23828 var placement = typeof this.placement == 'function' ?
23829 this.placement.call(this, this.el, on_el) :
23832 var autoToken = /\s?auto?\s?/i;
23833 var autoPlace = autoToken.test(placement);
23835 placement = placement.replace(autoToken, '') || 'top';
23839 //this.el.setXY([0,0]);
23841 //this.el.dom.style.display='block';
23843 //this.el.appendTo(on_el);
23845 var p = this.getPosition();
23846 var box = this.el.getBox();
23852 var align = Roo.bootstrap.Tooltip.alignment[placement];
23854 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23856 if(placement == 'top' || placement == 'bottom'){
23858 placement = 'right';
23861 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23862 placement = 'left';
23866 align = Roo.bootstrap.Tooltip.alignment[placement];
23868 this.el.alignTo(this.bindEl, align[0],align[1]);
23869 //var arrow = this.el.select('.arrow',true).first();
23870 //arrow.set(align[2],
23872 this.el.addClass(placement);
23874 this.el.addClass('in fade');
23876 this.hoverState = null;
23878 if (this.el.hasClass('fade')) {
23889 //this.el.setXY([0,0]);
23890 this.el.removeClass('in');
23906 * @class Roo.bootstrap.LocationPicker
23907 * @extends Roo.bootstrap.Component
23908 * Bootstrap LocationPicker class
23909 * @cfg {Number} latitude Position when init default 0
23910 * @cfg {Number} longitude Position when init default 0
23911 * @cfg {Number} zoom default 15
23912 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23913 * @cfg {Boolean} mapTypeControl default false
23914 * @cfg {Boolean} disableDoubleClickZoom default false
23915 * @cfg {Boolean} scrollwheel default true
23916 * @cfg {Boolean} streetViewControl default false
23917 * @cfg {Number} radius default 0
23918 * @cfg {String} locationName
23919 * @cfg {Boolean} draggable default true
23920 * @cfg {Boolean} enableAutocomplete default false
23921 * @cfg {Boolean} enableReverseGeocode default true
23922 * @cfg {String} markerTitle
23925 * Create a new LocationPicker
23926 * @param {Object} config The config object
23930 Roo.bootstrap.LocationPicker = function(config){
23932 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23937 * Fires when the picker initialized.
23938 * @param {Roo.bootstrap.LocationPicker} this
23939 * @param {Google Location} location
23943 * @event positionchanged
23944 * Fires when the picker position changed.
23945 * @param {Roo.bootstrap.LocationPicker} this
23946 * @param {Google Location} location
23948 positionchanged : true,
23951 * Fires when the map resize.
23952 * @param {Roo.bootstrap.LocationPicker} this
23957 * Fires when the map show.
23958 * @param {Roo.bootstrap.LocationPicker} this
23963 * Fires when the map hide.
23964 * @param {Roo.bootstrap.LocationPicker} this
23969 * Fires when click the map.
23970 * @param {Roo.bootstrap.LocationPicker} this
23971 * @param {Map event} e
23975 * @event mapRightClick
23976 * Fires when right click the map.
23977 * @param {Roo.bootstrap.LocationPicker} this
23978 * @param {Map event} e
23980 mapRightClick : true,
23982 * @event markerClick
23983 * Fires when click the marker.
23984 * @param {Roo.bootstrap.LocationPicker} this
23985 * @param {Map event} e
23987 markerClick : true,
23989 * @event markerRightClick
23990 * Fires when right click the marker.
23991 * @param {Roo.bootstrap.LocationPicker} this
23992 * @param {Map event} e
23994 markerRightClick : true,
23996 * @event OverlayViewDraw
23997 * Fires when OverlayView Draw
23998 * @param {Roo.bootstrap.LocationPicker} this
24000 OverlayViewDraw : true,
24002 * @event OverlayViewOnAdd
24003 * Fires when OverlayView Draw
24004 * @param {Roo.bootstrap.LocationPicker} this
24006 OverlayViewOnAdd : true,
24008 * @event OverlayViewOnRemove
24009 * Fires when OverlayView Draw
24010 * @param {Roo.bootstrap.LocationPicker} this
24012 OverlayViewOnRemove : true,
24014 * @event OverlayViewShow
24015 * Fires when OverlayView Draw
24016 * @param {Roo.bootstrap.LocationPicker} this
24017 * @param {Pixel} cpx
24019 OverlayViewShow : true,
24021 * @event OverlayViewHide
24022 * Fires when OverlayView Draw
24023 * @param {Roo.bootstrap.LocationPicker} this
24025 OverlayViewHide : true,
24027 * @event loadexception
24028 * Fires when load google lib failed.
24029 * @param {Roo.bootstrap.LocationPicker} this
24031 loadexception : true
24036 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
24038 gMapContext: false,
24044 mapTypeControl: false,
24045 disableDoubleClickZoom: false,
24047 streetViewControl: false,
24051 enableAutocomplete: false,
24052 enableReverseGeocode: true,
24055 getAutoCreate: function()
24060 cls: 'roo-location-picker'
24066 initEvents: function(ct, position)
24068 if(!this.el.getWidth() || this.isApplied()){
24072 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24077 initial: function()
24079 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24080 this.fireEvent('loadexception', this);
24084 if(!this.mapTypeId){
24085 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24088 this.gMapContext = this.GMapContext();
24090 this.initOverlayView();
24092 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24096 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24097 _this.setPosition(_this.gMapContext.marker.position);
24100 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24101 _this.fireEvent('mapClick', this, event);
24105 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24106 _this.fireEvent('mapRightClick', this, event);
24110 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24111 _this.fireEvent('markerClick', this, event);
24115 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24116 _this.fireEvent('markerRightClick', this, event);
24120 this.setPosition(this.gMapContext.location);
24122 this.fireEvent('initial', this, this.gMapContext.location);
24125 initOverlayView: function()
24129 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24133 _this.fireEvent('OverlayViewDraw', _this);
24138 _this.fireEvent('OverlayViewOnAdd', _this);
24141 onRemove: function()
24143 _this.fireEvent('OverlayViewOnRemove', _this);
24146 show: function(cpx)
24148 _this.fireEvent('OverlayViewShow', _this, cpx);
24153 _this.fireEvent('OverlayViewHide', _this);
24159 fromLatLngToContainerPixel: function(event)
24161 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24164 isApplied: function()
24166 return this.getGmapContext() == false ? false : true;
24169 getGmapContext: function()
24171 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24174 GMapContext: function()
24176 var position = new google.maps.LatLng(this.latitude, this.longitude);
24178 var _map = new google.maps.Map(this.el.dom, {
24181 mapTypeId: this.mapTypeId,
24182 mapTypeControl: this.mapTypeControl,
24183 disableDoubleClickZoom: this.disableDoubleClickZoom,
24184 scrollwheel: this.scrollwheel,
24185 streetViewControl: this.streetViewControl,
24186 locationName: this.locationName,
24187 draggable: this.draggable,
24188 enableAutocomplete: this.enableAutocomplete,
24189 enableReverseGeocode: this.enableReverseGeocode
24192 var _marker = new google.maps.Marker({
24193 position: position,
24195 title: this.markerTitle,
24196 draggable: this.draggable
24203 location: position,
24204 radius: this.radius,
24205 locationName: this.locationName,
24206 addressComponents: {
24207 formatted_address: null,
24208 addressLine1: null,
24209 addressLine2: null,
24211 streetNumber: null,
24215 stateOrProvince: null
24218 domContainer: this.el.dom,
24219 geodecoder: new google.maps.Geocoder()
24223 drawCircle: function(center, radius, options)
24225 if (this.gMapContext.circle != null) {
24226 this.gMapContext.circle.setMap(null);
24230 options = Roo.apply({}, options, {
24231 strokeColor: "#0000FF",
24232 strokeOpacity: .35,
24234 fillColor: "#0000FF",
24238 options.map = this.gMapContext.map;
24239 options.radius = radius;
24240 options.center = center;
24241 this.gMapContext.circle = new google.maps.Circle(options);
24242 return this.gMapContext.circle;
24248 setPosition: function(location)
24250 this.gMapContext.location = location;
24251 this.gMapContext.marker.setPosition(location);
24252 this.gMapContext.map.panTo(location);
24253 this.drawCircle(location, this.gMapContext.radius, {});
24257 if (this.gMapContext.settings.enableReverseGeocode) {
24258 this.gMapContext.geodecoder.geocode({
24259 latLng: this.gMapContext.location
24260 }, function(results, status) {
24262 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24263 _this.gMapContext.locationName = results[0].formatted_address;
24264 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24266 _this.fireEvent('positionchanged', this, location);
24273 this.fireEvent('positionchanged', this, location);
24278 google.maps.event.trigger(this.gMapContext.map, "resize");
24280 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24282 this.fireEvent('resize', this);
24285 setPositionByLatLng: function(latitude, longitude)
24287 this.setPosition(new google.maps.LatLng(latitude, longitude));
24290 getCurrentPosition: function()
24293 latitude: this.gMapContext.location.lat(),
24294 longitude: this.gMapContext.location.lng()
24298 getAddressName: function()
24300 return this.gMapContext.locationName;
24303 getAddressComponents: function()
24305 return this.gMapContext.addressComponents;
24308 address_component_from_google_geocode: function(address_components)
24312 for (var i = 0; i < address_components.length; i++) {
24313 var component = address_components[i];
24314 if (component.types.indexOf("postal_code") >= 0) {
24315 result.postalCode = component.short_name;
24316 } else if (component.types.indexOf("street_number") >= 0) {
24317 result.streetNumber = component.short_name;
24318 } else if (component.types.indexOf("route") >= 0) {
24319 result.streetName = component.short_name;
24320 } else if (component.types.indexOf("neighborhood") >= 0) {
24321 result.city = component.short_name;
24322 } else if (component.types.indexOf("locality") >= 0) {
24323 result.city = component.short_name;
24324 } else if (component.types.indexOf("sublocality") >= 0) {
24325 result.district = component.short_name;
24326 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24327 result.stateOrProvince = component.short_name;
24328 } else if (component.types.indexOf("country") >= 0) {
24329 result.country = component.short_name;
24333 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24334 result.addressLine2 = "";
24338 setZoomLevel: function(zoom)
24340 this.gMapContext.map.setZoom(zoom);
24353 this.fireEvent('show', this);
24364 this.fireEvent('hide', this);
24369 Roo.apply(Roo.bootstrap.LocationPicker, {
24371 OverlayView : function(map, options)
24373 options = options || {};
24387 * @class Roo.bootstrap.Alert
24388 * @extends Roo.bootstrap.Component
24389 * Bootstrap Alert class
24390 * @cfg {String} title The title of alert
24391 * @cfg {String} html The content of alert
24392 * @cfg {String} weight ( success | info | warning | danger )
24393 * @cfg {String} faicon font-awesomeicon
24396 * Create a new alert
24397 * @param {Object} config The config object
24401 Roo.bootstrap.Alert = function(config){
24402 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24406 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
24413 getAutoCreate : function()
24422 cls : 'roo-alert-icon'
24427 cls : 'roo-alert-title',
24432 cls : 'roo-alert-text',
24439 cfg.cn[0].cls += ' fa ' + this.faicon;
24443 cfg.cls += ' alert-' + this.weight;
24449 initEvents: function()
24451 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24454 setTitle : function(str)
24456 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24459 setText : function(str)
24461 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24464 setWeight : function(weight)
24467 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24470 this.weight = weight;
24472 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24475 setIcon : function(icon)
24478 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24481 this.faicon = icon;
24483 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24504 * @class Roo.bootstrap.UploadCropbox
24505 * @extends Roo.bootstrap.Component
24506 * Bootstrap UploadCropbox class
24507 * @cfg {String} emptyText show when image has been loaded
24508 * @cfg {String} rotateNotify show when image too small to rotate
24509 * @cfg {Number} errorTimeout default 3000
24510 * @cfg {Number} minWidth default 300
24511 * @cfg {Number} minHeight default 300
24512 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24513 * @cfg {Boolean} isDocument (true|false) default false
24514 * @cfg {String} url action url
24515 * @cfg {String} paramName default 'imageUpload'
24516 * @cfg {String} method default POST
24517 * @cfg {Boolean} loadMask (true|false) default true
24518 * @cfg {Boolean} loadingText default 'Loading...'
24521 * Create a new UploadCropbox
24522 * @param {Object} config The config object
24525 Roo.bootstrap.UploadCropbox = function(config){
24526 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24530 * @event beforeselectfile
24531 * Fire before select file
24532 * @param {Roo.bootstrap.UploadCropbox} this
24534 "beforeselectfile" : true,
24537 * Fire after initEvent
24538 * @param {Roo.bootstrap.UploadCropbox} this
24543 * Fire after initEvent
24544 * @param {Roo.bootstrap.UploadCropbox} this
24545 * @param {String} data
24550 * Fire when preparing the file data
24551 * @param {Roo.bootstrap.UploadCropbox} this
24552 * @param {Object} file
24557 * Fire when get exception
24558 * @param {Roo.bootstrap.UploadCropbox} this
24559 * @param {XMLHttpRequest} xhr
24561 "exception" : true,
24563 * @event beforeloadcanvas
24564 * Fire before load the canvas
24565 * @param {Roo.bootstrap.UploadCropbox} this
24566 * @param {String} src
24568 "beforeloadcanvas" : true,
24571 * Fire when trash image
24572 * @param {Roo.bootstrap.UploadCropbox} this
24577 * Fire when download the image
24578 * @param {Roo.bootstrap.UploadCropbox} this
24582 * @event footerbuttonclick
24583 * Fire when footerbuttonclick
24584 * @param {Roo.bootstrap.UploadCropbox} this
24585 * @param {String} type
24587 "footerbuttonclick" : true,
24591 * @param {Roo.bootstrap.UploadCropbox} this
24596 * Fire when rotate the image
24597 * @param {Roo.bootstrap.UploadCropbox} this
24598 * @param {String} pos
24603 * Fire when inspect the file
24604 * @param {Roo.bootstrap.UploadCropbox} this
24605 * @param {Object} file
24610 * Fire when xhr upload the file
24611 * @param {Roo.bootstrap.UploadCropbox} this
24612 * @param {Object} data
24617 * Fire when arrange the file data
24618 * @param {Roo.bootstrap.UploadCropbox} this
24619 * @param {Object} formData
24624 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24627 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24629 emptyText : 'Click to upload image',
24630 rotateNotify : 'Image is too small to rotate',
24631 errorTimeout : 3000,
24645 cropType : 'image/jpeg',
24647 canvasLoaded : false,
24648 isDocument : false,
24650 paramName : 'imageUpload',
24652 loadingText : 'Loading...',
24655 getAutoCreate : function()
24659 cls : 'roo-upload-cropbox',
24663 cls : 'roo-upload-cropbox-selector',
24668 cls : 'roo-upload-cropbox-body',
24669 style : 'cursor:pointer',
24673 cls : 'roo-upload-cropbox-preview'
24677 cls : 'roo-upload-cropbox-thumb'
24681 cls : 'roo-upload-cropbox-empty-notify',
24682 html : this.emptyText
24686 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24687 html : this.rotateNotify
24693 cls : 'roo-upload-cropbox-footer',
24696 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24706 onRender : function(ct, position)
24708 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24710 if (this.buttons.length) {
24712 Roo.each(this.buttons, function(bb) {
24714 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24716 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24722 this.maskEl = this.el;
24726 initEvents : function()
24728 this.urlAPI = (window.createObjectURL && window) ||
24729 (window.URL && URL.revokeObjectURL && URL) ||
24730 (window.webkitURL && webkitURL);
24732 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24733 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24735 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24736 this.selectorEl.hide();
24738 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24739 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24741 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24742 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24743 this.thumbEl.hide();
24745 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24746 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24748 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24749 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24750 this.errorEl.hide();
24752 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24753 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24754 this.footerEl.hide();
24756 this.setThumbBoxSize();
24762 this.fireEvent('initial', this);
24769 window.addEventListener("resize", function() { _this.resize(); } );
24771 this.bodyEl.on('click', this.beforeSelectFile, this);
24774 this.bodyEl.on('touchstart', this.onTouchStart, this);
24775 this.bodyEl.on('touchmove', this.onTouchMove, this);
24776 this.bodyEl.on('touchend', this.onTouchEnd, this);
24780 this.bodyEl.on('mousedown', this.onMouseDown, this);
24781 this.bodyEl.on('mousemove', this.onMouseMove, this);
24782 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24783 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24784 Roo.get(document).on('mouseup', this.onMouseUp, this);
24787 this.selectorEl.on('change', this.onFileSelected, this);
24793 this.baseScale = 1;
24795 this.baseRotate = 1;
24796 this.dragable = false;
24797 this.pinching = false;
24800 this.cropData = false;
24801 this.notifyEl.dom.innerHTML = this.emptyText;
24803 this.selectorEl.dom.value = '';
24807 resize : function()
24809 if(this.fireEvent('resize', this) != false){
24810 this.setThumbBoxPosition();
24811 this.setCanvasPosition();
24815 onFooterButtonClick : function(e, el, o, type)
24818 case 'rotate-left' :
24819 this.onRotateLeft(e);
24821 case 'rotate-right' :
24822 this.onRotateRight(e);
24825 this.beforeSelectFile(e);
24840 this.fireEvent('footerbuttonclick', this, type);
24843 beforeSelectFile : function(e)
24845 e.preventDefault();
24847 if(this.fireEvent('beforeselectfile', this) != false){
24848 this.selectorEl.dom.click();
24852 onFileSelected : function(e)
24854 e.preventDefault();
24856 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24860 var file = this.selectorEl.dom.files[0];
24862 if(this.fireEvent('inspect', this, file) != false){
24863 this.prepare(file);
24868 trash : function(e)
24870 this.fireEvent('trash', this);
24873 download : function(e)
24875 this.fireEvent('download', this);
24878 loadCanvas : function(src)
24880 if(this.fireEvent('beforeloadcanvas', this, src) != false){
24884 this.imageEl = document.createElement('img');
24888 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24890 this.imageEl.src = src;
24894 onLoadCanvas : function()
24896 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24897 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24899 this.bodyEl.un('click', this.beforeSelectFile, this);
24901 this.notifyEl.hide();
24902 this.thumbEl.show();
24903 this.footerEl.show();
24905 this.baseRotateLevel();
24907 if(this.isDocument){
24908 this.setThumbBoxSize();
24911 this.setThumbBoxPosition();
24913 this.baseScaleLevel();
24919 this.canvasLoaded = true;
24922 this.maskEl.unmask();
24927 setCanvasPosition : function()
24929 if(!this.canvasEl){
24933 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24934 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24936 this.previewEl.setLeft(pw);
24937 this.previewEl.setTop(ph);
24941 onMouseDown : function(e)
24945 this.dragable = true;
24946 this.pinching = false;
24948 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24949 this.dragable = false;
24953 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24954 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24958 onMouseMove : function(e)
24962 if(!this.canvasLoaded){
24966 if (!this.dragable){
24970 var minX = Math.ceil(this.thumbEl.getLeft(true));
24971 var minY = Math.ceil(this.thumbEl.getTop(true));
24973 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24974 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24976 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24977 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24979 x = x - this.mouseX;
24980 y = y - this.mouseY;
24982 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24983 var bgY = Math.ceil(y + this.previewEl.getTop(true));
24985 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24986 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24988 this.previewEl.setLeft(bgX);
24989 this.previewEl.setTop(bgY);
24991 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24992 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24995 onMouseUp : function(e)
24999 this.dragable = false;
25002 onMouseWheel : function(e)
25006 this.startScale = this.scale;
25008 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25010 if(!this.zoomable()){
25011 this.scale = this.startScale;
25020 zoomable : function()
25022 var minScale = this.thumbEl.getWidth() / this.minWidth;
25024 if(this.minWidth < this.minHeight){
25025 minScale = this.thumbEl.getHeight() / this.minHeight;
25028 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25029 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25033 (this.rotate == 0 || this.rotate == 180) &&
25035 width > this.imageEl.OriginWidth ||
25036 height > this.imageEl.OriginHeight ||
25037 (width < this.minWidth && height < this.minHeight)
25045 (this.rotate == 90 || this.rotate == 270) &&
25047 width > this.imageEl.OriginWidth ||
25048 height > this.imageEl.OriginHeight ||
25049 (width < this.minHeight && height < this.minWidth)
25056 !this.isDocument &&
25057 (this.rotate == 0 || this.rotate == 180) &&
25059 width < this.minWidth ||
25060 width > this.imageEl.OriginWidth ||
25061 height < this.minHeight ||
25062 height > this.imageEl.OriginHeight
25069 !this.isDocument &&
25070 (this.rotate == 90 || this.rotate == 270) &&
25072 width < this.minHeight ||
25073 width > this.imageEl.OriginWidth ||
25074 height < this.minWidth ||
25075 height > this.imageEl.OriginHeight
25085 onRotateLeft : function(e)
25087 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25089 var minScale = this.thumbEl.getWidth() / this.minWidth;
25091 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25092 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25094 this.startScale = this.scale;
25096 while (this.getScaleLevel() < minScale){
25098 this.scale = this.scale + 1;
25100 if(!this.zoomable()){
25105 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25106 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25111 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25118 this.scale = this.startScale;
25120 this.onRotateFail();
25125 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25127 if(this.isDocument){
25128 this.setThumbBoxSize();
25129 this.setThumbBoxPosition();
25130 this.setCanvasPosition();
25135 this.fireEvent('rotate', this, 'left');
25139 onRotateRight : function(e)
25141 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25143 var minScale = this.thumbEl.getWidth() / this.minWidth;
25145 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25146 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25148 this.startScale = this.scale;
25150 while (this.getScaleLevel() < minScale){
25152 this.scale = this.scale + 1;
25154 if(!this.zoomable()){
25159 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25160 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25165 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25172 this.scale = this.startScale;
25174 this.onRotateFail();
25179 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25181 if(this.isDocument){
25182 this.setThumbBoxSize();
25183 this.setThumbBoxPosition();
25184 this.setCanvasPosition();
25189 this.fireEvent('rotate', this, 'right');
25192 onRotateFail : function()
25194 this.errorEl.show(true);
25198 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25203 this.previewEl.dom.innerHTML = '';
25205 var canvasEl = document.createElement("canvas");
25207 var contextEl = canvasEl.getContext("2d");
25209 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25210 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25211 var center = this.imageEl.OriginWidth / 2;
25213 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25214 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25215 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25216 center = this.imageEl.OriginHeight / 2;
25219 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25221 contextEl.translate(center, center);
25222 contextEl.rotate(this.rotate * Math.PI / 180);
25224 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25226 this.canvasEl = document.createElement("canvas");
25228 this.contextEl = this.canvasEl.getContext("2d");
25230 switch (this.rotate) {
25233 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25234 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25236 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25241 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25242 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25244 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25245 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25249 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25254 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25255 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25257 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25258 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25262 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25267 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25268 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25270 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25271 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25275 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25282 this.previewEl.appendChild(this.canvasEl);
25284 this.setCanvasPosition();
25289 if(!this.canvasLoaded){
25293 var imageCanvas = document.createElement("canvas");
25295 var imageContext = imageCanvas.getContext("2d");
25297 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25298 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25300 var center = imageCanvas.width / 2;
25302 imageContext.translate(center, center);
25304 imageContext.rotate(this.rotate * Math.PI / 180);
25306 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25308 var canvas = document.createElement("canvas");
25310 var context = canvas.getContext("2d");
25312 canvas.width = this.minWidth;
25313 canvas.height = this.minHeight;
25315 switch (this.rotate) {
25318 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25319 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25321 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25322 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25324 var targetWidth = this.minWidth - 2 * x;
25325 var targetHeight = this.minHeight - 2 * y;
25329 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25330 scale = targetWidth / width;
25333 if(x > 0 && y == 0){
25334 scale = targetHeight / height;
25337 if(x > 0 && y > 0){
25338 scale = targetWidth / width;
25340 if(width < height){
25341 scale = targetHeight / height;
25345 context.scale(scale, scale);
25347 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25348 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25350 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25351 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25353 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25358 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25359 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25361 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25362 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25364 var targetWidth = this.minWidth - 2 * x;
25365 var targetHeight = this.minHeight - 2 * y;
25369 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25370 scale = targetWidth / width;
25373 if(x > 0 && y == 0){
25374 scale = targetHeight / height;
25377 if(x > 0 && y > 0){
25378 scale = targetWidth / width;
25380 if(width < height){
25381 scale = targetHeight / height;
25385 context.scale(scale, scale);
25387 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25388 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25390 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25391 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25393 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25395 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25400 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25401 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25403 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25404 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25406 var targetWidth = this.minWidth - 2 * x;
25407 var targetHeight = this.minHeight - 2 * y;
25411 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25412 scale = targetWidth / width;
25415 if(x > 0 && y == 0){
25416 scale = targetHeight / height;
25419 if(x > 0 && y > 0){
25420 scale = targetWidth / width;
25422 if(width < height){
25423 scale = targetHeight / height;
25427 context.scale(scale, scale);
25429 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25430 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25432 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25433 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25435 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25436 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25438 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25443 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25444 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25446 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25447 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25449 var targetWidth = this.minWidth - 2 * x;
25450 var targetHeight = this.minHeight - 2 * y;
25454 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25455 scale = targetWidth / width;
25458 if(x > 0 && y == 0){
25459 scale = targetHeight / height;
25462 if(x > 0 && y > 0){
25463 scale = targetWidth / width;
25465 if(width < height){
25466 scale = targetHeight / height;
25470 context.scale(scale, scale);
25472 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25473 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25475 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25476 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25478 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25480 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25487 this.cropData = canvas.toDataURL(this.cropType);
25489 if(this.fireEvent('crop', this, this.cropData) !== false){
25490 this.process(this.file, this.cropData);
25497 setThumbBoxSize : function()
25501 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25502 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25503 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25505 this.minWidth = width;
25506 this.minHeight = height;
25508 if(this.rotate == 90 || this.rotate == 270){
25509 this.minWidth = height;
25510 this.minHeight = width;
25515 width = Math.ceil(this.minWidth * height / this.minHeight);
25517 if(this.minWidth > this.minHeight){
25519 height = Math.ceil(this.minHeight * width / this.minWidth);
25522 this.thumbEl.setStyle({
25523 width : width + 'px',
25524 height : height + 'px'
25531 setThumbBoxPosition : function()
25533 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25534 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25536 this.thumbEl.setLeft(x);
25537 this.thumbEl.setTop(y);
25541 baseRotateLevel : function()
25543 this.baseRotate = 1;
25546 typeof(this.exif) != 'undefined' &&
25547 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25548 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25550 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25553 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25557 baseScaleLevel : function()
25561 if(this.isDocument){
25563 if(this.baseRotate == 6 || this.baseRotate == 8){
25565 height = this.thumbEl.getHeight();
25566 this.baseScale = height / this.imageEl.OriginWidth;
25568 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25569 width = this.thumbEl.getWidth();
25570 this.baseScale = width / this.imageEl.OriginHeight;
25576 height = this.thumbEl.getHeight();
25577 this.baseScale = height / this.imageEl.OriginHeight;
25579 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25580 width = this.thumbEl.getWidth();
25581 this.baseScale = width / this.imageEl.OriginWidth;
25587 if(this.baseRotate == 6 || this.baseRotate == 8){
25589 width = this.thumbEl.getHeight();
25590 this.baseScale = width / this.imageEl.OriginHeight;
25592 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25593 height = this.thumbEl.getWidth();
25594 this.baseScale = height / this.imageEl.OriginHeight;
25597 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25598 height = this.thumbEl.getWidth();
25599 this.baseScale = height / this.imageEl.OriginHeight;
25601 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25602 width = this.thumbEl.getHeight();
25603 this.baseScale = width / this.imageEl.OriginWidth;
25610 width = this.thumbEl.getWidth();
25611 this.baseScale = width / this.imageEl.OriginWidth;
25613 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25614 height = this.thumbEl.getHeight();
25615 this.baseScale = height / this.imageEl.OriginHeight;
25618 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25620 height = this.thumbEl.getHeight();
25621 this.baseScale = height / this.imageEl.OriginHeight;
25623 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25624 width = this.thumbEl.getWidth();
25625 this.baseScale = width / this.imageEl.OriginWidth;
25633 getScaleLevel : function()
25635 return this.baseScale * Math.pow(1.1, this.scale);
25638 onTouchStart : function(e)
25640 if(!this.canvasLoaded){
25641 this.beforeSelectFile(e);
25645 var touches = e.browserEvent.touches;
25651 if(touches.length == 1){
25652 this.onMouseDown(e);
25656 if(touches.length != 2){
25662 for(var i = 0, finger; finger = touches[i]; i++){
25663 coords.push(finger.pageX, finger.pageY);
25666 var x = Math.pow(coords[0] - coords[2], 2);
25667 var y = Math.pow(coords[1] - coords[3], 2);
25669 this.startDistance = Math.sqrt(x + y);
25671 this.startScale = this.scale;
25673 this.pinching = true;
25674 this.dragable = false;
25678 onTouchMove : function(e)
25680 if(!this.pinching && !this.dragable){
25684 var touches = e.browserEvent.touches;
25691 this.onMouseMove(e);
25697 for(var i = 0, finger; finger = touches[i]; i++){
25698 coords.push(finger.pageX, finger.pageY);
25701 var x = Math.pow(coords[0] - coords[2], 2);
25702 var y = Math.pow(coords[1] - coords[3], 2);
25704 this.endDistance = Math.sqrt(x + y);
25706 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25708 if(!this.zoomable()){
25709 this.scale = this.startScale;
25717 onTouchEnd : function(e)
25719 this.pinching = false;
25720 this.dragable = false;
25724 process : function(file, crop)
25727 this.maskEl.mask(this.loadingText);
25730 this.xhr = new XMLHttpRequest();
25732 file.xhr = this.xhr;
25734 this.xhr.open(this.method, this.url, true);
25737 "Accept": "application/json",
25738 "Cache-Control": "no-cache",
25739 "X-Requested-With": "XMLHttpRequest"
25742 for (var headerName in headers) {
25743 var headerValue = headers[headerName];
25745 this.xhr.setRequestHeader(headerName, headerValue);
25751 this.xhr.onload = function()
25753 _this.xhrOnLoad(_this.xhr);
25756 this.xhr.onerror = function()
25758 _this.xhrOnError(_this.xhr);
25761 var formData = new FormData();
25763 formData.append('returnHTML', 'NO');
25766 formData.append('crop', crop);
25769 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25770 formData.append(this.paramName, file, file.name);
25773 if(typeof(file.filename) != 'undefined'){
25774 formData.append('filename', file.filename);
25777 if(typeof(file.mimetype) != 'undefined'){
25778 formData.append('mimetype', file.mimetype);
25781 if(this.fireEvent('arrange', this, formData) != false){
25782 this.xhr.send(formData);
25786 xhrOnLoad : function(xhr)
25789 this.maskEl.unmask();
25792 if (xhr.readyState !== 4) {
25793 this.fireEvent('exception', this, xhr);
25797 var response = Roo.decode(xhr.responseText);
25799 if(!response.success){
25800 this.fireEvent('exception', this, xhr);
25804 var response = Roo.decode(xhr.responseText);
25806 this.fireEvent('upload', this, response);
25810 xhrOnError : function()
25813 this.maskEl.unmask();
25816 Roo.log('xhr on error');
25818 var response = Roo.decode(xhr.responseText);
25824 prepare : function(file)
25827 this.maskEl.mask(this.loadingText);
25833 if(typeof(file) === 'string'){
25834 this.loadCanvas(file);
25838 if(!file || !this.urlAPI){
25843 this.cropType = file.type;
25847 if(this.fireEvent('prepare', this, this.file) != false){
25849 var reader = new FileReader();
25851 reader.onload = function (e) {
25852 if (e.target.error) {
25853 Roo.log(e.target.error);
25857 var buffer = e.target.result,
25858 dataView = new DataView(buffer),
25860 maxOffset = dataView.byteLength - 4,
25864 if (dataView.getUint16(0) === 0xffd8) {
25865 while (offset < maxOffset) {
25866 markerBytes = dataView.getUint16(offset);
25868 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25869 markerLength = dataView.getUint16(offset + 2) + 2;
25870 if (offset + markerLength > dataView.byteLength) {
25871 Roo.log('Invalid meta data: Invalid segment size.');
25875 if(markerBytes == 0xffe1){
25876 _this.parseExifData(
25883 offset += markerLength;
25893 var url = _this.urlAPI.createObjectURL(_this.file);
25895 _this.loadCanvas(url);
25900 reader.readAsArrayBuffer(this.file);
25906 parseExifData : function(dataView, offset, length)
25908 var tiffOffset = offset + 10,
25912 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25913 // No Exif data, might be XMP data instead
25917 // Check for the ASCII code for "Exif" (0x45786966):
25918 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25919 // No Exif data, might be XMP data instead
25922 if (tiffOffset + 8 > dataView.byteLength) {
25923 Roo.log('Invalid Exif data: Invalid segment size.');
25926 // Check for the two null bytes:
25927 if (dataView.getUint16(offset + 8) !== 0x0000) {
25928 Roo.log('Invalid Exif data: Missing byte alignment offset.');
25931 // Check the byte alignment:
25932 switch (dataView.getUint16(tiffOffset)) {
25934 littleEndian = true;
25937 littleEndian = false;
25940 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25943 // Check for the TIFF tag marker (0x002A):
25944 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25945 Roo.log('Invalid Exif data: Missing TIFF marker.');
25948 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25949 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25951 this.parseExifTags(
25954 tiffOffset + dirOffset,
25959 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25964 if (dirOffset + 6 > dataView.byteLength) {
25965 Roo.log('Invalid Exif data: Invalid directory offset.');
25968 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25969 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25970 if (dirEndOffset + 4 > dataView.byteLength) {
25971 Roo.log('Invalid Exif data: Invalid directory size.');
25974 for (i = 0; i < tagsNumber; i += 1) {
25978 dirOffset + 2 + 12 * i, // tag offset
25982 // Return the offset to the next directory:
25983 return dataView.getUint32(dirEndOffset, littleEndian);
25986 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
25988 var tag = dataView.getUint16(offset, littleEndian);
25990 this.exif[tag] = this.getExifValue(
25994 dataView.getUint16(offset + 2, littleEndian), // tag type
25995 dataView.getUint32(offset + 4, littleEndian), // tag length
26000 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26002 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26011 Roo.log('Invalid Exif data: Invalid tag type.');
26015 tagSize = tagType.size * length;
26016 // Determine if the value is contained in the dataOffset bytes,
26017 // or if the value at the dataOffset is a pointer to the actual data:
26018 dataOffset = tagSize > 4 ?
26019 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26020 if (dataOffset + tagSize > dataView.byteLength) {
26021 Roo.log('Invalid Exif data: Invalid data offset.');
26024 if (length === 1) {
26025 return tagType.getValue(dataView, dataOffset, littleEndian);
26028 for (i = 0; i < length; i += 1) {
26029 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26032 if (tagType.ascii) {
26034 // Concatenate the chars:
26035 for (i = 0; i < values.length; i += 1) {
26037 // Ignore the terminating NULL byte(s):
26038 if (c === '\u0000') {
26050 Roo.apply(Roo.bootstrap.UploadCropbox, {
26052 'Orientation': 0x0112
26056 1: 0, //'top-left',
26058 3: 180, //'bottom-right',
26059 // 4: 'bottom-left',
26061 6: 90, //'right-top',
26062 // 7: 'right-bottom',
26063 8: 270 //'left-bottom'
26067 // byte, 8-bit unsigned int:
26069 getValue: function (dataView, dataOffset) {
26070 return dataView.getUint8(dataOffset);
26074 // ascii, 8-bit byte:
26076 getValue: function (dataView, dataOffset) {
26077 return String.fromCharCode(dataView.getUint8(dataOffset));
26082 // short, 16 bit int:
26084 getValue: function (dataView, dataOffset, littleEndian) {
26085 return dataView.getUint16(dataOffset, littleEndian);
26089 // long, 32 bit int:
26091 getValue: function (dataView, dataOffset, littleEndian) {
26092 return dataView.getUint32(dataOffset, littleEndian);
26096 // rational = two long values, first is numerator, second is denominator:
26098 getValue: function (dataView, dataOffset, littleEndian) {
26099 return dataView.getUint32(dataOffset, littleEndian) /
26100 dataView.getUint32(dataOffset + 4, littleEndian);
26104 // slong, 32 bit signed int:
26106 getValue: function (dataView, dataOffset, littleEndian) {
26107 return dataView.getInt32(dataOffset, littleEndian);
26111 // srational, two slongs, first is numerator, second is denominator:
26113 getValue: function (dataView, dataOffset, littleEndian) {
26114 return dataView.getInt32(dataOffset, littleEndian) /
26115 dataView.getInt32(dataOffset + 4, littleEndian);
26125 cls : 'btn-group roo-upload-cropbox-rotate-left',
26126 action : 'rotate-left',
26130 cls : 'btn btn-default',
26131 html : '<i class="fa fa-undo"></i>'
26137 cls : 'btn-group roo-upload-cropbox-picture',
26138 action : 'picture',
26142 cls : 'btn btn-default',
26143 html : '<i class="fa fa-picture-o"></i>'
26149 cls : 'btn-group roo-upload-cropbox-rotate-right',
26150 action : 'rotate-right',
26154 cls : 'btn btn-default',
26155 html : '<i class="fa fa-repeat"></i>'
26163 cls : 'btn-group roo-upload-cropbox-rotate-left',
26164 action : 'rotate-left',
26168 cls : 'btn btn-default',
26169 html : '<i class="fa fa-undo"></i>'
26175 cls : 'btn-group roo-upload-cropbox-download',
26176 action : 'download',
26180 cls : 'btn btn-default',
26181 html : '<i class="fa fa-download"></i>'
26187 cls : 'btn-group roo-upload-cropbox-crop',
26192 cls : 'btn btn-default',
26193 html : '<i class="fa fa-crop"></i>'
26199 cls : 'btn-group roo-upload-cropbox-trash',
26204 cls : 'btn btn-default',
26205 html : '<i class="fa fa-trash"></i>'
26211 cls : 'btn-group roo-upload-cropbox-rotate-right',
26212 action : 'rotate-right',
26216 cls : 'btn btn-default',
26217 html : '<i class="fa fa-repeat"></i>'
26225 cls : 'btn-group roo-upload-cropbox-rotate-left',
26226 action : 'rotate-left',
26230 cls : 'btn btn-default',
26231 html : '<i class="fa fa-undo"></i>'
26237 cls : 'btn-group roo-upload-cropbox-rotate-right',
26238 action : 'rotate-right',
26242 cls : 'btn btn-default',
26243 html : '<i class="fa fa-repeat"></i>'
26256 * @class Roo.bootstrap.DocumentManager
26257 * @extends Roo.bootstrap.Component
26258 * Bootstrap DocumentManager class
26259 * @cfg {String} paramName default 'imageUpload'
26260 * @cfg {String} method default POST
26261 * @cfg {String} url action url
26262 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26263 * @cfg {Boolean} multiple multiple upload default true
26264 * @cfg {Number} thumbSize default 300
26265 * @cfg {String} fieldLabel
26266 * @cfg {Number} labelWidth default 4
26267 * @cfg {String} labelAlign (left|top) default left
26268 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26271 * Create a new DocumentManager
26272 * @param {Object} config The config object
26275 Roo.bootstrap.DocumentManager = function(config){
26276 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26281 * Fire when initial the DocumentManager
26282 * @param {Roo.bootstrap.DocumentManager} this
26287 * inspect selected file
26288 * @param {Roo.bootstrap.DocumentManager} this
26289 * @param {File} file
26294 * Fire when xhr load exception
26295 * @param {Roo.bootstrap.DocumentManager} this
26296 * @param {XMLHttpRequest} xhr
26298 "exception" : true,
26301 * prepare the form data
26302 * @param {Roo.bootstrap.DocumentManager} this
26303 * @param {Object} formData
26308 * Fire when remove the file
26309 * @param {Roo.bootstrap.DocumentManager} this
26310 * @param {Object} file
26315 * Fire after refresh the file
26316 * @param {Roo.bootstrap.DocumentManager} this
26321 * Fire after click the image
26322 * @param {Roo.bootstrap.DocumentManager} this
26323 * @param {Object} file
26328 * Fire when upload a image and editable set to true
26329 * @param {Roo.bootstrap.DocumentManager} this
26330 * @param {Object} file
26334 * @event beforeselectfile
26335 * Fire before select file
26336 * @param {Roo.bootstrap.DocumentManager} this
26338 "beforeselectfile" : true,
26341 * Fire before process file
26342 * @param {Roo.bootstrap.DocumentManager} this
26343 * @param {Object} file
26350 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
26359 paramName : 'imageUpload',
26362 labelAlign : 'left',
26369 getAutoCreate : function()
26371 var managerWidget = {
26373 cls : 'roo-document-manager',
26377 cls : 'roo-document-manager-selector',
26382 cls : 'roo-document-manager-uploader',
26386 cls : 'roo-document-manager-upload-btn',
26387 html : '<i class="fa fa-plus"></i>'
26398 cls : 'column col-md-12',
26403 if(this.fieldLabel.length){
26408 cls : 'column col-md-12',
26409 html : this.fieldLabel
26413 cls : 'column col-md-12',
26418 if(this.labelAlign == 'left'){
26422 cls : 'column col-md-' + this.labelWidth,
26423 html : this.fieldLabel
26427 cls : 'column col-md-' + (12 - this.labelWidth),
26437 cls : 'row clearfix',
26445 initEvents : function()
26447 this.managerEl = this.el.select('.roo-document-manager', true).first();
26448 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26450 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26451 this.selectorEl.hide();
26454 this.selectorEl.attr('multiple', 'multiple');
26457 this.selectorEl.on('change', this.onFileSelected, this);
26459 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26460 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26462 this.uploader.on('click', this.onUploaderClick, this);
26464 this.renderProgressDialog();
26468 window.addEventListener("resize", function() { _this.refresh(); } );
26470 this.fireEvent('initial', this);
26473 renderProgressDialog : function()
26477 this.progressDialog = new Roo.bootstrap.Modal({
26478 cls : 'roo-document-manager-progress-dialog',
26479 allow_close : false,
26489 btnclick : function() {
26490 _this.uploadCancel();
26496 this.progressDialog.render(Roo.get(document.body));
26498 this.progress = new Roo.bootstrap.Progress({
26499 cls : 'roo-document-manager-progress',
26504 this.progress.render(this.progressDialog.getChildContainer());
26506 this.progressBar = new Roo.bootstrap.ProgressBar({
26507 cls : 'roo-document-manager-progress-bar',
26510 aria_valuemax : 12,
26514 this.progressBar.render(this.progress.getChildContainer());
26517 onUploaderClick : function(e)
26519 e.preventDefault();
26521 if(this.fireEvent('beforeselectfile', this) != false){
26522 this.selectorEl.dom.click();
26527 onFileSelected : function(e)
26529 e.preventDefault();
26531 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26535 Roo.each(this.selectorEl.dom.files, function(file){
26536 if(this.fireEvent('inspect', this, file) != false){
26537 this.files.push(file);
26547 this.selectorEl.dom.value = '';
26549 if(!this.files.length){
26553 if(this.boxes > 0 && this.files.length > this.boxes){
26554 this.files = this.files.slice(0, this.boxes);
26557 this.uploader.show();
26559 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26560 this.uploader.hide();
26569 Roo.each(this.files, function(file){
26571 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26572 var f = this.renderPreview(file);
26577 if(file.type.indexOf('image') != -1){
26578 this.delegates.push(
26580 _this.process(file);
26581 }).createDelegate(this)
26589 _this.process(file);
26590 }).createDelegate(this)
26595 this.files = files;
26597 this.delegates = this.delegates.concat(docs);
26599 if(!this.delegates.length){
26604 this.progressBar.aria_valuemax = this.delegates.length;
26611 arrange : function()
26613 if(!this.delegates.length){
26614 this.progressDialog.hide();
26619 var delegate = this.delegates.shift();
26621 this.progressDialog.show();
26623 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26625 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26630 refresh : function()
26632 this.uploader.show();
26634 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26635 this.uploader.hide();
26638 Roo.isTouch ? this.closable(false) : this.closable(true);
26640 this.fireEvent('refresh', this);
26643 onRemove : function(e, el, o)
26645 e.preventDefault();
26647 this.fireEvent('remove', this, o);
26651 remove : function(o)
26655 Roo.each(this.files, function(file){
26656 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26665 this.files = files;
26672 Roo.each(this.files, function(file){
26677 file.target.remove();
26686 onClick : function(e, el, o)
26688 e.preventDefault();
26690 this.fireEvent('click', this, o);
26694 closable : function(closable)
26696 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26698 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26710 xhrOnLoad : function(xhr)
26712 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26716 if (xhr.readyState !== 4) {
26718 this.fireEvent('exception', this, xhr);
26722 var response = Roo.decode(xhr.responseText);
26724 if(!response.success){
26726 this.fireEvent('exception', this, xhr);
26730 var file = this.renderPreview(response.data);
26732 this.files.push(file);
26738 xhrOnError : function()
26740 Roo.log('xhr on error');
26742 var response = Roo.decode(xhr.responseText);
26749 process : function(file)
26751 if(this.fireEvent('process', this, file) !== false){
26752 if(this.editable && file.type.indexOf('image') != -1){
26753 this.fireEvent('edit', this, file);
26757 this.uploadStart(file, false);
26764 uploadStart : function(file, crop)
26766 this.xhr = new XMLHttpRequest();
26768 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26773 file.xhr = this.xhr;
26775 this.managerEl.createChild({
26777 cls : 'roo-document-manager-loading',
26781 tooltip : file.name,
26782 cls : 'roo-document-manager-thumb',
26783 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26789 this.xhr.open(this.method, this.url, true);
26792 "Accept": "application/json",
26793 "Cache-Control": "no-cache",
26794 "X-Requested-With": "XMLHttpRequest"
26797 for (var headerName in headers) {
26798 var headerValue = headers[headerName];
26800 this.xhr.setRequestHeader(headerName, headerValue);
26806 this.xhr.onload = function()
26808 _this.xhrOnLoad(_this.xhr);
26811 this.xhr.onerror = function()
26813 _this.xhrOnError(_this.xhr);
26816 var formData = new FormData();
26818 formData.append('returnHTML', 'NO');
26821 formData.append('crop', crop);
26824 formData.append(this.paramName, file, file.name);
26826 if(this.fireEvent('prepare', this, formData) != false){
26827 this.xhr.send(formData);
26831 uploadCancel : function()
26838 this.delegates = [];
26840 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26847 renderPreview : function(file)
26849 if(typeof(file.target) != 'undefined' && file.target){
26853 var previewEl = this.managerEl.createChild({
26855 cls : 'roo-document-manager-preview',
26859 tooltip : file.filename,
26860 cls : 'roo-document-manager-thumb',
26861 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26866 html : '<i class="fa fa-times-circle"></i>'
26871 var close = previewEl.select('button.close', true).first();
26873 close.on('click', this.onRemove, this, file);
26875 file.target = previewEl;
26877 var image = previewEl.select('img', true).first();
26881 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26883 image.on('click', this.onClick, this, file);
26889 onPreviewLoad : function(file, image)
26891 if(typeof(file.target) == 'undefined' || !file.target){
26895 var width = image.dom.naturalWidth || image.dom.width;
26896 var height = image.dom.naturalHeight || image.dom.height;
26898 if(width > height){
26899 file.target.addClass('wide');
26903 file.target.addClass('tall');
26908 uploadFromSource : function(file, crop)
26910 this.xhr = new XMLHttpRequest();
26912 this.managerEl.createChild({
26914 cls : 'roo-document-manager-loading',
26918 tooltip : file.name,
26919 cls : 'roo-document-manager-thumb',
26920 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26926 this.xhr.open(this.method, this.url, true);
26929 "Accept": "application/json",
26930 "Cache-Control": "no-cache",
26931 "X-Requested-With": "XMLHttpRequest"
26934 for (var headerName in headers) {
26935 var headerValue = headers[headerName];
26937 this.xhr.setRequestHeader(headerName, headerValue);
26943 this.xhr.onload = function()
26945 _this.xhrOnLoad(_this.xhr);
26948 this.xhr.onerror = function()
26950 _this.xhrOnError(_this.xhr);
26953 var formData = new FormData();
26955 formData.append('returnHTML', 'NO');
26957 formData.append('crop', crop);
26959 if(typeof(file.filename) != 'undefined'){
26960 formData.append('filename', file.filename);
26963 if(typeof(file.mimetype) != 'undefined'){
26964 formData.append('mimetype', file.mimetype);
26967 if(this.fireEvent('prepare', this, formData) != false){
26968 this.xhr.send(formData);
26978 * @class Roo.bootstrap.DocumentViewer
26979 * @extends Roo.bootstrap.Component
26980 * Bootstrap DocumentViewer class
26983 * Create a new DocumentViewer
26984 * @param {Object} config The config object
26987 Roo.bootstrap.DocumentViewer = function(config){
26988 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26993 * Fire after initEvent
26994 * @param {Roo.bootstrap.DocumentViewer} this
27000 * @param {Roo.bootstrap.DocumentViewer} this
27005 * Fire after trash button
27006 * @param {Roo.bootstrap.DocumentViewer} this
27013 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
27015 getAutoCreate : function()
27019 cls : 'roo-document-viewer',
27023 cls : 'roo-document-viewer-body',
27027 cls : 'roo-document-viewer-thumb',
27031 cls : 'roo-document-viewer-image'
27039 cls : 'roo-document-viewer-footer',
27042 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27050 cls : 'btn btn-default roo-document-viewer-trash',
27051 html : '<i class="fa fa-trash"></i>'
27064 initEvents : function()
27067 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27068 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27070 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27071 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27073 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27074 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27076 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27077 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27079 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27080 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27082 this.bodyEl.on('click', this.onClick, this);
27084 this.trashBtn.on('click', this.onTrash, this);
27088 initial : function()
27090 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27093 this.fireEvent('initial', this);
27097 onClick : function(e)
27099 e.preventDefault();
27101 this.fireEvent('click', this);
27104 onTrash : function(e)
27106 e.preventDefault();
27108 this.fireEvent('trash', this);
27120 * @class Roo.bootstrap.NavProgressBar
27121 * @extends Roo.bootstrap.Component
27122 * Bootstrap NavProgressBar class
27125 * Create a new nav progress bar
27126 * @param {Object} config The config object
27129 Roo.bootstrap.NavProgressBar = function(config){
27130 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27132 this.bullets = this.bullets || [];
27134 // Roo.bootstrap.NavProgressBar.register(this);
27138 * Fires when the active item changes
27139 * @param {Roo.bootstrap.NavProgressBar} this
27140 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27141 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
27148 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
27153 getAutoCreate : function()
27155 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27159 cls : 'roo-navigation-bar-group',
27163 cls : 'roo-navigation-top-bar'
27167 cls : 'roo-navigation-bullets-bar',
27171 cls : 'roo-navigation-bar'
27178 cls : 'roo-navigation-bottom-bar'
27188 initEvents: function()
27193 onRender : function(ct, position)
27195 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27197 if(this.bullets.length){
27198 Roo.each(this.bullets, function(b){
27207 addItem : function(cfg)
27209 var item = new Roo.bootstrap.NavProgressItem(cfg);
27211 item.parentId = this.id;
27212 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27215 var top = new Roo.bootstrap.Element({
27217 cls : 'roo-navigation-bar-text'
27220 var bottom = new Roo.bootstrap.Element({
27222 cls : 'roo-navigation-bar-text'
27225 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27226 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27228 var topText = new Roo.bootstrap.Element({
27230 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27233 var bottomText = new Roo.bootstrap.Element({
27235 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27238 topText.onRender(top.el, null);
27239 bottomText.onRender(bottom.el, null);
27242 item.bottomEl = bottom;
27245 this.barItems.push(item);
27250 getActive : function()
27252 var active = false;
27254 Roo.each(this.barItems, function(v){
27256 if (!v.isActive()) {
27268 setActiveItem : function(item)
27272 Roo.each(this.barItems, function(v){
27273 if (v.rid == item.rid) {
27277 if (v.isActive()) {
27278 v.setActive(false);
27283 item.setActive(true);
27285 this.fireEvent('changed', this, item, prev);
27288 getBarItem: function(rid)
27292 Roo.each(this.barItems, function(e) {
27293 if (e.rid != rid) {
27304 indexOfItem : function(item)
27308 Roo.each(this.barItems, function(v, i){
27310 if (v.rid != item.rid) {
27321 setActiveNext : function()
27323 var i = this.indexOfItem(this.getActive());
27325 if (i > this.barItems.length) {
27329 this.setActiveItem(this.barItems[i+1]);
27332 setActivePrev : function()
27334 var i = this.indexOfItem(this.getActive());
27340 this.setActiveItem(this.barItems[i-1]);
27343 format : function()
27345 if(!this.barItems.length){
27349 var width = 100 / this.barItems.length;
27351 Roo.each(this.barItems, function(i){
27352 i.el.setStyle('width', width + '%');
27353 i.topEl.el.setStyle('width', width + '%');
27354 i.bottomEl.el.setStyle('width', width + '%');
27363 * Nav Progress Item
27368 * @class Roo.bootstrap.NavProgressItem
27369 * @extends Roo.bootstrap.Component
27370 * Bootstrap NavProgressItem class
27371 * @cfg {String} rid the reference id
27372 * @cfg {Boolean} active (true|false) Is item active default false
27373 * @cfg {Boolean} disabled (true|false) Is item active default false
27374 * @cfg {String} html
27375 * @cfg {String} position (top|bottom) text position default bottom
27376 * @cfg {String} icon show icon instead of number
27379 * Create a new NavProgressItem
27380 * @param {Object} config The config object
27382 Roo.bootstrap.NavProgressItem = function(config){
27383 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27388 * The raw click event for the entire grid.
27389 * @param {Roo.bootstrap.NavProgressItem} this
27390 * @param {Roo.EventObject} e
27397 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
27403 position : 'bottom',
27406 getAutoCreate : function()
27408 var iconCls = 'roo-navigation-bar-item-icon';
27410 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27414 cls: 'roo-navigation-bar-item',
27424 cfg.cls += ' active';
27427 cfg.cls += ' disabled';
27433 disable : function()
27435 this.setDisabled(true);
27438 enable : function()
27440 this.setDisabled(false);
27443 initEvents: function()
27445 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27447 this.iconEl.on('click', this.onClick, this);
27450 onClick : function(e)
27452 e.preventDefault();
27458 if(this.fireEvent('click', this, e) === false){
27462 this.parent().setActiveItem(this);
27465 isActive: function ()
27467 return this.active;
27470 setActive : function(state)
27472 if(this.active == state){
27476 this.active = state;
27479 this.el.addClass('active');
27483 this.el.removeClass('active');
27488 setDisabled : function(state)
27490 if(this.disabled == state){
27494 this.disabled = state;
27497 this.el.addClass('disabled');
27501 this.el.removeClass('disabled');
27504 tooltipEl : function()
27506 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27519 * @class Roo.bootstrap.FieldLabel
27520 * @extends Roo.bootstrap.Component
27521 * Bootstrap FieldLabel class
27522 * @cfg {String} html contents of the element
27523 * @cfg {String} tag tag of the element default label
27524 * @cfg {String} cls class of the element
27525 * @cfg {String} target label target
27526 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27527 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27528 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27529 * @cfg {String} iconTooltip default "This field is required"
27532 * Create a new FieldLabel
27533 * @param {Object} config The config object
27536 Roo.bootstrap.FieldLabel = function(config){
27537 Roo.bootstrap.Element.superclass.constructor.call(this, config);
27542 * Fires after the field has been marked as invalid.
27543 * @param {Roo.form.FieldLabel} this
27544 * @param {String} msg The validation message
27549 * Fires after the field has been validated with no errors.
27550 * @param {Roo.form.FieldLabel} this
27556 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
27563 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27564 validClass : 'text-success fa fa-lg fa-check',
27565 iconTooltip : 'This field is required',
27567 getAutoCreate : function(){
27571 cls : 'roo-bootstrap-field-label ' + this.cls,
27577 tooltip : this.iconTooltip
27589 initEvents: function()
27591 Roo.bootstrap.Element.superclass.initEvents.call(this);
27593 this.iconEl = this.el.select('i', true).first();
27595 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27597 Roo.bootstrap.FieldLabel.register(this);
27601 * Mark this field as valid
27603 markValid : function()
27605 this.iconEl.show();
27607 this.iconEl.removeClass(this.invalidClass);
27609 this.iconEl.addClass(this.validClass);
27611 this.fireEvent('valid', this);
27615 * Mark this field as invalid
27616 * @param {String} msg The validation message
27618 markInvalid : function(msg)
27620 this.iconEl.show();
27622 this.iconEl.removeClass(this.validClass);
27624 this.iconEl.addClass(this.invalidClass);
27626 this.fireEvent('invalid', this, msg);
27632 Roo.apply(Roo.bootstrap.FieldLabel, {
27637 * register a FieldLabel Group
27638 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27640 register : function(label)
27642 if(this.groups.hasOwnProperty(label.target)){
27646 this.groups[label.target] = label;
27650 * fetch a FieldLabel Group based on the target
27651 * @param {string} target
27652 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27654 get: function(target) {
27655 if (typeof(this.groups[target]) == 'undefined') {
27659 return this.groups[target] ;
27668 * page DateSplitField.
27674 * @class Roo.bootstrap.DateSplitField
27675 * @extends Roo.bootstrap.Component
27676 * Bootstrap DateSplitField class
27677 * @cfg {string} fieldLabel - the label associated
27678 * @cfg {Number} labelWidth set the width of label (0-12)
27679 * @cfg {String} labelAlign (top|left)
27680 * @cfg {Boolean} dayAllowBlank (true|false) default false
27681 * @cfg {Boolean} monthAllowBlank (true|false) default false
27682 * @cfg {Boolean} yearAllowBlank (true|false) default false
27683 * @cfg {string} dayPlaceholder
27684 * @cfg {string} monthPlaceholder
27685 * @cfg {string} yearPlaceholder
27686 * @cfg {string} dayFormat default 'd'
27687 * @cfg {string} monthFormat default 'm'
27688 * @cfg {string} yearFormat default 'Y'
27692 * Create a new DateSplitField
27693 * @param {Object} config The config object
27696 Roo.bootstrap.DateSplitField = function(config){
27697 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27703 * getting the data of years
27704 * @param {Roo.bootstrap.DateSplitField} this
27705 * @param {Object} years
27710 * getting the data of days
27711 * @param {Roo.bootstrap.DateSplitField} this
27712 * @param {Object} days
27717 * Fires after the field has been marked as invalid.
27718 * @param {Roo.form.Field} this
27719 * @param {String} msg The validation message
27724 * Fires after the field has been validated with no errors.
27725 * @param {Roo.form.Field} this
27731 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
27734 labelAlign : 'top',
27736 dayAllowBlank : false,
27737 monthAllowBlank : false,
27738 yearAllowBlank : false,
27739 dayPlaceholder : '',
27740 monthPlaceholder : '',
27741 yearPlaceholder : '',
27745 isFormField : true,
27747 getAutoCreate : function()
27751 cls : 'row roo-date-split-field-group',
27756 cls : 'form-hidden-field roo-date-split-field-group-value',
27762 if(this.fieldLabel){
27765 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27769 html : this.fieldLabel
27775 Roo.each(['day', 'month', 'year'], function(t){
27778 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27785 inputEl: function ()
27787 return this.el.select('.roo-date-split-field-group-value', true).first();
27790 onRender : function(ct, position)
27794 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27796 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27798 this.dayField = new Roo.bootstrap.ComboBox({
27799 allowBlank : this.dayAllowBlank,
27800 alwaysQuery : true,
27801 displayField : 'value',
27804 forceSelection : true,
27806 placeholder : this.dayPlaceholder,
27807 selectOnFocus : true,
27808 tpl : '<div class="select2-result"><b>{value}</b></div>',
27809 triggerAction : 'all',
27811 valueField : 'value',
27812 store : new Roo.data.SimpleStore({
27813 data : (function() {
27815 _this.fireEvent('days', _this, days);
27818 fields : [ 'value' ]
27821 select : function (_self, record, index)
27823 _this.setValue(_this.getValue());
27828 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27830 this.monthField = new Roo.bootstrap.MonthField({
27831 after : '<i class=\"fa fa-calendar\"></i>',
27832 allowBlank : this.monthAllowBlank,
27833 placeholder : this.monthPlaceholder,
27836 render : function (_self)
27838 this.el.select('span.input-group-addon', true).first().on('click', function(e){
27839 e.preventDefault();
27843 select : function (_self, oldvalue, newvalue)
27845 _this.setValue(_this.getValue());
27850 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27852 this.yearField = new Roo.bootstrap.ComboBox({
27853 allowBlank : this.yearAllowBlank,
27854 alwaysQuery : true,
27855 displayField : 'value',
27858 forceSelection : true,
27860 placeholder : this.yearPlaceholder,
27861 selectOnFocus : true,
27862 tpl : '<div class="select2-result"><b>{value}</b></div>',
27863 triggerAction : 'all',
27865 valueField : 'value',
27866 store : new Roo.data.SimpleStore({
27867 data : (function() {
27869 _this.fireEvent('years', _this, years);
27872 fields : [ 'value' ]
27875 select : function (_self, record, index)
27877 _this.setValue(_this.getValue());
27882 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
27885 setValue : function(v, format)
27887 this.inputEl.dom.value = v;
27889 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
27891 var d = Date.parseDate(v, f);
27898 this.setDay(d.format(this.dayFormat));
27899 this.setMonth(d.format(this.monthFormat));
27900 this.setYear(d.format(this.yearFormat));
27907 setDay : function(v)
27909 this.dayField.setValue(v);
27910 this.inputEl.dom.value = this.getValue();
27915 setMonth : function(v)
27917 this.monthField.setValue(v, true);
27918 this.inputEl.dom.value = this.getValue();
27923 setYear : function(v)
27925 this.yearField.setValue(v);
27926 this.inputEl.dom.value = this.getValue();
27931 getDay : function()
27933 return this.dayField.getValue();
27936 getMonth : function()
27938 return this.monthField.getValue();
27941 getYear : function()
27943 return this.yearField.getValue();
27946 getValue : function()
27948 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
27950 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
27960 this.inputEl.dom.value = '';
27965 validate : function()
27967 var d = this.dayField.validate();
27968 var m = this.monthField.validate();
27969 var y = this.yearField.validate();
27974 (!this.dayAllowBlank && !d) ||
27975 (!this.monthAllowBlank && !m) ||
27976 (!this.yearAllowBlank && !y)
27981 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
27990 this.markInvalid();
27995 markValid : function()
27998 var label = this.el.select('label', true).first();
27999 var icon = this.el.select('i.fa-star', true).first();
28005 this.fireEvent('valid', this);
28009 * Mark this field as invalid
28010 * @param {String} msg The validation message
28012 markInvalid : function(msg)
28015 var label = this.el.select('label', true).first();
28016 var icon = this.el.select('i.fa-star', true).first();
28018 if(label && !icon){
28019 this.el.select('.roo-date-split-field-label', true).createChild({
28021 cls : 'text-danger fa fa-lg fa-star',
28022 tooltip : 'This field is required',
28023 style : 'margin-right:5px;'
28027 this.fireEvent('invalid', this, msg);
28030 clearInvalid : function()
28032 var label = this.el.select('label', true).first();
28033 var icon = this.el.select('i.fa-star', true).first();
28039 this.fireEvent('valid', this);
28042 getName: function()