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);
7979 Roo.log(this.before);
7980 inputblock.cn.push({
7982 cls : 'roo-input-before input-group-' +
7983 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7987 inputblock.cn.push(input);
7989 if (this.after && typeof(this.after) == 'string') {
7990 inputblock.cn.push({
7992 cls : 'roo-input-after input-group-addon',
7996 if (this.after && typeof(this.after) == 'object') {
7997 this.after = Roo.factory(this.after);
7998 Roo.log(this.after);
7999 inputblock.cn.push({
8001 cls : 'roo-input-after input-group-' +
8002 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8006 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8007 inputblock.cls += ' has-feedback';
8008 inputblock.cn.push(feedback);
8012 if (align ==='left' && this.fieldLabel.length) {
8013 Roo.log("left and has label");
8019 cls : 'control-label col-sm-' + this.labelWidth,
8020 html : this.fieldLabel
8024 cls : "col-sm-" + (12 - this.labelWidth),
8031 } else if ( this.fieldLabel.length) {
8037 //cls : 'input-group-addon',
8038 html : this.fieldLabel
8048 Roo.log(" no label && no align");
8057 Roo.log('input-parentType: ' + this.parentType);
8059 if (this.parentType === 'Navbar' && this.parent().bar) {
8060 cfg.cls += ' navbar-form';
8068 * return the real input element.
8070 inputEl: function ()
8072 return this.el.select('input.form-control',true).first();
8075 tooltipEl : function()
8077 return this.inputEl();
8080 setDisabled : function(v)
8082 var i = this.inputEl().dom;
8084 i.removeAttribute('disabled');
8088 i.setAttribute('disabled','true');
8090 initEvents : function()
8093 this.inputEl().on("keydown" , this.fireKey, this);
8094 this.inputEl().on("focus", this.onFocus, this);
8095 this.inputEl().on("blur", this.onBlur, this);
8097 this.inputEl().relayEvent('keyup', this);
8099 // reference to original value for reset
8100 this.originalValue = this.getValue();
8101 //Roo.form.TextField.superclass.initEvents.call(this);
8102 if(this.validationEvent == 'keyup'){
8103 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8104 this.inputEl().on('keyup', this.filterValidation, this);
8106 else if(this.validationEvent !== false){
8107 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8110 if(this.selectOnFocus){
8111 this.on("focus", this.preFocus, this);
8114 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8115 this.inputEl().on("keypress", this.filterKeys, this);
8118 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8119 this.el.on("click", this.autoSize, this);
8122 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8123 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8126 if (typeof(this.before) == 'object') {
8127 this.before.render(this.el.select('.roo-input-before',true).first());
8129 if (typeof(this.after) == 'object') {
8130 this.after.render(this.el.select('.roo-input-after',true).first());
8135 filterValidation : function(e){
8136 if(!e.isNavKeyPress()){
8137 this.validationTask.delay(this.validationDelay);
8141 * Validates the field value
8142 * @return {Boolean} True if the value is valid, else false
8144 validate : function(){
8145 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8146 if(this.disabled || this.validateValue(this.getRawValue())){
8157 * Validates a value according to the field's validation rules and marks the field as invalid
8158 * if the validation fails
8159 * @param {Mixed} value The value to validate
8160 * @return {Boolean} True if the value is valid, else false
8162 validateValue : function(value){
8163 if(value.length < 1) { // if it's blank
8164 if(this.allowBlank){
8170 if(value.length < this.minLength){
8173 if(value.length > this.maxLength){
8177 var vt = Roo.form.VTypes;
8178 if(!vt[this.vtype](value, this)){
8182 if(typeof this.validator == "function"){
8183 var msg = this.validator(value);
8189 if(this.regex && !this.regex.test(value)){
8199 fireKey : function(e){
8200 //Roo.log('field ' + e.getKey());
8201 if(e.isNavKeyPress()){
8202 this.fireEvent("specialkey", this, e);
8205 focus : function (selectText){
8207 this.inputEl().focus();
8208 if(selectText === true){
8209 this.inputEl().dom.select();
8215 onFocus : function(){
8216 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8217 // this.el.addClass(this.focusClass);
8220 this.hasFocus = true;
8221 this.startValue = this.getValue();
8222 this.fireEvent("focus", this);
8226 beforeBlur : Roo.emptyFn,
8230 onBlur : function(){
8232 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8233 //this.el.removeClass(this.focusClass);
8235 this.hasFocus = false;
8236 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8239 var v = this.getValue();
8240 if(String(v) !== String(this.startValue)){
8241 this.fireEvent('change', this, v, this.startValue);
8243 this.fireEvent("blur", this);
8247 * Resets the current field value to the originally loaded value and clears any validation messages
8250 this.setValue(this.originalValue);
8254 * Returns the name of the field
8255 * @return {Mixed} name The name field
8257 getName: function(){
8261 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8262 * @return {Mixed} value The field value
8264 getValue : function(){
8266 var v = this.inputEl().getValue();
8271 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8272 * @return {Mixed} value The field value
8274 getRawValue : function(){
8275 var v = this.inputEl().getValue();
8281 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8282 * @param {Mixed} value The value to set
8284 setRawValue : function(v){
8285 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8288 selectText : function(start, end){
8289 var v = this.getRawValue();
8291 start = start === undefined ? 0 : start;
8292 end = end === undefined ? v.length : end;
8293 var d = this.inputEl().dom;
8294 if(d.setSelectionRange){
8295 d.setSelectionRange(start, end);
8296 }else if(d.createTextRange){
8297 var range = d.createTextRange();
8298 range.moveStart("character", start);
8299 range.moveEnd("character", v.length-end);
8306 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8307 * @param {Mixed} value The value to set
8309 setValue : function(v){
8312 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8318 processValue : function(value){
8319 if(this.stripCharsRe){
8320 var newValue = value.replace(this.stripCharsRe, '');
8321 if(newValue !== value){
8322 this.setRawValue(newValue);
8329 preFocus : function(){
8331 if(this.selectOnFocus){
8332 this.inputEl().dom.select();
8335 filterKeys : function(e){
8337 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8340 var c = e.getCharCode(), cc = String.fromCharCode(c);
8341 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8344 if(!this.maskRe.test(cc)){
8349 * Clear any invalid styles/messages for this field
8351 clearInvalid : function(){
8353 if(!this.el || this.preventMark){ // not rendered
8356 this.el.removeClass(this.invalidClass);
8358 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8360 var feedback = this.el.select('.form-control-feedback', true).first();
8363 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8368 this.fireEvent('valid', this);
8372 * Mark this field as valid
8374 markValid : function()
8376 if(!this.el || this.preventMark){ // not rendered
8380 this.el.removeClass([this.invalidClass, this.validClass]);
8382 var feedback = this.el.select('.form-control-feedback', true).first();
8385 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8388 if(this.disabled || this.allowBlank){
8392 var formGroup = this.el.findParent('.form-group', false, true);
8396 var label = formGroup.select('label', true).first();
8397 var icon = formGroup.select('i.fa-star', true).first();
8404 this.el.addClass(this.validClass);
8406 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8408 var feedback = this.el.select('.form-control-feedback', true).first();
8411 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8412 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8417 this.fireEvent('valid', this);
8421 * Mark this field as invalid
8422 * @param {String} msg The validation message
8424 markInvalid : function(msg)
8426 if(!this.el || this.preventMark){ // not rendered
8430 this.el.removeClass([this.invalidClass, this.validClass]);
8432 var feedback = this.el.select('.form-control-feedback', true).first();
8435 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8438 if(this.disabled || this.allowBlank){
8442 var formGroup = this.el.findParent('.form-group', false, true);
8445 var label = formGroup.select('label', true).first();
8446 var icon = formGroup.select('i.fa-star', true).first();
8448 if(!this.getValue().length && label && !icon){
8449 this.el.findParent('.form-group', false, true).createChild({
8451 cls : 'text-danger fa fa-lg fa-star',
8452 tooltip : 'This field is required',
8453 style : 'margin-right:5px;'
8459 this.el.addClass(this.invalidClass);
8461 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8463 var feedback = this.el.select('.form-control-feedback', true).first();
8466 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8468 if(this.getValue().length || this.forceFeedback){
8469 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8476 this.fireEvent('invalid', this, msg);
8479 SafariOnKeyDown : function(event)
8481 // this is a workaround for a password hang bug on chrome/ webkit.
8483 var isSelectAll = false;
8485 if(this.inputEl().dom.selectionEnd > 0){
8486 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8488 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8489 event.preventDefault();
8494 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8496 event.preventDefault();
8497 // this is very hacky as keydown always get's upper case.
8499 var cc = String.fromCharCode(event.getCharCode());
8500 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8504 adjustWidth : function(tag, w){
8505 tag = tag.toLowerCase();
8506 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8507 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8511 if(tag == 'textarea'){
8514 }else if(Roo.isOpera){
8518 if(tag == 'textarea'){
8537 * @class Roo.bootstrap.TextArea
8538 * @extends Roo.bootstrap.Input
8539 * Bootstrap TextArea class
8540 * @cfg {Number} cols Specifies the visible width of a text area
8541 * @cfg {Number} rows Specifies the visible number of lines in a text area
8542 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8543 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8544 * @cfg {string} html text
8547 * Create a new TextArea
8548 * @param {Object} config The config object
8551 Roo.bootstrap.TextArea = function(config){
8552 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8556 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8566 getAutoCreate : function(){
8568 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8579 value : this.value || '',
8580 html: this.html || '',
8581 cls : 'form-control',
8582 placeholder : this.placeholder || ''
8586 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8587 input.maxLength = this.maxLength;
8591 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8595 input.cols = this.cols;
8598 if (this.readOnly) {
8599 input.readonly = true;
8603 input.name = this.name;
8607 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8611 ['xs','sm','md','lg'].map(function(size){
8612 if (settings[size]) {
8613 cfg.cls += ' col-' + size + '-' + settings[size];
8617 var inputblock = input;
8619 if(this.hasFeedback && !this.allowBlank){
8623 cls: 'glyphicon form-control-feedback'
8627 cls : 'has-feedback',
8636 if (this.before || this.after) {
8639 cls : 'input-group',
8643 inputblock.cn.push({
8645 cls : 'input-group-addon',
8650 inputblock.cn.push(input);
8652 if(this.hasFeedback && !this.allowBlank){
8653 inputblock.cls += ' has-feedback';
8654 inputblock.cn.push(feedback);
8658 inputblock.cn.push({
8660 cls : 'input-group-addon',
8667 if (align ==='left' && this.fieldLabel.length) {
8668 Roo.log("left and has label");
8674 cls : 'control-label col-sm-' + this.labelWidth,
8675 html : this.fieldLabel
8679 cls : "col-sm-" + (12 - this.labelWidth),
8686 } else if ( this.fieldLabel.length) {
8692 //cls : 'input-group-addon',
8693 html : this.fieldLabel
8703 Roo.log(" no label && no align");
8713 if (this.disabled) {
8714 input.disabled=true;
8721 * return the real textarea element.
8723 inputEl: function ()
8725 return this.el.select('textarea.form-control',true).first();
8729 * Clear any invalid styles/messages for this field
8731 clearInvalid : function()
8734 if(!this.el || this.preventMark){ // not rendered
8738 var label = this.el.select('label', true).first();
8739 var icon = this.el.select('i.fa-star', true).first();
8745 this.el.removeClass(this.invalidClass);
8747 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8749 var feedback = this.el.select('.form-control-feedback', true).first();
8752 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8757 this.fireEvent('valid', this);
8761 * Mark this field as valid
8763 markValid : function()
8765 if(!this.el || this.preventMark){ // not rendered
8769 this.el.removeClass([this.invalidClass, this.validClass]);
8771 var feedback = this.el.select('.form-control-feedback', true).first();
8774 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8777 if(this.disabled || this.allowBlank){
8781 var label = this.el.select('label', true).first();
8782 var icon = this.el.select('i.fa-star', true).first();
8788 this.el.addClass(this.validClass);
8790 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8792 var feedback = this.el.select('.form-control-feedback', true).first();
8795 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8796 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8801 this.fireEvent('valid', this);
8805 * Mark this field as invalid
8806 * @param {String} msg The validation message
8808 markInvalid : function(msg)
8810 if(!this.el || this.preventMark){ // not rendered
8814 this.el.removeClass([this.invalidClass, this.validClass]);
8816 var feedback = this.el.select('.form-control-feedback', true).first();
8819 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8822 if(this.disabled || this.allowBlank){
8826 var label = this.el.select('label', true).first();
8827 var icon = this.el.select('i.fa-star', true).first();
8829 if(!this.getValue().length && label && !icon){
8830 this.el.createChild({
8832 cls : 'text-danger fa fa-lg fa-star',
8833 tooltip : 'This field is required',
8834 style : 'margin-right:5px;'
8838 this.el.addClass(this.invalidClass);
8840 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8842 var feedback = this.el.select('.form-control-feedback', true).first();
8845 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8847 if(this.getValue().length || this.forceFeedback){
8848 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8855 this.fireEvent('invalid', this, msg);
8863 * trigger field - base class for combo..
8868 * @class Roo.bootstrap.TriggerField
8869 * @extends Roo.bootstrap.Input
8870 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8871 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8872 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8873 * for which you can provide a custom implementation. For example:
8875 var trigger = new Roo.bootstrap.TriggerField();
8876 trigger.onTriggerClick = myTriggerFn;
8877 trigger.applyTo('my-field');
8880 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8881 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8882 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
8883 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8884 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8887 * Create a new TriggerField.
8888 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8889 * to the base TextField)
8891 Roo.bootstrap.TriggerField = function(config){
8892 this.mimicing = false;
8893 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8896 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
8898 * @cfg {String} triggerClass A CSS class to apply to the trigger
8901 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8906 * @cfg {Boolean} removable (true|false) special filter default false
8910 /** @cfg {Boolean} grow @hide */
8911 /** @cfg {Number} growMin @hide */
8912 /** @cfg {Number} growMax @hide */
8918 autoSize: Roo.emptyFn,
8925 actionMode : 'wrap',
8930 getAutoCreate : function(){
8932 var align = this.labelAlign || this.parentLabelAlign();
8937 cls: 'form-group' //input-group
8944 type : this.inputType,
8945 cls : 'form-control',
8946 autocomplete: 'new-password',
8947 placeholder : this.placeholder || ''
8951 input.name = this.name;
8954 input.cls += ' input-' + this.size;
8957 if (this.disabled) {
8958 input.disabled=true;
8961 var inputblock = input;
8963 if(this.hasFeedback && !this.allowBlank){
8967 cls: 'glyphicon form-control-feedback'
8970 if(this.removable && !this.editable && !this.tickable){
8972 cls : 'has-feedback',
8978 cls : 'roo-combo-removable-btn close'
8985 cls : 'has-feedback',
8994 if(this.removable && !this.editable && !this.tickable){
8996 cls : 'roo-removable',
9002 cls : 'roo-combo-removable-btn close'
9009 if (this.before || this.after) {
9012 cls : 'input-group',
9016 inputblock.cn.push({
9018 cls : 'input-group-addon',
9023 inputblock.cn.push(input);
9025 if(this.hasFeedback && !this.allowBlank){
9026 inputblock.cls += ' has-feedback';
9027 inputblock.cn.push(feedback);
9031 inputblock.cn.push({
9033 cls : 'input-group-addon',
9046 cls: 'form-hidden-field'
9054 // Roo.log('multiple');
9062 cls: 'form-hidden-field'
9066 cls: 'select2-choices',
9070 cls: 'select2-search-field',
9083 cls: 'select2-container input-group',
9088 // cls: 'typeahead typeahead-long dropdown-menu',
9089 // style: 'display:none'
9094 if(!this.multiple && this.showToggleBtn){
9100 if (this.caret != false) {
9103 cls: 'fa fa-' + this.caret
9110 cls : 'input-group-addon btn dropdown-toggle',
9115 cls: 'combobox-clear',
9129 combobox.cls += ' select2-container-multi';
9132 if (align ==='left' && this.fieldLabel.length) {
9134 // Roo.log("left and has label");
9140 cls : 'control-label col-sm-' + this.labelWidth,
9141 html : this.fieldLabel
9145 cls : "col-sm-" + (12 - this.labelWidth),
9152 } else if ( this.fieldLabel.length) {
9153 // Roo.log(" label");
9158 //cls : 'input-group-addon',
9159 html : this.fieldLabel
9169 // Roo.log(" no label && no align");
9176 ['xs','sm','md','lg'].map(function(size){
9177 if (settings[size]) {
9178 cfg.cls += ' col-' + size + '-' + settings[size];
9189 onResize : function(w, h){
9190 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9191 // if(typeof w == 'number'){
9192 // var x = w - this.trigger.getWidth();
9193 // this.inputEl().setWidth(this.adjustWidth('input', x));
9194 // this.trigger.setStyle('left', x+'px');
9199 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9202 getResizeEl : function(){
9203 return this.inputEl();
9207 getPositionEl : function(){
9208 return this.inputEl();
9212 alignErrorIcon : function(){
9213 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9217 initEvents : function(){
9221 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9222 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9223 if(!this.multiple && this.showToggleBtn){
9224 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9225 if(this.hideTrigger){
9226 this.trigger.setDisplayed(false);
9228 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9232 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9235 if(this.removable && !this.editable && !this.tickable){
9236 var close = this.closeTriggerEl();
9239 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9240 close.on('click', this.removeBtnClick, this, close);
9244 //this.trigger.addClassOnOver('x-form-trigger-over');
9245 //this.trigger.addClassOnClick('x-form-trigger-click');
9248 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9252 closeTriggerEl : function()
9254 var close = this.el.select('.roo-combo-removable-btn', true).first();
9255 return close ? close : false;
9258 removeBtnClick : function(e, h, el)
9262 if(this.fireEvent("remove", this) !== false){
9267 createList : function()
9269 this.list = Roo.get(document.body).createChild({
9271 cls: 'typeahead typeahead-long dropdown-menu',
9272 style: 'display:none'
9275 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9280 initTrigger : function(){
9285 onDestroy : function(){
9287 this.trigger.removeAllListeners();
9288 // this.trigger.remove();
9291 // this.wrap.remove();
9293 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9297 onFocus : function(){
9298 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9301 this.wrap.addClass('x-trigger-wrap-focus');
9302 this.mimicing = true;
9303 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9304 if(this.monitorTab){
9305 this.el.on("keydown", this.checkTab, this);
9312 checkTab : function(e){
9313 if(e.getKey() == e.TAB){
9319 onBlur : function(){
9324 mimicBlur : function(e, t){
9326 if(!this.wrap.contains(t) && this.validateBlur()){
9333 triggerBlur : function(){
9334 this.mimicing = false;
9335 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9336 if(this.monitorTab){
9337 this.el.un("keydown", this.checkTab, this);
9339 //this.wrap.removeClass('x-trigger-wrap-focus');
9340 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9344 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9345 validateBlur : function(e, t){
9350 onDisable : function(){
9351 this.inputEl().dom.disabled = true;
9352 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9354 // this.wrap.addClass('x-item-disabled');
9359 onEnable : function(){
9360 this.inputEl().dom.disabled = false;
9361 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9363 // this.el.removeClass('x-item-disabled');
9368 onShow : function(){
9369 var ae = this.getActionEl();
9372 ae.dom.style.display = '';
9373 ae.dom.style.visibility = 'visible';
9379 onHide : function(){
9380 var ae = this.getActionEl();
9381 ae.dom.style.display = 'none';
9385 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9386 * by an implementing function.
9388 * @param {EventObject} e
9390 onTriggerClick : Roo.emptyFn
9394 * Ext JS Library 1.1.1
9395 * Copyright(c) 2006-2007, Ext JS, LLC.
9397 * Originally Released Under LGPL - original licence link has changed is not relivant.
9400 * <script type="text/javascript">
9405 * @class Roo.data.SortTypes
9407 * Defines the default sorting (casting?) comparison functions used when sorting data.
9409 Roo.data.SortTypes = {
9411 * Default sort that does nothing
9412 * @param {Mixed} s The value being converted
9413 * @return {Mixed} The comparison value
9420 * The regular expression used to strip tags
9424 stripTagsRE : /<\/?[^>]+>/gi,
9427 * Strips all HTML tags to sort on text only
9428 * @param {Mixed} s The value being converted
9429 * @return {String} The comparison value
9431 asText : function(s){
9432 return String(s).replace(this.stripTagsRE, "");
9436 * Strips all HTML tags to sort on text only - Case insensitive
9437 * @param {Mixed} s The value being converted
9438 * @return {String} The comparison value
9440 asUCText : function(s){
9441 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9445 * Case insensitive string
9446 * @param {Mixed} s The value being converted
9447 * @return {String} The comparison value
9449 asUCString : function(s) {
9450 return String(s).toUpperCase();
9455 * @param {Mixed} s The value being converted
9456 * @return {Number} The comparison value
9458 asDate : function(s) {
9462 if(s instanceof Date){
9465 return Date.parse(String(s));
9470 * @param {Mixed} s The value being converted
9471 * @return {Float} The comparison value
9473 asFloat : function(s) {
9474 var val = parseFloat(String(s).replace(/,/g, ""));
9483 * @param {Mixed} s The value being converted
9484 * @return {Number} The comparison value
9486 asInt : function(s) {
9487 var val = parseInt(String(s).replace(/,/g, ""));
9495 * Ext JS Library 1.1.1
9496 * Copyright(c) 2006-2007, Ext JS, LLC.
9498 * Originally Released Under LGPL - original licence link has changed is not relivant.
9501 * <script type="text/javascript">
9505 * @class Roo.data.Record
9506 * Instances of this class encapsulate both record <em>definition</em> information, and record
9507 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9508 * to access Records cached in an {@link Roo.data.Store} object.<br>
9510 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9511 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9514 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9516 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9517 * {@link #create}. The parameters are the same.
9518 * @param {Array} data An associative Array of data values keyed by the field name.
9519 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9520 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9521 * not specified an integer id is generated.
9523 Roo.data.Record = function(data, id){
9524 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9529 * Generate a constructor for a specific record layout.
9530 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9531 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9532 * Each field definition object may contain the following properties: <ul>
9533 * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
9534 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9535 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9536 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9537 * is being used, then this is a string containing the javascript expression to reference the data relative to
9538 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9539 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9540 * this may be omitted.</p></li>
9541 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9542 * <ul><li>auto (Default, implies no conversion)</li>
9547 * <li>date</li></ul></p></li>
9548 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9549 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9550 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9551 * by the Reader into an object that will be stored in the Record. It is passed the
9552 * following parameters:<ul>
9553 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9555 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9557 * <br>usage:<br><pre><code>
9558 var TopicRecord = Roo.data.Record.create(
9559 {name: 'title', mapping: 'topic_title'},
9560 {name: 'author', mapping: 'username'},
9561 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9562 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9563 {name: 'lastPoster', mapping: 'user2'},
9564 {name: 'excerpt', mapping: 'post_text'}
9567 var myNewRecord = new TopicRecord({
9568 title: 'Do my job please',
9571 lastPost: new Date(),
9572 lastPoster: 'Animal',
9573 excerpt: 'No way dude!'
9575 myStore.add(myNewRecord);
9580 Roo.data.Record.create = function(o){
9582 f.superclass.constructor.apply(this, arguments);
9584 Roo.extend(f, Roo.data.Record);
9585 var p = f.prototype;
9586 p.fields = new Roo.util.MixedCollection(false, function(field){
9589 for(var i = 0, len = o.length; i < len; i++){
9590 p.fields.add(new Roo.data.Field(o[i]));
9592 f.getField = function(name){
9593 return p.fields.get(name);
9598 Roo.data.Record.AUTO_ID = 1000;
9599 Roo.data.Record.EDIT = 'edit';
9600 Roo.data.Record.REJECT = 'reject';
9601 Roo.data.Record.COMMIT = 'commit';
9603 Roo.data.Record.prototype = {
9605 * Readonly flag - true if this record has been modified.
9614 join : function(store){
9619 * Set the named field to the specified value.
9620 * @param {String} name The name of the field to set.
9621 * @param {Object} value The value to set the field to.
9623 set : function(name, value){
9624 if(this.data[name] == value){
9631 if(typeof this.modified[name] == 'undefined'){
9632 this.modified[name] = this.data[name];
9634 this.data[name] = value;
9635 if(!this.editing && this.store){
9636 this.store.afterEdit(this);
9641 * Get the value of the named field.
9642 * @param {String} name The name of the field to get the value of.
9643 * @return {Object} The value of the field.
9645 get : function(name){
9646 return this.data[name];
9650 beginEdit : function(){
9651 this.editing = true;
9656 cancelEdit : function(){
9657 this.editing = false;
9658 delete this.modified;
9662 endEdit : function(){
9663 this.editing = false;
9664 if(this.dirty && this.store){
9665 this.store.afterEdit(this);
9670 * Usually called by the {@link Roo.data.Store} which owns the Record.
9671 * Rejects all changes made to the Record since either creation, or the last commit operation.
9672 * Modified fields are reverted to their original values.
9674 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9675 * of reject operations.
9677 reject : function(){
9678 var m = this.modified;
9680 if(typeof m[n] != "function"){
9681 this.data[n] = m[n];
9685 delete this.modified;
9686 this.editing = false;
9688 this.store.afterReject(this);
9693 * Usually called by the {@link Roo.data.Store} which owns the Record.
9694 * Commits all changes made to the Record since either creation, or the last commit operation.
9696 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9697 * of commit operations.
9699 commit : function(){
9701 delete this.modified;
9702 this.editing = false;
9704 this.store.afterCommit(this);
9709 hasError : function(){
9710 return this.error != null;
9714 clearError : function(){
9719 * Creates a copy of this record.
9720 * @param {String} id (optional) A new record id if you don't want to use this record's id
9723 copy : function(newId) {
9724 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9728 * Ext JS Library 1.1.1
9729 * Copyright(c) 2006-2007, Ext JS, LLC.
9731 * Originally Released Under LGPL - original licence link has changed is not relivant.
9734 * <script type="text/javascript">
9740 * @class Roo.data.Store
9741 * @extends Roo.util.Observable
9742 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9743 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9745 * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
9746 * has no knowledge of the format of the data returned by the Proxy.<br>
9748 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9749 * instances from the data object. These records are cached and made available through accessor functions.
9751 * Creates a new Store.
9752 * @param {Object} config A config object containing the objects needed for the Store to access data,
9753 * and read the data into Records.
9755 Roo.data.Store = function(config){
9756 this.data = new Roo.util.MixedCollection(false);
9757 this.data.getKey = function(o){
9760 this.baseParams = {};
9767 "multisort" : "_multisort"
9770 if(config && config.data){
9771 this.inlineData = config.data;
9775 Roo.apply(this, config);
9777 if(this.reader){ // reader passed
9778 this.reader = Roo.factory(this.reader, Roo.data);
9779 this.reader.xmodule = this.xmodule || false;
9780 if(!this.recordType){
9781 this.recordType = this.reader.recordType;
9783 if(this.reader.onMetaChange){
9784 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9788 if(this.recordType){
9789 this.fields = this.recordType.prototype.fields;
9795 * @event datachanged
9796 * Fires when the data cache has changed, and a widget which is using this Store
9797 * as a Record cache should refresh its view.
9798 * @param {Store} this
9803 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9804 * @param {Store} this
9805 * @param {Object} meta The JSON metadata
9810 * Fires when Records have been added to the Store
9811 * @param {Store} this
9812 * @param {Roo.data.Record[]} records The array of Records added
9813 * @param {Number} index The index at which the record(s) were added
9818 * Fires when a Record has been removed from the Store
9819 * @param {Store} this
9820 * @param {Roo.data.Record} record The Record that was removed
9821 * @param {Number} index The index at which the record was removed
9826 * Fires when a Record has been updated
9827 * @param {Store} this
9828 * @param {Roo.data.Record} record The Record that was updated
9829 * @param {String} operation The update operation being performed. Value may be one of:
9831 Roo.data.Record.EDIT
9832 Roo.data.Record.REJECT
9833 Roo.data.Record.COMMIT
9839 * Fires when the data cache has been cleared.
9840 * @param {Store} this
9845 * Fires before a request is made for a new data object. If the beforeload handler returns false
9846 * the load action will be canceled.
9847 * @param {Store} this
9848 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9852 * @event beforeloadadd
9853 * Fires after a new set of Records has been loaded.
9854 * @param {Store} this
9855 * @param {Roo.data.Record[]} records The Records that were loaded
9856 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9858 beforeloadadd : true,
9861 * Fires after a new set of Records has been loaded, before they are added to the store.
9862 * @param {Store} this
9863 * @param {Roo.data.Record[]} records The Records that were loaded
9864 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9865 * @params {Object} return from reader
9869 * @event loadexception
9870 * Fires if an exception occurs in the Proxy during loading.
9871 * Called with the signature of the Proxy's "loadexception" event.
9872 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9875 * @param {Object} return from JsonData.reader() - success, totalRecords, records
9876 * @param {Object} load options
9877 * @param {Object} jsonData from your request (normally this contains the Exception)
9879 loadexception : true
9883 this.proxy = Roo.factory(this.proxy, Roo.data);
9884 this.proxy.xmodule = this.xmodule || false;
9885 this.relayEvents(this.proxy, ["loadexception"]);
9887 this.sortToggle = {};
9888 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9890 Roo.data.Store.superclass.constructor.call(this);
9892 if(this.inlineData){
9893 this.loadData(this.inlineData);
9894 delete this.inlineData;
9898 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9900 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
9901 * without a remote query - used by combo/forms at present.
9905 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9908 * @cfg {Array} data Inline data to be loaded when the store is initialized.
9911 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9912 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9915 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9916 * on any HTTP request
9919 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9922 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9926 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9927 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9932 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9933 * loaded or when a record is removed. (defaults to false).
9935 pruneModifiedRecords : false,
9941 * Add Records to the Store and fires the add event.
9942 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9944 add : function(records){
9945 records = [].concat(records);
9946 for(var i = 0, len = records.length; i < len; i++){
9947 records[i].join(this);
9949 var index = this.data.length;
9950 this.data.addAll(records);
9951 this.fireEvent("add", this, records, index);
9955 * Remove a Record from the Store and fires the remove event.
9956 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9958 remove : function(record){
9959 var index = this.data.indexOf(record);
9960 this.data.removeAt(index);
9961 if(this.pruneModifiedRecords){
9962 this.modified.remove(record);
9964 this.fireEvent("remove", this, record, index);
9968 * Remove all Records from the Store and fires the clear event.
9970 removeAll : function(){
9972 if(this.pruneModifiedRecords){
9975 this.fireEvent("clear", this);
9979 * Inserts Records to the Store at the given index and fires the add event.
9980 * @param {Number} index The start index at which to insert the passed Records.
9981 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9983 insert : function(index, records){
9984 records = [].concat(records);
9985 for(var i = 0, len = records.length; i < len; i++){
9986 this.data.insert(index, records[i]);
9987 records[i].join(this);
9989 this.fireEvent("add", this, records, index);
9993 * Get the index within the cache of the passed Record.
9994 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9995 * @return {Number} The index of the passed Record. Returns -1 if not found.
9997 indexOf : function(record){
9998 return this.data.indexOf(record);
10002 * Get the index within the cache of the Record with the passed id.
10003 * @param {String} id The id of the Record to find.
10004 * @return {Number} The index of the Record. Returns -1 if not found.
10006 indexOfId : function(id){
10007 return this.data.indexOfKey(id);
10011 * Get the Record with the specified id.
10012 * @param {String} id The id of the Record to find.
10013 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10015 getById : function(id){
10016 return this.data.key(id);
10020 * Get the Record at the specified index.
10021 * @param {Number} index The index of the Record to find.
10022 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10024 getAt : function(index){
10025 return this.data.itemAt(index);
10029 * Returns a range of Records between specified indices.
10030 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10031 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10032 * @return {Roo.data.Record[]} An array of Records
10034 getRange : function(start, end){
10035 return this.data.getRange(start, end);
10039 storeOptions : function(o){
10040 o = Roo.apply({}, o);
10043 this.lastOptions = o;
10047 * Loads the Record cache from the configured Proxy using the configured Reader.
10049 * If using remote paging, then the first load call must specify the <em>start</em>
10050 * and <em>limit</em> properties in the options.params property to establish the initial
10051 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10053 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10054 * and this call will return before the new data has been loaded. Perform any post-processing
10055 * in a callback function, or in a "load" event handler.</strong>
10057 * @param {Object} options An object containing properties which control loading options:<ul>
10058 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10059 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10060 * passed the following arguments:<ul>
10061 * <li>r : Roo.data.Record[]</li>
10062 * <li>options: Options object from the load call</li>
10063 * <li>success: Boolean success indicator</li></ul></li>
10064 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10065 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10068 load : function(options){
10069 options = options || {};
10070 if(this.fireEvent("beforeload", this, options) !== false){
10071 this.storeOptions(options);
10072 var p = Roo.apply(options.params || {}, this.baseParams);
10073 // if meta was not loaded from remote source.. try requesting it.
10074 if (!this.reader.metaFromRemote) {
10075 p._requestMeta = 1;
10077 if(this.sortInfo && this.remoteSort){
10078 var pn = this.paramNames;
10079 p[pn["sort"]] = this.sortInfo.field;
10080 p[pn["dir"]] = this.sortInfo.direction;
10082 if (this.multiSort) {
10083 var pn = this.paramNames;
10084 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10087 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10092 * Reloads the Record cache from the configured Proxy using the configured Reader and
10093 * the options from the last load operation performed.
10094 * @param {Object} options (optional) An object containing properties which may override the options
10095 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10096 * the most recently used options are reused).
10098 reload : function(options){
10099 this.load(Roo.applyIf(options||{}, this.lastOptions));
10103 // Called as a callback by the Reader during a load operation.
10104 loadRecords : function(o, options, success){
10105 if(!o || success === false){
10106 if(success !== false){
10107 this.fireEvent("load", this, [], options, o);
10109 if(options.callback){
10110 options.callback.call(options.scope || this, [], options, false);
10114 // if data returned failure - throw an exception.
10115 if (o.success === false) {
10116 // show a message if no listener is registered.
10117 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10118 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10120 // loadmask wil be hooked into this..
10121 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10124 var r = o.records, t = o.totalRecords || r.length;
10126 this.fireEvent("beforeloadadd", this, r, options, o);
10128 if(!options || options.add !== true){
10129 if(this.pruneModifiedRecords){
10130 this.modified = [];
10132 for(var i = 0, len = r.length; i < len; i++){
10136 this.data = this.snapshot;
10137 delete this.snapshot;
10140 this.data.addAll(r);
10141 this.totalLength = t;
10143 this.fireEvent("datachanged", this);
10145 this.totalLength = Math.max(t, this.data.length+r.length);
10148 this.fireEvent("load", this, r, options, o);
10149 if(options.callback){
10150 options.callback.call(options.scope || this, r, options, true);
10156 * Loads data from a passed data block. A Reader which understands the format of the data
10157 * must have been configured in the constructor.
10158 * @param {Object} data The data block from which to read the Records. The format of the data expected
10159 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10160 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10162 loadData : function(o, append){
10163 var r = this.reader.readRecords(o);
10164 this.loadRecords(r, {add: append}, true);
10168 * Gets the number of cached records.
10170 * <em>If using paging, this may not be the total size of the dataset. If the data object
10171 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10172 * the data set size</em>
10174 getCount : function(){
10175 return this.data.length || 0;
10179 * Gets the total number of records in the dataset as returned by the server.
10181 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10182 * the dataset size</em>
10184 getTotalCount : function(){
10185 return this.totalLength || 0;
10189 * Returns the sort state of the Store as an object with two properties:
10191 field {String} The name of the field by which the Records are sorted
10192 direction {String} The sort order, "ASC" or "DESC"
10195 getSortState : function(){
10196 return this.sortInfo;
10200 applySort : function(){
10201 if(this.sortInfo && !this.remoteSort){
10202 var s = this.sortInfo, f = s.field;
10203 var st = this.fields.get(f).sortType;
10204 var fn = function(r1, r2){
10205 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10206 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10208 this.data.sort(s.direction, fn);
10209 if(this.snapshot && this.snapshot != this.data){
10210 this.snapshot.sort(s.direction, fn);
10216 * Sets the default sort column and order to be used by the next load operation.
10217 * @param {String} fieldName The name of the field to sort by.
10218 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10220 setDefaultSort : function(field, dir){
10221 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10225 * Sort the Records.
10226 * If remote sorting is used, the sort is performed on the server, and the cache is
10227 * reloaded. If local sorting is used, the cache is sorted internally.
10228 * @param {String} fieldName The name of the field to sort by.
10229 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10231 sort : function(fieldName, dir){
10232 var f = this.fields.get(fieldName);
10234 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10236 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10237 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10242 this.sortToggle[f.name] = dir;
10243 this.sortInfo = {field: f.name, direction: dir};
10244 if(!this.remoteSort){
10246 this.fireEvent("datachanged", this);
10248 this.load(this.lastOptions);
10253 * Calls the specified function for each of the Records in the cache.
10254 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10255 * Returning <em>false</em> aborts and exits the iteration.
10256 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10258 each : function(fn, scope){
10259 this.data.each(fn, scope);
10263 * Gets all records modified since the last commit. Modified records are persisted across load operations
10264 * (e.g., during paging).
10265 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10267 getModifiedRecords : function(){
10268 return this.modified;
10272 createFilterFn : function(property, value, anyMatch){
10273 if(!value.exec){ // not a regex
10274 value = String(value);
10275 if(value.length == 0){
10278 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10280 return function(r){
10281 return value.test(r.data[property]);
10286 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10287 * @param {String} property A field on your records
10288 * @param {Number} start The record index to start at (defaults to 0)
10289 * @param {Number} end The last record index to include (defaults to length - 1)
10290 * @return {Number} The sum
10292 sum : function(property, start, end){
10293 var rs = this.data.items, v = 0;
10294 start = start || 0;
10295 end = (end || end === 0) ? end : rs.length-1;
10297 for(var i = start; i <= end; i++){
10298 v += (rs[i].data[property] || 0);
10304 * Filter the records by a specified property.
10305 * @param {String} field A field on your records
10306 * @param {String/RegExp} value Either a string that the field
10307 * should start with or a RegExp to test against the field
10308 * @param {Boolean} anyMatch True to match any part not just the beginning
10310 filter : function(property, value, anyMatch){
10311 var fn = this.createFilterFn(property, value, anyMatch);
10312 return fn ? this.filterBy(fn) : this.clearFilter();
10316 * Filter by a function. The specified function will be called with each
10317 * record in this data source. If the function returns true the record is included,
10318 * otherwise it is filtered.
10319 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10320 * @param {Object} scope (optional) The scope of the function (defaults to this)
10322 filterBy : function(fn, scope){
10323 this.snapshot = this.snapshot || this.data;
10324 this.data = this.queryBy(fn, scope||this);
10325 this.fireEvent("datachanged", this);
10329 * Query the records by a specified property.
10330 * @param {String} field A field on your records
10331 * @param {String/RegExp} value Either a string that the field
10332 * should start with or a RegExp to test against the field
10333 * @param {Boolean} anyMatch True to match any part not just the beginning
10334 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10336 query : function(property, value, anyMatch){
10337 var fn = this.createFilterFn(property, value, anyMatch);
10338 return fn ? this.queryBy(fn) : this.data.clone();
10342 * Query by a function. The specified function will be called with each
10343 * record in this data source. If the function returns true the record is included
10345 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10346 * @param {Object} scope (optional) The scope of the function (defaults to this)
10347 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10349 queryBy : function(fn, scope){
10350 var data = this.snapshot || this.data;
10351 return data.filterBy(fn, scope||this);
10355 * Collects unique values for a particular dataIndex from this store.
10356 * @param {String} dataIndex The property to collect
10357 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10358 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10359 * @return {Array} An array of the unique values
10361 collect : function(dataIndex, allowNull, bypassFilter){
10362 var d = (bypassFilter === true && this.snapshot) ?
10363 this.snapshot.items : this.data.items;
10364 var v, sv, r = [], l = {};
10365 for(var i = 0, len = d.length; i < len; i++){
10366 v = d[i].data[dataIndex];
10368 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10377 * Revert to a view of the Record cache with no filtering applied.
10378 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10380 clearFilter : function(suppressEvent){
10381 if(this.snapshot && this.snapshot != this.data){
10382 this.data = this.snapshot;
10383 delete this.snapshot;
10384 if(suppressEvent !== true){
10385 this.fireEvent("datachanged", this);
10391 afterEdit : function(record){
10392 if(this.modified.indexOf(record) == -1){
10393 this.modified.push(record);
10395 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10399 afterReject : function(record){
10400 this.modified.remove(record);
10401 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10405 afterCommit : function(record){
10406 this.modified.remove(record);
10407 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10411 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10412 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10414 commitChanges : function(){
10415 var m = this.modified.slice(0);
10416 this.modified = [];
10417 for(var i = 0, len = m.length; i < len; i++){
10423 * Cancel outstanding changes on all changed records.
10425 rejectChanges : function(){
10426 var m = this.modified.slice(0);
10427 this.modified = [];
10428 for(var i = 0, len = m.length; i < len; i++){
10433 onMetaChange : function(meta, rtype, o){
10434 this.recordType = rtype;
10435 this.fields = rtype.prototype.fields;
10436 delete this.snapshot;
10437 this.sortInfo = meta.sortInfo || this.sortInfo;
10438 this.modified = [];
10439 this.fireEvent('metachange', this, this.reader.meta);
10442 moveIndex : function(data, type)
10444 var index = this.indexOf(data);
10446 var newIndex = index + type;
10450 this.insert(newIndex, data);
10455 * Ext JS Library 1.1.1
10456 * Copyright(c) 2006-2007, Ext JS, LLC.
10458 * Originally Released Under LGPL - original licence link has changed is not relivant.
10461 * <script type="text/javascript">
10465 * @class Roo.data.SimpleStore
10466 * @extends Roo.data.Store
10467 * Small helper class to make creating Stores from Array data easier.
10468 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10469 * @cfg {Array} fields An array of field definition objects, or field name strings.
10470 * @cfg {Array} data The multi-dimensional array of data
10472 * @param {Object} config
10474 Roo.data.SimpleStore = function(config){
10475 Roo.data.SimpleStore.superclass.constructor.call(this, {
10477 reader: new Roo.data.ArrayReader({
10480 Roo.data.Record.create(config.fields)
10482 proxy : new Roo.data.MemoryProxy(config.data)
10486 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10488 * Ext JS Library 1.1.1
10489 * Copyright(c) 2006-2007, Ext JS, LLC.
10491 * Originally Released Under LGPL - original licence link has changed is not relivant.
10494 * <script type="text/javascript">
10499 * @extends Roo.data.Store
10500 * @class Roo.data.JsonStore
10501 * Small helper class to make creating Stores for JSON data easier. <br/>
10503 var store = new Roo.data.JsonStore({
10504 url: 'get-images.php',
10506 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10509 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10510 * JsonReader and HttpProxy (unless inline data is provided).</b>
10511 * @cfg {Array} fields An array of field definition objects, or field name strings.
10513 * @param {Object} config
10515 Roo.data.JsonStore = function(c){
10516 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10517 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10518 reader: new Roo.data.JsonReader(c, c.fields)
10521 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10523 * Ext JS Library 1.1.1
10524 * Copyright(c) 2006-2007, Ext JS, LLC.
10526 * Originally Released Under LGPL - original licence link has changed is not relivant.
10529 * <script type="text/javascript">
10533 Roo.data.Field = function(config){
10534 if(typeof config == "string"){
10535 config = {name: config};
10537 Roo.apply(this, config);
10540 this.type = "auto";
10543 var st = Roo.data.SortTypes;
10544 // named sortTypes are supported, here we look them up
10545 if(typeof this.sortType == "string"){
10546 this.sortType = st[this.sortType];
10549 // set default sortType for strings and dates
10550 if(!this.sortType){
10553 this.sortType = st.asUCString;
10556 this.sortType = st.asDate;
10559 this.sortType = st.none;
10564 var stripRe = /[\$,%]/g;
10566 // prebuilt conversion function for this field, instead of
10567 // switching every time we're reading a value
10569 var cv, dateFormat = this.dateFormat;
10574 cv = function(v){ return v; };
10577 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10581 return v !== undefined && v !== null && v !== '' ?
10582 parseInt(String(v).replace(stripRe, ""), 10) : '';
10587 return v !== undefined && v !== null && v !== '' ?
10588 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10593 cv = function(v){ return v === true || v === "true" || v == 1; };
10600 if(v instanceof Date){
10604 if(dateFormat == "timestamp"){
10605 return new Date(v*1000);
10607 return Date.parseDate(v, dateFormat);
10609 var parsed = Date.parse(v);
10610 return parsed ? new Date(parsed) : null;
10619 Roo.data.Field.prototype = {
10627 * Ext JS Library 1.1.1
10628 * Copyright(c) 2006-2007, Ext JS, LLC.
10630 * Originally Released Under LGPL - original licence link has changed is not relivant.
10633 * <script type="text/javascript">
10636 // Base class for reading structured data from a data source. This class is intended to be
10637 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10640 * @class Roo.data.DataReader
10641 * Base class for reading structured data from a data source. This class is intended to be
10642 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10645 Roo.data.DataReader = function(meta, recordType){
10649 this.recordType = recordType instanceof Array ?
10650 Roo.data.Record.create(recordType) : recordType;
10653 Roo.data.DataReader.prototype = {
10655 * Create an empty record
10656 * @param {Object} data (optional) - overlay some values
10657 * @return {Roo.data.Record} record created.
10659 newRow : function(d) {
10661 this.recordType.prototype.fields.each(function(c) {
10663 case 'int' : da[c.name] = 0; break;
10664 case 'date' : da[c.name] = new Date(); break;
10665 case 'float' : da[c.name] = 0.0; break;
10666 case 'boolean' : da[c.name] = false; break;
10667 default : da[c.name] = ""; break;
10671 return new this.recordType(Roo.apply(da, d));
10676 * Ext JS Library 1.1.1
10677 * Copyright(c) 2006-2007, Ext JS, LLC.
10679 * Originally Released Under LGPL - original licence link has changed is not relivant.
10682 * <script type="text/javascript">
10686 * @class Roo.data.DataProxy
10687 * @extends Roo.data.Observable
10688 * This class is an abstract base class for implementations which provide retrieval of
10689 * unformatted data objects.<br>
10691 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10692 * (of the appropriate type which knows how to parse the data object) to provide a block of
10693 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10695 * Custom implementations must implement the load method as described in
10696 * {@link Roo.data.HttpProxy#load}.
10698 Roo.data.DataProxy = function(){
10701 * @event beforeload
10702 * Fires before a network request is made to retrieve a data object.
10703 * @param {Object} This DataProxy object.
10704 * @param {Object} params The params parameter to the load function.
10709 * Fires before the load method's callback is called.
10710 * @param {Object} This DataProxy object.
10711 * @param {Object} o The data object.
10712 * @param {Object} arg The callback argument object passed to the load function.
10716 * @event loadexception
10717 * Fires if an Exception occurs during data retrieval.
10718 * @param {Object} This DataProxy object.
10719 * @param {Object} o The data object.
10720 * @param {Object} arg The callback argument object passed to the load function.
10721 * @param {Object} e The Exception.
10723 loadexception : true
10725 Roo.data.DataProxy.superclass.constructor.call(this);
10728 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10731 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10735 * Ext JS Library 1.1.1
10736 * Copyright(c) 2006-2007, Ext JS, LLC.
10738 * Originally Released Under LGPL - original licence link has changed is not relivant.
10741 * <script type="text/javascript">
10744 * @class Roo.data.MemoryProxy
10745 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10746 * to the Reader when its load method is called.
10748 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10750 Roo.data.MemoryProxy = function(data){
10754 Roo.data.MemoryProxy.superclass.constructor.call(this);
10758 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10760 * Load data from the requested source (in this case an in-memory
10761 * data object passed to the constructor), read the data object into
10762 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10763 * process that block using the passed callback.
10764 * @param {Object} params This parameter is not used by the MemoryProxy class.
10765 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10766 * object into a block of Roo.data.Records.
10767 * @param {Function} callback The function into which to pass the block of Roo.data.records.
10768 * The function must be passed <ul>
10769 * <li>The Record block object</li>
10770 * <li>The "arg" argument from the load function</li>
10771 * <li>A boolean success indicator</li>
10773 * @param {Object} scope The scope in which to call the callback
10774 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10776 load : function(params, reader, callback, scope, arg){
10777 params = params || {};
10780 result = reader.readRecords(this.data);
10782 this.fireEvent("loadexception", this, arg, null, e);
10783 callback.call(scope, null, arg, false);
10786 callback.call(scope, result, arg, true);
10790 update : function(params, records){
10795 * Ext JS Library 1.1.1
10796 * Copyright(c) 2006-2007, Ext JS, LLC.
10798 * Originally Released Under LGPL - original licence link has changed is not relivant.
10801 * <script type="text/javascript">
10804 * @class Roo.data.HttpProxy
10805 * @extends Roo.data.DataProxy
10806 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10807 * configured to reference a certain URL.<br><br>
10809 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10810 * from which the running page was served.<br><br>
10812 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10814 * Be aware that to enable the browser to parse an XML document, the server must set
10815 * the Content-Type header in the HTTP response to "text/xml".
10817 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10818 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
10819 * will be used to make the request.
10821 Roo.data.HttpProxy = function(conn){
10822 Roo.data.HttpProxy.superclass.constructor.call(this);
10823 // is conn a conn config or a real conn?
10825 this.useAjax = !conn || !conn.events;
10829 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10830 // thse are take from connection...
10833 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10836 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10837 * extra parameters to each request made by this object. (defaults to undefined)
10840 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10841 * to each request made by this object. (defaults to undefined)
10844 * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
10847 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10850 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10856 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10860 * Return the {@link Roo.data.Connection} object being used by this Proxy.
10861 * @return {Connection} The Connection object. This object may be used to subscribe to events on
10862 * a finer-grained basis than the DataProxy events.
10864 getConnection : function(){
10865 return this.useAjax ? Roo.Ajax : this.conn;
10869 * Load data from the configured {@link Roo.data.Connection}, read the data object into
10870 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10871 * process that block using the passed callback.
10872 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10873 * for the request to the remote server.
10874 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10875 * object into a block of Roo.data.Records.
10876 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10877 * The function must be passed <ul>
10878 * <li>The Record block object</li>
10879 * <li>The "arg" argument from the load function</li>
10880 * <li>A boolean success indicator</li>
10882 * @param {Object} scope The scope in which to call the callback
10883 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10885 load : function(params, reader, callback, scope, arg){
10886 if(this.fireEvent("beforeload", this, params) !== false){
10888 params : params || {},
10890 callback : callback,
10895 callback : this.loadResponse,
10899 Roo.applyIf(o, this.conn);
10900 if(this.activeRequest){
10901 Roo.Ajax.abort(this.activeRequest);
10903 this.activeRequest = Roo.Ajax.request(o);
10905 this.conn.request(o);
10908 callback.call(scope||this, null, arg, false);
10913 loadResponse : function(o, success, response){
10914 delete this.activeRequest;
10916 this.fireEvent("loadexception", this, o, response);
10917 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10922 result = o.reader.read(response);
10924 this.fireEvent("loadexception", this, o, response, e);
10925 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10929 this.fireEvent("load", this, o, o.request.arg);
10930 o.request.callback.call(o.request.scope, result, o.request.arg, true);
10934 update : function(dataSet){
10939 updateResponse : function(dataSet){
10944 * Ext JS Library 1.1.1
10945 * Copyright(c) 2006-2007, Ext JS, LLC.
10947 * Originally Released Under LGPL - original licence link has changed is not relivant.
10950 * <script type="text/javascript">
10954 * @class Roo.data.ScriptTagProxy
10955 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10956 * other than the originating domain of the running page.<br><br>
10958 * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
10959 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10961 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10962 * source code that is used as the source inside a <script> tag.<br><br>
10964 * In order for the browser to process the returned data, the server must wrap the data object
10965 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10966 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10967 * depending on whether the callback name was passed:
10970 boolean scriptTag = false;
10971 String cb = request.getParameter("callback");
10974 response.setContentType("text/javascript");
10976 response.setContentType("application/x-json");
10978 Writer out = response.getWriter();
10980 out.write(cb + "(");
10982 out.print(dataBlock.toJsonString());
10989 * @param {Object} config A configuration object.
10991 Roo.data.ScriptTagProxy = function(config){
10992 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10993 Roo.apply(this, config);
10994 this.head = document.getElementsByTagName("head")[0];
10997 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10999 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11001 * @cfg {String} url The URL from which to request the data object.
11004 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11008 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11009 * the server the name of the callback function set up by the load call to process the returned data object.
11010 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11011 * javascript output which calls this named function passing the data object as its only parameter.
11013 callbackParam : "callback",
11015 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11016 * name to the request.
11021 * Load data from the configured URL, read the data object into
11022 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11023 * process that block using the passed callback.
11024 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11025 * for the request to the remote server.
11026 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11027 * object into a block of Roo.data.Records.
11028 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11029 * The function must be passed <ul>
11030 * <li>The Record block object</li>
11031 * <li>The "arg" argument from the load function</li>
11032 * <li>A boolean success indicator</li>
11034 * @param {Object} scope The scope in which to call the callback
11035 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11037 load : function(params, reader, callback, scope, arg){
11038 if(this.fireEvent("beforeload", this, params) !== false){
11040 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11042 var url = this.url;
11043 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11045 url += "&_dc=" + (new Date().getTime());
11047 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11050 cb : "stcCallback"+transId,
11051 scriptId : "stcScript"+transId,
11055 callback : callback,
11061 window[trans.cb] = function(o){
11062 conn.handleResponse(o, trans);
11065 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11067 if(this.autoAbort !== false){
11071 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11073 var script = document.createElement("script");
11074 script.setAttribute("src", url);
11075 script.setAttribute("type", "text/javascript");
11076 script.setAttribute("id", trans.scriptId);
11077 this.head.appendChild(script);
11079 this.trans = trans;
11081 callback.call(scope||this, null, arg, false);
11086 isLoading : function(){
11087 return this.trans ? true : false;
11091 * Abort the current server request.
11093 abort : function(){
11094 if(this.isLoading()){
11095 this.destroyTrans(this.trans);
11100 destroyTrans : function(trans, isLoaded){
11101 this.head.removeChild(document.getElementById(trans.scriptId));
11102 clearTimeout(trans.timeoutId);
11104 window[trans.cb] = undefined;
11106 delete window[trans.cb];
11109 // if hasn't been loaded, wait for load to remove it to prevent script error
11110 window[trans.cb] = function(){
11111 window[trans.cb] = undefined;
11113 delete window[trans.cb];
11120 handleResponse : function(o, trans){
11121 this.trans = false;
11122 this.destroyTrans(trans, true);
11125 result = trans.reader.readRecords(o);
11127 this.fireEvent("loadexception", this, o, trans.arg, e);
11128 trans.callback.call(trans.scope||window, null, trans.arg, false);
11131 this.fireEvent("load", this, o, trans.arg);
11132 trans.callback.call(trans.scope||window, result, trans.arg, true);
11136 handleFailure : function(trans){
11137 this.trans = false;
11138 this.destroyTrans(trans, false);
11139 this.fireEvent("loadexception", this, null, trans.arg);
11140 trans.callback.call(trans.scope||window, null, trans.arg, false);
11144 * Ext JS Library 1.1.1
11145 * Copyright(c) 2006-2007, Ext JS, LLC.
11147 * Originally Released Under LGPL - original licence link has changed is not relivant.
11150 * <script type="text/javascript">
11154 * @class Roo.data.JsonReader
11155 * @extends Roo.data.DataReader
11156 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11157 * based on mappings in a provided Roo.data.Record constructor.
11159 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11160 * in the reply previously.
11165 var RecordDef = Roo.data.Record.create([
11166 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11167 {name: 'occupation'} // This field will use "occupation" as the mapping.
11169 var myReader = new Roo.data.JsonReader({
11170 totalProperty: "results", // The property which contains the total dataset size (optional)
11171 root: "rows", // The property which contains an Array of row objects
11172 id: "id" // The property within each row object that provides an ID for the record (optional)
11176 * This would consume a JSON file like this:
11178 { 'results': 2, 'rows': [
11179 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11180 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11183 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11184 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11185 * paged from the remote server.
11186 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11187 * @cfg {String} root name of the property which contains the Array of row objects.
11188 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11189 * @cfg {Array} fields Array of field definition objects
11191 * Create a new JsonReader
11192 * @param {Object} meta Metadata configuration options
11193 * @param {Object} recordType Either an Array of field definition objects,
11194 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11196 Roo.data.JsonReader = function(meta, recordType){
11199 // set some defaults:
11200 Roo.applyIf(meta, {
11201 totalProperty: 'total',
11202 successProperty : 'success',
11207 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11209 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11212 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11213 * Used by Store query builder to append _requestMeta to params.
11216 metaFromRemote : false,
11218 * This method is only used by a DataProxy which has retrieved data from a remote server.
11219 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11220 * @return {Object} data A data block which is used by an Roo.data.Store object as
11221 * a cache of Roo.data.Records.
11223 read : function(response){
11224 var json = response.responseText;
11226 var o = /* eval:var:o */ eval("("+json+")");
11228 throw {message: "JsonReader.read: Json object not found"};
11234 this.metaFromRemote = true;
11235 this.meta = o.metaData;
11236 this.recordType = Roo.data.Record.create(o.metaData.fields);
11237 this.onMetaChange(this.meta, this.recordType, o);
11239 return this.readRecords(o);
11242 // private function a store will implement
11243 onMetaChange : function(meta, recordType, o){
11250 simpleAccess: function(obj, subsc) {
11257 getJsonAccessor: function(){
11259 return function(expr) {
11261 return(re.test(expr))
11262 ? new Function("obj", "return obj." + expr)
11267 return Roo.emptyFn;
11272 * Create a data block containing Roo.data.Records from an XML document.
11273 * @param {Object} o An object which contains an Array of row objects in the property specified
11274 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11275 * which contains the total size of the dataset.
11276 * @return {Object} data A data block which is used by an Roo.data.Store object as
11277 * a cache of Roo.data.Records.
11279 readRecords : function(o){
11281 * After any data loads, the raw JSON data is available for further custom processing.
11285 var s = this.meta, Record = this.recordType,
11286 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11288 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11290 if(s.totalProperty) {
11291 this.getTotal = this.getJsonAccessor(s.totalProperty);
11293 if(s.successProperty) {
11294 this.getSuccess = this.getJsonAccessor(s.successProperty);
11296 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11298 var g = this.getJsonAccessor(s.id);
11299 this.getId = function(rec) {
11301 return (r === undefined || r === "") ? null : r;
11304 this.getId = function(){return null;};
11307 for(var jj = 0; jj < fl; jj++){
11309 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11310 this.ef[jj] = this.getJsonAccessor(map);
11314 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11315 if(s.totalProperty){
11316 var vt = parseInt(this.getTotal(o), 10);
11321 if(s.successProperty){
11322 var vs = this.getSuccess(o);
11323 if(vs === false || vs === 'false'){
11328 for(var i = 0; i < c; i++){
11331 var id = this.getId(n);
11332 for(var j = 0; j < fl; j++){
11334 var v = this.ef[j](n);
11336 Roo.log('missing convert for ' + f.name);
11340 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11342 var record = new Record(values, id);
11344 records[i] = record;
11350 totalRecords : totalRecords
11355 * Ext JS Library 1.1.1
11356 * Copyright(c) 2006-2007, Ext JS, LLC.
11358 * Originally Released Under LGPL - original licence link has changed is not relivant.
11361 * <script type="text/javascript">
11365 * @class Roo.data.ArrayReader
11366 * @extends Roo.data.DataReader
11367 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11368 * Each element of that Array represents a row of data fields. The
11369 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11370 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11374 var RecordDef = Roo.data.Record.create([
11375 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11376 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11378 var myReader = new Roo.data.ArrayReader({
11379 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11383 * This would consume an Array like this:
11385 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11387 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11389 * Create a new JsonReader
11390 * @param {Object} meta Metadata configuration options.
11391 * @param {Object} recordType Either an Array of field definition objects
11392 * as specified to {@link Roo.data.Record#create},
11393 * or an {@link Roo.data.Record} object
11394 * created using {@link Roo.data.Record#create}.
11396 Roo.data.ArrayReader = function(meta, recordType){
11397 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11400 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11402 * Create a data block containing Roo.data.Records from an XML document.
11403 * @param {Object} o An Array of row objects which represents the dataset.
11404 * @return {Object} data A data block which is used by an Roo.data.Store object as
11405 * a cache of Roo.data.Records.
11407 readRecords : function(o){
11408 var sid = this.meta ? this.meta.id : null;
11409 var recordType = this.recordType, fields = recordType.prototype.fields;
11412 for(var i = 0; i < root.length; i++){
11415 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11416 for(var j = 0, jlen = fields.length; j < jlen; j++){
11417 var f = fields.items[j];
11418 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11419 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11421 values[f.name] = v;
11423 var record = new recordType(values, id);
11425 records[records.length] = record;
11429 totalRecords : records.length
11438 * @class Roo.bootstrap.ComboBox
11439 * @extends Roo.bootstrap.TriggerField
11440 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11441 * @cfg {Boolean} append (true|false) default false
11442 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11443 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11444 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11445 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11446 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11447 * @cfg {Boolean} animate default true
11448 * @cfg {Boolean} emptyResultText only for touch device
11449 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11451 * Create a new ComboBox.
11452 * @param {Object} config Configuration options
11454 Roo.bootstrap.ComboBox = function(config){
11455 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11459 * Fires when the dropdown list is expanded
11460 * @param {Roo.bootstrap.ComboBox} combo This combo box
11465 * Fires when the dropdown list is collapsed
11466 * @param {Roo.bootstrap.ComboBox} combo This combo box
11470 * @event beforeselect
11471 * Fires before a list item is selected. Return false to cancel the selection.
11472 * @param {Roo.bootstrap.ComboBox} combo This combo box
11473 * @param {Roo.data.Record} record The data record returned from the underlying store
11474 * @param {Number} index The index of the selected item in the dropdown list
11476 'beforeselect' : true,
11479 * Fires when a list item is selected
11480 * @param {Roo.bootstrap.ComboBox} combo This combo box
11481 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11482 * @param {Number} index The index of the selected item in the dropdown list
11486 * @event beforequery
11487 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11488 * The event object passed has these properties:
11489 * @param {Roo.bootstrap.ComboBox} combo This combo box
11490 * @param {String} query The query
11491 * @param {Boolean} forceAll true to force "all" query
11492 * @param {Boolean} cancel true to cancel the query
11493 * @param {Object} e The query event object
11495 'beforequery': true,
11498 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11499 * @param {Roo.bootstrap.ComboBox} combo This combo box
11504 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11505 * @param {Roo.bootstrap.ComboBox} combo This combo box
11506 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11511 * Fires when the remove value from the combobox array
11512 * @param {Roo.bootstrap.ComboBox} combo This combo box
11516 * @event specialfilter
11517 * Fires when specialfilter
11518 * @param {Roo.bootstrap.ComboBox} combo This combo box
11520 'specialfilter' : true,
11523 * Fires when tick the element
11524 * @param {Roo.bootstrap.ComboBox} combo This combo box
11528 * @event touchviewdisplay
11529 * Fires when touch view require special display (default is using displayField)
11530 * @param {Roo.bootstrap.ComboBox} combo This combo box
11531 * @param {Object} cfg set html .
11533 'touchviewdisplay' : true
11538 this.tickItems = [];
11540 this.selectedIndex = -1;
11541 if(this.mode == 'local'){
11542 if(config.queryDelay === undefined){
11543 this.queryDelay = 10;
11545 if(config.minChars === undefined){
11551 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11554 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11555 * rendering into an Roo.Editor, defaults to false)
11558 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11559 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11562 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11565 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11566 * the dropdown list (defaults to undefined, with no header element)
11570 * @cfg {String/Roo.Template} tpl The template to use to render the output
11574 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11576 listWidth: undefined,
11578 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11579 * mode = 'remote' or 'text' if mode = 'local')
11581 displayField: undefined,
11584 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11585 * mode = 'remote' or 'value' if mode = 'local').
11586 * Note: use of a valueField requires the user make a selection
11587 * in order for a value to be mapped.
11589 valueField: undefined,
11593 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11594 * field's data value (defaults to the underlying DOM element's name)
11596 hiddenName: undefined,
11598 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11602 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11604 selectedClass: 'active',
11607 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11611 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11612 * anchor positions (defaults to 'tl-bl')
11614 listAlign: 'tl-bl?',
11616 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11620 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11621 * query specified by the allQuery config option (defaults to 'query')
11623 triggerAction: 'query',
11625 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11626 * (defaults to 4, does not apply if editable = false)
11630 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11631 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11635 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11636 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11640 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11641 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11645 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11646 * when editable = true (defaults to false)
11648 selectOnFocus:false,
11650 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11652 queryParam: 'query',
11654 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
11655 * when mode = 'remote' (defaults to 'Loading...')
11657 loadingText: 'Loading...',
11659 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11663 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11667 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11668 * traditional select (defaults to true)
11672 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11676 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11680 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11681 * listWidth has a higher value)
11685 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11686 * allow the user to set arbitrary text into the field (defaults to false)
11688 forceSelection:false,
11690 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11691 * if typeAhead = true (defaults to 250)
11693 typeAheadDelay : 250,
11695 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11696 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11698 valueNotFoundText : undefined,
11700 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11702 blockFocus : false,
11705 * @cfg {Boolean} disableClear Disable showing of clear button.
11707 disableClear : false,
11709 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
11711 alwaysQuery : false,
11714 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
11719 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11721 invalidClass : "has-warning",
11724 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11726 validClass : "has-success",
11729 * @cfg {Boolean} specialFilter (true|false) special filter default false
11731 specialFilter : false,
11734 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11736 mobileTouchView : true,
11748 btnPosition : 'right',
11749 triggerList : true,
11750 showToggleBtn : true,
11752 emptyResultText: 'Empty',
11753 triggerText : 'Select',
11755 // element that contains real text value.. (when hidden is used..)
11757 getAutoCreate : function()
11765 if(Roo.isTouch && this.mobileTouchView){
11766 cfg = this.getAutoCreateTouchView();
11773 if(!this.tickable){
11774 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11779 * ComboBox with tickable selections
11782 var align = this.labelAlign || this.parentLabelAlign();
11785 cls : 'form-group roo-combobox-tickable' //input-group
11790 cls : 'tickable-buttons',
11795 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11796 html : this.triggerText
11802 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11809 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11816 buttons.cn.unshift({
11818 cls: 'select2-search-field-input'
11824 Roo.each(buttons.cn, function(c){
11826 c.cls += ' btn-' + _this.size;
11829 if (_this.disabled) {
11840 cls: 'form-hidden-field'
11844 cls: 'select2-choices',
11848 cls: 'select2-search-field',
11860 cls: 'select2-container input-group select2-container-multi',
11865 // cls: 'typeahead typeahead-long dropdown-menu',
11866 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
11871 if(this.hasFeedback && !this.allowBlank){
11875 cls: 'glyphicon form-control-feedback'
11878 combobox.cn.push(feedback);
11881 if (align ==='left' && this.fieldLabel.length) {
11883 Roo.log("left and has label");
11889 cls : 'control-label col-sm-' + this.labelWidth,
11890 html : this.fieldLabel
11894 cls : "col-sm-" + (12 - this.labelWidth),
11901 } else if ( this.fieldLabel.length) {
11907 //cls : 'input-group-addon',
11908 html : this.fieldLabel
11918 Roo.log(" no label && no align");
11925 ['xs','sm','md','lg'].map(function(size){
11926 if (settings[size]) {
11927 cfg.cls += ' col-' + size + '-' + settings[size];
11935 _initEventsCalled : false,
11938 initEvents: function()
11941 if (this._initEventsCalled) { // as we call render... prevent looping...
11944 this._initEventsCalled = true;
11947 throw "can not find store for combo";
11950 this.store = Roo.factory(this.store, Roo.data);
11952 // if we are building from html. then this element is so complex, that we can not really
11953 // use the rendered HTML.
11954 // so we have to trash and replace the previous code.
11955 if (Roo.XComponent.build_from_html) {
11957 // remove this element....
11958 var e = this.el.dom, k=0;
11959 while (e ) { e = e.previousSibling; ++k;}
11964 this.rendered = false;
11966 this.render(this.parent().getChildContainer(true), k);
11977 if(Roo.isTouch && this.mobileTouchView){
11978 this.initTouchView();
11983 this.initTickableEvents();
11987 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11989 if(this.hiddenName){
11991 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11993 this.hiddenField.dom.value =
11994 this.hiddenValue !== undefined ? this.hiddenValue :
11995 this.value !== undefined ? this.value : '';
11997 // prevent input submission
11998 this.el.dom.removeAttribute('name');
11999 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12004 // this.el.dom.setAttribute('autocomplete', 'off');
12007 var cls = 'x-combo-list';
12009 //this.list = new Roo.Layer({
12010 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12016 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12017 _this.list.setWidth(lw);
12020 this.list.on('mouseover', this.onViewOver, this);
12021 this.list.on('mousemove', this.onViewMove, this);
12023 this.list.on('scroll', this.onViewScroll, this);
12026 this.list.swallowEvent('mousewheel');
12027 this.assetHeight = 0;
12030 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12031 this.assetHeight += this.header.getHeight();
12034 this.innerList = this.list.createChild({cls:cls+'-inner'});
12035 this.innerList.on('mouseover', this.onViewOver, this);
12036 this.innerList.on('mousemove', this.onViewMove, this);
12037 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12039 if(this.allowBlank && !this.pageSize && !this.disableClear){
12040 this.footer = this.list.createChild({cls:cls+'-ft'});
12041 this.pageTb = new Roo.Toolbar(this.footer);
12045 this.footer = this.list.createChild({cls:cls+'-ft'});
12046 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12047 {pageSize: this.pageSize});
12051 if (this.pageTb && this.allowBlank && !this.disableClear) {
12053 this.pageTb.add(new Roo.Toolbar.Fill(), {
12054 cls: 'x-btn-icon x-btn-clear',
12056 handler: function()
12059 _this.clearValue();
12060 _this.onSelect(false, -1);
12065 this.assetHeight += this.footer.getHeight();
12070 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12073 this.view = new Roo.View(this.list, this.tpl, {
12074 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12076 //this.view.wrapEl.setDisplayed(false);
12077 this.view.on('click', this.onViewClick, this);
12081 this.store.on('beforeload', this.onBeforeLoad, this);
12082 this.store.on('load', this.onLoad, this);
12083 this.store.on('loadexception', this.onLoadException, this);
12085 if(this.resizable){
12086 this.resizer = new Roo.Resizable(this.list, {
12087 pinned:true, handles:'se'
12089 this.resizer.on('resize', function(r, w, h){
12090 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12091 this.listWidth = w;
12092 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12093 this.restrictHeight();
12095 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12098 if(!this.editable){
12099 this.editable = true;
12100 this.setEditable(false);
12105 if (typeof(this.events.add.listeners) != 'undefined') {
12107 this.addicon = this.wrap.createChild(
12108 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12110 this.addicon.on('click', function(e) {
12111 this.fireEvent('add', this);
12114 if (typeof(this.events.edit.listeners) != 'undefined') {
12116 this.editicon = this.wrap.createChild(
12117 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12118 if (this.addicon) {
12119 this.editicon.setStyle('margin-left', '40px');
12121 this.editicon.on('click', function(e) {
12123 // we fire even if inothing is selected..
12124 this.fireEvent('edit', this, this.lastData );
12130 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12131 "up" : function(e){
12132 this.inKeyMode = true;
12136 "down" : function(e){
12137 if(!this.isExpanded()){
12138 this.onTriggerClick();
12140 this.inKeyMode = true;
12145 "enter" : function(e){
12146 // this.onViewClick();
12150 if(this.fireEvent("specialkey", this, e)){
12151 this.onViewClick(false);
12157 "esc" : function(e){
12161 "tab" : function(e){
12164 if(this.fireEvent("specialkey", this, e)){
12165 this.onViewClick(false);
12173 doRelay : function(foo, bar, hname){
12174 if(hname == 'down' || this.scope.isExpanded()){
12175 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12184 this.queryDelay = Math.max(this.queryDelay || 10,
12185 this.mode == 'local' ? 10 : 250);
12188 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12190 if(this.typeAhead){
12191 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12193 if(this.editable !== false){
12194 this.inputEl().on("keyup", this.onKeyUp, this);
12196 if(this.forceSelection){
12197 this.inputEl().on('blur', this.doForce, this);
12201 this.choices = this.el.select('ul.select2-choices', true).first();
12202 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12206 initTickableEvents: function()
12210 if(this.hiddenName){
12212 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12214 this.hiddenField.dom.value =
12215 this.hiddenValue !== undefined ? this.hiddenValue :
12216 this.value !== undefined ? this.value : '';
12218 // prevent input submission
12219 this.el.dom.removeAttribute('name');
12220 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12225 // this.list = this.el.select('ul.dropdown-menu',true).first();
12227 this.choices = this.el.select('ul.select2-choices', true).first();
12228 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12229 if(this.triggerList){
12230 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12233 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12234 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12236 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12237 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12239 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12240 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12242 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12243 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12244 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12247 this.cancelBtn.hide();
12252 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12253 _this.list.setWidth(lw);
12256 this.list.on('mouseover', this.onViewOver, this);
12257 this.list.on('mousemove', this.onViewMove, this);
12259 this.list.on('scroll', this.onViewScroll, this);
12262 this.tpl = '<li class="select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
12265 this.view = new Roo.View(this.list, this.tpl, {
12266 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12269 //this.view.wrapEl.setDisplayed(false);
12270 this.view.on('click', this.onViewClick, this);
12274 this.store.on('beforeload', this.onBeforeLoad, this);
12275 this.store.on('load', this.onLoad, this);
12276 this.store.on('loadexception', this.onLoadException, this);
12279 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12280 "up" : function(e){
12281 this.inKeyMode = true;
12285 "down" : function(e){
12286 this.inKeyMode = true;
12290 "enter" : function(e){
12291 if(this.fireEvent("specialkey", this, e)){
12292 this.onViewClick(false);
12298 "esc" : function(e){
12299 this.onTickableFooterButtonClick(e, false, false);
12302 "tab" : function(e){
12303 this.fireEvent("specialkey", this, e);
12305 this.onTickableFooterButtonClick(e, false, false);
12312 doRelay : function(e, fn, key){
12313 if(this.scope.isExpanded()){
12314 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12323 this.queryDelay = Math.max(this.queryDelay || 10,
12324 this.mode == 'local' ? 10 : 250);
12327 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12329 if(this.typeAhead){
12330 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12333 if(this.editable !== false){
12334 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12339 onDestroy : function(){
12341 this.view.setStore(null);
12342 this.view.el.removeAllListeners();
12343 this.view.el.remove();
12344 this.view.purgeListeners();
12347 this.list.dom.innerHTML = '';
12351 this.store.un('beforeload', this.onBeforeLoad, this);
12352 this.store.un('load', this.onLoad, this);
12353 this.store.un('loadexception', this.onLoadException, this);
12355 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12359 fireKey : function(e){
12360 if(e.isNavKeyPress() && !this.list.isVisible()){
12361 this.fireEvent("specialkey", this, e);
12366 onResize: function(w, h){
12367 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12369 // if(typeof w != 'number'){
12370 // // we do not handle it!?!?
12373 // var tw = this.trigger.getWidth();
12374 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12375 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12377 // this.inputEl().setWidth( this.adjustWidth('input', x));
12379 // //this.trigger.setStyle('left', x+'px');
12381 // if(this.list && this.listWidth === undefined){
12382 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12383 // this.list.setWidth(lw);
12384 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12392 * Allow or prevent the user from directly editing the field text. If false is passed,
12393 * the user will only be able to select from the items defined in the dropdown list. This method
12394 * is the runtime equivalent of setting the 'editable' config option at config time.
12395 * @param {Boolean} value True to allow the user to directly edit the field text
12397 setEditable : function(value){
12398 if(value == this.editable){
12401 this.editable = value;
12403 this.inputEl().dom.setAttribute('readOnly', true);
12404 this.inputEl().on('mousedown', this.onTriggerClick, this);
12405 this.inputEl().addClass('x-combo-noedit');
12407 this.inputEl().dom.setAttribute('readOnly', false);
12408 this.inputEl().un('mousedown', this.onTriggerClick, this);
12409 this.inputEl().removeClass('x-combo-noedit');
12415 onBeforeLoad : function(combo,opts){
12416 if(!this.hasFocus){
12420 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12422 this.restrictHeight();
12423 this.selectedIndex = -1;
12427 onLoad : function(){
12429 this.hasQuery = false;
12431 if(!this.hasFocus){
12435 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12436 this.loading.hide();
12439 if(this.store.getCount() > 0){
12441 this.restrictHeight();
12442 if(this.lastQuery == this.allQuery){
12443 if(this.editable && !this.tickable){
12444 this.inputEl().dom.select();
12448 !this.selectByValue(this.value, true) &&
12451 !this.store.lastOptions ||
12452 typeof(this.store.lastOptions.add) == 'undefined' ||
12453 this.store.lastOptions.add != true
12456 this.select(0, true);
12459 if(this.autoFocus){
12462 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12463 this.taTask.delay(this.typeAheadDelay);
12467 this.onEmptyResults();
12473 onLoadException : function()
12475 this.hasQuery = false;
12477 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12478 this.loading.hide();
12481 if(this.tickable && this.editable){
12486 // only causes errors at present
12487 //Roo.log(this.store.reader.jsonData);
12488 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12490 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12496 onTypeAhead : function(){
12497 if(this.store.getCount() > 0){
12498 var r = this.store.getAt(0);
12499 var newValue = r.data[this.displayField];
12500 var len = newValue.length;
12501 var selStart = this.getRawValue().length;
12503 if(selStart != len){
12504 this.setRawValue(newValue);
12505 this.selectText(selStart, newValue.length);
12511 onSelect : function(record, index){
12513 if(this.fireEvent('beforeselect', this, record, index) !== false){
12515 this.setFromData(index > -1 ? record.data : false);
12518 this.fireEvent('select', this, record, index);
12523 * Returns the currently selected field value or empty string if no value is set.
12524 * @return {String} value The selected value
12526 getValue : function(){
12529 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12532 if(this.valueField){
12533 return typeof this.value != 'undefined' ? this.value : '';
12535 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12540 * Clears any text/value currently set in the field
12542 clearValue : function(){
12543 if(this.hiddenField){
12544 this.hiddenField.dom.value = '';
12547 this.setRawValue('');
12548 this.lastSelectionText = '';
12549 this.lastData = false;
12551 var close = this.closeTriggerEl();
12560 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12561 * will be displayed in the field. If the value does not match the data value of an existing item,
12562 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12563 * Otherwise the field will be blank (although the value will still be set).
12564 * @param {String} value The value to match
12566 setValue : function(v){
12573 if(this.valueField){
12574 var r = this.findRecord(this.valueField, v);
12576 text = r.data[this.displayField];
12577 }else if(this.valueNotFoundText !== undefined){
12578 text = this.valueNotFoundText;
12581 this.lastSelectionText = text;
12582 if(this.hiddenField){
12583 this.hiddenField.dom.value = v;
12585 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12588 var close = this.closeTriggerEl();
12591 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12595 * @property {Object} the last set data for the element
12600 * Sets the value of the field based on a object which is related to the record format for the store.
12601 * @param {Object} value the value to set as. or false on reset?
12603 setFromData : function(o){
12610 var dv = ''; // display value
12611 var vv = ''; // value value..
12613 if (this.displayField) {
12614 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12616 // this is an error condition!!!
12617 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12620 if(this.valueField){
12621 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12624 var close = this.closeTriggerEl();
12627 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12630 if(this.hiddenField){
12631 this.hiddenField.dom.value = vv;
12633 this.lastSelectionText = dv;
12634 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12638 // no hidden field.. - we store the value in 'value', but still display
12639 // display field!!!!
12640 this.lastSelectionText = dv;
12641 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12648 reset : function(){
12649 // overridden so that last data is reset..
12656 this.setValue(this.originalValue);
12657 this.clearInvalid();
12658 this.lastData = false;
12660 this.view.clearSelections();
12664 findRecord : function(prop, value){
12666 if(this.store.getCount() > 0){
12667 this.store.each(function(r){
12668 if(r.data[prop] == value){
12678 getName: function()
12680 // returns hidden if it's set..
12681 if (!this.rendered) {return ''};
12682 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
12686 onViewMove : function(e, t){
12687 this.inKeyMode = false;
12691 onViewOver : function(e, t){
12692 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12695 var item = this.view.findItemFromChild(t);
12698 var index = this.view.indexOf(item);
12699 this.select(index, false);
12704 onViewClick : function(view, doFocus, el, e)
12706 var index = this.view.getSelectedIndexes()[0];
12708 var r = this.store.getAt(index);
12712 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12719 Roo.each(this.tickItems, function(v,k){
12721 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12723 _this.tickItems.splice(k, 1);
12725 if(typeof(e) == 'undefined' && view == false){
12726 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12738 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
12739 this.tickItems.push(r.data);
12742 if(typeof(e) == 'undefined' && view == false){
12743 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12750 this.onSelect(r, index);
12752 if(doFocus !== false && !this.blockFocus){
12753 this.inputEl().focus();
12758 restrictHeight : function(){
12759 //this.innerList.dom.style.height = '';
12760 //var inner = this.innerList.dom;
12761 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12762 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12763 //this.list.beginUpdate();
12764 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12765 this.list.alignTo(this.inputEl(), this.listAlign);
12766 this.list.alignTo(this.inputEl(), this.listAlign);
12767 //this.list.endUpdate();
12771 onEmptyResults : function(){
12773 if(this.tickable && this.editable){
12774 this.restrictHeight();
12782 * Returns true if the dropdown list is expanded, else false.
12784 isExpanded : function(){
12785 return this.list.isVisible();
12789 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12790 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12791 * @param {String} value The data value of the item to select
12792 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12793 * selected item if it is not currently in view (defaults to true)
12794 * @return {Boolean} True if the value matched an item in the list, else false
12796 selectByValue : function(v, scrollIntoView){
12797 if(v !== undefined && v !== null){
12798 var r = this.findRecord(this.valueField || this.displayField, v);
12800 this.select(this.store.indexOf(r), scrollIntoView);
12808 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12809 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12810 * @param {Number} index The zero-based index of the list item to select
12811 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12812 * selected item if it is not currently in view (defaults to true)
12814 select : function(index, scrollIntoView){
12815 this.selectedIndex = index;
12816 this.view.select(index);
12817 if(scrollIntoView !== false){
12818 var el = this.view.getNode(index);
12820 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12823 this.list.scrollChildIntoView(el, false);
12829 selectNext : function(){
12830 var ct = this.store.getCount();
12832 if(this.selectedIndex == -1){
12834 }else if(this.selectedIndex < ct-1){
12835 this.select(this.selectedIndex+1);
12841 selectPrev : function(){
12842 var ct = this.store.getCount();
12844 if(this.selectedIndex == -1){
12846 }else if(this.selectedIndex != 0){
12847 this.select(this.selectedIndex-1);
12853 onKeyUp : function(e){
12854 if(this.editable !== false && !e.isSpecialKey()){
12855 this.lastKey = e.getKey();
12856 this.dqTask.delay(this.queryDelay);
12861 validateBlur : function(){
12862 return !this.list || !this.list.isVisible();
12866 initQuery : function(){
12868 var v = this.getRawValue();
12870 if(this.tickable && this.editable){
12871 v = this.tickableInputEl().getValue();
12878 doForce : function(){
12879 if(this.inputEl().dom.value.length > 0){
12880 this.inputEl().dom.value =
12881 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12887 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
12888 * query allowing the query action to be canceled if needed.
12889 * @param {String} query The SQL query to execute
12890 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12891 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
12892 * saved in the current store (defaults to false)
12894 doQuery : function(q, forceAll){
12896 if(q === undefined || q === null){
12901 forceAll: forceAll,
12905 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12910 forceAll = qe.forceAll;
12911 if(forceAll === true || (q.length >= this.minChars)){
12913 this.hasQuery = true;
12915 if(this.lastQuery != q || this.alwaysQuery){
12916 this.lastQuery = q;
12917 if(this.mode == 'local'){
12918 this.selectedIndex = -1;
12920 this.store.clearFilter();
12923 if(this.specialFilter){
12924 this.fireEvent('specialfilter', this);
12929 this.store.filter(this.displayField, q);
12932 this.store.fireEvent("datachanged", this.store);
12939 this.store.baseParams[this.queryParam] = q;
12941 var options = {params : this.getParams(q)};
12944 options.add = true;
12945 options.params.start = this.page * this.pageSize;
12948 this.store.load(options);
12951 * this code will make the page width larger, at the beginning, the list not align correctly,
12952 * we should expand the list on onLoad
12953 * so command out it
12958 this.selectedIndex = -1;
12963 this.loadNext = false;
12967 getParams : function(q){
12969 //p[this.queryParam] = q;
12973 p.limit = this.pageSize;
12979 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12981 collapse : function(){
12982 if(!this.isExpanded()){
12989 this.hasFocus = false;
12991 this.cancelBtn.hide();
12992 this.trigger.show();
12995 this.tickableInputEl().dom.value = '';
12996 this.tickableInputEl().blur();
13001 Roo.get(document).un('mousedown', this.collapseIf, this);
13002 Roo.get(document).un('mousewheel', this.collapseIf, this);
13003 if (!this.editable) {
13004 Roo.get(document).un('keydown', this.listKeyPress, this);
13006 this.fireEvent('collapse', this);
13010 collapseIf : function(e){
13011 var in_combo = e.within(this.el);
13012 var in_list = e.within(this.list);
13013 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13015 if (in_combo || in_list || is_list) {
13016 //e.stopPropagation();
13021 this.onTickableFooterButtonClick(e, false, false);
13029 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13031 expand : function(){
13033 if(this.isExpanded() || !this.hasFocus){
13037 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13038 this.list.setWidth(lw);
13045 this.restrictHeight();
13049 this.tickItems = Roo.apply([], this.item);
13052 this.cancelBtn.show();
13053 this.trigger.hide();
13056 this.tickableInputEl().focus();
13061 Roo.get(document).on('mousedown', this.collapseIf, this);
13062 Roo.get(document).on('mousewheel', this.collapseIf, this);
13063 if (!this.editable) {
13064 Roo.get(document).on('keydown', this.listKeyPress, this);
13067 this.fireEvent('expand', this);
13071 // Implements the default empty TriggerField.onTriggerClick function
13072 onTriggerClick : function(e)
13074 Roo.log('trigger click');
13076 if(this.disabled || !this.triggerList){
13081 this.loadNext = false;
13083 if(this.isExpanded()){
13085 if (!this.blockFocus) {
13086 this.inputEl().focus();
13090 this.hasFocus = true;
13091 if(this.triggerAction == 'all') {
13092 this.doQuery(this.allQuery, true);
13094 this.doQuery(this.getRawValue());
13096 if (!this.blockFocus) {
13097 this.inputEl().focus();
13102 onTickableTriggerClick : function(e)
13109 this.loadNext = false;
13110 this.hasFocus = true;
13112 if(this.triggerAction == 'all') {
13113 this.doQuery(this.allQuery, true);
13115 this.doQuery(this.getRawValue());
13119 onSearchFieldClick : function(e)
13121 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13122 this.onTickableFooterButtonClick(e, false, false);
13126 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13131 this.loadNext = false;
13132 this.hasFocus = true;
13134 if(this.triggerAction == 'all') {
13135 this.doQuery(this.allQuery, true);
13137 this.doQuery(this.getRawValue());
13141 listKeyPress : function(e)
13143 //Roo.log('listkeypress');
13144 // scroll to first matching element based on key pres..
13145 if (e.isSpecialKey()) {
13148 var k = String.fromCharCode(e.getKey()).toUpperCase();
13151 var csel = this.view.getSelectedNodes();
13152 var cselitem = false;
13154 var ix = this.view.indexOf(csel[0]);
13155 cselitem = this.store.getAt(ix);
13156 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13162 this.store.each(function(v) {
13164 // start at existing selection.
13165 if (cselitem.id == v.id) {
13171 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13172 match = this.store.indexOf(v);
13178 if (match === false) {
13179 return true; // no more action?
13182 this.view.select(match);
13183 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13184 sn.scrollIntoView(sn.dom.parentNode, false);
13187 onViewScroll : function(e, t){
13189 if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
13193 this.hasQuery = true;
13195 this.loading = this.list.select('.loading', true).first();
13197 if(this.loading === null){
13198 this.list.createChild({
13200 cls: 'loading select2-more-results select2-active',
13201 html: 'Loading more results...'
13204 this.loading = this.list.select('.loading', true).first();
13206 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13208 this.loading.hide();
13211 this.loading.show();
13216 this.loadNext = true;
13218 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13223 addItem : function(o)
13225 var dv = ''; // display value
13227 if (this.displayField) {
13228 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13230 // this is an error condition!!!
13231 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13238 var choice = this.choices.createChild({
13240 cls: 'select2-search-choice',
13249 cls: 'select2-search-choice-close',
13254 }, this.searchField);
13256 var close = choice.select('a.select2-search-choice-close', true).first();
13258 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13266 this.inputEl().dom.value = '';
13271 onRemoveItem : function(e, _self, o)
13273 e.preventDefault();
13275 this.lastItem = Roo.apply([], this.item);
13277 var index = this.item.indexOf(o.data) * 1;
13280 Roo.log('not this item?!');
13284 this.item.splice(index, 1);
13289 this.fireEvent('remove', this, e);
13295 syncValue : function()
13297 if(!this.item.length){
13304 Roo.each(this.item, function(i){
13305 if(_this.valueField){
13306 value.push(i[_this.valueField]);
13313 this.value = value.join(',');
13315 if(this.hiddenField){
13316 this.hiddenField.dom.value = this.value;
13319 this.store.fireEvent("datachanged", this.store);
13322 clearItem : function()
13324 if(!this.multiple){
13330 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13338 if(this.tickable && !Roo.isTouch){
13339 this.view.refresh();
13343 inputEl: function ()
13345 if(Roo.isTouch && this.mobileTouchView){
13346 return this.el.select('input.form-control',true).first();
13350 return this.searchField;
13353 return this.el.select('input.form-control',true).first();
13357 onTickableFooterButtonClick : function(e, btn, el)
13359 e.preventDefault();
13361 this.lastItem = Roo.apply([], this.item);
13363 if(btn && btn.name == 'cancel'){
13364 this.tickItems = Roo.apply([], this.item);
13373 Roo.each(this.tickItems, function(o){
13381 validate : function()
13383 var v = this.getRawValue();
13386 v = this.getValue();
13389 if(this.disabled || this.allowBlank || v.length){
13394 this.markInvalid();
13398 tickableInputEl : function()
13400 if(!this.tickable || !this.editable){
13401 return this.inputEl();
13404 return this.inputEl().select('.select2-search-field-input', true).first();
13408 getAutoCreateTouchView : function()
13413 cls: 'form-group' //input-group
13419 type : this.inputType,
13420 cls : 'form-control x-combo-noedit',
13421 autocomplete: 'new-password',
13422 placeholder : this.placeholder || '',
13427 input.name = this.name;
13431 input.cls += ' input-' + this.size;
13434 if (this.disabled) {
13435 input.disabled = true;
13446 inputblock.cls += ' input-group';
13448 inputblock.cn.unshift({
13450 cls : 'input-group-addon',
13455 if(this.removable && !this.multiple){
13456 inputblock.cls += ' roo-removable';
13458 inputblock.cn.push({
13461 cls : 'roo-combo-removable-btn close'
13465 if(this.hasFeedback && !this.allowBlank){
13467 inputblock.cls += ' has-feedback';
13469 inputblock.cn.push({
13471 cls: 'glyphicon form-control-feedback'
13478 inputblock.cls += (this.before) ? '' : ' input-group';
13480 inputblock.cn.push({
13482 cls : 'input-group-addon',
13493 cls: 'form-hidden-field'
13507 cls: 'form-hidden-field'
13511 cls: 'select2-choices',
13515 cls: 'select2-search-field',
13528 cls: 'select2-container input-group',
13535 combobox.cls += ' select2-container-multi';
13538 var align = this.labelAlign || this.parentLabelAlign();
13542 if(this.fieldLabel.length){
13544 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13545 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13550 cls : 'control-label ' + lw,
13551 html : this.fieldLabel
13563 var settings = this;
13565 ['xs','sm','md','lg'].map(function(size){
13566 if (settings[size]) {
13567 cfg.cls += ' col-' + size + '-' + settings[size];
13574 initTouchView : function()
13576 this.renderTouchView();
13578 this.touchViewEl.on('scroll', function(){
13579 this.el.dom.scrollTop = 0;
13582 this.originalValue = this.getValue();
13584 this.inputEl().on("click", this.showTouchView, this);
13586 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13587 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13589 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13591 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13592 this.store.on('load', this.onTouchViewLoad, this);
13593 this.store.on('loadexception', this.onTouchViewLoadException, this);
13595 if(this.hiddenName){
13597 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13599 this.hiddenField.dom.value =
13600 this.hiddenValue !== undefined ? this.hiddenValue :
13601 this.value !== undefined ? this.value : '';
13603 this.el.dom.removeAttribute('name');
13604 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13608 this.choices = this.el.select('ul.select2-choices', true).first();
13609 this.searchField = this.el.select('ul li.select2-search-field', true).first();
13612 if(this.removable && !this.multiple){
13613 var close = this.closeTriggerEl();
13615 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13616 close.on('click', this.removeBtnClick, this, close);
13620 * fix the bug in Safari iOS8
13622 this.inputEl().on("focus", function(e){
13623 document.activeElement.blur();
13631 renderTouchView : function()
13633 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13634 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13636 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13637 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13639 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13640 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13641 this.touchViewBodyEl.setStyle('overflow', 'auto');
13643 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13644 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13646 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13647 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13651 showTouchView : function()
13657 this.touchViewHeaderEl.hide();
13659 if(this.fieldLabel.length){
13660 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13661 this.touchViewHeaderEl.show();
13664 this.touchViewEl.show();
13666 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13667 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13669 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13671 if(this.fieldLabel.length){
13672 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13675 this.touchViewBodyEl.setHeight(bodyHeight);
13679 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13681 this.touchViewEl.addClass('in');
13684 this.doTouchViewQuery();
13688 hideTouchView : function()
13690 this.touchViewEl.removeClass('in');
13694 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13696 this.touchViewEl.setStyle('display', 'none');
13701 setTouchViewValue : function()
13708 Roo.each(this.tickItems, function(o){
13713 this.hideTouchView();
13716 doTouchViewQuery : function()
13725 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13729 if(!this.alwaysQuery || this.mode == 'local'){
13730 this.onTouchViewLoad();
13737 onTouchViewBeforeLoad : function(combo,opts)
13743 onTouchViewLoad : function()
13745 if(this.store.getCount() < 1){
13746 this.onTouchViewEmptyResults();
13750 this.clearTouchView();
13752 var rawValue = this.getRawValue();
13754 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13756 this.tickItems = [];
13758 this.store.data.each(function(d, rowIndex){
13759 var row = this.touchViewListGroup.createChild(template);
13761 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13764 html : d.data[this.displayField]
13767 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
13768 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
13772 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13773 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13776 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13777 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13778 this.tickItems.push(d.data);
13781 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13785 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13787 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13789 if(this.fieldLabel.length){
13790 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13793 var listHeight = this.touchViewListGroup.getHeight();
13797 if(firstChecked && listHeight > bodyHeight){
13798 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13803 onTouchViewLoadException : function()
13805 this.hideTouchView();
13808 onTouchViewEmptyResults : function()
13810 this.clearTouchView();
13812 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13814 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13818 clearTouchView : function()
13820 this.touchViewListGroup.dom.innerHTML = '';
13823 onTouchViewClick : function(e, el, o)
13825 e.preventDefault();
13828 var rowIndex = o.rowIndex;
13830 var r = this.store.getAt(rowIndex);
13832 if(!this.multiple){
13833 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13834 c.dom.removeAttribute('checked');
13837 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13839 this.setFromData(r.data);
13841 var close = this.closeTriggerEl();
13847 this.hideTouchView();
13849 this.fireEvent('select', this, r, rowIndex);
13854 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13855 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13856 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13860 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13861 this.addItem(r.data);
13862 this.tickItems.push(r.data);
13868 * @cfg {Boolean} grow
13872 * @cfg {Number} growMin
13876 * @cfg {Number} growMax
13885 Roo.apply(Roo.bootstrap.ComboBox, {
13889 cls: 'modal-header',
13911 cls: 'list-group-item',
13915 cls: 'roo-combobox-list-group-item-value'
13919 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13933 listItemCheckbox : {
13935 cls: 'list-group-item',
13939 cls: 'roo-combobox-list-group-item-value'
13943 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13959 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13964 cls: 'modal-footer',
13972 cls: 'col-xs-6 text-left',
13975 cls: 'btn btn-danger roo-touch-view-cancel',
13981 cls: 'col-xs-6 text-right',
13984 cls: 'btn btn-success roo-touch-view-ok',
13995 Roo.apply(Roo.bootstrap.ComboBox, {
13997 touchViewTemplate : {
13999 cls: 'modal fade roo-combobox-touch-view',
14003 cls: 'modal-dialog',
14004 style : 'position:fixed', // we have to fix position....
14008 cls: 'modal-content',
14010 Roo.bootstrap.ComboBox.header,
14011 Roo.bootstrap.ComboBox.body,
14012 Roo.bootstrap.ComboBox.footer
14021 * Ext JS Library 1.1.1
14022 * Copyright(c) 2006-2007, Ext JS, LLC.
14024 * Originally Released Under LGPL - original licence link has changed is not relivant.
14027 * <script type="text/javascript">
14032 * @extends Roo.util.Observable
14033 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14034 * This class also supports single and multi selection modes. <br>
14035 * Create a data model bound view:
14037 var store = new Roo.data.Store(...);
14039 var view = new Roo.View({
14041 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14043 singleSelect: true,
14044 selectedClass: "ydataview-selected",
14048 // listen for node click?
14049 view.on("click", function(vw, index, node, e){
14050 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14054 dataModel.load("foobar.xml");
14056 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14058 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14059 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14061 * Note: old style constructor is still suported (container, template, config)
14064 * Create a new View
14065 * @param {Object} config The config object
14068 Roo.View = function(config, depreciated_tpl, depreciated_config){
14070 this.parent = false;
14072 if (typeof(depreciated_tpl) == 'undefined') {
14073 // new way.. - universal constructor.
14074 Roo.apply(this, config);
14075 this.el = Roo.get(this.el);
14078 this.el = Roo.get(config);
14079 this.tpl = depreciated_tpl;
14080 Roo.apply(this, depreciated_config);
14082 this.wrapEl = this.el.wrap().wrap();
14083 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14086 if(typeof(this.tpl) == "string"){
14087 this.tpl = new Roo.Template(this.tpl);
14089 // support xtype ctors..
14090 this.tpl = new Roo.factory(this.tpl, Roo);
14094 this.tpl.compile();
14099 * @event beforeclick
14100 * Fires before a click is processed. Returns false to cancel the default action.
14101 * @param {Roo.View} this
14102 * @param {Number} index The index of the target node
14103 * @param {HTMLElement} node The target node
14104 * @param {Roo.EventObject} e The raw event object
14106 "beforeclick" : true,
14109 * Fires when a template node is clicked.
14110 * @param {Roo.View} this
14111 * @param {Number} index The index of the target node
14112 * @param {HTMLElement} node The target node
14113 * @param {Roo.EventObject} e The raw event object
14118 * Fires when a template node is double clicked.
14119 * @param {Roo.View} this
14120 * @param {Number} index The index of the target node
14121 * @param {HTMLElement} node The target node
14122 * @param {Roo.EventObject} e The raw event object
14126 * @event contextmenu
14127 * Fires when a template node is right clicked.
14128 * @param {Roo.View} this
14129 * @param {Number} index The index of the target node
14130 * @param {HTMLElement} node The target node
14131 * @param {Roo.EventObject} e The raw event object
14133 "contextmenu" : true,
14135 * @event selectionchange
14136 * Fires when the selected nodes change.
14137 * @param {Roo.View} this
14138 * @param {Array} selections Array of the selected nodes
14140 "selectionchange" : true,
14143 * @event beforeselect
14144 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14145 * @param {Roo.View} this
14146 * @param {HTMLElement} node The node to be selected
14147 * @param {Array} selections Array of currently selected nodes
14149 "beforeselect" : true,
14151 * @event preparedata
14152 * Fires on every row to render, to allow you to change the data.
14153 * @param {Roo.View} this
14154 * @param {Object} data to be rendered (change this)
14156 "preparedata" : true
14164 "click": this.onClick,
14165 "dblclick": this.onDblClick,
14166 "contextmenu": this.onContextMenu,
14170 this.selections = [];
14172 this.cmp = new Roo.CompositeElementLite([]);
14174 this.store = Roo.factory(this.store, Roo.data);
14175 this.setStore(this.store, true);
14178 if ( this.footer && this.footer.xtype) {
14180 var fctr = this.wrapEl.appendChild(document.createElement("div"));
14182 this.footer.dataSource = this.store;
14183 this.footer.container = fctr;
14184 this.footer = Roo.factory(this.footer, Roo);
14185 fctr.insertFirst(this.el);
14187 // this is a bit insane - as the paging toolbar seems to detach the el..
14188 // dom.parentNode.parentNode.parentNode
14189 // they get detached?
14193 Roo.View.superclass.constructor.call(this);
14198 Roo.extend(Roo.View, Roo.util.Observable, {
14201 * @cfg {Roo.data.Store} store Data store to load data from.
14206 * @cfg {String|Roo.Element} el The container element.
14211 * @cfg {String|Roo.Template} tpl The template used by this View
14215 * @cfg {String} dataName the named area of the template to use as the data area
14216 * Works with domtemplates roo-name="name"
14220 * @cfg {String} selectedClass The css class to add to selected nodes
14222 selectedClass : "x-view-selected",
14224 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14229 * @cfg {String} text to display on mask (default Loading)
14233 * @cfg {Boolean} multiSelect Allow multiple selection
14235 multiSelect : false,
14237 * @cfg {Boolean} singleSelect Allow single selection
14239 singleSelect: false,
14242 * @cfg {Boolean} toggleSelect - selecting
14244 toggleSelect : false,
14247 * @cfg {Boolean} tickable - selecting
14252 * Returns the element this view is bound to.
14253 * @return {Roo.Element}
14255 getEl : function(){
14256 return this.wrapEl;
14262 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14264 refresh : function(){
14265 //Roo.log('refresh');
14268 // if we are using something like 'domtemplate', then
14269 // the what gets used is:
14270 // t.applySubtemplate(NAME, data, wrapping data..)
14271 // the outer template then get' applied with
14272 // the store 'extra data'
14273 // and the body get's added to the
14274 // roo-name="data" node?
14275 // <span class='roo-tpl-{name}'></span> ?????
14279 this.clearSelections();
14280 this.el.update("");
14282 var records = this.store.getRange();
14283 if(records.length < 1) {
14285 // is this valid?? = should it render a template??
14287 this.el.update(this.emptyText);
14291 if (this.dataName) {
14292 this.el.update(t.apply(this.store.meta)); //????
14293 el = this.el.child('.roo-tpl-' + this.dataName);
14296 for(var i = 0, len = records.length; i < len; i++){
14297 var data = this.prepareData(records[i].data, i, records[i]);
14298 this.fireEvent("preparedata", this, data, i, records[i]);
14300 var d = Roo.apply({}, data);
14303 Roo.apply(d, {'roo-id' : Roo.id()});
14307 Roo.each(this.parent.item, function(item){
14308 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14311 Roo.apply(d, {'roo-data-checked' : 'checked'});
14315 html[html.length] = Roo.util.Format.trim(
14317 t.applySubtemplate(this.dataName, d, this.store.meta) :
14324 el.update(html.join(""));
14325 this.nodes = el.dom.childNodes;
14326 this.updateIndexes(0);
14331 * Function to override to reformat the data that is sent to
14332 * the template for each node.
14333 * DEPRICATED - use the preparedata event handler.
14334 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14335 * a JSON object for an UpdateManager bound view).
14337 prepareData : function(data, index, record)
14339 this.fireEvent("preparedata", this, data, index, record);
14343 onUpdate : function(ds, record){
14344 // Roo.log('on update');
14345 this.clearSelections();
14346 var index = this.store.indexOf(record);
14347 var n = this.nodes[index];
14348 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14349 n.parentNode.removeChild(n);
14350 this.updateIndexes(index, index);
14356 onAdd : function(ds, records, index)
14358 //Roo.log(['on Add', ds, records, index] );
14359 this.clearSelections();
14360 if(this.nodes.length == 0){
14364 var n = this.nodes[index];
14365 for(var i = 0, len = records.length; i < len; i++){
14366 var d = this.prepareData(records[i].data, i, records[i]);
14368 this.tpl.insertBefore(n, d);
14371 this.tpl.append(this.el, d);
14374 this.updateIndexes(index);
14377 onRemove : function(ds, record, index){
14378 // Roo.log('onRemove');
14379 this.clearSelections();
14380 var el = this.dataName ?
14381 this.el.child('.roo-tpl-' + this.dataName) :
14384 el.dom.removeChild(this.nodes[index]);
14385 this.updateIndexes(index);
14389 * Refresh an individual node.
14390 * @param {Number} index
14392 refreshNode : function(index){
14393 this.onUpdate(this.store, this.store.getAt(index));
14396 updateIndexes : function(startIndex, endIndex){
14397 var ns = this.nodes;
14398 startIndex = startIndex || 0;
14399 endIndex = endIndex || ns.length - 1;
14400 for(var i = startIndex; i <= endIndex; i++){
14401 ns[i].nodeIndex = i;
14406 * Changes the data store this view uses and refresh the view.
14407 * @param {Store} store
14409 setStore : function(store, initial){
14410 if(!initial && this.store){
14411 this.store.un("datachanged", this.refresh);
14412 this.store.un("add", this.onAdd);
14413 this.store.un("remove", this.onRemove);
14414 this.store.un("update", this.onUpdate);
14415 this.store.un("clear", this.refresh);
14416 this.store.un("beforeload", this.onBeforeLoad);
14417 this.store.un("load", this.onLoad);
14418 this.store.un("loadexception", this.onLoad);
14422 store.on("datachanged", this.refresh, this);
14423 store.on("add", this.onAdd, this);
14424 store.on("remove", this.onRemove, this);
14425 store.on("update", this.onUpdate, this);
14426 store.on("clear", this.refresh, this);
14427 store.on("beforeload", this.onBeforeLoad, this);
14428 store.on("load", this.onLoad, this);
14429 store.on("loadexception", this.onLoad, this);
14437 * onbeforeLoad - masks the loading area.
14440 onBeforeLoad : function(store,opts)
14442 //Roo.log('onBeforeLoad');
14444 this.el.update("");
14446 this.el.mask(this.mask ? this.mask : "Loading" );
14448 onLoad : function ()
14455 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14456 * @param {HTMLElement} node
14457 * @return {HTMLElement} The template node
14459 findItemFromChild : function(node){
14460 var el = this.dataName ?
14461 this.el.child('.roo-tpl-' + this.dataName,true) :
14464 if(!node || node.parentNode == el){
14467 var p = node.parentNode;
14468 while(p && p != el){
14469 if(p.parentNode == el){
14478 onClick : function(e){
14479 var item = this.findItemFromChild(e.getTarget());
14481 var index = this.indexOf(item);
14482 if(this.onItemClick(item, index, e) !== false){
14483 this.fireEvent("click", this, index, item, e);
14486 this.clearSelections();
14491 onContextMenu : function(e){
14492 var item = this.findItemFromChild(e.getTarget());
14494 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14499 onDblClick : function(e){
14500 var item = this.findItemFromChild(e.getTarget());
14502 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14506 onItemClick : function(item, index, e)
14508 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14511 if (this.toggleSelect) {
14512 var m = this.isSelected(item) ? 'unselect' : 'select';
14515 _t[m](item, true, false);
14518 if(this.multiSelect || this.singleSelect){
14519 if(this.multiSelect && e.shiftKey && this.lastSelection){
14520 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14522 this.select(item, this.multiSelect && e.ctrlKey);
14523 this.lastSelection = item;
14526 if(!this.tickable){
14527 e.preventDefault();
14535 * Get the number of selected nodes.
14538 getSelectionCount : function(){
14539 return this.selections.length;
14543 * Get the currently selected nodes.
14544 * @return {Array} An array of HTMLElements
14546 getSelectedNodes : function(){
14547 return this.selections;
14551 * Get the indexes of the selected nodes.
14554 getSelectedIndexes : function(){
14555 var indexes = [], s = this.selections;
14556 for(var i = 0, len = s.length; i < len; i++){
14557 indexes.push(s[i].nodeIndex);
14563 * Clear all selections
14564 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14566 clearSelections : function(suppressEvent){
14567 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14568 this.cmp.elements = this.selections;
14569 this.cmp.removeClass(this.selectedClass);
14570 this.selections = [];
14571 if(!suppressEvent){
14572 this.fireEvent("selectionchange", this, this.selections);
14578 * Returns true if the passed node is selected
14579 * @param {HTMLElement/Number} node The node or node index
14580 * @return {Boolean}
14582 isSelected : function(node){
14583 var s = this.selections;
14587 node = this.getNode(node);
14588 return s.indexOf(node) !== -1;
14593 * @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
14594 * @param {Boolean} keepExisting (optional) true to keep existing selections
14595 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14597 select : function(nodeInfo, keepExisting, suppressEvent){
14598 if(nodeInfo instanceof Array){
14600 this.clearSelections(true);
14602 for(var i = 0, len = nodeInfo.length; i < len; i++){
14603 this.select(nodeInfo[i], true, true);
14607 var node = this.getNode(nodeInfo);
14608 if(!node || this.isSelected(node)){
14609 return; // already selected.
14612 this.clearSelections(true);
14615 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14616 Roo.fly(node).addClass(this.selectedClass);
14617 this.selections.push(node);
14618 if(!suppressEvent){
14619 this.fireEvent("selectionchange", this, this.selections);
14627 * @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
14628 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14629 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14631 unselect : function(nodeInfo, keepExisting, suppressEvent)
14633 if(nodeInfo instanceof Array){
14634 Roo.each(this.selections, function(s) {
14635 this.unselect(s, nodeInfo);
14639 var node = this.getNode(nodeInfo);
14640 if(!node || !this.isSelected(node)){
14641 //Roo.log("not selected");
14642 return; // not selected.
14646 Roo.each(this.selections, function(s) {
14648 Roo.fly(node).removeClass(this.selectedClass);
14655 this.selections= ns;
14656 this.fireEvent("selectionchange", this, this.selections);
14660 * Gets a template node.
14661 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14662 * @return {HTMLElement} The node or null if it wasn't found
14664 getNode : function(nodeInfo){
14665 if(typeof nodeInfo == "string"){
14666 return document.getElementById(nodeInfo);
14667 }else if(typeof nodeInfo == "number"){
14668 return this.nodes[nodeInfo];
14674 * Gets a range template nodes.
14675 * @param {Number} startIndex
14676 * @param {Number} endIndex
14677 * @return {Array} An array of nodes
14679 getNodes : function(start, end){
14680 var ns = this.nodes;
14681 start = start || 0;
14682 end = typeof end == "undefined" ? ns.length - 1 : end;
14685 for(var i = start; i <= end; i++){
14689 for(var i = start; i >= end; i--){
14697 * Finds the index of the passed node
14698 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14699 * @return {Number} The index of the node or -1
14701 indexOf : function(node){
14702 node = this.getNode(node);
14703 if(typeof node.nodeIndex == "number"){
14704 return node.nodeIndex;
14706 var ns = this.nodes;
14707 for(var i = 0, len = ns.length; i < len; i++){
14718 * based on jquery fullcalendar
14722 Roo.bootstrap = Roo.bootstrap || {};
14724 * @class Roo.bootstrap.Calendar
14725 * @extends Roo.bootstrap.Component
14726 * Bootstrap Calendar class
14727 * @cfg {Boolean} loadMask (true|false) default false
14728 * @cfg {Object} header generate the user specific header of the calendar, default false
14731 * Create a new Container
14732 * @param {Object} config The config object
14737 Roo.bootstrap.Calendar = function(config){
14738 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14742 * Fires when a date is selected
14743 * @param {DatePicker} this
14744 * @param {Date} date The selected date
14748 * @event monthchange
14749 * Fires when the displayed month changes
14750 * @param {DatePicker} this
14751 * @param {Date} date The selected month
14753 'monthchange': true,
14755 * @event evententer
14756 * Fires when mouse over an event
14757 * @param {Calendar} this
14758 * @param {event} Event
14760 'evententer': true,
14762 * @event eventleave
14763 * Fires when the mouse leaves an
14764 * @param {Calendar} this
14767 'eventleave': true,
14769 * @event eventclick
14770 * Fires when the mouse click an
14771 * @param {Calendar} this
14780 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
14783 * @cfg {Number} startDay
14784 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14792 getAutoCreate : function(){
14795 var fc_button = function(name, corner, style, content ) {
14796 return Roo.apply({},{
14798 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
14800 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14803 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14814 style : 'width:100%',
14821 cls : 'fc-header-left',
14823 fc_button('prev', 'left', 'arrow', '‹' ),
14824 fc_button('next', 'right', 'arrow', '›' ),
14825 { tag: 'span', cls: 'fc-header-space' },
14826 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
14834 cls : 'fc-header-center',
14838 cls: 'fc-header-title',
14841 html : 'month / year'
14849 cls : 'fc-header-right',
14851 /* fc_button('month', 'left', '', 'month' ),
14852 fc_button('week', '', '', 'week' ),
14853 fc_button('day', 'right', '', 'day' )
14865 header = this.header;
14868 var cal_heads = function() {
14870 // fixme - handle this.
14872 for (var i =0; i < Date.dayNames.length; i++) {
14873 var d = Date.dayNames[i];
14876 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14877 html : d.substring(0,3)
14881 ret[0].cls += ' fc-first';
14882 ret[6].cls += ' fc-last';
14885 var cal_cell = function(n) {
14888 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14893 cls: 'fc-day-number',
14897 cls: 'fc-day-content',
14901 style: 'position: relative;' // height: 17px;
14913 var cal_rows = function() {
14916 for (var r = 0; r < 6; r++) {
14923 for (var i =0; i < Date.dayNames.length; i++) {
14924 var d = Date.dayNames[i];
14925 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14928 row.cn[0].cls+=' fc-first';
14929 row.cn[0].cn[0].style = 'min-height:90px';
14930 row.cn[6].cls+=' fc-last';
14934 ret[0].cls += ' fc-first';
14935 ret[4].cls += ' fc-prev-last';
14936 ret[5].cls += ' fc-last';
14943 cls: 'fc-border-separate',
14944 style : 'width:100%',
14952 cls : 'fc-first fc-last',
14970 cls : 'fc-content',
14971 style : "position: relative;",
14974 cls : 'fc-view fc-view-month fc-grid',
14975 style : 'position: relative',
14976 unselectable : 'on',
14979 cls : 'fc-event-container',
14980 style : 'position:absolute;z-index:8;top:0;left:0;'
14998 initEvents : function()
15001 throw "can not find store for calendar";
15007 style: "text-align:center",
15011 style: "background-color:white;width:50%;margin:250 auto",
15015 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15026 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15028 var size = this.el.select('.fc-content', true).first().getSize();
15029 this.maskEl.setSize(size.width, size.height);
15030 this.maskEl.enableDisplayMode("block");
15031 if(!this.loadMask){
15032 this.maskEl.hide();
15035 this.store = Roo.factory(this.store, Roo.data);
15036 this.store.on('load', this.onLoad, this);
15037 this.store.on('beforeload', this.onBeforeLoad, this);
15041 this.cells = this.el.select('.fc-day',true);
15042 //Roo.log(this.cells);
15043 this.textNodes = this.el.query('.fc-day-number');
15044 this.cells.addClassOnOver('fc-state-hover');
15046 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15047 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15048 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15049 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15051 this.on('monthchange', this.onMonthChange, this);
15053 this.update(new Date().clearTime());
15056 resize : function() {
15057 var sz = this.el.getSize();
15059 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15060 this.el.select('.fc-day-content div',true).setHeight(34);
15065 showPrevMonth : function(e){
15066 this.update(this.activeDate.add("mo", -1));
15068 showToday : function(e){
15069 this.update(new Date().clearTime());
15072 showNextMonth : function(e){
15073 this.update(this.activeDate.add("mo", 1));
15077 showPrevYear : function(){
15078 this.update(this.activeDate.add("y", -1));
15082 showNextYear : function(){
15083 this.update(this.activeDate.add("y", 1));
15088 update : function(date)
15090 var vd = this.activeDate;
15091 this.activeDate = date;
15092 // if(vd && this.el){
15093 // var t = date.getTime();
15094 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15095 // Roo.log('using add remove');
15097 // this.fireEvent('monthchange', this, date);
15099 // this.cells.removeClass("fc-state-highlight");
15100 // this.cells.each(function(c){
15101 // if(c.dateValue == t){
15102 // c.addClass("fc-state-highlight");
15103 // setTimeout(function(){
15104 // try{c.dom.firstChild.focus();}catch(e){}
15114 var days = date.getDaysInMonth();
15116 var firstOfMonth = date.getFirstDateOfMonth();
15117 var startingPos = firstOfMonth.getDay()-this.startDay;
15119 if(startingPos < this.startDay){
15123 var pm = date.add(Date.MONTH, -1);
15124 var prevStart = pm.getDaysInMonth()-startingPos;
15126 this.cells = this.el.select('.fc-day',true);
15127 this.textNodes = this.el.query('.fc-day-number');
15128 this.cells.addClassOnOver('fc-state-hover');
15130 var cells = this.cells.elements;
15131 var textEls = this.textNodes;
15133 Roo.each(cells, function(cell){
15134 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15137 days += startingPos;
15139 // convert everything to numbers so it's fast
15140 var day = 86400000;
15141 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15144 //Roo.log(prevStart);
15146 var today = new Date().clearTime().getTime();
15147 var sel = date.clearTime().getTime();
15148 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15149 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15150 var ddMatch = this.disabledDatesRE;
15151 var ddText = this.disabledDatesText;
15152 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15153 var ddaysText = this.disabledDaysText;
15154 var format = this.format;
15156 var setCellClass = function(cal, cell){
15160 //Roo.log('set Cell Class');
15162 var t = d.getTime();
15166 cell.dateValue = t;
15168 cell.className += " fc-today";
15169 cell.className += " fc-state-highlight";
15170 cell.title = cal.todayText;
15173 // disable highlight in other month..
15174 //cell.className += " fc-state-highlight";
15179 cell.className = " fc-state-disabled";
15180 cell.title = cal.minText;
15184 cell.className = " fc-state-disabled";
15185 cell.title = cal.maxText;
15189 if(ddays.indexOf(d.getDay()) != -1){
15190 cell.title = ddaysText;
15191 cell.className = " fc-state-disabled";
15194 if(ddMatch && format){
15195 var fvalue = d.dateFormat(format);
15196 if(ddMatch.test(fvalue)){
15197 cell.title = ddText.replace("%0", fvalue);
15198 cell.className = " fc-state-disabled";
15202 if (!cell.initialClassName) {
15203 cell.initialClassName = cell.dom.className;
15206 cell.dom.className = cell.initialClassName + ' ' + cell.className;
15211 for(; i < startingPos; i++) {
15212 textEls[i].innerHTML = (++prevStart);
15213 d.setDate(d.getDate()+1);
15215 cells[i].className = "fc-past fc-other-month";
15216 setCellClass(this, cells[i]);
15221 for(; i < days; i++){
15222 intDay = i - startingPos + 1;
15223 textEls[i].innerHTML = (intDay);
15224 d.setDate(d.getDate()+1);
15226 cells[i].className = ''; // "x-date-active";
15227 setCellClass(this, cells[i]);
15231 for(; i < 42; i++) {
15232 textEls[i].innerHTML = (++extraDays);
15233 d.setDate(d.getDate()+1);
15235 cells[i].className = "fc-future fc-other-month";
15236 setCellClass(this, cells[i]);
15239 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15241 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15243 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15244 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15246 if(totalRows != 6){
15247 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15248 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15251 this.fireEvent('monthchange', this, date);
15255 if(!this.internalRender){
15256 var main = this.el.dom.firstChild;
15257 var w = main.offsetWidth;
15258 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15259 Roo.fly(main).setWidth(w);
15260 this.internalRender = true;
15261 // opera does not respect the auto grow header center column
15262 // then, after it gets a width opera refuses to recalculate
15263 // without a second pass
15264 if(Roo.isOpera && !this.secondPass){
15265 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15266 this.secondPass = true;
15267 this.update.defer(10, this, [date]);
15274 findCell : function(dt) {
15275 dt = dt.clearTime().getTime();
15277 this.cells.each(function(c){
15278 //Roo.log("check " +c.dateValue + '?=' + dt);
15279 if(c.dateValue == dt){
15289 findCells : function(ev) {
15290 var s = ev.start.clone().clearTime().getTime();
15292 var e= ev.end.clone().clearTime().getTime();
15295 this.cells.each(function(c){
15296 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15298 if(c.dateValue > e){
15301 if(c.dateValue < s){
15310 // findBestRow: function(cells)
15314 // for (var i =0 ; i < cells.length;i++) {
15315 // ret = Math.max(cells[i].rows || 0,ret);
15322 addItem : function(ev)
15324 // look for vertical location slot in
15325 var cells = this.findCells(ev);
15327 // ev.row = this.findBestRow(cells);
15329 // work out the location.
15333 for(var i =0; i < cells.length; i++) {
15335 cells[i].row = cells[0].row;
15338 cells[i].row = cells[i].row + 1;
15348 if (crow.start.getY() == cells[i].getY()) {
15350 crow.end = cells[i];
15367 cells[0].events.push(ev);
15369 this.calevents.push(ev);
15372 clearEvents: function() {
15374 if(!this.calevents){
15378 Roo.each(this.cells.elements, function(c){
15384 Roo.each(this.calevents, function(e) {
15385 Roo.each(e.els, function(el) {
15386 el.un('mouseenter' ,this.onEventEnter, this);
15387 el.un('mouseleave' ,this.onEventLeave, this);
15392 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15398 renderEvents: function()
15402 this.cells.each(function(c) {
15411 if(c.row != c.events.length){
15412 r = 4 - (4 - (c.row - c.events.length));
15415 c.events = ev.slice(0, r);
15416 c.more = ev.slice(r);
15418 if(c.more.length && c.more.length == 1){
15419 c.events.push(c.more.pop());
15422 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15426 this.cells.each(function(c) {
15428 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15431 for (var e = 0; e < c.events.length; e++){
15432 var ev = c.events[e];
15433 var rows = ev.rows;
15435 for(var i = 0; i < rows.length; i++) {
15437 // how many rows should it span..
15440 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15441 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15443 unselectable : "on",
15446 cls: 'fc-event-inner',
15450 // cls: 'fc-event-time',
15451 // html : cells.length > 1 ? '' : ev.time
15455 cls: 'fc-event-title',
15456 html : String.format('{0}', ev.title)
15463 cls: 'ui-resizable-handle ui-resizable-e',
15464 html : '  '
15471 cfg.cls += ' fc-event-start';
15473 if ((i+1) == rows.length) {
15474 cfg.cls += ' fc-event-end';
15477 var ctr = _this.el.select('.fc-event-container',true).first();
15478 var cg = ctr.createChild(cfg);
15480 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15481 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15483 var r = (c.more.length) ? 1 : 0;
15484 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15485 cg.setWidth(ebox.right - sbox.x -2);
15487 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15488 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15489 cg.on('click', _this.onEventClick, _this, ev);
15500 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15501 style : 'position: absolute',
15502 unselectable : "on",
15505 cls: 'fc-event-inner',
15509 cls: 'fc-event-title',
15517 cls: 'ui-resizable-handle ui-resizable-e',
15518 html : '  '
15524 var ctr = _this.el.select('.fc-event-container',true).first();
15525 var cg = ctr.createChild(cfg);
15527 var sbox = c.select('.fc-day-content',true).first().getBox();
15528 var ebox = c.select('.fc-day-content',true).first().getBox();
15530 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15531 cg.setWidth(ebox.right - sbox.x -2);
15533 cg.on('click', _this.onMoreEventClick, _this, c.more);
15543 onEventEnter: function (e, el,event,d) {
15544 this.fireEvent('evententer', this, el, event);
15547 onEventLeave: function (e, el,event,d) {
15548 this.fireEvent('eventleave', this, el, event);
15551 onEventClick: function (e, el,event,d) {
15552 this.fireEvent('eventclick', this, el, event);
15555 onMonthChange: function () {
15559 onMoreEventClick: function(e, el, more)
15563 this.calpopover.placement = 'right';
15564 this.calpopover.setTitle('More');
15566 this.calpopover.setContent('');
15568 var ctr = this.calpopover.el.select('.popover-content', true).first();
15570 Roo.each(more, function(m){
15572 cls : 'fc-event-hori fc-event-draggable',
15575 var cg = ctr.createChild(cfg);
15577 cg.on('click', _this.onEventClick, _this, m);
15580 this.calpopover.show(el);
15585 onLoad: function ()
15587 this.calevents = [];
15590 if(this.store.getCount() > 0){
15591 this.store.data.each(function(d){
15594 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15595 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15596 time : d.data.start_time,
15597 title : d.data.title,
15598 description : d.data.description,
15599 venue : d.data.venue
15604 this.renderEvents();
15606 if(this.calevents.length && this.loadMask){
15607 this.maskEl.hide();
15611 onBeforeLoad: function()
15613 this.clearEvents();
15615 this.maskEl.show();
15629 * @class Roo.bootstrap.Popover
15630 * @extends Roo.bootstrap.Component
15631 * Bootstrap Popover class
15632 * @cfg {String} html contents of the popover (or false to use children..)
15633 * @cfg {String} title of popover (or false to hide)
15634 * @cfg {String} placement how it is placed
15635 * @cfg {String} trigger click || hover (or false to trigger manually)
15636 * @cfg {String} over what (parent or false to trigger manually.)
15637 * @cfg {Number} delay - delay before showing
15640 * Create a new Popover
15641 * @param {Object} config The config object
15644 Roo.bootstrap.Popover = function(config){
15645 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15648 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
15650 title: 'Fill in a title',
15653 placement : 'right',
15654 trigger : 'hover', // hover
15660 can_build_overlaid : false,
15662 getChildContainer : function()
15664 return this.el.select('.popover-content',true).first();
15667 getAutoCreate : function(){
15670 cls : 'popover roo-dynamic',
15671 style: 'display:block',
15677 cls : 'popover-inner',
15681 cls: 'popover-title',
15685 cls : 'popover-content',
15696 setTitle: function(str)
15699 this.el.select('.popover-title',true).first().dom.innerHTML = str;
15701 setContent: function(str)
15704 this.el.select('.popover-content',true).first().dom.innerHTML = str;
15706 // as it get's added to the bottom of the page.
15707 onRender : function(ct, position)
15709 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15711 var cfg = Roo.apply({}, this.getAutoCreate());
15715 cfg.cls += ' ' + this.cls;
15718 cfg.style = this.style;
15720 //Roo.log("adding to ");
15721 this.el = Roo.get(document.body).createChild(cfg, position);
15722 // Roo.log(this.el);
15727 initEvents : function()
15729 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15730 this.el.enableDisplayMode('block');
15732 if (this.over === false) {
15735 if (this.triggers === false) {
15738 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15739 var triggers = this.trigger ? this.trigger.split(' ') : [];
15740 Roo.each(triggers, function(trigger) {
15742 if (trigger == 'click') {
15743 on_el.on('click', this.toggle, this);
15744 } else if (trigger != 'manual') {
15745 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
15746 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15748 on_el.on(eventIn ,this.enter, this);
15749 on_el.on(eventOut, this.leave, this);
15760 toggle : function () {
15761 this.hoverState == 'in' ? this.leave() : this.enter();
15764 enter : function () {
15767 clearTimeout(this.timeout);
15769 this.hoverState = 'in';
15771 if (!this.delay || !this.delay.show) {
15776 this.timeout = setTimeout(function () {
15777 if (_t.hoverState == 'in') {
15780 }, this.delay.show)
15782 leave : function() {
15783 clearTimeout(this.timeout);
15785 this.hoverState = 'out';
15787 if (!this.delay || !this.delay.hide) {
15792 this.timeout = setTimeout(function () {
15793 if (_t.hoverState == 'out') {
15796 }, this.delay.hide)
15799 show : function (on_el)
15802 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15805 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15806 if (this.html !== false) {
15807 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15809 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15810 if (!this.title.length) {
15811 this.el.select('.popover-title',true).hide();
15814 var placement = typeof this.placement == 'function' ?
15815 this.placement.call(this, this.el, on_el) :
15818 var autoToken = /\s?auto?\s?/i;
15819 var autoPlace = autoToken.test(placement);
15821 placement = placement.replace(autoToken, '') || 'top';
15825 //this.el.setXY([0,0]);
15827 this.el.dom.style.display='block';
15828 this.el.addClass(placement);
15830 //this.el.appendTo(on_el);
15832 var p = this.getPosition();
15833 var box = this.el.getBox();
15838 var align = Roo.bootstrap.Popover.alignment[placement];
15839 this.el.alignTo(on_el, align[0],align[1]);
15840 //var arrow = this.el.select('.arrow',true).first();
15841 //arrow.set(align[2],
15843 this.el.addClass('in');
15846 if (this.el.hasClass('fade')) {
15853 this.el.setXY([0,0]);
15854 this.el.removeClass('in');
15856 this.hoverState = null;
15862 Roo.bootstrap.Popover.alignment = {
15863 'left' : ['r-l', [-10,0], 'right'],
15864 'right' : ['l-r', [10,0], 'left'],
15865 'bottom' : ['t-b', [0,10], 'top'],
15866 'top' : [ 'b-t', [0,-10], 'bottom']
15877 * @class Roo.bootstrap.Progress
15878 * @extends Roo.bootstrap.Component
15879 * Bootstrap Progress class
15880 * @cfg {Boolean} striped striped of the progress bar
15881 * @cfg {Boolean} active animated of the progress bar
15885 * Create a new Progress
15886 * @param {Object} config The config object
15889 Roo.bootstrap.Progress = function(config){
15890 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15893 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
15898 getAutoCreate : function(){
15906 cfg.cls += ' progress-striped';
15910 cfg.cls += ' active';
15929 * @class Roo.bootstrap.ProgressBar
15930 * @extends Roo.bootstrap.Component
15931 * Bootstrap ProgressBar class
15932 * @cfg {Number} aria_valuenow aria-value now
15933 * @cfg {Number} aria_valuemin aria-value min
15934 * @cfg {Number} aria_valuemax aria-value max
15935 * @cfg {String} label label for the progress bar
15936 * @cfg {String} panel (success | info | warning | danger )
15937 * @cfg {String} role role of the progress bar
15938 * @cfg {String} sr_only text
15942 * Create a new ProgressBar
15943 * @param {Object} config The config object
15946 Roo.bootstrap.ProgressBar = function(config){
15947 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15950 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
15954 aria_valuemax : 100,
15960 getAutoCreate : function()
15965 cls: 'progress-bar',
15966 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15978 cfg.role = this.role;
15981 if(this.aria_valuenow){
15982 cfg['aria-valuenow'] = this.aria_valuenow;
15985 if(this.aria_valuemin){
15986 cfg['aria-valuemin'] = this.aria_valuemin;
15989 if(this.aria_valuemax){
15990 cfg['aria-valuemax'] = this.aria_valuemax;
15993 if(this.label && !this.sr_only){
15994 cfg.html = this.label;
15998 cfg.cls += ' progress-bar-' + this.panel;
16004 update : function(aria_valuenow)
16006 this.aria_valuenow = aria_valuenow;
16008 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16023 * @class Roo.bootstrap.TabGroup
16024 * @extends Roo.bootstrap.Column
16025 * Bootstrap Column class
16026 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16027 * @cfg {Boolean} carousel true to make the group behave like a carousel
16028 * @cfg {Boolean} bullets show bullets for the panels
16029 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16030 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16031 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16034 * Create a new TabGroup
16035 * @param {Object} config The config object
16038 Roo.bootstrap.TabGroup = function(config){
16039 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16041 this.navId = Roo.id();
16044 Roo.bootstrap.TabGroup.register(this);
16048 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16051 transition : false,
16056 slideOnTouch : false,
16058 getAutoCreate : function()
16060 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16062 cfg.cls += ' tab-content';
16064 if (this.carousel) {
16065 cfg.cls += ' carousel slide';
16068 cls : 'carousel-inner'
16071 if(this.bullets && !Roo.isTouch){
16074 cls : 'carousel-bullets',
16078 if(this.bullets_cls){
16079 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16082 for (var i = 0; i < this.bullets; i++){
16084 cls : 'bullet bullet-' + i
16092 cfg.cn[0].cn = bullets;
16099 initEvents: function()
16101 if(Roo.isTouch && this.slideOnTouch){
16102 this.el.on("touchstart", this.onTouchStart, this);
16105 if(this.autoslide){
16108 this.slideFn = window.setInterval(function() {
16109 _this.showPanelNext();
16115 onTouchStart : function(e, el, o)
16117 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16121 this.showPanelNext();
16124 getChildContainer : function()
16126 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16130 * register a Navigation item
16131 * @param {Roo.bootstrap.NavItem} the navitem to add
16133 register : function(item)
16135 this.tabs.push( item);
16136 item.navId = this.navId; // not really needed..
16141 getActivePanel : function()
16144 Roo.each(this.tabs, function(t) {
16154 getPanelByName : function(n)
16157 Roo.each(this.tabs, function(t) {
16158 if (t.tabId == n) {
16166 indexOfPanel : function(p)
16169 Roo.each(this.tabs, function(t,i) {
16170 if (t.tabId == p.tabId) {
16179 * show a specific panel
16180 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16181 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16183 showPanel : function (pan)
16185 if(this.transition || typeof(pan) == 'undefined'){
16186 Roo.log("waiting for the transitionend");
16190 if (typeof(pan) == 'number') {
16191 pan = this.tabs[pan];
16194 if (typeof(pan) == 'string') {
16195 pan = this.getPanelByName(pan);
16198 var cur = this.getActivePanel();
16201 Roo.log('pan or acitve pan is undefined');
16205 if (pan.tabId == this.getActivePanel().tabId) {
16209 if (false === cur.fireEvent('beforedeactivate')) {
16213 if(this.bullets > 0 && !Roo.isTouch){
16214 this.setActiveBullet(this.indexOfPanel(pan));
16217 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16219 this.transition = true;
16220 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16221 var lr = dir == 'next' ? 'left' : 'right';
16222 pan.el.addClass(dir); // or prev
16223 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16224 cur.el.addClass(lr); // or right
16225 pan.el.addClass(lr);
16228 cur.el.on('transitionend', function() {
16229 Roo.log("trans end?");
16231 pan.el.removeClass([lr,dir]);
16232 pan.setActive(true);
16234 cur.el.removeClass([lr]);
16235 cur.setActive(false);
16237 _this.transition = false;
16239 }, this, { single: true } );
16244 cur.setActive(false);
16245 pan.setActive(true);
16250 showPanelNext : function()
16252 var i = this.indexOfPanel(this.getActivePanel());
16254 if (i >= this.tabs.length - 1 && !this.autoslide) {
16258 if (i >= this.tabs.length - 1 && this.autoslide) {
16262 this.showPanel(this.tabs[i+1]);
16265 showPanelPrev : function()
16267 var i = this.indexOfPanel(this.getActivePanel());
16269 if (i < 1 && !this.autoslide) {
16273 if (i < 1 && this.autoslide) {
16274 i = this.tabs.length;
16277 this.showPanel(this.tabs[i-1]);
16281 addBullet: function()
16283 if(!this.bullets || Roo.isTouch){
16286 var ctr = this.el.select('.carousel-bullets',true).first();
16287 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16288 var bullet = ctr.createChild({
16289 cls : 'bullet bullet-' + i
16290 },ctr.dom.lastChild);
16295 bullet.on('click', (function(e, el, o, ii, t){
16297 e.preventDefault();
16299 this.showPanel(ii);
16301 if(this.autoslide && this.slideFn){
16302 clearInterval(this.slideFn);
16303 this.slideFn = window.setInterval(function() {
16304 _this.showPanelNext();
16308 }).createDelegate(this, [i, bullet], true));
16313 setActiveBullet : function(i)
16319 Roo.each(this.el.select('.bullet', true).elements, function(el){
16320 el.removeClass('selected');
16323 var bullet = this.el.select('.bullet-' + i, true).first();
16329 bullet.addClass('selected');
16340 Roo.apply(Roo.bootstrap.TabGroup, {
16344 * register a Navigation Group
16345 * @param {Roo.bootstrap.NavGroup} the navgroup to add
16347 register : function(navgrp)
16349 this.groups[navgrp.navId] = navgrp;
16353 * fetch a Navigation Group based on the navigation ID
16354 * if one does not exist , it will get created.
16355 * @param {string} the navgroup to add
16356 * @returns {Roo.bootstrap.NavGroup} the navgroup
16358 get: function(navId) {
16359 if (typeof(this.groups[navId]) == 'undefined') {
16360 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16362 return this.groups[navId] ;
16377 * @class Roo.bootstrap.TabPanel
16378 * @extends Roo.bootstrap.Component
16379 * Bootstrap TabPanel class
16380 * @cfg {Boolean} active panel active
16381 * @cfg {String} html panel content
16382 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16383 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16387 * Create a new TabPanel
16388 * @param {Object} config The config object
16391 Roo.bootstrap.TabPanel = function(config){
16392 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16396 * Fires when the active status changes
16397 * @param {Roo.bootstrap.TabPanel} this
16398 * @param {Boolean} state the new state
16403 * @event beforedeactivate
16404 * Fires before a tab is de-activated - can be used to do validation on a form.
16405 * @param {Roo.bootstrap.TabPanel} this
16406 * @return {Boolean} false if there is an error
16409 'beforedeactivate': true
16412 this.tabId = this.tabId || Roo.id();
16416 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16423 getAutoCreate : function(){
16426 // item is needed for carousel - not sure if it has any effect otherwise
16427 cls: 'tab-pane item',
16428 html: this.html || ''
16432 cfg.cls += ' active';
16436 cfg.tabId = this.tabId;
16443 initEvents: function()
16445 var p = this.parent();
16446 this.navId = this.navId || p.navId;
16448 if (typeof(this.navId) != 'undefined') {
16449 // not really needed.. but just in case.. parent should be a NavGroup.
16450 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16454 var i = tg.tabs.length - 1;
16456 if(this.active && tg.bullets > 0 && i < tg.bullets){
16457 tg.setActiveBullet(i);
16464 onRender : function(ct, position)
16466 // Roo.log("Call onRender: " + this.xtype);
16468 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16476 setActive: function(state)
16478 Roo.log("panel - set active " + this.tabId + "=" + state);
16480 this.active = state;
16482 this.el.removeClass('active');
16484 } else if (!this.el.hasClass('active')) {
16485 this.el.addClass('active');
16488 this.fireEvent('changed', this, state);
16505 * @class Roo.bootstrap.DateField
16506 * @extends Roo.bootstrap.Input
16507 * Bootstrap DateField class
16508 * @cfg {Number} weekStart default 0
16509 * @cfg {String} viewMode default empty, (months|years)
16510 * @cfg {String} minViewMode default empty, (months|years)
16511 * @cfg {Number} startDate default -Infinity
16512 * @cfg {Number} endDate default Infinity
16513 * @cfg {Boolean} todayHighlight default false
16514 * @cfg {Boolean} todayBtn default false
16515 * @cfg {Boolean} calendarWeeks default false
16516 * @cfg {Object} daysOfWeekDisabled default empty
16517 * @cfg {Boolean} singleMode default false (true | false)
16519 * @cfg {Boolean} keyboardNavigation default true
16520 * @cfg {String} language default en
16523 * Create a new DateField
16524 * @param {Object} config The config object
16527 Roo.bootstrap.DateField = function(config){
16528 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16532 * Fires when this field show.
16533 * @param {Roo.bootstrap.DateField} this
16534 * @param {Mixed} date The date value
16539 * Fires when this field hide.
16540 * @param {Roo.bootstrap.DateField} this
16541 * @param {Mixed} date The date value
16546 * Fires when select a date.
16547 * @param {Roo.bootstrap.DateField} this
16548 * @param {Mixed} date The date value
16552 * @event beforeselect
16553 * Fires when before select a date.
16554 * @param {Roo.bootstrap.DateField} this
16555 * @param {Mixed} date The date value
16557 beforeselect : true
16561 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16564 * @cfg {String} format
16565 * The default date format string which can be overriden for localization support. The format must be
16566 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16570 * @cfg {String} altFormats
16571 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16572 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16574 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16582 todayHighlight : false,
16588 keyboardNavigation: true,
16590 calendarWeeks: false,
16592 startDate: -Infinity,
16596 daysOfWeekDisabled: [],
16600 singleMode : false,
16602 UTCDate: function()
16604 return new Date(Date.UTC.apply(Date, arguments));
16607 UTCToday: function()
16609 var today = new Date();
16610 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16613 getDate: function() {
16614 var d = this.getUTCDate();
16615 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16618 getUTCDate: function() {
16622 setDate: function(d) {
16623 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16626 setUTCDate: function(d) {
16628 this.setValue(this.formatDate(this.date));
16631 onRender: function(ct, position)
16634 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16636 this.language = this.language || 'en';
16637 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16638 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16640 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16641 this.format = this.format || 'm/d/y';
16642 this.isInline = false;
16643 this.isInput = true;
16644 this.component = this.el.select('.add-on', true).first() || false;
16645 this.component = (this.component && this.component.length === 0) ? false : this.component;
16646 this.hasInput = this.component && this.inputEL().length;
16648 if (typeof(this.minViewMode === 'string')) {
16649 switch (this.minViewMode) {
16651 this.minViewMode = 1;
16654 this.minViewMode = 2;
16657 this.minViewMode = 0;
16662 if (typeof(this.viewMode === 'string')) {
16663 switch (this.viewMode) {
16676 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16678 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16680 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16682 this.picker().on('mousedown', this.onMousedown, this);
16683 this.picker().on('click', this.onClick, this);
16685 this.picker().addClass('datepicker-dropdown');
16687 this.startViewMode = this.viewMode;
16689 if(this.singleMode){
16690 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16691 v.setVisibilityMode(Roo.Element.DISPLAY);
16695 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16696 v.setStyle('width', '189px');
16700 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16701 if(!this.calendarWeeks){
16706 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16707 v.attr('colspan', function(i, val){
16708 return parseInt(val) + 1;
16713 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16715 this.setStartDate(this.startDate);
16716 this.setEndDate(this.endDate);
16718 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16725 if(this.isInline) {
16730 picker : function()
16732 return this.pickerEl;
16733 // return this.el.select('.datepicker', true).first();
16736 fillDow: function()
16738 var dowCnt = this.weekStart;
16747 if(this.calendarWeeks){
16755 while (dowCnt < this.weekStart + 7) {
16759 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16763 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16766 fillMonths: function()
16769 var months = this.picker().select('>.datepicker-months td', true).first();
16771 months.dom.innerHTML = '';
16777 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16780 months.createChild(month);
16787 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;
16789 if (this.date < this.startDate) {
16790 this.viewDate = new Date(this.startDate);
16791 } else if (this.date > this.endDate) {
16792 this.viewDate = new Date(this.endDate);
16794 this.viewDate = new Date(this.date);
16802 var d = new Date(this.viewDate),
16803 year = d.getUTCFullYear(),
16804 month = d.getUTCMonth(),
16805 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16806 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16807 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16808 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16809 currentDate = this.date && this.date.valueOf(),
16810 today = this.UTCToday();
16812 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16814 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16816 // this.picker.select('>tfoot th.today').
16817 // .text(dates[this.language].today)
16818 // .toggle(this.todayBtn !== false);
16820 this.updateNavArrows();
16823 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16825 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16827 prevMonth.setUTCDate(day);
16829 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16831 var nextMonth = new Date(prevMonth);
16833 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16835 nextMonth = nextMonth.valueOf();
16837 var fillMonths = false;
16839 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16841 while(prevMonth.valueOf() < nextMonth) {
16844 if (prevMonth.getUTCDay() === this.weekStart) {
16846 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16854 if(this.calendarWeeks){
16855 // ISO 8601: First week contains first thursday.
16856 // ISO also states week starts on Monday, but we can be more abstract here.
16858 // Start of current week: based on weekstart/current date
16859 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16860 // Thursday of this week
16861 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16862 // First Thursday of year, year from thursday
16863 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16864 // Calendar week: ms between thursdays, div ms per day, div 7 days
16865 calWeek = (th - yth) / 864e5 / 7 + 1;
16867 fillMonths.cn.push({
16875 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16877 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16880 if (this.todayHighlight &&
16881 prevMonth.getUTCFullYear() == today.getFullYear() &&
16882 prevMonth.getUTCMonth() == today.getMonth() &&
16883 prevMonth.getUTCDate() == today.getDate()) {
16884 clsName += ' today';
16887 if (currentDate && prevMonth.valueOf() === currentDate) {
16888 clsName += ' active';
16891 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16892 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16893 clsName += ' disabled';
16896 fillMonths.cn.push({
16898 cls: 'day ' + clsName,
16899 html: prevMonth.getDate()
16902 prevMonth.setDate(prevMonth.getDate()+1);
16905 var currentYear = this.date && this.date.getUTCFullYear();
16906 var currentMonth = this.date && this.date.getUTCMonth();
16908 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16910 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16911 v.removeClass('active');
16913 if(currentYear === year && k === currentMonth){
16914 v.addClass('active');
16917 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16918 v.addClass('disabled');
16924 year = parseInt(year/10, 10) * 10;
16926 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16928 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16931 for (var i = -1; i < 11; i++) {
16932 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16934 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16942 showMode: function(dir)
16945 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16948 Roo.each(this.picker().select('>div',true).elements, function(v){
16949 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16952 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16957 if(this.isInline) {
16961 this.picker().removeClass(['bottom', 'top']);
16963 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16965 * place to the top of element!
16969 this.picker().addClass('top');
16970 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16975 this.picker().addClass('bottom');
16977 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16980 parseDate : function(value)
16982 if(!value || value instanceof Date){
16985 var v = Date.parseDate(value, this.format);
16986 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16987 v = Date.parseDate(value, 'Y-m-d');
16989 if(!v && this.altFormats){
16990 if(!this.altFormatsArray){
16991 this.altFormatsArray = this.altFormats.split("|");
16993 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16994 v = Date.parseDate(value, this.altFormatsArray[i]);
17000 formatDate : function(date, fmt)
17002 return (!date || !(date instanceof Date)) ?
17003 date : date.dateFormat(fmt || this.format);
17006 onFocus : function()
17008 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17012 onBlur : function()
17014 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17016 var d = this.inputEl().getValue();
17025 this.picker().show();
17029 this.fireEvent('show', this, this.date);
17034 if(this.isInline) {
17037 this.picker().hide();
17038 this.viewMode = this.startViewMode;
17041 this.fireEvent('hide', this, this.date);
17045 onMousedown: function(e)
17047 e.stopPropagation();
17048 e.preventDefault();
17053 Roo.bootstrap.DateField.superclass.keyup.call(this);
17057 setValue: function(v)
17059 if(this.fireEvent('beforeselect', this, v) !== false){
17060 var d = new Date(this.parseDate(v) ).clearTime();
17062 if(isNaN(d.getTime())){
17063 this.date = this.viewDate = '';
17064 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17068 v = this.formatDate(d);
17070 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17072 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17076 this.fireEvent('select', this, this.date);
17080 getValue: function()
17082 return this.formatDate(this.date);
17085 fireKey: function(e)
17087 if (!this.picker().isVisible()){
17088 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17094 var dateChanged = false,
17096 newDate, newViewDate;
17101 e.preventDefault();
17105 if (!this.keyboardNavigation) {
17108 dir = e.keyCode == 37 ? -1 : 1;
17111 newDate = this.moveYear(this.date, dir);
17112 newViewDate = this.moveYear(this.viewDate, dir);
17113 } else if (e.shiftKey){
17114 newDate = this.moveMonth(this.date, dir);
17115 newViewDate = this.moveMonth(this.viewDate, dir);
17117 newDate = new Date(this.date);
17118 newDate.setUTCDate(this.date.getUTCDate() + dir);
17119 newViewDate = new Date(this.viewDate);
17120 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17122 if (this.dateWithinRange(newDate)){
17123 this.date = newDate;
17124 this.viewDate = newViewDate;
17125 this.setValue(this.formatDate(this.date));
17127 e.preventDefault();
17128 dateChanged = true;
17133 if (!this.keyboardNavigation) {
17136 dir = e.keyCode == 38 ? -1 : 1;
17138 newDate = this.moveYear(this.date, dir);
17139 newViewDate = this.moveYear(this.viewDate, dir);
17140 } else if (e.shiftKey){
17141 newDate = this.moveMonth(this.date, dir);
17142 newViewDate = this.moveMonth(this.viewDate, dir);
17144 newDate = new Date(this.date);
17145 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17146 newViewDate = new Date(this.viewDate);
17147 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17149 if (this.dateWithinRange(newDate)){
17150 this.date = newDate;
17151 this.viewDate = newViewDate;
17152 this.setValue(this.formatDate(this.date));
17154 e.preventDefault();
17155 dateChanged = true;
17159 this.setValue(this.formatDate(this.date));
17161 e.preventDefault();
17164 this.setValue(this.formatDate(this.date));
17178 onClick: function(e)
17180 e.stopPropagation();
17181 e.preventDefault();
17183 var target = e.getTarget();
17185 if(target.nodeName.toLowerCase() === 'i'){
17186 target = Roo.get(target).dom.parentNode;
17189 var nodeName = target.nodeName;
17190 var className = target.className;
17191 var html = target.innerHTML;
17192 //Roo.log(nodeName);
17194 switch(nodeName.toLowerCase()) {
17196 switch(className) {
17202 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17203 switch(this.viewMode){
17205 this.viewDate = this.moveMonth(this.viewDate, dir);
17209 this.viewDate = this.moveYear(this.viewDate, dir);
17215 var date = new Date();
17216 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17218 this.setValue(this.formatDate(this.date));
17225 if (className.indexOf('disabled') < 0) {
17226 this.viewDate.setUTCDate(1);
17227 if (className.indexOf('month') > -1) {
17228 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17230 var year = parseInt(html, 10) || 0;
17231 this.viewDate.setUTCFullYear(year);
17235 if(this.singleMode){
17236 this.setValue(this.formatDate(this.viewDate));
17247 //Roo.log(className);
17248 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17249 var day = parseInt(html, 10) || 1;
17250 var year = this.viewDate.getUTCFullYear(),
17251 month = this.viewDate.getUTCMonth();
17253 if (className.indexOf('old') > -1) {
17260 } else if (className.indexOf('new') > -1) {
17268 //Roo.log([year,month,day]);
17269 this.date = this.UTCDate(year, month, day,0,0,0,0);
17270 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17272 //Roo.log(this.formatDate(this.date));
17273 this.setValue(this.formatDate(this.date));
17280 setStartDate: function(startDate)
17282 this.startDate = startDate || -Infinity;
17283 if (this.startDate !== -Infinity) {
17284 this.startDate = this.parseDate(this.startDate);
17287 this.updateNavArrows();
17290 setEndDate: function(endDate)
17292 this.endDate = endDate || Infinity;
17293 if (this.endDate !== Infinity) {
17294 this.endDate = this.parseDate(this.endDate);
17297 this.updateNavArrows();
17300 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17302 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17303 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17304 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17306 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17307 return parseInt(d, 10);
17310 this.updateNavArrows();
17313 updateNavArrows: function()
17315 if(this.singleMode){
17319 var d = new Date(this.viewDate),
17320 year = d.getUTCFullYear(),
17321 month = d.getUTCMonth();
17323 Roo.each(this.picker().select('.prev', true).elements, function(v){
17325 switch (this.viewMode) {
17328 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17334 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17341 Roo.each(this.picker().select('.next', true).elements, function(v){
17343 switch (this.viewMode) {
17346 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17352 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17360 moveMonth: function(date, dir)
17365 var new_date = new Date(date.valueOf()),
17366 day = new_date.getUTCDate(),
17367 month = new_date.getUTCMonth(),
17368 mag = Math.abs(dir),
17370 dir = dir > 0 ? 1 : -1;
17373 // If going back one month, make sure month is not current month
17374 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17376 return new_date.getUTCMonth() == month;
17378 // If going forward one month, make sure month is as expected
17379 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17381 return new_date.getUTCMonth() != new_month;
17383 new_month = month + dir;
17384 new_date.setUTCMonth(new_month);
17385 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17386 if (new_month < 0 || new_month > 11) {
17387 new_month = (new_month + 12) % 12;
17390 // For magnitudes >1, move one month at a time...
17391 for (var i=0; i<mag; i++) {
17392 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17393 new_date = this.moveMonth(new_date, dir);
17395 // ...then reset the day, keeping it in the new month
17396 new_month = new_date.getUTCMonth();
17397 new_date.setUTCDate(day);
17399 return new_month != new_date.getUTCMonth();
17402 // Common date-resetting loop -- if date is beyond end of month, make it
17405 new_date.setUTCDate(--day);
17406 new_date.setUTCMonth(new_month);
17411 moveYear: function(date, dir)
17413 return this.moveMonth(date, dir*12);
17416 dateWithinRange: function(date)
17418 return date >= this.startDate && date <= this.endDate;
17424 this.picker().remove();
17429 Roo.apply(Roo.bootstrap.DateField, {
17440 html: '<i class="fa fa-arrow-left"/>'
17450 html: '<i class="fa fa-arrow-right"/>'
17492 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17493 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17494 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17495 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17496 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17509 navFnc: 'FullYear',
17514 navFnc: 'FullYear',
17519 Roo.apply(Roo.bootstrap.DateField, {
17523 cls: 'datepicker dropdown-menu roo-dynamic',
17527 cls: 'datepicker-days',
17531 cls: 'table-condensed',
17533 Roo.bootstrap.DateField.head,
17537 Roo.bootstrap.DateField.footer
17544 cls: 'datepicker-months',
17548 cls: 'table-condensed',
17550 Roo.bootstrap.DateField.head,
17551 Roo.bootstrap.DateField.content,
17552 Roo.bootstrap.DateField.footer
17559 cls: 'datepicker-years',
17563 cls: 'table-condensed',
17565 Roo.bootstrap.DateField.head,
17566 Roo.bootstrap.DateField.content,
17567 Roo.bootstrap.DateField.footer
17586 * @class Roo.bootstrap.TimeField
17587 * @extends Roo.bootstrap.Input
17588 * Bootstrap DateField class
17592 * Create a new TimeField
17593 * @param {Object} config The config object
17596 Roo.bootstrap.TimeField = function(config){
17597 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17601 * Fires when this field show.
17602 * @param {Roo.bootstrap.DateField} thisthis
17603 * @param {Mixed} date The date value
17608 * Fires when this field hide.
17609 * @param {Roo.bootstrap.DateField} this
17610 * @param {Mixed} date The date value
17615 * Fires when select a date.
17616 * @param {Roo.bootstrap.DateField} this
17617 * @param {Mixed} date The date value
17623 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17626 * @cfg {String} format
17627 * The default time format string which can be overriden for localization support. The format must be
17628 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17632 onRender: function(ct, position)
17635 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17637 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17639 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17641 this.pop = this.picker().select('>.datepicker-time',true).first();
17642 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17644 this.picker().on('mousedown', this.onMousedown, this);
17645 this.picker().on('click', this.onClick, this);
17647 this.picker().addClass('datepicker-dropdown');
17652 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17653 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17654 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17655 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17656 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17657 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17661 fireKey: function(e){
17662 if (!this.picker().isVisible()){
17663 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17669 e.preventDefault();
17677 this.onTogglePeriod();
17680 this.onIncrementMinutes();
17683 this.onDecrementMinutes();
17692 onClick: function(e) {
17693 e.stopPropagation();
17694 e.preventDefault();
17697 picker : function()
17699 return this.el.select('.datepicker', true).first();
17702 fillTime: function()
17704 var time = this.pop.select('tbody', true).first();
17706 time.dom.innerHTML = '';
17721 cls: 'hours-up glyphicon glyphicon-chevron-up'
17741 cls: 'minutes-up glyphicon glyphicon-chevron-up'
17762 cls: 'timepicker-hour',
17777 cls: 'timepicker-minute',
17792 cls: 'btn btn-primary period',
17814 cls: 'hours-down glyphicon glyphicon-chevron-down'
17834 cls: 'minutes-down glyphicon glyphicon-chevron-down'
17852 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17859 var hours = this.time.getHours();
17860 var minutes = this.time.getMinutes();
17873 hours = hours - 12;
17877 hours = '0' + hours;
17881 minutes = '0' + minutes;
17884 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17885 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17886 this.pop.select('button', true).first().dom.innerHTML = period;
17892 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17894 var cls = ['bottom'];
17896 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17903 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17908 this.picker().addClass(cls.join('-'));
17912 Roo.each(cls, function(c){
17914 _this.picker().setTop(_this.inputEl().getHeight());
17918 _this.picker().setTop(0 - _this.picker().getHeight());
17923 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17927 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17934 onFocus : function()
17936 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17940 onBlur : function()
17942 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17948 this.picker().show();
17953 this.fireEvent('show', this, this.date);
17958 this.picker().hide();
17961 this.fireEvent('hide', this, this.date);
17964 setTime : function()
17967 this.setValue(this.time.format(this.format));
17969 this.fireEvent('select', this, this.date);
17974 onMousedown: function(e){
17975 e.stopPropagation();
17976 e.preventDefault();
17979 onIncrementHours: function()
17981 Roo.log('onIncrementHours');
17982 this.time = this.time.add(Date.HOUR, 1);
17987 onDecrementHours: function()
17989 Roo.log('onDecrementHours');
17990 this.time = this.time.add(Date.HOUR, -1);
17994 onIncrementMinutes: function()
17996 Roo.log('onIncrementMinutes');
17997 this.time = this.time.add(Date.MINUTE, 1);
18001 onDecrementMinutes: function()
18003 Roo.log('onDecrementMinutes');
18004 this.time = this.time.add(Date.MINUTE, -1);
18008 onTogglePeriod: function()
18010 Roo.log('onTogglePeriod');
18011 this.time = this.time.add(Date.HOUR, 12);
18018 Roo.apply(Roo.bootstrap.TimeField, {
18048 cls: 'btn btn-info ok',
18060 Roo.apply(Roo.bootstrap.TimeField, {
18064 cls: 'datepicker dropdown-menu',
18068 cls: 'datepicker-time',
18072 cls: 'table-condensed',
18074 Roo.bootstrap.TimeField.content,
18075 Roo.bootstrap.TimeField.footer
18094 * @class Roo.bootstrap.MonthField
18095 * @extends Roo.bootstrap.Input
18096 * Bootstrap MonthField class
18098 * @cfg {String} language default en
18101 * Create a new MonthField
18102 * @param {Object} config The config object
18105 Roo.bootstrap.MonthField = function(config){
18106 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18111 * Fires when this field show.
18112 * @param {Roo.bootstrap.MonthField} this
18113 * @param {Mixed} date The date value
18118 * Fires when this field hide.
18119 * @param {Roo.bootstrap.MonthField} this
18120 * @param {Mixed} date The date value
18125 * Fires when select a date.
18126 * @param {Roo.bootstrap.MonthField} this
18127 * @param {String} oldvalue The old value
18128 * @param {String} newvalue The new value
18134 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
18136 onRender: function(ct, position)
18139 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18141 this.language = this.language || 'en';
18142 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18143 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18145 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18146 this.isInline = false;
18147 this.isInput = true;
18148 this.component = this.el.select('.add-on', true).first() || false;
18149 this.component = (this.component && this.component.length === 0) ? false : this.component;
18150 this.hasInput = this.component && this.inputEL().length;
18152 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18154 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18156 this.picker().on('mousedown', this.onMousedown, this);
18157 this.picker().on('click', this.onClick, this);
18159 this.picker().addClass('datepicker-dropdown');
18161 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18162 v.setStyle('width', '189px');
18169 if(this.isInline) {
18175 setValue: function(v, suppressEvent)
18177 var o = this.getValue();
18179 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18183 if(suppressEvent !== true){
18184 this.fireEvent('select', this, o, v);
18189 getValue: function()
18194 onClick: function(e)
18196 e.stopPropagation();
18197 e.preventDefault();
18199 var target = e.getTarget();
18201 if(target.nodeName.toLowerCase() === 'i'){
18202 target = Roo.get(target).dom.parentNode;
18205 var nodeName = target.nodeName;
18206 var className = target.className;
18207 var html = target.innerHTML;
18209 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18213 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18215 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18221 picker : function()
18223 return this.pickerEl;
18226 fillMonths: function()
18229 var months = this.picker().select('>.datepicker-months td', true).first();
18231 months.dom.innerHTML = '';
18237 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18240 months.createChild(month);
18249 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18250 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18253 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18254 e.removeClass('active');
18256 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18257 e.addClass('active');
18264 if(this.isInline) {
18268 this.picker().removeClass(['bottom', 'top']);
18270 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18272 * place to the top of element!
18276 this.picker().addClass('top');
18277 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18282 this.picker().addClass('bottom');
18284 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18287 onFocus : function()
18289 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18293 onBlur : function()
18295 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18297 var d = this.inputEl().getValue();
18306 this.picker().show();
18307 this.picker().select('>.datepicker-months', true).first().show();
18311 this.fireEvent('show', this, this.date);
18316 if(this.isInline) {
18319 this.picker().hide();
18320 this.fireEvent('hide', this, this.date);
18324 onMousedown: function(e)
18326 e.stopPropagation();
18327 e.preventDefault();
18332 Roo.bootstrap.MonthField.superclass.keyup.call(this);
18336 fireKey: function(e)
18338 if (!this.picker().isVisible()){
18339 if (e.keyCode == 27) {// allow escape to hide and re-show picker
18350 e.preventDefault();
18354 dir = e.keyCode == 37 ? -1 : 1;
18356 this.vIndex = this.vIndex + dir;
18358 if(this.vIndex < 0){
18362 if(this.vIndex > 11){
18366 if(isNaN(this.vIndex)){
18370 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18376 dir = e.keyCode == 38 ? -1 : 1;
18378 this.vIndex = this.vIndex + dir * 4;
18380 if(this.vIndex < 0){
18384 if(this.vIndex > 11){
18388 if(isNaN(this.vIndex)){
18392 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18397 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18398 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18402 e.preventDefault();
18405 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18406 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18422 this.picker().remove();
18427 Roo.apply(Roo.bootstrap.MonthField, {
18446 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18447 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18452 Roo.apply(Roo.bootstrap.MonthField, {
18456 cls: 'datepicker dropdown-menu roo-dynamic',
18460 cls: 'datepicker-months',
18464 cls: 'table-condensed',
18466 Roo.bootstrap.DateField.content
18486 * @class Roo.bootstrap.CheckBox
18487 * @extends Roo.bootstrap.Input
18488 * Bootstrap CheckBox class
18490 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18491 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18492 * @cfg {String} boxLabel The text that appears beside the checkbox
18493 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18494 * @cfg {Boolean} checked initnal the element
18495 * @cfg {Boolean} inline inline the element (default false)
18496 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18499 * Create a new CheckBox
18500 * @param {Object} config The config object
18503 Roo.bootstrap.CheckBox = function(config){
18504 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18509 * Fires when the element is checked or unchecked.
18510 * @param {Roo.bootstrap.CheckBox} this This input
18511 * @param {Boolean} checked The new checked value
18518 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18520 inputType: 'checkbox',
18528 getAutoCreate : function()
18530 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18536 cfg.cls = 'form-group ' + this.inputType; //input-group
18539 cfg.cls += ' ' + this.inputType + '-inline';
18545 type : this.inputType,
18546 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18547 cls : 'roo-' + this.inputType, //'form-box',
18548 placeholder : this.placeholder || ''
18552 if (this.weight) { // Validity check?
18553 cfg.cls += " " + this.inputType + "-" + this.weight;
18556 if (this.disabled) {
18557 input.disabled=true;
18561 input.checked = this.checked;
18565 input.name = this.name;
18569 input.cls += ' input-' + this.size;
18574 ['xs','sm','md','lg'].map(function(size){
18575 if (settings[size]) {
18576 cfg.cls += ' col-' + size + '-' + settings[size];
18580 var inputblock = input;
18582 if (this.before || this.after) {
18585 cls : 'input-group',
18590 inputblock.cn.push({
18592 cls : 'input-group-addon',
18597 inputblock.cn.push(input);
18600 inputblock.cn.push({
18602 cls : 'input-group-addon',
18609 if (align ==='left' && this.fieldLabel.length) {
18610 Roo.log("left and has label");
18616 cls : 'control-label col-md-' + this.labelWidth,
18617 html : this.fieldLabel
18621 cls : "col-md-" + (12 - this.labelWidth),
18628 } else if ( this.fieldLabel.length) {
18633 tag: this.boxLabel ? 'span' : 'label',
18635 cls: 'control-label box-input-label',
18636 //cls : 'input-group-addon',
18637 html : this.fieldLabel
18647 Roo.log(" no label && no align");
18648 cfg.cn = [ inputblock ] ;
18653 var boxLabelCfg = {
18655 //'for': id, // box label is handled by onclick - so no for...
18657 html: this.boxLabel
18661 boxLabelCfg.tooltip = this.tooltip;
18664 cfg.cn.push(boxLabelCfg);
18674 * return the real input element.
18676 inputEl: function ()
18678 return this.el.select('input.roo-' + this.inputType,true).first();
18681 labelEl: function()
18683 return this.el.select('label.control-label',true).first();
18685 /* depricated... */
18689 return this.labelEl();
18692 initEvents : function()
18694 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18696 this.inputEl().on('click', this.onClick, this);
18698 if (this.boxLabel) {
18699 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18702 this.startValue = this.getValue();
18705 Roo.bootstrap.CheckBox.register(this);
18709 onClick : function()
18711 this.setChecked(!this.checked);
18714 setChecked : function(state,suppressEvent)
18716 this.startValue = this.getValue();
18718 if(this.inputType == 'radio'){
18720 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18721 e.dom.checked = false;
18724 this.inputEl().dom.checked = true;
18726 this.inputEl().dom.value = this.inputValue;
18728 if(suppressEvent !== true){
18729 this.fireEvent('check', this, true);
18737 this.checked = state;
18739 this.inputEl().dom.checked = state;
18741 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18743 if(suppressEvent !== true){
18744 this.fireEvent('check', this, state);
18750 getValue : function()
18752 if(this.inputType == 'radio'){
18753 return this.getGroupValue();
18756 return this.inputEl().getValue();
18760 getGroupValue : function()
18762 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18766 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18769 setValue : function(v,suppressEvent)
18771 if(this.inputType == 'radio'){
18772 this.setGroupValue(v, suppressEvent);
18776 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18781 setGroupValue : function(v, suppressEvent)
18783 this.startValue = this.getValue();
18785 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18786 e.dom.checked = false;
18788 if(e.dom.value == v){
18789 e.dom.checked = true;
18793 if(suppressEvent !== true){
18794 this.fireEvent('check', this, true);
18802 validate : function()
18806 (this.inputType == 'radio' && this.validateRadio()) ||
18807 (this.inputType == 'checkbox' && this.validateCheckbox())
18813 this.markInvalid();
18817 validateRadio : function()
18821 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18822 if(!e.dom.checked){
18834 validateCheckbox : function()
18837 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18840 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18848 for(var i in group){
18853 r = (group[i].getValue() == group[i].inputValue) ? true : false;
18860 * Mark this field as valid
18862 markValid : function()
18864 if(this.allowBlank){
18870 this.fireEvent('valid', this);
18872 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18875 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18882 if(this.inputType == 'radio'){
18883 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18884 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18885 e.findParent('.form-group', false, true).addClass(_this.validClass);
18892 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18893 this.el.findParent('.form-group', false, true).addClass(this.validClass);
18897 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18903 for(var i in group){
18904 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18905 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18910 * Mark this field as invalid
18911 * @param {String} msg The validation message
18913 markInvalid : function(msg)
18915 if(this.allowBlank){
18921 this.fireEvent('invalid', this, msg);
18923 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18926 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18930 label.markInvalid();
18933 if(this.inputType == 'radio'){
18934 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18935 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18936 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18943 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18944 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18948 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18954 for(var i in group){
18955 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18956 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18963 Roo.apply(Roo.bootstrap.CheckBox, {
18968 * register a CheckBox Group
18969 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18971 register : function(checkbox)
18973 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18974 this.groups[checkbox.groupId] = {};
18977 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18981 this.groups[checkbox.groupId][checkbox.name] = checkbox;
18985 * fetch a CheckBox Group based on the group ID
18986 * @param {string} the group ID
18987 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18989 get: function(groupId) {
18990 if (typeof(this.groups[groupId]) == 'undefined') {
18994 return this.groups[groupId] ;
19006 *<div class="radio">
19008 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19009 Option one is this and that—be sure to include why it's great
19016 *<label class="radio-inline">fieldLabel</label>
19017 *<label class="radio-inline">
19018 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19026 * @class Roo.bootstrap.Radio
19027 * @extends Roo.bootstrap.CheckBox
19028 * Bootstrap Radio class
19031 * Create a new Radio
19032 * @param {Object} config The config object
19035 Roo.bootstrap.Radio = function(config){
19036 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19040 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
19042 inputType: 'radio',
19046 getAutoCreate : function()
19048 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19049 align = align || 'left'; // default...
19056 tag : this.inline ? 'span' : 'div',
19061 var inline = this.inline ? ' radio-inline' : '';
19065 // does not need for, as we wrap the input with it..
19067 cls : 'control-label box-label' + inline,
19070 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19074 //cls : 'control-label' + inline,
19075 html : this.fieldLabel,
19076 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19085 type : this.inputType,
19086 //value : (!this.checked) ? this.valueOff : this.inputValue,
19087 value : this.inputValue,
19089 placeholder : this.placeholder || '' // ?? needed????
19092 if (this.weight) { // Validity check?
19093 input.cls += " radio-" + this.weight;
19095 if (this.disabled) {
19096 input.disabled=true;
19100 input.checked = this.checked;
19104 input.name = this.name;
19108 input.cls += ' input-' + this.size;
19111 //?? can span's inline have a width??
19114 ['xs','sm','md','lg'].map(function(size){
19115 if (settings[size]) {
19116 cfg.cls += ' col-' + size + '-' + settings[size];
19120 var inputblock = input;
19122 if (this.before || this.after) {
19125 cls : 'input-group',
19130 inputblock.cn.push({
19132 cls : 'input-group-addon',
19136 inputblock.cn.push(input);
19138 inputblock.cn.push({
19140 cls : 'input-group-addon',
19148 if (this.fieldLabel && this.fieldLabel.length) {
19149 cfg.cn.push(fieldLabel);
19152 // normal bootstrap puts the input inside the label.
19153 // however with our styled version - it has to go after the input.
19155 //lbl.cn.push(inputblock);
19159 cls: 'radio' + inline,
19166 cfg.cn.push( lblwrap);
19171 html: this.boxLabel
19180 initEvents : function()
19182 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19184 this.inputEl().on('click', this.onClick, this);
19185 if (this.boxLabel) {
19186 //Roo.log('find label');
19187 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
19192 inputEl: function ()
19194 return this.el.select('input.roo-radio',true).first();
19196 onClick : function()
19199 this.setChecked(true);
19202 setChecked : function(state,suppressEvent)
19205 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19206 v.dom.checked = false;
19209 Roo.log(this.inputEl().dom);
19210 this.checked = state;
19211 this.inputEl().dom.checked = state;
19213 if(suppressEvent !== true){
19214 this.fireEvent('check', this, state);
19217 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19221 getGroupValue : function()
19224 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19225 if(v.dom.checked == true){
19226 value = v.dom.value;
19234 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
19235 * @return {Mixed} value The field value
19237 getValue : function(){
19238 return this.getGroupValue();
19244 //<script type="text/javascript">
19247 * Based Ext JS Library 1.1.1
19248 * Copyright(c) 2006-2007, Ext JS, LLC.
19254 * @class Roo.HtmlEditorCore
19255 * @extends Roo.Component
19256 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19258 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19261 Roo.HtmlEditorCore = function(config){
19264 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19269 * @event initialize
19270 * Fires when the editor is fully initialized (including the iframe)
19271 * @param {Roo.HtmlEditorCore} this
19276 * Fires when the editor is first receives the focus. Any insertion must wait
19277 * until after this event.
19278 * @param {Roo.HtmlEditorCore} this
19282 * @event beforesync
19283 * Fires before the textarea is updated with content from the editor iframe. Return false
19284 * to cancel the sync.
19285 * @param {Roo.HtmlEditorCore} this
19286 * @param {String} html
19290 * @event beforepush
19291 * Fires before the iframe editor is updated with content from the textarea. Return false
19292 * to cancel the push.
19293 * @param {Roo.HtmlEditorCore} this
19294 * @param {String} html
19299 * Fires when the textarea is updated with content from the editor iframe.
19300 * @param {Roo.HtmlEditorCore} this
19301 * @param {String} html
19306 * Fires when the iframe editor is updated with content from the textarea.
19307 * @param {Roo.HtmlEditorCore} this
19308 * @param {String} html
19313 * @event editorevent
19314 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19315 * @param {Roo.HtmlEditorCore} this
19321 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19323 // defaults : white / black...
19324 this.applyBlacklists();
19331 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
19335 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
19341 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
19346 * @cfg {Number} height (in pixels)
19350 * @cfg {Number} width (in pixels)
19355 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19358 stylesheets: false,
19363 // private properties
19364 validationEvent : false,
19366 initialized : false,
19368 sourceEditMode : false,
19369 onFocus : Roo.emptyFn,
19371 hideMode:'offsets',
19375 // blacklist + whitelisted elements..
19382 * Protected method that will not generally be called directly. It
19383 * is called when the editor initializes the iframe with HTML contents. Override this method if you
19384 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19386 getDocMarkup : function(){
19390 // inherit styels from page...??
19391 if (this.stylesheets === false) {
19393 Roo.get(document.head).select('style').each(function(node) {
19394 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19397 Roo.get(document.head).select('link').each(function(node) {
19398 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19401 } else if (!this.stylesheets.length) {
19403 st = '<style type="text/css">' +
19404 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19410 st += '<style type="text/css">' +
19411 'IMG { cursor: pointer } ' +
19415 return '<html><head>' + st +
19416 //<style type="text/css">' +
19417 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19419 ' </head><body class="roo-htmleditor-body"></body></html>';
19423 onRender : function(ct, position)
19426 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19427 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19430 this.el.dom.style.border = '0 none';
19431 this.el.dom.setAttribute('tabIndex', -1);
19432 this.el.addClass('x-hidden hide');
19436 if(Roo.isIE){ // fix IE 1px bogus margin
19437 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19441 this.frameId = Roo.id();
19445 var iframe = this.owner.wrap.createChild({
19447 cls: 'form-control', // bootstrap..
19449 name: this.frameId,
19450 frameBorder : 'no',
19451 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19456 this.iframe = iframe.dom;
19458 this.assignDocWin();
19460 this.doc.designMode = 'on';
19463 this.doc.write(this.getDocMarkup());
19467 var task = { // must defer to wait for browser to be ready
19469 //console.log("run task?" + this.doc.readyState);
19470 this.assignDocWin();
19471 if(this.doc.body || this.doc.readyState == 'complete'){
19473 this.doc.designMode="on";
19477 Roo.TaskMgr.stop(task);
19478 this.initEditor.defer(10, this);
19485 Roo.TaskMgr.start(task);
19490 onResize : function(w, h)
19492 Roo.log('resize: ' +w + ',' + h );
19493 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19497 if(typeof w == 'number'){
19499 this.iframe.style.width = w + 'px';
19501 if(typeof h == 'number'){
19503 this.iframe.style.height = h + 'px';
19505 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19512 * Toggles the editor between standard and source edit mode.
19513 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19515 toggleSourceEdit : function(sourceEditMode){
19517 this.sourceEditMode = sourceEditMode === true;
19519 if(this.sourceEditMode){
19521 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19524 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19525 //this.iframe.className = '';
19528 //this.setSize(this.owner.wrap.getSize());
19529 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19536 * Protected method that will not generally be called directly. If you need/want
19537 * custom HTML cleanup, this is the method you should override.
19538 * @param {String} html The HTML to be cleaned
19539 * return {String} The cleaned HTML
19541 cleanHtml : function(html){
19542 html = String(html);
19543 if(html.length > 5){
19544 if(Roo.isSafari){ // strip safari nonsense
19545 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19548 if(html == ' '){
19555 * HTML Editor -> Textarea
19556 * Protected method that will not generally be called directly. Syncs the contents
19557 * of the editor iframe with the textarea.
19559 syncValue : function(){
19560 if(this.initialized){
19561 var bd = (this.doc.body || this.doc.documentElement);
19562 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19563 var html = bd.innerHTML;
19565 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19566 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19568 html = '<div style="'+m[0]+'">' + html + '</div>';
19571 html = this.cleanHtml(html);
19572 // fix up the special chars.. normaly like back quotes in word...
19573 // however we do not want to do this with chinese..
19574 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19575 var cc = b.charCodeAt();
19577 (cc >= 0x4E00 && cc < 0xA000 ) ||
19578 (cc >= 0x3400 && cc < 0x4E00 ) ||
19579 (cc >= 0xf900 && cc < 0xfb00 )
19585 if(this.owner.fireEvent('beforesync', this, html) !== false){
19586 this.el.dom.value = html;
19587 this.owner.fireEvent('sync', this, html);
19593 * Protected method that will not generally be called directly. Pushes the value of the textarea
19594 * into the iframe editor.
19596 pushValue : function(){
19597 if(this.initialized){
19598 var v = this.el.dom.value.trim();
19600 // if(v.length < 1){
19604 if(this.owner.fireEvent('beforepush', this, v) !== false){
19605 var d = (this.doc.body || this.doc.documentElement);
19607 this.cleanUpPaste();
19608 this.el.dom.value = d.innerHTML;
19609 this.owner.fireEvent('push', this, v);
19615 deferFocus : function(){
19616 this.focus.defer(10, this);
19620 focus : function(){
19621 if(this.win && !this.sourceEditMode){
19628 assignDocWin: function()
19630 var iframe = this.iframe;
19633 this.doc = iframe.contentWindow.document;
19634 this.win = iframe.contentWindow;
19636 // if (!Roo.get(this.frameId)) {
19639 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19640 // this.win = Roo.get(this.frameId).dom.contentWindow;
19642 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19646 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19647 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19652 initEditor : function(){
19653 //console.log("INIT EDITOR");
19654 this.assignDocWin();
19658 this.doc.designMode="on";
19660 this.doc.write(this.getDocMarkup());
19663 var dbody = (this.doc.body || this.doc.documentElement);
19664 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19665 // this copies styles from the containing element into thsi one..
19666 // not sure why we need all of this..
19667 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19669 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19670 //ss['background-attachment'] = 'fixed'; // w3c
19671 dbody.bgProperties = 'fixed'; // ie
19672 //Roo.DomHelper.applyStyles(dbody, ss);
19673 Roo.EventManager.on(this.doc, {
19674 //'mousedown': this.onEditorEvent,
19675 'mouseup': this.onEditorEvent,
19676 'dblclick': this.onEditorEvent,
19677 'click': this.onEditorEvent,
19678 'keyup': this.onEditorEvent,
19683 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19685 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19686 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19688 this.initialized = true;
19690 this.owner.fireEvent('initialize', this);
19695 onDestroy : function(){
19701 //for (var i =0; i < this.toolbars.length;i++) {
19702 // // fixme - ask toolbars for heights?
19703 // this.toolbars[i].onDestroy();
19706 //this.wrap.dom.innerHTML = '';
19707 //this.wrap.remove();
19712 onFirstFocus : function(){
19714 this.assignDocWin();
19717 this.activated = true;
19720 if(Roo.isGecko){ // prevent silly gecko errors
19722 var s = this.win.getSelection();
19723 if(!s.focusNode || s.focusNode.nodeType != 3){
19724 var r = s.getRangeAt(0);
19725 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19730 this.execCmd('useCSS', true);
19731 this.execCmd('styleWithCSS', false);
19734 this.owner.fireEvent('activate', this);
19738 adjustFont: function(btn){
19739 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19740 //if(Roo.isSafari){ // safari
19743 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19744 if(Roo.isSafari){ // safari
19745 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19746 v = (v < 10) ? 10 : v;
19747 v = (v > 48) ? 48 : v;
19748 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19753 v = Math.max(1, v+adjust);
19755 this.execCmd('FontSize', v );
19758 onEditorEvent : function(e)
19760 this.owner.fireEvent('editorevent', this, e);
19761 // this.updateToolbar();
19762 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19765 insertTag : function(tg)
19767 // could be a bit smarter... -> wrap the current selected tRoo..
19768 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19770 range = this.createRange(this.getSelection());
19771 var wrappingNode = this.doc.createElement(tg.toLowerCase());
19772 wrappingNode.appendChild(range.extractContents());
19773 range.insertNode(wrappingNode);
19780 this.execCmd("formatblock", tg);
19784 insertText : function(txt)
19788 var range = this.createRange();
19789 range.deleteContents();
19790 //alert(Sender.getAttribute('label'));
19792 range.insertNode(this.doc.createTextNode(txt));
19798 * Executes a Midas editor command on the editor document and performs necessary focus and
19799 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19800 * @param {String} cmd The Midas command
19801 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19803 relayCmd : function(cmd, value){
19805 this.execCmd(cmd, value);
19806 this.owner.fireEvent('editorevent', this);
19807 //this.updateToolbar();
19808 this.owner.deferFocus();
19812 * Executes a Midas editor command directly on the editor document.
19813 * For visual commands, you should use {@link #relayCmd} instead.
19814 * <b>This should only be called after the editor is initialized.</b>
19815 * @param {String} cmd The Midas command
19816 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19818 execCmd : function(cmd, value){
19819 this.doc.execCommand(cmd, false, value === undefined ? null : value);
19826 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19828 * @param {String} text | dom node..
19830 insertAtCursor : function(text)
19835 if(!this.activated){
19841 var r = this.doc.selection.createRange();
19852 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19856 // from jquery ui (MIT licenced)
19858 var win = this.win;
19860 if (win.getSelection && win.getSelection().getRangeAt) {
19861 range = win.getSelection().getRangeAt(0);
19862 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19863 range.insertNode(node);
19864 } else if (win.document.selection && win.document.selection.createRange) {
19865 // no firefox support
19866 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19867 win.document.selection.createRange().pasteHTML(txt);
19869 // no firefox support
19870 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19871 this.execCmd('InsertHTML', txt);
19880 mozKeyPress : function(e){
19882 var c = e.getCharCode(), cmd;
19885 c = String.fromCharCode(c).toLowerCase();
19899 this.cleanUpPaste.defer(100, this);
19907 e.preventDefault();
19915 fixKeys : function(){ // load time branching for fastest keydown performance
19917 return function(e){
19918 var k = e.getKey(), r;
19921 r = this.doc.selection.createRange();
19924 r.pasteHTML('    ');
19931 r = this.doc.selection.createRange();
19933 var target = r.parentElement();
19934 if(!target || target.tagName.toLowerCase() != 'li'){
19936 r.pasteHTML('<br />');
19942 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19943 this.cleanUpPaste.defer(100, this);
19949 }else if(Roo.isOpera){
19950 return function(e){
19951 var k = e.getKey();
19955 this.execCmd('InsertHTML','    ');
19958 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19959 this.cleanUpPaste.defer(100, this);
19964 }else if(Roo.isSafari){
19965 return function(e){
19966 var k = e.getKey();
19970 this.execCmd('InsertText','\t');
19974 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19975 this.cleanUpPaste.defer(100, this);
19983 getAllAncestors: function()
19985 var p = this.getSelectedNode();
19988 a.push(p); // push blank onto stack..
19989 p = this.getParentElement();
19993 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19997 a.push(this.doc.body);
20001 lastSelNode : false,
20004 getSelection : function()
20006 this.assignDocWin();
20007 return Roo.isIE ? this.doc.selection : this.win.getSelection();
20010 getSelectedNode: function()
20012 // this may only work on Gecko!!!
20014 // should we cache this!!!!
20019 var range = this.createRange(this.getSelection()).cloneRange();
20022 var parent = range.parentElement();
20024 var testRange = range.duplicate();
20025 testRange.moveToElementText(parent);
20026 if (testRange.inRange(range)) {
20029 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20032 parent = parent.parentElement;
20037 // is ancestor a text element.
20038 var ac = range.commonAncestorContainer;
20039 if (ac.nodeType == 3) {
20040 ac = ac.parentNode;
20043 var ar = ac.childNodes;
20046 var other_nodes = [];
20047 var has_other_nodes = false;
20048 for (var i=0;i<ar.length;i++) {
20049 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
20052 // fullly contained node.
20054 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20059 // probably selected..
20060 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20061 other_nodes.push(ar[i]);
20065 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
20070 has_other_nodes = true;
20072 if (!nodes.length && other_nodes.length) {
20073 nodes= other_nodes;
20075 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20081 createRange: function(sel)
20083 // this has strange effects when using with
20084 // top toolbar - not sure if it's a great idea.
20085 //this.editor.contentWindow.focus();
20086 if (typeof sel != "undefined") {
20088 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20090 return this.doc.createRange();
20093 return this.doc.createRange();
20096 getParentElement: function()
20099 this.assignDocWin();
20100 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20102 var range = this.createRange(sel);
20105 var p = range.commonAncestorContainer;
20106 while (p.nodeType == 3) { // text node
20117 * Range intersection.. the hard stuff...
20121 * [ -- selected range --- ]
20125 * if end is before start or hits it. fail.
20126 * if start is after end or hits it fail.
20128 * if either hits (but other is outside. - then it's not
20134 // @see http://www.thismuchiknow.co.uk/?p=64.
20135 rangeIntersectsNode : function(range, node)
20137 var nodeRange = node.ownerDocument.createRange();
20139 nodeRange.selectNode(node);
20141 nodeRange.selectNodeContents(node);
20144 var rangeStartRange = range.cloneRange();
20145 rangeStartRange.collapse(true);
20147 var rangeEndRange = range.cloneRange();
20148 rangeEndRange.collapse(false);
20150 var nodeStartRange = nodeRange.cloneRange();
20151 nodeStartRange.collapse(true);
20153 var nodeEndRange = nodeRange.cloneRange();
20154 nodeEndRange.collapse(false);
20156 return rangeStartRange.compareBoundaryPoints(
20157 Range.START_TO_START, nodeEndRange) == -1 &&
20158 rangeEndRange.compareBoundaryPoints(
20159 Range.START_TO_START, nodeStartRange) == 1;
20163 rangeCompareNode : function(range, node)
20165 var nodeRange = node.ownerDocument.createRange();
20167 nodeRange.selectNode(node);
20169 nodeRange.selectNodeContents(node);
20173 range.collapse(true);
20175 nodeRange.collapse(true);
20177 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20178 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
20180 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20182 var nodeIsBefore = ss == 1;
20183 var nodeIsAfter = ee == -1;
20185 if (nodeIsBefore && nodeIsAfter) {
20188 if (!nodeIsBefore && nodeIsAfter) {
20189 return 1; //right trailed.
20192 if (nodeIsBefore && !nodeIsAfter) {
20193 return 2; // left trailed.
20199 // private? - in a new class?
20200 cleanUpPaste : function()
20202 // cleans up the whole document..
20203 Roo.log('cleanuppaste');
20205 this.cleanUpChildren(this.doc.body);
20206 var clean = this.cleanWordChars(this.doc.body.innerHTML);
20207 if (clean != this.doc.body.innerHTML) {
20208 this.doc.body.innerHTML = clean;
20213 cleanWordChars : function(input) {// change the chars to hex code
20214 var he = Roo.HtmlEditorCore;
20216 var output = input;
20217 Roo.each(he.swapCodes, function(sw) {
20218 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20220 output = output.replace(swapper, sw[1]);
20227 cleanUpChildren : function (n)
20229 if (!n.childNodes.length) {
20232 for (var i = n.childNodes.length-1; i > -1 ; i--) {
20233 this.cleanUpChild(n.childNodes[i]);
20240 cleanUpChild : function (node)
20243 //console.log(node);
20244 if (node.nodeName == "#text") {
20245 // clean up silly Windows -- stuff?
20248 if (node.nodeName == "#comment") {
20249 node.parentNode.removeChild(node);
20250 // clean up silly Windows -- stuff?
20253 var lcname = node.tagName.toLowerCase();
20254 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20255 // whitelist of tags..
20257 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20259 node.parentNode.removeChild(node);
20264 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20266 // remove <a name=....> as rendering on yahoo mailer is borked with this.
20267 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20269 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20270 // remove_keep_children = true;
20273 if (remove_keep_children) {
20274 this.cleanUpChildren(node);
20275 // inserts everything just before this node...
20276 while (node.childNodes.length) {
20277 var cn = node.childNodes[0];
20278 node.removeChild(cn);
20279 node.parentNode.insertBefore(cn, node);
20281 node.parentNode.removeChild(node);
20285 if (!node.attributes || !node.attributes.length) {
20286 this.cleanUpChildren(node);
20290 function cleanAttr(n,v)
20293 if (v.match(/^\./) || v.match(/^\//)) {
20296 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20299 if (v.match(/^#/)) {
20302 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20303 node.removeAttribute(n);
20307 var cwhite = this.cwhite;
20308 var cblack = this.cblack;
20310 function cleanStyle(n,v)
20312 if (v.match(/expression/)) { //XSS?? should we even bother..
20313 node.removeAttribute(n);
20317 var parts = v.split(/;/);
20320 Roo.each(parts, function(p) {
20321 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20325 var l = p.split(':').shift().replace(/\s+/g,'');
20326 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20328 if ( cwhite.length && cblack.indexOf(l) > -1) {
20329 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20330 //node.removeAttribute(n);
20334 // only allow 'c whitelisted system attributes'
20335 if ( cwhite.length && cwhite.indexOf(l) < 0) {
20336 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20337 //node.removeAttribute(n);
20347 if (clean.length) {
20348 node.setAttribute(n, clean.join(';'));
20350 node.removeAttribute(n);
20356 for (var i = node.attributes.length-1; i > -1 ; i--) {
20357 var a = node.attributes[i];
20360 if (a.name.toLowerCase().substr(0,2)=='on') {
20361 node.removeAttribute(a.name);
20364 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20365 node.removeAttribute(a.name);
20368 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20369 cleanAttr(a.name,a.value); // fixme..
20372 if (a.name == 'style') {
20373 cleanStyle(a.name,a.value);
20376 /// clean up MS crap..
20377 // tecnically this should be a list of valid class'es..
20380 if (a.name == 'class') {
20381 if (a.value.match(/^Mso/)) {
20382 node.className = '';
20385 if (a.value.match(/body/)) {
20386 node.className = '';
20397 this.cleanUpChildren(node);
20403 * Clean up MS wordisms...
20405 cleanWord : function(node)
20410 this.cleanWord(this.doc.body);
20413 if (node.nodeName == "#text") {
20414 // clean up silly Windows -- stuff?
20417 if (node.nodeName == "#comment") {
20418 node.parentNode.removeChild(node);
20419 // clean up silly Windows -- stuff?
20423 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20424 node.parentNode.removeChild(node);
20428 // remove - but keep children..
20429 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20430 while (node.childNodes.length) {
20431 var cn = node.childNodes[0];
20432 node.removeChild(cn);
20433 node.parentNode.insertBefore(cn, node);
20435 node.parentNode.removeChild(node);
20436 this.iterateChildren(node, this.cleanWord);
20440 if (node.className.length) {
20442 var cn = node.className.split(/\W+/);
20444 Roo.each(cn, function(cls) {
20445 if (cls.match(/Mso[a-zA-Z]+/)) {
20450 node.className = cna.length ? cna.join(' ') : '';
20452 node.removeAttribute("class");
20456 if (node.hasAttribute("lang")) {
20457 node.removeAttribute("lang");
20460 if (node.hasAttribute("style")) {
20462 var styles = node.getAttribute("style").split(";");
20464 Roo.each(styles, function(s) {
20465 if (!s.match(/:/)) {
20468 var kv = s.split(":");
20469 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20472 // what ever is left... we allow.
20475 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20476 if (!nstyle.length) {
20477 node.removeAttribute('style');
20480 this.iterateChildren(node, this.cleanWord);
20486 * iterateChildren of a Node, calling fn each time, using this as the scole..
20487 * @param {DomNode} node node to iterate children of.
20488 * @param {Function} fn method of this class to call on each item.
20490 iterateChildren : function(node, fn)
20492 if (!node.childNodes.length) {
20495 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20496 fn.call(this, node.childNodes[i])
20502 * cleanTableWidths.
20504 * Quite often pasting from word etc.. results in tables with column and widths.
20505 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20508 cleanTableWidths : function(node)
20513 this.cleanTableWidths(this.doc.body);
20518 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20521 Roo.log(node.tagName);
20522 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20523 this.iterateChildren(node, this.cleanTableWidths);
20526 if (node.hasAttribute('width')) {
20527 node.removeAttribute('width');
20531 if (node.hasAttribute("style")) {
20534 var styles = node.getAttribute("style").split(";");
20536 Roo.each(styles, function(s) {
20537 if (!s.match(/:/)) {
20540 var kv = s.split(":");
20541 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20544 // what ever is left... we allow.
20547 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20548 if (!nstyle.length) {
20549 node.removeAttribute('style');
20553 this.iterateChildren(node, this.cleanTableWidths);
20561 domToHTML : function(currentElement, depth, nopadtext) {
20563 depth = depth || 0;
20564 nopadtext = nopadtext || false;
20566 if (!currentElement) {
20567 return this.domToHTML(this.doc.body);
20570 //Roo.log(currentElement);
20572 var allText = false;
20573 var nodeName = currentElement.nodeName;
20574 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20576 if (nodeName == '#text') {
20578 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20583 if (nodeName != 'BODY') {
20586 // Prints the node tagName, such as <A>, <IMG>, etc
20589 for(i = 0; i < currentElement.attributes.length;i++) {
20591 var aname = currentElement.attributes.item(i).name;
20592 if (!currentElement.attributes.item(i).value.length) {
20595 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20598 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20607 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20610 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20615 // Traverse the tree
20617 var currentElementChild = currentElement.childNodes.item(i);
20618 var allText = true;
20619 var innerHTML = '';
20621 while (currentElementChild) {
20622 // Formatting code (indent the tree so it looks nice on the screen)
20623 var nopad = nopadtext;
20624 if (lastnode == 'SPAN') {
20628 if (currentElementChild.nodeName == '#text') {
20629 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20630 toadd = nopadtext ? toadd : toadd.trim();
20631 if (!nopad && toadd.length > 80) {
20632 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20634 innerHTML += toadd;
20637 currentElementChild = currentElement.childNodes.item(i);
20643 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20645 // Recursively traverse the tree structure of the child node
20646 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20647 lastnode = currentElementChild.nodeName;
20649 currentElementChild=currentElement.childNodes.item(i);
20655 // The remaining code is mostly for formatting the tree
20656 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20661 ret+= "</"+tagName+">";
20667 applyBlacklists : function()
20669 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20670 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20674 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20675 if (b.indexOf(tag) > -1) {
20678 this.white.push(tag);
20682 Roo.each(w, function(tag) {
20683 if (b.indexOf(tag) > -1) {
20686 if (this.white.indexOf(tag) > -1) {
20689 this.white.push(tag);
20694 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20695 if (w.indexOf(tag) > -1) {
20698 this.black.push(tag);
20702 Roo.each(b, function(tag) {
20703 if (w.indexOf(tag) > -1) {
20706 if (this.black.indexOf(tag) > -1) {
20709 this.black.push(tag);
20714 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20715 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20719 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20720 if (b.indexOf(tag) > -1) {
20723 this.cwhite.push(tag);
20727 Roo.each(w, function(tag) {
20728 if (b.indexOf(tag) > -1) {
20731 if (this.cwhite.indexOf(tag) > -1) {
20734 this.cwhite.push(tag);
20739 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20740 if (w.indexOf(tag) > -1) {
20743 this.cblack.push(tag);
20747 Roo.each(b, function(tag) {
20748 if (w.indexOf(tag) > -1) {
20751 if (this.cblack.indexOf(tag) > -1) {
20754 this.cblack.push(tag);
20759 setStylesheets : function(stylesheets)
20761 if(typeof(stylesheets) == 'string'){
20762 Roo.get(this.iframe.contentDocument.head).createChild({
20764 rel : 'stylesheet',
20773 Roo.each(stylesheets, function(s) {
20778 Roo.get(_this.iframe.contentDocument.head).createChild({
20780 rel : 'stylesheet',
20789 removeStylesheets : function()
20793 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20798 // hide stuff that is not compatible
20812 * @event specialkey
20816 * @cfg {String} fieldClass @hide
20819 * @cfg {String} focusClass @hide
20822 * @cfg {String} autoCreate @hide
20825 * @cfg {String} inputType @hide
20828 * @cfg {String} invalidClass @hide
20831 * @cfg {String} invalidText @hide
20834 * @cfg {String} msgFx @hide
20837 * @cfg {String} validateOnBlur @hide
20841 Roo.HtmlEditorCore.white = [
20842 'area', 'br', 'img', 'input', 'hr', 'wbr',
20844 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
20845 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
20846 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
20847 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
20848 'table', 'ul', 'xmp',
20850 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
20853 'dir', 'menu', 'ol', 'ul', 'dl',
20859 Roo.HtmlEditorCore.black = [
20860 // 'embed', 'object', // enable - backend responsiblity to clean thiese
20862 'base', 'basefont', 'bgsound', 'blink', 'body',
20863 'frame', 'frameset', 'head', 'html', 'ilayer',
20864 'iframe', 'layer', 'link', 'meta', 'object',
20865 'script', 'style' ,'title', 'xml' // clean later..
20867 Roo.HtmlEditorCore.clean = [
20868 'script', 'style', 'title', 'xml'
20870 Roo.HtmlEditorCore.remove = [
20875 Roo.HtmlEditorCore.ablack = [
20879 Roo.HtmlEditorCore.aclean = [
20880 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
20884 Roo.HtmlEditorCore.pwhite= [
20885 'http', 'https', 'mailto'
20888 // white listed style attributes.
20889 Roo.HtmlEditorCore.cwhite= [
20890 // 'text-align', /// default is to allow most things..
20896 // black listed style attributes.
20897 Roo.HtmlEditorCore.cblack= [
20898 // 'font-size' -- this can be set by the project
20902 Roo.HtmlEditorCore.swapCodes =[
20921 * @class Roo.bootstrap.HtmlEditor
20922 * @extends Roo.bootstrap.TextArea
20923 * Bootstrap HtmlEditor class
20926 * Create a new HtmlEditor
20927 * @param {Object} config The config object
20930 Roo.bootstrap.HtmlEditor = function(config){
20931 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20932 if (!this.toolbars) {
20933 this.toolbars = [];
20935 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20938 * @event initialize
20939 * Fires when the editor is fully initialized (including the iframe)
20940 * @param {HtmlEditor} this
20945 * Fires when the editor is first receives the focus. Any insertion must wait
20946 * until after this event.
20947 * @param {HtmlEditor} this
20951 * @event beforesync
20952 * Fires before the textarea is updated with content from the editor iframe. Return false
20953 * to cancel the sync.
20954 * @param {HtmlEditor} this
20955 * @param {String} html
20959 * @event beforepush
20960 * Fires before the iframe editor is updated with content from the textarea. Return false
20961 * to cancel the push.
20962 * @param {HtmlEditor} this
20963 * @param {String} html
20968 * Fires when the textarea is updated with content from the editor iframe.
20969 * @param {HtmlEditor} this
20970 * @param {String} html
20975 * Fires when the iframe editor is updated with content from the textarea.
20976 * @param {HtmlEditor} this
20977 * @param {String} html
20981 * @event editmodechange
20982 * Fires when the editor switches edit modes
20983 * @param {HtmlEditor} this
20984 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20986 editmodechange: true,
20988 * @event editorevent
20989 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20990 * @param {HtmlEditor} this
20994 * @event firstfocus
20995 * Fires when on first focus - needed by toolbars..
20996 * @param {HtmlEditor} this
21001 * Auto save the htmlEditor value as a file into Events
21002 * @param {HtmlEditor} this
21006 * @event savedpreview
21007 * preview the saved version of htmlEditor
21008 * @param {HtmlEditor} this
21015 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
21019 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21024 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21029 * @cfg {Number} height (in pixels)
21033 * @cfg {Number} width (in pixels)
21038 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21041 stylesheets: false,
21046 // private properties
21047 validationEvent : false,
21049 initialized : false,
21052 onFocus : Roo.emptyFn,
21054 hideMode:'offsets',
21057 tbContainer : false,
21059 toolbarContainer :function() {
21060 return this.wrap.select('.x-html-editor-tb',true).first();
21064 * Protected method that will not generally be called directly. It
21065 * is called when the editor creates its toolbar. Override this method if you need to
21066 * add custom toolbar buttons.
21067 * @param {HtmlEditor} editor
21069 createToolbar : function(){
21071 Roo.log("create toolbars");
21073 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21074 this.toolbars[0].render(this.toolbarContainer());
21078 // if (!editor.toolbars || !editor.toolbars.length) {
21079 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21082 // for (var i =0 ; i < editor.toolbars.length;i++) {
21083 // editor.toolbars[i] = Roo.factory(
21084 // typeof(editor.toolbars[i]) == 'string' ?
21085 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
21086 // Roo.bootstrap.HtmlEditor);
21087 // editor.toolbars[i].init(editor);
21093 onRender : function(ct, position)
21095 // Roo.log("Call onRender: " + this.xtype);
21097 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21099 this.wrap = this.inputEl().wrap({
21100 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21103 this.editorcore.onRender(ct, position);
21105 if (this.resizable) {
21106 this.resizeEl = new Roo.Resizable(this.wrap, {
21110 minHeight : this.height,
21111 height: this.height,
21112 handles : this.resizable,
21115 resize : function(r, w, h) {
21116 _t.onResize(w,h); // -something
21122 this.createToolbar(this);
21125 if(!this.width && this.resizable){
21126 this.setSize(this.wrap.getSize());
21128 if (this.resizeEl) {
21129 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21130 // should trigger onReize..
21136 onResize : function(w, h)
21138 Roo.log('resize: ' +w + ',' + h );
21139 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21143 if(this.inputEl() ){
21144 if(typeof w == 'number'){
21145 var aw = w - this.wrap.getFrameWidth('lr');
21146 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21149 if(typeof h == 'number'){
21150 var tbh = -11; // fixme it needs to tool bar size!
21151 for (var i =0; i < this.toolbars.length;i++) {
21152 // fixme - ask toolbars for heights?
21153 tbh += this.toolbars[i].el.getHeight();
21154 //if (this.toolbars[i].footer) {
21155 // tbh += this.toolbars[i].footer.el.getHeight();
21163 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21164 ah -= 5; // knock a few pixes off for look..
21165 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21169 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21170 this.editorcore.onResize(ew,eh);
21175 * Toggles the editor between standard and source edit mode.
21176 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21178 toggleSourceEdit : function(sourceEditMode)
21180 this.editorcore.toggleSourceEdit(sourceEditMode);
21182 if(this.editorcore.sourceEditMode){
21183 Roo.log('editor - showing textarea');
21186 // Roo.log(this.syncValue());
21188 this.inputEl().removeClass(['hide', 'x-hidden']);
21189 this.inputEl().dom.removeAttribute('tabIndex');
21190 this.inputEl().focus();
21192 Roo.log('editor - hiding textarea');
21194 // Roo.log(this.pushValue());
21197 this.inputEl().addClass(['hide', 'x-hidden']);
21198 this.inputEl().dom.setAttribute('tabIndex', -1);
21199 //this.deferFocus();
21202 if(this.resizable){
21203 this.setSize(this.wrap.getSize());
21206 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21209 // private (for BoxComponent)
21210 adjustSize : Roo.BoxComponent.prototype.adjustSize,
21212 // private (for BoxComponent)
21213 getResizeEl : function(){
21217 // private (for BoxComponent)
21218 getPositionEl : function(){
21223 initEvents : function(){
21224 this.originalValue = this.getValue();
21228 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21231 // markInvalid : Roo.emptyFn,
21233 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21236 // clearInvalid : Roo.emptyFn,
21238 setValue : function(v){
21239 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21240 this.editorcore.pushValue();
21245 deferFocus : function(){
21246 this.focus.defer(10, this);
21250 focus : function(){
21251 this.editorcore.focus();
21257 onDestroy : function(){
21263 for (var i =0; i < this.toolbars.length;i++) {
21264 // fixme - ask toolbars for heights?
21265 this.toolbars[i].onDestroy();
21268 this.wrap.dom.innerHTML = '';
21269 this.wrap.remove();
21274 onFirstFocus : function(){
21275 //Roo.log("onFirstFocus");
21276 this.editorcore.onFirstFocus();
21277 for (var i =0; i < this.toolbars.length;i++) {
21278 this.toolbars[i].onFirstFocus();
21284 syncValue : function()
21286 this.editorcore.syncValue();
21289 pushValue : function()
21291 this.editorcore.pushValue();
21295 // hide stuff that is not compatible
21309 * @event specialkey
21313 * @cfg {String} fieldClass @hide
21316 * @cfg {String} focusClass @hide
21319 * @cfg {String} autoCreate @hide
21322 * @cfg {String} inputType @hide
21325 * @cfg {String} invalidClass @hide
21328 * @cfg {String} invalidText @hide
21331 * @cfg {String} msgFx @hide
21334 * @cfg {String} validateOnBlur @hide
21343 Roo.namespace('Roo.bootstrap.htmleditor');
21345 * @class Roo.bootstrap.HtmlEditorToolbar1
21350 new Roo.bootstrap.HtmlEditor({
21353 new Roo.bootstrap.HtmlEditorToolbar1({
21354 disable : { fonts: 1 , format: 1, ..., ... , ...],
21360 * @cfg {Object} disable List of elements to disable..
21361 * @cfg {Array} btns List of additional buttons.
21365 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21368 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21371 Roo.apply(this, config);
21373 // default disabled, based on 'good practice'..
21374 this.disable = this.disable || {};
21375 Roo.applyIf(this.disable, {
21378 specialElements : true
21380 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21382 this.editor = config.editor;
21383 this.editorcore = config.editor.editorcore;
21385 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21387 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21388 // dont call parent... till later.
21390 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
21395 editorcore : false,
21400 "h1","h2","h3","h4","h5","h6",
21402 "abbr", "acronym", "address", "cite", "samp", "var",
21406 onRender : function(ct, position)
21408 // Roo.log("Call onRender: " + this.xtype);
21410 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21412 this.el.dom.style.marginBottom = '0';
21414 var editorcore = this.editorcore;
21415 var editor= this.editor;
21418 var btn = function(id,cmd , toggle, handler){
21420 var event = toggle ? 'toggle' : 'click';
21425 xns: Roo.bootstrap,
21428 enableToggle:toggle !== false,
21430 pressed : toggle ? false : null,
21433 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21434 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21443 xns: Roo.bootstrap,
21444 glyphicon : 'font',
21448 xns: Roo.bootstrap,
21452 Roo.each(this.formats, function(f) {
21453 style.menu.items.push({
21455 xns: Roo.bootstrap,
21456 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21461 editorcore.insertTag(this.tagname);
21468 children.push(style);
21471 btn('bold',false,true);
21472 btn('italic',false,true);
21473 btn('align-left', 'justifyleft',true);
21474 btn('align-center', 'justifycenter',true);
21475 btn('align-right' , 'justifyright',true);
21476 btn('link', false, false, function(btn) {
21477 //Roo.log("create link?");
21478 var url = prompt(this.createLinkText, this.defaultLinkValue);
21479 if(url && url != 'http:/'+'/'){
21480 this.editorcore.relayCmd('createlink', url);
21483 btn('list','insertunorderedlist',true);
21484 btn('pencil', false,true, function(btn){
21487 this.toggleSourceEdit(btn.pressed);
21493 xns: Roo.bootstrap,
21498 xns: Roo.bootstrap,
21503 cog.menu.items.push({
21505 xns: Roo.bootstrap,
21506 html : Clean styles,
21511 editorcore.insertTag(this.tagname);
21520 this.xtype = 'NavSimplebar';
21522 for(var i=0;i< children.length;i++) {
21524 this.buttons.add(this.addxtypeChild(children[i]));
21528 editor.on('editorevent', this.updateToolbar, this);
21530 onBtnClick : function(id)
21532 this.editorcore.relayCmd(id);
21533 this.editorcore.focus();
21537 * Protected method that will not generally be called directly. It triggers
21538 * a toolbar update by reading the markup state of the current selection in the editor.
21540 updateToolbar: function(){
21542 if(!this.editorcore.activated){
21543 this.editor.onFirstFocus(); // is this neeed?
21547 var btns = this.buttons;
21548 var doc = this.editorcore.doc;
21549 btns.get('bold').setActive(doc.queryCommandState('bold'));
21550 btns.get('italic').setActive(doc.queryCommandState('italic'));
21551 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21553 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21554 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21555 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21557 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21558 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21561 var ans = this.editorcore.getAllAncestors();
21562 if (this.formatCombo) {
21565 var store = this.formatCombo.store;
21566 this.formatCombo.setValue("");
21567 for (var i =0; i < ans.length;i++) {
21568 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21570 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21578 // hides menus... - so this cant be on a menu...
21579 Roo.bootstrap.MenuMgr.hideAll();
21581 Roo.bootstrap.MenuMgr.hideAll();
21582 //this.editorsyncValue();
21584 onFirstFocus: function() {
21585 this.buttons.each(function(item){
21589 toggleSourceEdit : function(sourceEditMode){
21592 if(sourceEditMode){
21593 Roo.log("disabling buttons");
21594 this.buttons.each( function(item){
21595 if(item.cmd != 'pencil'){
21601 Roo.log("enabling buttons");
21602 if(this.editorcore.initialized){
21603 this.buttons.each( function(item){
21609 Roo.log("calling toggole on editor");
21610 // tell the editor that it's been pressed..
21611 this.editor.toggleSourceEdit(sourceEditMode);
21621 * @class Roo.bootstrap.Table.AbstractSelectionModel
21622 * @extends Roo.util.Observable
21623 * Abstract base class for grid SelectionModels. It provides the interface that should be
21624 * implemented by descendant classes. This class should not be directly instantiated.
21627 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21628 this.locked = false;
21629 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21633 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21634 /** @ignore Called by the grid automatically. Do not call directly. */
21635 init : function(grid){
21641 * Locks the selections.
21644 this.locked = true;
21648 * Unlocks the selections.
21650 unlock : function(){
21651 this.locked = false;
21655 * Returns true if the selections are locked.
21656 * @return {Boolean}
21658 isLocked : function(){
21659 return this.locked;
21663 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21664 * @class Roo.bootstrap.Table.RowSelectionModel
21665 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21666 * It supports multiple selections and keyboard selection/navigation.
21668 * @param {Object} config
21671 Roo.bootstrap.Table.RowSelectionModel = function(config){
21672 Roo.apply(this, config);
21673 this.selections = new Roo.util.MixedCollection(false, function(o){
21678 this.lastActive = false;
21682 * @event selectionchange
21683 * Fires when the selection changes
21684 * @param {SelectionModel} this
21686 "selectionchange" : true,
21688 * @event afterselectionchange
21689 * Fires after the selection changes (eg. by key press or clicking)
21690 * @param {SelectionModel} this
21692 "afterselectionchange" : true,
21694 * @event beforerowselect
21695 * Fires when a row is selected being selected, return false to cancel.
21696 * @param {SelectionModel} this
21697 * @param {Number} rowIndex The selected index
21698 * @param {Boolean} keepExisting False if other selections will be cleared
21700 "beforerowselect" : true,
21703 * Fires when a row is selected.
21704 * @param {SelectionModel} this
21705 * @param {Number} rowIndex The selected index
21706 * @param {Roo.data.Record} r The record
21708 "rowselect" : true,
21710 * @event rowdeselect
21711 * Fires when a row is deselected.
21712 * @param {SelectionModel} this
21713 * @param {Number} rowIndex The selected index
21715 "rowdeselect" : true
21717 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21718 this.locked = false;
21721 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21723 * @cfg {Boolean} singleSelect
21724 * True to allow selection of only one row at a time (defaults to false)
21726 singleSelect : false,
21729 initEvents : function(){
21731 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21732 this.grid.on("mousedown", this.handleMouseDown, this);
21733 }else{ // allow click to work like normal
21734 this.grid.on("rowclick", this.handleDragableRowClick, this);
21737 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21738 "up" : function(e){
21740 this.selectPrevious(e.shiftKey);
21741 }else if(this.last !== false && this.lastActive !== false){
21742 var last = this.last;
21743 this.selectRange(this.last, this.lastActive-1);
21744 this.grid.getView().focusRow(this.lastActive);
21745 if(last !== false){
21749 this.selectFirstRow();
21751 this.fireEvent("afterselectionchange", this);
21753 "down" : function(e){
21755 this.selectNext(e.shiftKey);
21756 }else if(this.last !== false && this.lastActive !== false){
21757 var last = this.last;
21758 this.selectRange(this.last, this.lastActive+1);
21759 this.grid.getView().focusRow(this.lastActive);
21760 if(last !== false){
21764 this.selectFirstRow();
21766 this.fireEvent("afterselectionchange", this);
21771 var view = this.grid.view;
21772 view.on("refresh", this.onRefresh, this);
21773 view.on("rowupdated", this.onRowUpdated, this);
21774 view.on("rowremoved", this.onRemove, this);
21778 onRefresh : function(){
21779 var ds = this.grid.dataSource, i, v = this.grid.view;
21780 var s = this.selections;
21781 s.each(function(r){
21782 if((i = ds.indexOfId(r.id)) != -1){
21791 onRemove : function(v, index, r){
21792 this.selections.remove(r);
21796 onRowUpdated : function(v, index, r){
21797 if(this.isSelected(r)){
21798 v.onRowSelect(index);
21804 * @param {Array} records The records to select
21805 * @param {Boolean} keepExisting (optional) True to keep existing selections
21807 selectRecords : function(records, keepExisting){
21809 this.clearSelections();
21811 var ds = this.grid.dataSource;
21812 for(var i = 0, len = records.length; i < len; i++){
21813 this.selectRow(ds.indexOf(records[i]), true);
21818 * Gets the number of selected rows.
21821 getCount : function(){
21822 return this.selections.length;
21826 * Selects the first row in the grid.
21828 selectFirstRow : function(){
21833 * Select the last row.
21834 * @param {Boolean} keepExisting (optional) True to keep existing selections
21836 selectLastRow : function(keepExisting){
21837 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21841 * Selects the row immediately following the last selected row.
21842 * @param {Boolean} keepExisting (optional) True to keep existing selections
21844 selectNext : function(keepExisting){
21845 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21846 this.selectRow(this.last+1, keepExisting);
21847 this.grid.getView().focusRow(this.last);
21852 * Selects the row that precedes the last selected row.
21853 * @param {Boolean} keepExisting (optional) True to keep existing selections
21855 selectPrevious : function(keepExisting){
21857 this.selectRow(this.last-1, keepExisting);
21858 this.grid.getView().focusRow(this.last);
21863 * Returns the selected records
21864 * @return {Array} Array of selected records
21866 getSelections : function(){
21867 return [].concat(this.selections.items);
21871 * Returns the first selected record.
21874 getSelected : function(){
21875 return this.selections.itemAt(0);
21880 * Clears all selections.
21882 clearSelections : function(fast){
21887 var ds = this.grid.dataSource;
21888 var s = this.selections;
21889 s.each(function(r){
21890 this.deselectRow(ds.indexOfId(r.id));
21894 this.selections.clear();
21901 * Selects all rows.
21903 selectAll : function(){
21907 this.selections.clear();
21908 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21909 this.selectRow(i, true);
21914 * Returns True if there is a selection.
21915 * @return {Boolean}
21917 hasSelection : function(){
21918 return this.selections.length > 0;
21922 * Returns True if the specified row is selected.
21923 * @param {Number/Record} record The record or index of the record to check
21924 * @return {Boolean}
21926 isSelected : function(index){
21927 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21928 return (r && this.selections.key(r.id) ? true : false);
21932 * Returns True if the specified record id is selected.
21933 * @param {String} id The id of record to check
21934 * @return {Boolean}
21936 isIdSelected : function(id){
21937 return (this.selections.key(id) ? true : false);
21941 handleMouseDown : function(e, t){
21942 var view = this.grid.getView(), rowIndex;
21943 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21946 if(e.shiftKey && this.last !== false){
21947 var last = this.last;
21948 this.selectRange(last, rowIndex, e.ctrlKey);
21949 this.last = last; // reset the last
21950 view.focusRow(rowIndex);
21952 var isSelected = this.isSelected(rowIndex);
21953 if(e.button !== 0 && isSelected){
21954 view.focusRow(rowIndex);
21955 }else if(e.ctrlKey && isSelected){
21956 this.deselectRow(rowIndex);
21957 }else if(!isSelected){
21958 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21959 view.focusRow(rowIndex);
21962 this.fireEvent("afterselectionchange", this);
21965 handleDragableRowClick : function(grid, rowIndex, e)
21967 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21968 this.selectRow(rowIndex, false);
21969 grid.view.focusRow(rowIndex);
21970 this.fireEvent("afterselectionchange", this);
21975 * Selects multiple rows.
21976 * @param {Array} rows Array of the indexes of the row to select
21977 * @param {Boolean} keepExisting (optional) True to keep existing selections
21979 selectRows : function(rows, keepExisting){
21981 this.clearSelections();
21983 for(var i = 0, len = rows.length; i < len; i++){
21984 this.selectRow(rows[i], true);
21989 * Selects a range of rows. All rows in between startRow and endRow are also selected.
21990 * @param {Number} startRow The index of the first row in the range
21991 * @param {Number} endRow The index of the last row in the range
21992 * @param {Boolean} keepExisting (optional) True to retain existing selections
21994 selectRange : function(startRow, endRow, keepExisting){
21999 this.clearSelections();
22001 if(startRow <= endRow){
22002 for(var i = startRow; i <= endRow; i++){
22003 this.selectRow(i, true);
22006 for(var i = startRow; i >= endRow; i--){
22007 this.selectRow(i, true);
22013 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22014 * @param {Number} startRow The index of the first row in the range
22015 * @param {Number} endRow The index of the last row in the range
22017 deselectRange : function(startRow, endRow, preventViewNotify){
22021 for(var i = startRow; i <= endRow; i++){
22022 this.deselectRow(i, preventViewNotify);
22028 * @param {Number} row The index of the row to select
22029 * @param {Boolean} keepExisting (optional) True to keep existing selections
22031 selectRow : function(index, keepExisting, preventViewNotify){
22032 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22035 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22036 if(!keepExisting || this.singleSelect){
22037 this.clearSelections();
22039 var r = this.grid.dataSource.getAt(index);
22040 this.selections.add(r);
22041 this.last = this.lastActive = index;
22042 if(!preventViewNotify){
22043 this.grid.getView().onRowSelect(index);
22045 this.fireEvent("rowselect", this, index, r);
22046 this.fireEvent("selectionchange", this);
22052 * @param {Number} row The index of the row to deselect
22054 deselectRow : function(index, preventViewNotify){
22058 if(this.last == index){
22061 if(this.lastActive == index){
22062 this.lastActive = false;
22064 var r = this.grid.dataSource.getAt(index);
22065 this.selections.remove(r);
22066 if(!preventViewNotify){
22067 this.grid.getView().onRowDeselect(index);
22069 this.fireEvent("rowdeselect", this, index);
22070 this.fireEvent("selectionchange", this);
22074 restoreLast : function(){
22076 this.last = this._last;
22081 acceptsNav : function(row, col, cm){
22082 return !cm.isHidden(col) && cm.isCellEditable(col, row);
22086 onEditorKey : function(field, e){
22087 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22092 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22094 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22096 }else if(k == e.ENTER && !e.ctrlKey){
22100 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22102 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22104 }else if(k == e.ESC){
22108 g.startEditing(newCell[0], newCell[1]);
22113 * Ext JS Library 1.1.1
22114 * Copyright(c) 2006-2007, Ext JS, LLC.
22116 * Originally Released Under LGPL - original licence link has changed is not relivant.
22119 * <script type="text/javascript">
22123 * @class Roo.bootstrap.PagingToolbar
22124 * @extends Roo.bootstrap.NavSimplebar
22125 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22127 * Create a new PagingToolbar
22128 * @param {Object} config The config object
22129 * @param {Roo.data.Store} store
22131 Roo.bootstrap.PagingToolbar = function(config)
22133 // old args format still supported... - xtype is prefered..
22134 // created from xtype...
22136 this.ds = config.dataSource;
22138 if (config.store && !this.ds) {
22139 this.store= Roo.factory(config.store, Roo.data);
22140 this.ds = this.store;
22141 this.ds.xmodule = this.xmodule || false;
22144 this.toolbarItems = [];
22145 if (config.items) {
22146 this.toolbarItems = config.items;
22149 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22154 this.bind(this.ds);
22157 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22161 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22163 * @cfg {Roo.data.Store} dataSource
22164 * The underlying data store providing the paged data
22167 * @cfg {String/HTMLElement/Element} container
22168 * container The id or element that will contain the toolbar
22171 * @cfg {Boolean} displayInfo
22172 * True to display the displayMsg (defaults to false)
22175 * @cfg {Number} pageSize
22176 * The number of records to display per page (defaults to 20)
22180 * @cfg {String} displayMsg
22181 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22183 displayMsg : 'Displaying {0} - {1} of {2}',
22185 * @cfg {String} emptyMsg
22186 * The message to display when no records are found (defaults to "No data to display")
22188 emptyMsg : 'No data to display',
22190 * Customizable piece of the default paging text (defaults to "Page")
22193 beforePageText : "Page",
22195 * Customizable piece of the default paging text (defaults to "of %0")
22198 afterPageText : "of {0}",
22200 * Customizable piece of the default paging text (defaults to "First Page")
22203 firstText : "First Page",
22205 * Customizable piece of the default paging text (defaults to "Previous Page")
22208 prevText : "Previous Page",
22210 * Customizable piece of the default paging text (defaults to "Next Page")
22213 nextText : "Next Page",
22215 * Customizable piece of the default paging text (defaults to "Last Page")
22218 lastText : "Last Page",
22220 * Customizable piece of the default paging text (defaults to "Refresh")
22223 refreshText : "Refresh",
22227 onRender : function(ct, position)
22229 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22230 this.navgroup.parentId = this.id;
22231 this.navgroup.onRender(this.el, null);
22232 // add the buttons to the navgroup
22234 if(this.displayInfo){
22235 Roo.log(this.el.select('ul.navbar-nav',true).first());
22236 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22237 this.displayEl = this.el.select('.x-paging-info', true).first();
22238 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22239 // this.displayEl = navel.el.select('span',true).first();
22245 Roo.each(_this.buttons, function(e){ // this might need to use render????
22246 Roo.factory(e).onRender(_this.el, null);
22250 Roo.each(_this.toolbarItems, function(e) {
22251 _this.navgroup.addItem(e);
22255 this.first = this.navgroup.addItem({
22256 tooltip: this.firstText,
22258 icon : 'fa fa-backward',
22260 preventDefault: true,
22261 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22264 this.prev = this.navgroup.addItem({
22265 tooltip: this.prevText,
22267 icon : 'fa fa-step-backward',
22269 preventDefault: true,
22270 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
22272 //this.addSeparator();
22275 var field = this.navgroup.addItem( {
22277 cls : 'x-paging-position',
22279 html : this.beforePageText +
22280 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22281 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
22284 this.field = field.el.select('input', true).first();
22285 this.field.on("keydown", this.onPagingKeydown, this);
22286 this.field.on("focus", function(){this.dom.select();});
22289 this.afterTextEl = field.el.select('.x-paging-after',true).first();
22290 //this.field.setHeight(18);
22291 //this.addSeparator();
22292 this.next = this.navgroup.addItem({
22293 tooltip: this.nextText,
22295 html : ' <i class="fa fa-step-forward">',
22297 preventDefault: true,
22298 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
22300 this.last = this.navgroup.addItem({
22301 tooltip: this.lastText,
22302 icon : 'fa fa-forward',
22305 preventDefault: true,
22306 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
22308 //this.addSeparator();
22309 this.loading = this.navgroup.addItem({
22310 tooltip: this.refreshText,
22311 icon: 'fa fa-refresh',
22312 preventDefault: true,
22313 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22319 updateInfo : function(){
22320 if(this.displayEl){
22321 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22322 var msg = count == 0 ?
22326 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
22328 this.displayEl.update(msg);
22333 onLoad : function(ds, r, o){
22334 this.cursor = o.params ? o.params.start : 0;
22335 var d = this.getPageData(),
22339 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22340 this.field.dom.value = ap;
22341 this.first.setDisabled(ap == 1);
22342 this.prev.setDisabled(ap == 1);
22343 this.next.setDisabled(ap == ps);
22344 this.last.setDisabled(ap == ps);
22345 this.loading.enable();
22350 getPageData : function(){
22351 var total = this.ds.getTotalCount();
22354 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22355 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22360 onLoadError : function(){
22361 this.loading.enable();
22365 onPagingKeydown : function(e){
22366 var k = e.getKey();
22367 var d = this.getPageData();
22369 var v = this.field.dom.value, pageNum;
22370 if(!v || isNaN(pageNum = parseInt(v, 10))){
22371 this.field.dom.value = d.activePage;
22374 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22375 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22378 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))
22380 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22381 this.field.dom.value = pageNum;
22382 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22385 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22387 var v = this.field.dom.value, pageNum;
22388 var increment = (e.shiftKey) ? 10 : 1;
22389 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22392 if(!v || isNaN(pageNum = parseInt(v, 10))) {
22393 this.field.dom.value = d.activePage;
22396 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22398 this.field.dom.value = parseInt(v, 10) + increment;
22399 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22400 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22407 beforeLoad : function(){
22409 this.loading.disable();
22414 onClick : function(which){
22423 ds.load({params:{start: 0, limit: this.pageSize}});
22426 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22429 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22432 var total = ds.getTotalCount();
22433 var extra = total % this.pageSize;
22434 var lastStart = extra ? (total - extra) : total-this.pageSize;
22435 ds.load({params:{start: lastStart, limit: this.pageSize}});
22438 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22444 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22445 * @param {Roo.data.Store} store The data store to unbind
22447 unbind : function(ds){
22448 ds.un("beforeload", this.beforeLoad, this);
22449 ds.un("load", this.onLoad, this);
22450 ds.un("loadexception", this.onLoadError, this);
22451 ds.un("remove", this.updateInfo, this);
22452 ds.un("add", this.updateInfo, this);
22453 this.ds = undefined;
22457 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22458 * @param {Roo.data.Store} store The data store to bind
22460 bind : function(ds){
22461 ds.on("beforeload", this.beforeLoad, this);
22462 ds.on("load", this.onLoad, this);
22463 ds.on("loadexception", this.onLoadError, this);
22464 ds.on("remove", this.updateInfo, this);
22465 ds.on("add", this.updateInfo, this);
22476 * @class Roo.bootstrap.MessageBar
22477 * @extends Roo.bootstrap.Component
22478 * Bootstrap MessageBar class
22479 * @cfg {String} html contents of the MessageBar
22480 * @cfg {String} weight (info | success | warning | danger) default info
22481 * @cfg {String} beforeClass insert the bar before the given class
22482 * @cfg {Boolean} closable (true | false) default false
22483 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22486 * Create a new Element
22487 * @param {Object} config The config object
22490 Roo.bootstrap.MessageBar = function(config){
22491 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22494 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22500 beforeClass: 'bootstrap-sticky-wrap',
22502 getAutoCreate : function(){
22506 cls: 'alert alert-dismissable alert-' + this.weight,
22511 html: this.html || ''
22517 cfg.cls += ' alert-messages-fixed';
22531 onRender : function(ct, position)
22533 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22536 var cfg = Roo.apply({}, this.getAutoCreate());
22540 cfg.cls += ' ' + this.cls;
22543 cfg.style = this.style;
22545 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22547 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22550 this.el.select('>button.close').on('click', this.hide, this);
22556 if (!this.rendered) {
22562 this.fireEvent('show', this);
22568 if (!this.rendered) {
22574 this.fireEvent('hide', this);
22577 update : function()
22579 // var e = this.el.dom.firstChild;
22581 // if(this.closable){
22582 // e = e.nextSibling;
22585 // e.data = this.html || '';
22587 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22603 * @class Roo.bootstrap.Graph
22604 * @extends Roo.bootstrap.Component
22605 * Bootstrap Graph class
22609 @cfg {String} graphtype bar | vbar | pie
22610 @cfg {number} g_x coodinator | centre x (pie)
22611 @cfg {number} g_y coodinator | centre y (pie)
22612 @cfg {number} g_r radius (pie)
22613 @cfg {number} g_height height of the chart (respected by all elements in the set)
22614 @cfg {number} g_width width of the chart (respected by all elements in the set)
22615 @cfg {Object} title The title of the chart
22618 -opts (object) options for the chart
22620 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22621 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22623 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.
22624 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22626 o stretch (boolean)
22628 -opts (object) options for the pie
22631 o startAngle (number)
22632 o endAngle (number)
22636 * Create a new Input
22637 * @param {Object} config The config object
22640 Roo.bootstrap.Graph = function(config){
22641 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22647 * The img click event for the img.
22648 * @param {Roo.EventObject} e
22654 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22665 //g_colors: this.colors,
22672 getAutoCreate : function(){
22683 onRender : function(ct,position){
22684 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22685 this.raphael = Raphael(this.el.dom);
22687 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22688 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22689 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22690 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22692 r.text(160, 10, "Single Series Chart").attr(txtattr);
22693 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22694 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22695 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22697 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22698 r.barchart(330, 10, 300, 220, data1);
22699 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22700 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22703 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22704 // r.barchart(30, 30, 560, 250, xdata, {
22705 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22706 // axis : "0 0 1 1",
22707 // axisxlabels : xdata
22708 // //yvalues : cols,
22711 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22713 // this.load(null,xdata,{
22714 // axis : "0 0 1 1",
22715 // axisxlabels : xdata
22720 load : function(graphtype,xdata,opts){
22721 this.raphael.clear();
22723 graphtype = this.graphtype;
22728 var r = this.raphael,
22729 fin = function () {
22730 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22732 fout = function () {
22733 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22735 pfin = function() {
22736 this.sector.stop();
22737 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22740 this.label[0].stop();
22741 this.label[0].attr({ r: 7.5 });
22742 this.label[1].attr({ "font-weight": 800 });
22745 pfout = function() {
22746 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22749 this.label[0].animate({ r: 5 }, 500, "bounce");
22750 this.label[1].attr({ "font-weight": 400 });
22756 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22759 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22762 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
22763 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22765 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22772 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22777 setTitle: function(o)
22782 initEvents: function() {
22785 this.el.on('click', this.onClick, this);
22789 onClick : function(e)
22791 Roo.log('img onclick');
22792 this.fireEvent('click', this, e);
22804 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22807 * @class Roo.bootstrap.dash.NumberBox
22808 * @extends Roo.bootstrap.Component
22809 * Bootstrap NumberBox class
22810 * @cfg {String} headline Box headline
22811 * @cfg {String} content Box content
22812 * @cfg {String} icon Box icon
22813 * @cfg {String} footer Footer text
22814 * @cfg {String} fhref Footer href
22817 * Create a new NumberBox
22818 * @param {Object} config The config object
22822 Roo.bootstrap.dash.NumberBox = function(config){
22823 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22827 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
22836 getAutoCreate : function(){
22840 cls : 'small-box ',
22848 cls : 'roo-headline',
22849 html : this.headline
22853 cls : 'roo-content',
22854 html : this.content
22868 cls : 'ion ' + this.icon
22877 cls : 'small-box-footer',
22878 href : this.fhref || '#',
22882 cfg.cn.push(footer);
22889 onRender : function(ct,position){
22890 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22897 setHeadline: function (value)
22899 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22902 setFooter: function (value, href)
22904 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22907 this.el.select('a.small-box-footer',true).first().attr('href', href);
22912 setContent: function (value)
22914 this.el.select('.roo-content',true).first().dom.innerHTML = value;
22917 initEvents: function()
22931 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22934 * @class Roo.bootstrap.dash.TabBox
22935 * @extends Roo.bootstrap.Component
22936 * Bootstrap TabBox class
22937 * @cfg {String} title Title of the TabBox
22938 * @cfg {String} icon Icon of the TabBox
22939 * @cfg {Boolean} showtabs (true|false) show the tabs default true
22940 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22943 * Create a new TabBox
22944 * @param {Object} config The config object
22948 Roo.bootstrap.dash.TabBox = function(config){
22949 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22954 * When a pane is added
22955 * @param {Roo.bootstrap.dash.TabPane} pane
22959 * @event activatepane
22960 * When a pane is activated
22961 * @param {Roo.bootstrap.dash.TabPane} pane
22963 "activatepane" : true
22971 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
22976 tabScrollable : false,
22978 getChildContainer : function()
22980 return this.el.select('.tab-content', true).first();
22983 getAutoCreate : function(){
22987 cls: 'pull-left header',
22995 cls: 'fa ' + this.icon
23001 cls: 'nav nav-tabs pull-right',
23007 if(this.tabScrollable){
23014 cls: 'nav nav-tabs pull-right',
23025 cls: 'nav-tabs-custom',
23030 cls: 'tab-content no-padding',
23038 initEvents : function()
23040 //Roo.log('add add pane handler');
23041 this.on('addpane', this.onAddPane, this);
23044 * Updates the box title
23045 * @param {String} html to set the title to.
23047 setTitle : function(value)
23049 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23051 onAddPane : function(pane)
23053 this.panes.push(pane);
23054 //Roo.log('addpane');
23056 // tabs are rendere left to right..
23057 if(!this.showtabs){
23061 var ctr = this.el.select('.nav-tabs', true).first();
23064 var existing = ctr.select('.nav-tab',true);
23065 var qty = existing.getCount();;
23068 var tab = ctr.createChild({
23070 cls : 'nav-tab' + (qty ? '' : ' active'),
23078 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23081 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23083 pane.el.addClass('active');
23088 onTabClick : function(ev,un,ob,pane)
23090 //Roo.log('tab - prev default');
23091 ev.preventDefault();
23094 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23095 pane.tab.addClass('active');
23096 //Roo.log(pane.title);
23097 this.getChildContainer().select('.tab-pane',true).removeClass('active');
23098 // technically we should have a deactivate event.. but maybe add later.
23099 // and it should not de-activate the selected tab...
23100 this.fireEvent('activatepane', pane);
23101 pane.el.addClass('active');
23102 pane.fireEvent('activate');
23107 getActivePane : function()
23110 Roo.each(this.panes, function(p) {
23111 if(p.el.hasClass('active')){
23132 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23134 * @class Roo.bootstrap.TabPane
23135 * @extends Roo.bootstrap.Component
23136 * Bootstrap TabPane class
23137 * @cfg {Boolean} active (false | true) Default false
23138 * @cfg {String} title title of panel
23142 * Create a new TabPane
23143 * @param {Object} config The config object
23146 Roo.bootstrap.dash.TabPane = function(config){
23147 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23153 * When a pane is activated
23154 * @param {Roo.bootstrap.dash.TabPane} pane
23161 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
23166 // the tabBox that this is attached to.
23169 getAutoCreate : function()
23177 cfg.cls += ' active';
23182 initEvents : function()
23184 //Roo.log('trigger add pane handler');
23185 this.parent().fireEvent('addpane', this)
23189 * Updates the tab title
23190 * @param {String} html to set the title to.
23192 setTitle: function(str)
23198 this.tab.select('a', true).first().dom.innerHTML = str;
23215 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23218 * @class Roo.bootstrap.menu.Menu
23219 * @extends Roo.bootstrap.Component
23220 * Bootstrap Menu class - container for Menu
23221 * @cfg {String} html Text of the menu
23222 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23223 * @cfg {String} icon Font awesome icon
23224 * @cfg {String} pos Menu align to (top | bottom) default bottom
23228 * Create a new Menu
23229 * @param {Object} config The config object
23233 Roo.bootstrap.menu.Menu = function(config){
23234 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23238 * @event beforeshow
23239 * Fires before this menu is displayed
23240 * @param {Roo.bootstrap.menu.Menu} this
23244 * @event beforehide
23245 * Fires before this menu is hidden
23246 * @param {Roo.bootstrap.menu.Menu} this
23251 * Fires after this menu is displayed
23252 * @param {Roo.bootstrap.menu.Menu} this
23257 * Fires after this menu is hidden
23258 * @param {Roo.bootstrap.menu.Menu} this
23263 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23264 * @param {Roo.bootstrap.menu.Menu} this
23265 * @param {Roo.EventObject} e
23272 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
23276 weight : 'default',
23281 getChildContainer : function() {
23282 if(this.isSubMenu){
23286 return this.el.select('ul.dropdown-menu', true).first();
23289 getAutoCreate : function()
23294 cls : 'roo-menu-text',
23302 cls : 'fa ' + this.icon
23313 cls : 'dropdown-button btn btn-' + this.weight,
23318 cls : 'dropdown-toggle btn btn-' + this.weight,
23328 cls : 'dropdown-menu'
23334 if(this.pos == 'top'){
23335 cfg.cls += ' dropup';
23338 if(this.isSubMenu){
23341 cls : 'dropdown-menu'
23348 onRender : function(ct, position)
23350 this.isSubMenu = ct.hasClass('dropdown-submenu');
23352 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23355 initEvents : function()
23357 if(this.isSubMenu){
23361 this.hidden = true;
23363 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23364 this.triggerEl.on('click', this.onTriggerPress, this);
23366 this.buttonEl = this.el.select('button.dropdown-button', true).first();
23367 this.buttonEl.on('click', this.onClick, this);
23373 if(this.isSubMenu){
23377 return this.el.select('ul.dropdown-menu', true).first();
23380 onClick : function(e)
23382 this.fireEvent("click", this, e);
23385 onTriggerPress : function(e)
23387 if (this.isVisible()) {
23394 isVisible : function(){
23395 return !this.hidden;
23400 this.fireEvent("beforeshow", this);
23402 this.hidden = false;
23403 this.el.addClass('open');
23405 Roo.get(document).on("mouseup", this.onMouseUp, this);
23407 this.fireEvent("show", this);
23414 this.fireEvent("beforehide", this);
23416 this.hidden = true;
23417 this.el.removeClass('open');
23419 Roo.get(document).un("mouseup", this.onMouseUp);
23421 this.fireEvent("hide", this);
23424 onMouseUp : function()
23438 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23441 * @class Roo.bootstrap.menu.Item
23442 * @extends Roo.bootstrap.Component
23443 * Bootstrap MenuItem class
23444 * @cfg {Boolean} submenu (true | false) default false
23445 * @cfg {String} html text of the item
23446 * @cfg {String} href the link
23447 * @cfg {Boolean} disable (true | false) default false
23448 * @cfg {Boolean} preventDefault (true | false) default true
23449 * @cfg {String} icon Font awesome icon
23450 * @cfg {String} pos Submenu align to (left | right) default right
23454 * Create a new Item
23455 * @param {Object} config The config object
23459 Roo.bootstrap.menu.Item = function(config){
23460 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23464 * Fires when the mouse is hovering over this menu
23465 * @param {Roo.bootstrap.menu.Item} this
23466 * @param {Roo.EventObject} e
23471 * Fires when the mouse exits this menu
23472 * @param {Roo.bootstrap.menu.Item} this
23473 * @param {Roo.EventObject} e
23479 * The raw click event for the entire grid.
23480 * @param {Roo.EventObject} e
23486 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23491 preventDefault: true,
23496 getAutoCreate : function()
23501 cls : 'roo-menu-item-text',
23509 cls : 'fa ' + this.icon
23518 href : this.href || '#',
23525 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23529 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23531 if(this.pos == 'left'){
23532 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23539 initEvents : function()
23541 this.el.on('mouseover', this.onMouseOver, this);
23542 this.el.on('mouseout', this.onMouseOut, this);
23544 this.el.select('a', true).first().on('click', this.onClick, this);
23548 onClick : function(e)
23550 if(this.preventDefault){
23551 e.preventDefault();
23554 this.fireEvent("click", this, e);
23557 onMouseOver : function(e)
23559 if(this.submenu && this.pos == 'left'){
23560 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23563 this.fireEvent("mouseover", this, e);
23566 onMouseOut : function(e)
23568 this.fireEvent("mouseout", this, e);
23580 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23583 * @class Roo.bootstrap.menu.Separator
23584 * @extends Roo.bootstrap.Component
23585 * Bootstrap Separator class
23588 * Create a new Separator
23589 * @param {Object} config The config object
23593 Roo.bootstrap.menu.Separator = function(config){
23594 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23597 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23599 getAutoCreate : function(){
23620 * @class Roo.bootstrap.Tooltip
23621 * Bootstrap Tooltip class
23622 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23623 * to determine which dom element triggers the tooltip.
23625 * It needs to add support for additional attributes like tooltip-position
23628 * Create a new Toolti
23629 * @param {Object} config The config object
23632 Roo.bootstrap.Tooltip = function(config){
23633 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23636 Roo.apply(Roo.bootstrap.Tooltip, {
23638 * @function init initialize tooltip monitoring.
23642 currentTip : false,
23643 currentRegion : false,
23649 Roo.get(document).on('mouseover', this.enter ,this);
23650 Roo.get(document).on('mouseout', this.leave, this);
23653 this.currentTip = new Roo.bootstrap.Tooltip();
23656 enter : function(ev)
23658 var dom = ev.getTarget();
23660 //Roo.log(['enter',dom]);
23661 var el = Roo.fly(dom);
23662 if (this.currentEl) {
23664 //Roo.log(this.currentEl);
23665 //Roo.log(this.currentEl.contains(dom));
23666 if (this.currentEl == el) {
23669 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23675 if (this.currentTip.el) {
23676 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23681 // you can not look for children, as if el is the body.. then everythign is the child..
23682 if (!el.attr('tooltip')) { //
23683 if (!el.select("[tooltip]").elements.length) {
23686 // is the mouse over this child...?
23687 bindEl = el.select("[tooltip]").first();
23688 var xy = ev.getXY();
23689 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23690 //Roo.log("not in region.");
23693 //Roo.log("child element over..");
23696 this.currentEl = bindEl;
23697 this.currentTip.bind(bindEl);
23698 this.currentRegion = Roo.lib.Region.getRegion(dom);
23699 this.currentTip.enter();
23702 leave : function(ev)
23704 var dom = ev.getTarget();
23705 //Roo.log(['leave',dom]);
23706 if (!this.currentEl) {
23711 if (dom != this.currentEl.dom) {
23714 var xy = ev.getXY();
23715 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23718 // only activate leave if mouse cursor is outside... bounding box..
23723 if (this.currentTip) {
23724 this.currentTip.leave();
23726 //Roo.log('clear currentEl');
23727 this.currentEl = false;
23732 'left' : ['r-l', [-2,0], 'right'],
23733 'right' : ['l-r', [2,0], 'left'],
23734 'bottom' : ['t-b', [0,2], 'top'],
23735 'top' : [ 'b-t', [0,-2], 'bottom']
23741 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
23746 delay : null, // can be { show : 300 , hide: 500}
23750 hoverState : null, //???
23752 placement : 'bottom',
23754 getAutoCreate : function(){
23761 cls : 'tooltip-arrow'
23764 cls : 'tooltip-inner'
23771 bind : function(el)
23777 enter : function () {
23779 if (this.timeout != null) {
23780 clearTimeout(this.timeout);
23783 this.hoverState = 'in';
23784 //Roo.log("enter - show");
23785 if (!this.delay || !this.delay.show) {
23790 this.timeout = setTimeout(function () {
23791 if (_t.hoverState == 'in') {
23794 }, this.delay.show);
23798 clearTimeout(this.timeout);
23800 this.hoverState = 'out';
23801 if (!this.delay || !this.delay.hide) {
23807 this.timeout = setTimeout(function () {
23808 //Roo.log("leave - timeout");
23810 if (_t.hoverState == 'out') {
23812 Roo.bootstrap.Tooltip.currentEl = false;
23820 this.render(document.body);
23823 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23825 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23827 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23829 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23831 var placement = typeof this.placement == 'function' ?
23832 this.placement.call(this, this.el, on_el) :
23835 var autoToken = /\s?auto?\s?/i;
23836 var autoPlace = autoToken.test(placement);
23838 placement = placement.replace(autoToken, '') || 'top';
23842 //this.el.setXY([0,0]);
23844 //this.el.dom.style.display='block';
23846 //this.el.appendTo(on_el);
23848 var p = this.getPosition();
23849 var box = this.el.getBox();
23855 var align = Roo.bootstrap.Tooltip.alignment[placement];
23857 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23859 if(placement == 'top' || placement == 'bottom'){
23861 placement = 'right';
23864 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23865 placement = 'left';
23869 align = Roo.bootstrap.Tooltip.alignment[placement];
23871 this.el.alignTo(this.bindEl, align[0],align[1]);
23872 //var arrow = this.el.select('.arrow',true).first();
23873 //arrow.set(align[2],
23875 this.el.addClass(placement);
23877 this.el.addClass('in fade');
23879 this.hoverState = null;
23881 if (this.el.hasClass('fade')) {
23892 //this.el.setXY([0,0]);
23893 this.el.removeClass('in');
23909 * @class Roo.bootstrap.LocationPicker
23910 * @extends Roo.bootstrap.Component
23911 * Bootstrap LocationPicker class
23912 * @cfg {Number} latitude Position when init default 0
23913 * @cfg {Number} longitude Position when init default 0
23914 * @cfg {Number} zoom default 15
23915 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23916 * @cfg {Boolean} mapTypeControl default false
23917 * @cfg {Boolean} disableDoubleClickZoom default false
23918 * @cfg {Boolean} scrollwheel default true
23919 * @cfg {Boolean} streetViewControl default false
23920 * @cfg {Number} radius default 0
23921 * @cfg {String} locationName
23922 * @cfg {Boolean} draggable default true
23923 * @cfg {Boolean} enableAutocomplete default false
23924 * @cfg {Boolean} enableReverseGeocode default true
23925 * @cfg {String} markerTitle
23928 * Create a new LocationPicker
23929 * @param {Object} config The config object
23933 Roo.bootstrap.LocationPicker = function(config){
23935 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23940 * Fires when the picker initialized.
23941 * @param {Roo.bootstrap.LocationPicker} this
23942 * @param {Google Location} location
23946 * @event positionchanged
23947 * Fires when the picker position changed.
23948 * @param {Roo.bootstrap.LocationPicker} this
23949 * @param {Google Location} location
23951 positionchanged : true,
23954 * Fires when the map resize.
23955 * @param {Roo.bootstrap.LocationPicker} this
23960 * Fires when the map show.
23961 * @param {Roo.bootstrap.LocationPicker} this
23966 * Fires when the map hide.
23967 * @param {Roo.bootstrap.LocationPicker} this
23972 * Fires when click the map.
23973 * @param {Roo.bootstrap.LocationPicker} this
23974 * @param {Map event} e
23978 * @event mapRightClick
23979 * Fires when right click the map.
23980 * @param {Roo.bootstrap.LocationPicker} this
23981 * @param {Map event} e
23983 mapRightClick : true,
23985 * @event markerClick
23986 * Fires when click the marker.
23987 * @param {Roo.bootstrap.LocationPicker} this
23988 * @param {Map event} e
23990 markerClick : true,
23992 * @event markerRightClick
23993 * Fires when right click the marker.
23994 * @param {Roo.bootstrap.LocationPicker} this
23995 * @param {Map event} e
23997 markerRightClick : true,
23999 * @event OverlayViewDraw
24000 * Fires when OverlayView Draw
24001 * @param {Roo.bootstrap.LocationPicker} this
24003 OverlayViewDraw : true,
24005 * @event OverlayViewOnAdd
24006 * Fires when OverlayView Draw
24007 * @param {Roo.bootstrap.LocationPicker} this
24009 OverlayViewOnAdd : true,
24011 * @event OverlayViewOnRemove
24012 * Fires when OverlayView Draw
24013 * @param {Roo.bootstrap.LocationPicker} this
24015 OverlayViewOnRemove : true,
24017 * @event OverlayViewShow
24018 * Fires when OverlayView Draw
24019 * @param {Roo.bootstrap.LocationPicker} this
24020 * @param {Pixel} cpx
24022 OverlayViewShow : true,
24024 * @event OverlayViewHide
24025 * Fires when OverlayView Draw
24026 * @param {Roo.bootstrap.LocationPicker} this
24028 OverlayViewHide : true,
24030 * @event loadexception
24031 * Fires when load google lib failed.
24032 * @param {Roo.bootstrap.LocationPicker} this
24034 loadexception : true
24039 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
24041 gMapContext: false,
24047 mapTypeControl: false,
24048 disableDoubleClickZoom: false,
24050 streetViewControl: false,
24054 enableAutocomplete: false,
24055 enableReverseGeocode: true,
24058 getAutoCreate: function()
24063 cls: 'roo-location-picker'
24069 initEvents: function(ct, position)
24071 if(!this.el.getWidth() || this.isApplied()){
24075 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24080 initial: function()
24082 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24083 this.fireEvent('loadexception', this);
24087 if(!this.mapTypeId){
24088 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24091 this.gMapContext = this.GMapContext();
24093 this.initOverlayView();
24095 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24099 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24100 _this.setPosition(_this.gMapContext.marker.position);
24103 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24104 _this.fireEvent('mapClick', this, event);
24108 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24109 _this.fireEvent('mapRightClick', this, event);
24113 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24114 _this.fireEvent('markerClick', this, event);
24118 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24119 _this.fireEvent('markerRightClick', this, event);
24123 this.setPosition(this.gMapContext.location);
24125 this.fireEvent('initial', this, this.gMapContext.location);
24128 initOverlayView: function()
24132 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24136 _this.fireEvent('OverlayViewDraw', _this);
24141 _this.fireEvent('OverlayViewOnAdd', _this);
24144 onRemove: function()
24146 _this.fireEvent('OverlayViewOnRemove', _this);
24149 show: function(cpx)
24151 _this.fireEvent('OverlayViewShow', _this, cpx);
24156 _this.fireEvent('OverlayViewHide', _this);
24162 fromLatLngToContainerPixel: function(event)
24164 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24167 isApplied: function()
24169 return this.getGmapContext() == false ? false : true;
24172 getGmapContext: function()
24174 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24177 GMapContext: function()
24179 var position = new google.maps.LatLng(this.latitude, this.longitude);
24181 var _map = new google.maps.Map(this.el.dom, {
24184 mapTypeId: this.mapTypeId,
24185 mapTypeControl: this.mapTypeControl,
24186 disableDoubleClickZoom: this.disableDoubleClickZoom,
24187 scrollwheel: this.scrollwheel,
24188 streetViewControl: this.streetViewControl,
24189 locationName: this.locationName,
24190 draggable: this.draggable,
24191 enableAutocomplete: this.enableAutocomplete,
24192 enableReverseGeocode: this.enableReverseGeocode
24195 var _marker = new google.maps.Marker({
24196 position: position,
24198 title: this.markerTitle,
24199 draggable: this.draggable
24206 location: position,
24207 radius: this.radius,
24208 locationName: this.locationName,
24209 addressComponents: {
24210 formatted_address: null,
24211 addressLine1: null,
24212 addressLine2: null,
24214 streetNumber: null,
24218 stateOrProvince: null
24221 domContainer: this.el.dom,
24222 geodecoder: new google.maps.Geocoder()
24226 drawCircle: function(center, radius, options)
24228 if (this.gMapContext.circle != null) {
24229 this.gMapContext.circle.setMap(null);
24233 options = Roo.apply({}, options, {
24234 strokeColor: "#0000FF",
24235 strokeOpacity: .35,
24237 fillColor: "#0000FF",
24241 options.map = this.gMapContext.map;
24242 options.radius = radius;
24243 options.center = center;
24244 this.gMapContext.circle = new google.maps.Circle(options);
24245 return this.gMapContext.circle;
24251 setPosition: function(location)
24253 this.gMapContext.location = location;
24254 this.gMapContext.marker.setPosition(location);
24255 this.gMapContext.map.panTo(location);
24256 this.drawCircle(location, this.gMapContext.radius, {});
24260 if (this.gMapContext.settings.enableReverseGeocode) {
24261 this.gMapContext.geodecoder.geocode({
24262 latLng: this.gMapContext.location
24263 }, function(results, status) {
24265 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24266 _this.gMapContext.locationName = results[0].formatted_address;
24267 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24269 _this.fireEvent('positionchanged', this, location);
24276 this.fireEvent('positionchanged', this, location);
24281 google.maps.event.trigger(this.gMapContext.map, "resize");
24283 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24285 this.fireEvent('resize', this);
24288 setPositionByLatLng: function(latitude, longitude)
24290 this.setPosition(new google.maps.LatLng(latitude, longitude));
24293 getCurrentPosition: function()
24296 latitude: this.gMapContext.location.lat(),
24297 longitude: this.gMapContext.location.lng()
24301 getAddressName: function()
24303 return this.gMapContext.locationName;
24306 getAddressComponents: function()
24308 return this.gMapContext.addressComponents;
24311 address_component_from_google_geocode: function(address_components)
24315 for (var i = 0; i < address_components.length; i++) {
24316 var component = address_components[i];
24317 if (component.types.indexOf("postal_code") >= 0) {
24318 result.postalCode = component.short_name;
24319 } else if (component.types.indexOf("street_number") >= 0) {
24320 result.streetNumber = component.short_name;
24321 } else if (component.types.indexOf("route") >= 0) {
24322 result.streetName = component.short_name;
24323 } else if (component.types.indexOf("neighborhood") >= 0) {
24324 result.city = component.short_name;
24325 } else if (component.types.indexOf("locality") >= 0) {
24326 result.city = component.short_name;
24327 } else if (component.types.indexOf("sublocality") >= 0) {
24328 result.district = component.short_name;
24329 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24330 result.stateOrProvince = component.short_name;
24331 } else if (component.types.indexOf("country") >= 0) {
24332 result.country = component.short_name;
24336 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24337 result.addressLine2 = "";
24341 setZoomLevel: function(zoom)
24343 this.gMapContext.map.setZoom(zoom);
24356 this.fireEvent('show', this);
24367 this.fireEvent('hide', this);
24372 Roo.apply(Roo.bootstrap.LocationPicker, {
24374 OverlayView : function(map, options)
24376 options = options || {};
24390 * @class Roo.bootstrap.Alert
24391 * @extends Roo.bootstrap.Component
24392 * Bootstrap Alert class
24393 * @cfg {String} title The title of alert
24394 * @cfg {String} html The content of alert
24395 * @cfg {String} weight ( success | info | warning | danger )
24396 * @cfg {String} faicon font-awesomeicon
24399 * Create a new alert
24400 * @param {Object} config The config object
24404 Roo.bootstrap.Alert = function(config){
24405 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24409 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
24416 getAutoCreate : function()
24425 cls : 'roo-alert-icon'
24430 cls : 'roo-alert-title',
24435 cls : 'roo-alert-text',
24442 cfg.cn[0].cls += ' fa ' + this.faicon;
24446 cfg.cls += ' alert-' + this.weight;
24452 initEvents: function()
24454 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24457 setTitle : function(str)
24459 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24462 setText : function(str)
24464 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24467 setWeight : function(weight)
24470 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24473 this.weight = weight;
24475 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24478 setIcon : function(icon)
24481 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24484 this.faicon = icon;
24486 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24507 * @class Roo.bootstrap.UploadCropbox
24508 * @extends Roo.bootstrap.Component
24509 * Bootstrap UploadCropbox class
24510 * @cfg {String} emptyText show when image has been loaded
24511 * @cfg {String} rotateNotify show when image too small to rotate
24512 * @cfg {Number} errorTimeout default 3000
24513 * @cfg {Number} minWidth default 300
24514 * @cfg {Number} minHeight default 300
24515 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24516 * @cfg {Boolean} isDocument (true|false) default false
24517 * @cfg {String} url action url
24518 * @cfg {String} paramName default 'imageUpload'
24519 * @cfg {String} method default POST
24520 * @cfg {Boolean} loadMask (true|false) default true
24521 * @cfg {Boolean} loadingText default 'Loading...'
24524 * Create a new UploadCropbox
24525 * @param {Object} config The config object
24528 Roo.bootstrap.UploadCropbox = function(config){
24529 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24533 * @event beforeselectfile
24534 * Fire before select file
24535 * @param {Roo.bootstrap.UploadCropbox} this
24537 "beforeselectfile" : true,
24540 * Fire after initEvent
24541 * @param {Roo.bootstrap.UploadCropbox} this
24546 * Fire after initEvent
24547 * @param {Roo.bootstrap.UploadCropbox} this
24548 * @param {String} data
24553 * Fire when preparing the file data
24554 * @param {Roo.bootstrap.UploadCropbox} this
24555 * @param {Object} file
24560 * Fire when get exception
24561 * @param {Roo.bootstrap.UploadCropbox} this
24562 * @param {XMLHttpRequest} xhr
24564 "exception" : true,
24566 * @event beforeloadcanvas
24567 * Fire before load the canvas
24568 * @param {Roo.bootstrap.UploadCropbox} this
24569 * @param {String} src
24571 "beforeloadcanvas" : true,
24574 * Fire when trash image
24575 * @param {Roo.bootstrap.UploadCropbox} this
24580 * Fire when download the image
24581 * @param {Roo.bootstrap.UploadCropbox} this
24585 * @event footerbuttonclick
24586 * Fire when footerbuttonclick
24587 * @param {Roo.bootstrap.UploadCropbox} this
24588 * @param {String} type
24590 "footerbuttonclick" : true,
24594 * @param {Roo.bootstrap.UploadCropbox} this
24599 * Fire when rotate the image
24600 * @param {Roo.bootstrap.UploadCropbox} this
24601 * @param {String} pos
24606 * Fire when inspect the file
24607 * @param {Roo.bootstrap.UploadCropbox} this
24608 * @param {Object} file
24613 * Fire when xhr upload the file
24614 * @param {Roo.bootstrap.UploadCropbox} this
24615 * @param {Object} data
24620 * Fire when arrange the file data
24621 * @param {Roo.bootstrap.UploadCropbox} this
24622 * @param {Object} formData
24627 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24630 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24632 emptyText : 'Click to upload image',
24633 rotateNotify : 'Image is too small to rotate',
24634 errorTimeout : 3000,
24648 cropType : 'image/jpeg',
24650 canvasLoaded : false,
24651 isDocument : false,
24653 paramName : 'imageUpload',
24655 loadingText : 'Loading...',
24658 getAutoCreate : function()
24662 cls : 'roo-upload-cropbox',
24666 cls : 'roo-upload-cropbox-selector',
24671 cls : 'roo-upload-cropbox-body',
24672 style : 'cursor:pointer',
24676 cls : 'roo-upload-cropbox-preview'
24680 cls : 'roo-upload-cropbox-thumb'
24684 cls : 'roo-upload-cropbox-empty-notify',
24685 html : this.emptyText
24689 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24690 html : this.rotateNotify
24696 cls : 'roo-upload-cropbox-footer',
24699 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24709 onRender : function(ct, position)
24711 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24713 if (this.buttons.length) {
24715 Roo.each(this.buttons, function(bb) {
24717 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24719 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24725 this.maskEl = this.el;
24729 initEvents : function()
24731 this.urlAPI = (window.createObjectURL && window) ||
24732 (window.URL && URL.revokeObjectURL && URL) ||
24733 (window.webkitURL && webkitURL);
24735 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24736 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24738 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24739 this.selectorEl.hide();
24741 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24742 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24744 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24745 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24746 this.thumbEl.hide();
24748 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24749 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24751 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24752 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24753 this.errorEl.hide();
24755 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24756 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24757 this.footerEl.hide();
24759 this.setThumbBoxSize();
24765 this.fireEvent('initial', this);
24772 window.addEventListener("resize", function() { _this.resize(); } );
24774 this.bodyEl.on('click', this.beforeSelectFile, this);
24777 this.bodyEl.on('touchstart', this.onTouchStart, this);
24778 this.bodyEl.on('touchmove', this.onTouchMove, this);
24779 this.bodyEl.on('touchend', this.onTouchEnd, this);
24783 this.bodyEl.on('mousedown', this.onMouseDown, this);
24784 this.bodyEl.on('mousemove', this.onMouseMove, this);
24785 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24786 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24787 Roo.get(document).on('mouseup', this.onMouseUp, this);
24790 this.selectorEl.on('change', this.onFileSelected, this);
24796 this.baseScale = 1;
24798 this.baseRotate = 1;
24799 this.dragable = false;
24800 this.pinching = false;
24803 this.cropData = false;
24804 this.notifyEl.dom.innerHTML = this.emptyText;
24806 this.selectorEl.dom.value = '';
24810 resize : function()
24812 if(this.fireEvent('resize', this) != false){
24813 this.setThumbBoxPosition();
24814 this.setCanvasPosition();
24818 onFooterButtonClick : function(e, el, o, type)
24821 case 'rotate-left' :
24822 this.onRotateLeft(e);
24824 case 'rotate-right' :
24825 this.onRotateRight(e);
24828 this.beforeSelectFile(e);
24843 this.fireEvent('footerbuttonclick', this, type);
24846 beforeSelectFile : function(e)
24848 e.preventDefault();
24850 if(this.fireEvent('beforeselectfile', this) != false){
24851 this.selectorEl.dom.click();
24855 onFileSelected : function(e)
24857 e.preventDefault();
24859 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24863 var file = this.selectorEl.dom.files[0];
24865 if(this.fireEvent('inspect', this, file) != false){
24866 this.prepare(file);
24871 trash : function(e)
24873 this.fireEvent('trash', this);
24876 download : function(e)
24878 this.fireEvent('download', this);
24881 loadCanvas : function(src)
24883 if(this.fireEvent('beforeloadcanvas', this, src) != false){
24887 this.imageEl = document.createElement('img');
24891 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24893 this.imageEl.src = src;
24897 onLoadCanvas : function()
24899 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24900 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24902 this.bodyEl.un('click', this.beforeSelectFile, this);
24904 this.notifyEl.hide();
24905 this.thumbEl.show();
24906 this.footerEl.show();
24908 this.baseRotateLevel();
24910 if(this.isDocument){
24911 this.setThumbBoxSize();
24914 this.setThumbBoxPosition();
24916 this.baseScaleLevel();
24922 this.canvasLoaded = true;
24925 this.maskEl.unmask();
24930 setCanvasPosition : function()
24932 if(!this.canvasEl){
24936 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24937 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24939 this.previewEl.setLeft(pw);
24940 this.previewEl.setTop(ph);
24944 onMouseDown : function(e)
24948 this.dragable = true;
24949 this.pinching = false;
24951 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24952 this.dragable = false;
24956 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24957 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24961 onMouseMove : function(e)
24965 if(!this.canvasLoaded){
24969 if (!this.dragable){
24973 var minX = Math.ceil(this.thumbEl.getLeft(true));
24974 var minY = Math.ceil(this.thumbEl.getTop(true));
24976 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24977 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24979 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24980 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24982 x = x - this.mouseX;
24983 y = y - this.mouseY;
24985 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24986 var bgY = Math.ceil(y + this.previewEl.getTop(true));
24988 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24989 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24991 this.previewEl.setLeft(bgX);
24992 this.previewEl.setTop(bgY);
24994 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24995 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24998 onMouseUp : function(e)
25002 this.dragable = false;
25005 onMouseWheel : function(e)
25009 this.startScale = this.scale;
25011 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25013 if(!this.zoomable()){
25014 this.scale = this.startScale;
25023 zoomable : function()
25025 var minScale = this.thumbEl.getWidth() / this.minWidth;
25027 if(this.minWidth < this.minHeight){
25028 minScale = this.thumbEl.getHeight() / this.minHeight;
25031 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25032 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25036 (this.rotate == 0 || this.rotate == 180) &&
25038 width > this.imageEl.OriginWidth ||
25039 height > this.imageEl.OriginHeight ||
25040 (width < this.minWidth && height < this.minHeight)
25048 (this.rotate == 90 || this.rotate == 270) &&
25050 width > this.imageEl.OriginWidth ||
25051 height > this.imageEl.OriginHeight ||
25052 (width < this.minHeight && height < this.minWidth)
25059 !this.isDocument &&
25060 (this.rotate == 0 || this.rotate == 180) &&
25062 width < this.minWidth ||
25063 width > this.imageEl.OriginWidth ||
25064 height < this.minHeight ||
25065 height > this.imageEl.OriginHeight
25072 !this.isDocument &&
25073 (this.rotate == 90 || this.rotate == 270) &&
25075 width < this.minHeight ||
25076 width > this.imageEl.OriginWidth ||
25077 height < this.minWidth ||
25078 height > this.imageEl.OriginHeight
25088 onRotateLeft : function(e)
25090 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25092 var minScale = this.thumbEl.getWidth() / this.minWidth;
25094 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25095 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25097 this.startScale = this.scale;
25099 while (this.getScaleLevel() < minScale){
25101 this.scale = this.scale + 1;
25103 if(!this.zoomable()){
25108 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25109 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25114 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25121 this.scale = this.startScale;
25123 this.onRotateFail();
25128 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25130 if(this.isDocument){
25131 this.setThumbBoxSize();
25132 this.setThumbBoxPosition();
25133 this.setCanvasPosition();
25138 this.fireEvent('rotate', this, 'left');
25142 onRotateRight : function(e)
25144 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25146 var minScale = this.thumbEl.getWidth() / this.minWidth;
25148 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25149 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25151 this.startScale = this.scale;
25153 while (this.getScaleLevel() < minScale){
25155 this.scale = this.scale + 1;
25157 if(!this.zoomable()){
25162 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25163 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25168 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25175 this.scale = this.startScale;
25177 this.onRotateFail();
25182 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25184 if(this.isDocument){
25185 this.setThumbBoxSize();
25186 this.setThumbBoxPosition();
25187 this.setCanvasPosition();
25192 this.fireEvent('rotate', this, 'right');
25195 onRotateFail : function()
25197 this.errorEl.show(true);
25201 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25206 this.previewEl.dom.innerHTML = '';
25208 var canvasEl = document.createElement("canvas");
25210 var contextEl = canvasEl.getContext("2d");
25212 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25213 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25214 var center = this.imageEl.OriginWidth / 2;
25216 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25217 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25218 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25219 center = this.imageEl.OriginHeight / 2;
25222 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25224 contextEl.translate(center, center);
25225 contextEl.rotate(this.rotate * Math.PI / 180);
25227 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25229 this.canvasEl = document.createElement("canvas");
25231 this.contextEl = this.canvasEl.getContext("2d");
25233 switch (this.rotate) {
25236 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25237 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25239 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25244 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25245 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25247 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25248 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);
25252 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25257 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25258 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25260 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25261 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);
25265 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);
25270 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25271 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25273 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25274 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25278 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);
25285 this.previewEl.appendChild(this.canvasEl);
25287 this.setCanvasPosition();
25292 if(!this.canvasLoaded){
25296 var imageCanvas = document.createElement("canvas");
25298 var imageContext = imageCanvas.getContext("2d");
25300 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25301 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25303 var center = imageCanvas.width / 2;
25305 imageContext.translate(center, center);
25307 imageContext.rotate(this.rotate * Math.PI / 180);
25309 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25311 var canvas = document.createElement("canvas");
25313 var context = canvas.getContext("2d");
25315 canvas.width = this.minWidth;
25316 canvas.height = this.minHeight;
25318 switch (this.rotate) {
25321 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25322 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25324 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25325 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25327 var targetWidth = this.minWidth - 2 * x;
25328 var targetHeight = this.minHeight - 2 * y;
25332 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25333 scale = targetWidth / width;
25336 if(x > 0 && y == 0){
25337 scale = targetHeight / height;
25340 if(x > 0 && y > 0){
25341 scale = targetWidth / width;
25343 if(width < height){
25344 scale = targetHeight / height;
25348 context.scale(scale, scale);
25350 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25351 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25353 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25354 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25356 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25361 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25362 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25364 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25365 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25367 var targetWidth = this.minWidth - 2 * x;
25368 var targetHeight = this.minHeight - 2 * y;
25372 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25373 scale = targetWidth / width;
25376 if(x > 0 && y == 0){
25377 scale = targetHeight / height;
25380 if(x > 0 && y > 0){
25381 scale = targetWidth / width;
25383 if(width < height){
25384 scale = targetHeight / height;
25388 context.scale(scale, scale);
25390 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25391 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25393 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25394 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25396 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25398 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25403 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25404 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25406 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25407 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25409 var targetWidth = this.minWidth - 2 * x;
25410 var targetHeight = this.minHeight - 2 * y;
25414 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25415 scale = targetWidth / width;
25418 if(x > 0 && y == 0){
25419 scale = targetHeight / height;
25422 if(x > 0 && y > 0){
25423 scale = targetWidth / width;
25425 if(width < height){
25426 scale = targetHeight / height;
25430 context.scale(scale, scale);
25432 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25433 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25435 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25436 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25438 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25439 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25441 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25446 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25447 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25449 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25450 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25452 var targetWidth = this.minWidth - 2 * x;
25453 var targetHeight = this.minHeight - 2 * y;
25457 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25458 scale = targetWidth / width;
25461 if(x > 0 && y == 0){
25462 scale = targetHeight / height;
25465 if(x > 0 && y > 0){
25466 scale = targetWidth / width;
25468 if(width < height){
25469 scale = targetHeight / height;
25473 context.scale(scale, scale);
25475 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25476 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25478 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25479 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25481 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25483 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25490 this.cropData = canvas.toDataURL(this.cropType);
25492 if(this.fireEvent('crop', this, this.cropData) !== false){
25493 this.process(this.file, this.cropData);
25500 setThumbBoxSize : function()
25504 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25505 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25506 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25508 this.minWidth = width;
25509 this.minHeight = height;
25511 if(this.rotate == 90 || this.rotate == 270){
25512 this.minWidth = height;
25513 this.minHeight = width;
25518 width = Math.ceil(this.minWidth * height / this.minHeight);
25520 if(this.minWidth > this.minHeight){
25522 height = Math.ceil(this.minHeight * width / this.minWidth);
25525 this.thumbEl.setStyle({
25526 width : width + 'px',
25527 height : height + 'px'
25534 setThumbBoxPosition : function()
25536 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25537 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25539 this.thumbEl.setLeft(x);
25540 this.thumbEl.setTop(y);
25544 baseRotateLevel : function()
25546 this.baseRotate = 1;
25549 typeof(this.exif) != 'undefined' &&
25550 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25551 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25553 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25556 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25560 baseScaleLevel : function()
25564 if(this.isDocument){
25566 if(this.baseRotate == 6 || this.baseRotate == 8){
25568 height = this.thumbEl.getHeight();
25569 this.baseScale = height / this.imageEl.OriginWidth;
25571 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25572 width = this.thumbEl.getWidth();
25573 this.baseScale = width / this.imageEl.OriginHeight;
25579 height = this.thumbEl.getHeight();
25580 this.baseScale = height / this.imageEl.OriginHeight;
25582 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25583 width = this.thumbEl.getWidth();
25584 this.baseScale = width / this.imageEl.OriginWidth;
25590 if(this.baseRotate == 6 || this.baseRotate == 8){
25592 width = this.thumbEl.getHeight();
25593 this.baseScale = width / this.imageEl.OriginHeight;
25595 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25596 height = this.thumbEl.getWidth();
25597 this.baseScale = height / this.imageEl.OriginHeight;
25600 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25601 height = this.thumbEl.getWidth();
25602 this.baseScale = height / this.imageEl.OriginHeight;
25604 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25605 width = this.thumbEl.getHeight();
25606 this.baseScale = width / this.imageEl.OriginWidth;
25613 width = this.thumbEl.getWidth();
25614 this.baseScale = width / this.imageEl.OriginWidth;
25616 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25617 height = this.thumbEl.getHeight();
25618 this.baseScale = height / this.imageEl.OriginHeight;
25621 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25623 height = this.thumbEl.getHeight();
25624 this.baseScale = height / this.imageEl.OriginHeight;
25626 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25627 width = this.thumbEl.getWidth();
25628 this.baseScale = width / this.imageEl.OriginWidth;
25636 getScaleLevel : function()
25638 return this.baseScale * Math.pow(1.1, this.scale);
25641 onTouchStart : function(e)
25643 if(!this.canvasLoaded){
25644 this.beforeSelectFile(e);
25648 var touches = e.browserEvent.touches;
25654 if(touches.length == 1){
25655 this.onMouseDown(e);
25659 if(touches.length != 2){
25665 for(var i = 0, finger; finger = touches[i]; i++){
25666 coords.push(finger.pageX, finger.pageY);
25669 var x = Math.pow(coords[0] - coords[2], 2);
25670 var y = Math.pow(coords[1] - coords[3], 2);
25672 this.startDistance = Math.sqrt(x + y);
25674 this.startScale = this.scale;
25676 this.pinching = true;
25677 this.dragable = false;
25681 onTouchMove : function(e)
25683 if(!this.pinching && !this.dragable){
25687 var touches = e.browserEvent.touches;
25694 this.onMouseMove(e);
25700 for(var i = 0, finger; finger = touches[i]; i++){
25701 coords.push(finger.pageX, finger.pageY);
25704 var x = Math.pow(coords[0] - coords[2], 2);
25705 var y = Math.pow(coords[1] - coords[3], 2);
25707 this.endDistance = Math.sqrt(x + y);
25709 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25711 if(!this.zoomable()){
25712 this.scale = this.startScale;
25720 onTouchEnd : function(e)
25722 this.pinching = false;
25723 this.dragable = false;
25727 process : function(file, crop)
25730 this.maskEl.mask(this.loadingText);
25733 this.xhr = new XMLHttpRequest();
25735 file.xhr = this.xhr;
25737 this.xhr.open(this.method, this.url, true);
25740 "Accept": "application/json",
25741 "Cache-Control": "no-cache",
25742 "X-Requested-With": "XMLHttpRequest"
25745 for (var headerName in headers) {
25746 var headerValue = headers[headerName];
25748 this.xhr.setRequestHeader(headerName, headerValue);
25754 this.xhr.onload = function()
25756 _this.xhrOnLoad(_this.xhr);
25759 this.xhr.onerror = function()
25761 _this.xhrOnError(_this.xhr);
25764 var formData = new FormData();
25766 formData.append('returnHTML', 'NO');
25769 formData.append('crop', crop);
25772 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25773 formData.append(this.paramName, file, file.name);
25776 if(typeof(file.filename) != 'undefined'){
25777 formData.append('filename', file.filename);
25780 if(typeof(file.mimetype) != 'undefined'){
25781 formData.append('mimetype', file.mimetype);
25784 if(this.fireEvent('arrange', this, formData) != false){
25785 this.xhr.send(formData);
25789 xhrOnLoad : function(xhr)
25792 this.maskEl.unmask();
25795 if (xhr.readyState !== 4) {
25796 this.fireEvent('exception', this, xhr);
25800 var response = Roo.decode(xhr.responseText);
25802 if(!response.success){
25803 this.fireEvent('exception', this, xhr);
25807 var response = Roo.decode(xhr.responseText);
25809 this.fireEvent('upload', this, response);
25813 xhrOnError : function()
25816 this.maskEl.unmask();
25819 Roo.log('xhr on error');
25821 var response = Roo.decode(xhr.responseText);
25827 prepare : function(file)
25830 this.maskEl.mask(this.loadingText);
25836 if(typeof(file) === 'string'){
25837 this.loadCanvas(file);
25841 if(!file || !this.urlAPI){
25846 this.cropType = file.type;
25850 if(this.fireEvent('prepare', this, this.file) != false){
25852 var reader = new FileReader();
25854 reader.onload = function (e) {
25855 if (e.target.error) {
25856 Roo.log(e.target.error);
25860 var buffer = e.target.result,
25861 dataView = new DataView(buffer),
25863 maxOffset = dataView.byteLength - 4,
25867 if (dataView.getUint16(0) === 0xffd8) {
25868 while (offset < maxOffset) {
25869 markerBytes = dataView.getUint16(offset);
25871 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25872 markerLength = dataView.getUint16(offset + 2) + 2;
25873 if (offset + markerLength > dataView.byteLength) {
25874 Roo.log('Invalid meta data: Invalid segment size.');
25878 if(markerBytes == 0xffe1){
25879 _this.parseExifData(
25886 offset += markerLength;
25896 var url = _this.urlAPI.createObjectURL(_this.file);
25898 _this.loadCanvas(url);
25903 reader.readAsArrayBuffer(this.file);
25909 parseExifData : function(dataView, offset, length)
25911 var tiffOffset = offset + 10,
25915 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25916 // No Exif data, might be XMP data instead
25920 // Check for the ASCII code for "Exif" (0x45786966):
25921 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25922 // No Exif data, might be XMP data instead
25925 if (tiffOffset + 8 > dataView.byteLength) {
25926 Roo.log('Invalid Exif data: Invalid segment size.');
25929 // Check for the two null bytes:
25930 if (dataView.getUint16(offset + 8) !== 0x0000) {
25931 Roo.log('Invalid Exif data: Missing byte alignment offset.');
25934 // Check the byte alignment:
25935 switch (dataView.getUint16(tiffOffset)) {
25937 littleEndian = true;
25940 littleEndian = false;
25943 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25946 // Check for the TIFF tag marker (0x002A):
25947 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25948 Roo.log('Invalid Exif data: Missing TIFF marker.');
25951 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25952 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25954 this.parseExifTags(
25957 tiffOffset + dirOffset,
25962 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25967 if (dirOffset + 6 > dataView.byteLength) {
25968 Roo.log('Invalid Exif data: Invalid directory offset.');
25971 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25972 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25973 if (dirEndOffset + 4 > dataView.byteLength) {
25974 Roo.log('Invalid Exif data: Invalid directory size.');
25977 for (i = 0; i < tagsNumber; i += 1) {
25981 dirOffset + 2 + 12 * i, // tag offset
25985 // Return the offset to the next directory:
25986 return dataView.getUint32(dirEndOffset, littleEndian);
25989 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
25991 var tag = dataView.getUint16(offset, littleEndian);
25993 this.exif[tag] = this.getExifValue(
25997 dataView.getUint16(offset + 2, littleEndian), // tag type
25998 dataView.getUint32(offset + 4, littleEndian), // tag length
26003 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26005 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26014 Roo.log('Invalid Exif data: Invalid tag type.');
26018 tagSize = tagType.size * length;
26019 // Determine if the value is contained in the dataOffset bytes,
26020 // or if the value at the dataOffset is a pointer to the actual data:
26021 dataOffset = tagSize > 4 ?
26022 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26023 if (dataOffset + tagSize > dataView.byteLength) {
26024 Roo.log('Invalid Exif data: Invalid data offset.');
26027 if (length === 1) {
26028 return tagType.getValue(dataView, dataOffset, littleEndian);
26031 for (i = 0; i < length; i += 1) {
26032 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26035 if (tagType.ascii) {
26037 // Concatenate the chars:
26038 for (i = 0; i < values.length; i += 1) {
26040 // Ignore the terminating NULL byte(s):
26041 if (c === '\u0000') {
26053 Roo.apply(Roo.bootstrap.UploadCropbox, {
26055 'Orientation': 0x0112
26059 1: 0, //'top-left',
26061 3: 180, //'bottom-right',
26062 // 4: 'bottom-left',
26064 6: 90, //'right-top',
26065 // 7: 'right-bottom',
26066 8: 270 //'left-bottom'
26070 // byte, 8-bit unsigned int:
26072 getValue: function (dataView, dataOffset) {
26073 return dataView.getUint8(dataOffset);
26077 // ascii, 8-bit byte:
26079 getValue: function (dataView, dataOffset) {
26080 return String.fromCharCode(dataView.getUint8(dataOffset));
26085 // short, 16 bit int:
26087 getValue: function (dataView, dataOffset, littleEndian) {
26088 return dataView.getUint16(dataOffset, littleEndian);
26092 // long, 32 bit int:
26094 getValue: function (dataView, dataOffset, littleEndian) {
26095 return dataView.getUint32(dataOffset, littleEndian);
26099 // rational = two long values, first is numerator, second is denominator:
26101 getValue: function (dataView, dataOffset, littleEndian) {
26102 return dataView.getUint32(dataOffset, littleEndian) /
26103 dataView.getUint32(dataOffset + 4, littleEndian);
26107 // slong, 32 bit signed int:
26109 getValue: function (dataView, dataOffset, littleEndian) {
26110 return dataView.getInt32(dataOffset, littleEndian);
26114 // srational, two slongs, first is numerator, second is denominator:
26116 getValue: function (dataView, dataOffset, littleEndian) {
26117 return dataView.getInt32(dataOffset, littleEndian) /
26118 dataView.getInt32(dataOffset + 4, littleEndian);
26128 cls : 'btn-group roo-upload-cropbox-rotate-left',
26129 action : 'rotate-left',
26133 cls : 'btn btn-default',
26134 html : '<i class="fa fa-undo"></i>'
26140 cls : 'btn-group roo-upload-cropbox-picture',
26141 action : 'picture',
26145 cls : 'btn btn-default',
26146 html : '<i class="fa fa-picture-o"></i>'
26152 cls : 'btn-group roo-upload-cropbox-rotate-right',
26153 action : 'rotate-right',
26157 cls : 'btn btn-default',
26158 html : '<i class="fa fa-repeat"></i>'
26166 cls : 'btn-group roo-upload-cropbox-rotate-left',
26167 action : 'rotate-left',
26171 cls : 'btn btn-default',
26172 html : '<i class="fa fa-undo"></i>'
26178 cls : 'btn-group roo-upload-cropbox-download',
26179 action : 'download',
26183 cls : 'btn btn-default',
26184 html : '<i class="fa fa-download"></i>'
26190 cls : 'btn-group roo-upload-cropbox-crop',
26195 cls : 'btn btn-default',
26196 html : '<i class="fa fa-crop"></i>'
26202 cls : 'btn-group roo-upload-cropbox-trash',
26207 cls : 'btn btn-default',
26208 html : '<i class="fa fa-trash"></i>'
26214 cls : 'btn-group roo-upload-cropbox-rotate-right',
26215 action : 'rotate-right',
26219 cls : 'btn btn-default',
26220 html : '<i class="fa fa-repeat"></i>'
26228 cls : 'btn-group roo-upload-cropbox-rotate-left',
26229 action : 'rotate-left',
26233 cls : 'btn btn-default',
26234 html : '<i class="fa fa-undo"></i>'
26240 cls : 'btn-group roo-upload-cropbox-rotate-right',
26241 action : 'rotate-right',
26245 cls : 'btn btn-default',
26246 html : '<i class="fa fa-repeat"></i>'
26259 * @class Roo.bootstrap.DocumentManager
26260 * @extends Roo.bootstrap.Component
26261 * Bootstrap DocumentManager class
26262 * @cfg {String} paramName default 'imageUpload'
26263 * @cfg {String} method default POST
26264 * @cfg {String} url action url
26265 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26266 * @cfg {Boolean} multiple multiple upload default true
26267 * @cfg {Number} thumbSize default 300
26268 * @cfg {String} fieldLabel
26269 * @cfg {Number} labelWidth default 4
26270 * @cfg {String} labelAlign (left|top) default left
26271 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26274 * Create a new DocumentManager
26275 * @param {Object} config The config object
26278 Roo.bootstrap.DocumentManager = function(config){
26279 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26284 * Fire when initial the DocumentManager
26285 * @param {Roo.bootstrap.DocumentManager} this
26290 * inspect selected file
26291 * @param {Roo.bootstrap.DocumentManager} this
26292 * @param {File} file
26297 * Fire when xhr load exception
26298 * @param {Roo.bootstrap.DocumentManager} this
26299 * @param {XMLHttpRequest} xhr
26301 "exception" : true,
26304 * prepare the form data
26305 * @param {Roo.bootstrap.DocumentManager} this
26306 * @param {Object} formData
26311 * Fire when remove the file
26312 * @param {Roo.bootstrap.DocumentManager} this
26313 * @param {Object} file
26318 * Fire after refresh the file
26319 * @param {Roo.bootstrap.DocumentManager} this
26324 * Fire after click the image
26325 * @param {Roo.bootstrap.DocumentManager} this
26326 * @param {Object} file
26331 * Fire when upload a image and editable set to true
26332 * @param {Roo.bootstrap.DocumentManager} this
26333 * @param {Object} file
26337 * @event beforeselectfile
26338 * Fire before select file
26339 * @param {Roo.bootstrap.DocumentManager} this
26341 "beforeselectfile" : true,
26344 * Fire before process file
26345 * @param {Roo.bootstrap.DocumentManager} this
26346 * @param {Object} file
26353 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
26362 paramName : 'imageUpload',
26365 labelAlign : 'left',
26372 getAutoCreate : function()
26374 var managerWidget = {
26376 cls : 'roo-document-manager',
26380 cls : 'roo-document-manager-selector',
26385 cls : 'roo-document-manager-uploader',
26389 cls : 'roo-document-manager-upload-btn',
26390 html : '<i class="fa fa-plus"></i>'
26401 cls : 'column col-md-12',
26406 if(this.fieldLabel.length){
26411 cls : 'column col-md-12',
26412 html : this.fieldLabel
26416 cls : 'column col-md-12',
26421 if(this.labelAlign == 'left'){
26425 cls : 'column col-md-' + this.labelWidth,
26426 html : this.fieldLabel
26430 cls : 'column col-md-' + (12 - this.labelWidth),
26440 cls : 'row clearfix',
26448 initEvents : function()
26450 this.managerEl = this.el.select('.roo-document-manager', true).first();
26451 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26453 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26454 this.selectorEl.hide();
26457 this.selectorEl.attr('multiple', 'multiple');
26460 this.selectorEl.on('change', this.onFileSelected, this);
26462 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26463 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26465 this.uploader.on('click', this.onUploaderClick, this);
26467 this.renderProgressDialog();
26471 window.addEventListener("resize", function() { _this.refresh(); } );
26473 this.fireEvent('initial', this);
26476 renderProgressDialog : function()
26480 this.progressDialog = new Roo.bootstrap.Modal({
26481 cls : 'roo-document-manager-progress-dialog',
26482 allow_close : false,
26492 btnclick : function() {
26493 _this.uploadCancel();
26499 this.progressDialog.render(Roo.get(document.body));
26501 this.progress = new Roo.bootstrap.Progress({
26502 cls : 'roo-document-manager-progress',
26507 this.progress.render(this.progressDialog.getChildContainer());
26509 this.progressBar = new Roo.bootstrap.ProgressBar({
26510 cls : 'roo-document-manager-progress-bar',
26513 aria_valuemax : 12,
26517 this.progressBar.render(this.progress.getChildContainer());
26520 onUploaderClick : function(e)
26522 e.preventDefault();
26524 if(this.fireEvent('beforeselectfile', this) != false){
26525 this.selectorEl.dom.click();
26530 onFileSelected : function(e)
26532 e.preventDefault();
26534 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26538 Roo.each(this.selectorEl.dom.files, function(file){
26539 if(this.fireEvent('inspect', this, file) != false){
26540 this.files.push(file);
26550 this.selectorEl.dom.value = '';
26552 if(!this.files.length){
26556 if(this.boxes > 0 && this.files.length > this.boxes){
26557 this.files = this.files.slice(0, this.boxes);
26560 this.uploader.show();
26562 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26563 this.uploader.hide();
26572 Roo.each(this.files, function(file){
26574 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26575 var f = this.renderPreview(file);
26580 if(file.type.indexOf('image') != -1){
26581 this.delegates.push(
26583 _this.process(file);
26584 }).createDelegate(this)
26592 _this.process(file);
26593 }).createDelegate(this)
26598 this.files = files;
26600 this.delegates = this.delegates.concat(docs);
26602 if(!this.delegates.length){
26607 this.progressBar.aria_valuemax = this.delegates.length;
26614 arrange : function()
26616 if(!this.delegates.length){
26617 this.progressDialog.hide();
26622 var delegate = this.delegates.shift();
26624 this.progressDialog.show();
26626 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26628 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26633 refresh : function()
26635 this.uploader.show();
26637 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26638 this.uploader.hide();
26641 Roo.isTouch ? this.closable(false) : this.closable(true);
26643 this.fireEvent('refresh', this);
26646 onRemove : function(e, el, o)
26648 e.preventDefault();
26650 this.fireEvent('remove', this, o);
26654 remove : function(o)
26658 Roo.each(this.files, function(file){
26659 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26668 this.files = files;
26675 Roo.each(this.files, function(file){
26680 file.target.remove();
26689 onClick : function(e, el, o)
26691 e.preventDefault();
26693 this.fireEvent('click', this, o);
26697 closable : function(closable)
26699 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26701 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26713 xhrOnLoad : function(xhr)
26715 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26719 if (xhr.readyState !== 4) {
26721 this.fireEvent('exception', this, xhr);
26725 var response = Roo.decode(xhr.responseText);
26727 if(!response.success){
26729 this.fireEvent('exception', this, xhr);
26733 var file = this.renderPreview(response.data);
26735 this.files.push(file);
26741 xhrOnError : function()
26743 Roo.log('xhr on error');
26745 var response = Roo.decode(xhr.responseText);
26752 process : function(file)
26754 if(this.fireEvent('process', this, file) !== false){
26755 if(this.editable && file.type.indexOf('image') != -1){
26756 this.fireEvent('edit', this, file);
26760 this.uploadStart(file, false);
26767 uploadStart : function(file, crop)
26769 this.xhr = new XMLHttpRequest();
26771 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26776 file.xhr = this.xhr;
26778 this.managerEl.createChild({
26780 cls : 'roo-document-manager-loading',
26784 tooltip : file.name,
26785 cls : 'roo-document-manager-thumb',
26786 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26792 this.xhr.open(this.method, this.url, true);
26795 "Accept": "application/json",
26796 "Cache-Control": "no-cache",
26797 "X-Requested-With": "XMLHttpRequest"
26800 for (var headerName in headers) {
26801 var headerValue = headers[headerName];
26803 this.xhr.setRequestHeader(headerName, headerValue);
26809 this.xhr.onload = function()
26811 _this.xhrOnLoad(_this.xhr);
26814 this.xhr.onerror = function()
26816 _this.xhrOnError(_this.xhr);
26819 var formData = new FormData();
26821 formData.append('returnHTML', 'NO');
26824 formData.append('crop', crop);
26827 formData.append(this.paramName, file, file.name);
26829 if(this.fireEvent('prepare', this, formData) != false){
26830 this.xhr.send(formData);
26834 uploadCancel : function()
26841 this.delegates = [];
26843 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26850 renderPreview : function(file)
26852 if(typeof(file.target) != 'undefined' && file.target){
26856 var previewEl = this.managerEl.createChild({
26858 cls : 'roo-document-manager-preview',
26862 tooltip : file.filename,
26863 cls : 'roo-document-manager-thumb',
26864 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26869 html : '<i class="fa fa-times-circle"></i>'
26874 var close = previewEl.select('button.close', true).first();
26876 close.on('click', this.onRemove, this, file);
26878 file.target = previewEl;
26880 var image = previewEl.select('img', true).first();
26884 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26886 image.on('click', this.onClick, this, file);
26892 onPreviewLoad : function(file, image)
26894 if(typeof(file.target) == 'undefined' || !file.target){
26898 var width = image.dom.naturalWidth || image.dom.width;
26899 var height = image.dom.naturalHeight || image.dom.height;
26901 if(width > height){
26902 file.target.addClass('wide');
26906 file.target.addClass('tall');
26911 uploadFromSource : function(file, crop)
26913 this.xhr = new XMLHttpRequest();
26915 this.managerEl.createChild({
26917 cls : 'roo-document-manager-loading',
26921 tooltip : file.name,
26922 cls : 'roo-document-manager-thumb',
26923 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26929 this.xhr.open(this.method, this.url, true);
26932 "Accept": "application/json",
26933 "Cache-Control": "no-cache",
26934 "X-Requested-With": "XMLHttpRequest"
26937 for (var headerName in headers) {
26938 var headerValue = headers[headerName];
26940 this.xhr.setRequestHeader(headerName, headerValue);
26946 this.xhr.onload = function()
26948 _this.xhrOnLoad(_this.xhr);
26951 this.xhr.onerror = function()
26953 _this.xhrOnError(_this.xhr);
26956 var formData = new FormData();
26958 formData.append('returnHTML', 'NO');
26960 formData.append('crop', crop);
26962 if(typeof(file.filename) != 'undefined'){
26963 formData.append('filename', file.filename);
26966 if(typeof(file.mimetype) != 'undefined'){
26967 formData.append('mimetype', file.mimetype);
26970 if(this.fireEvent('prepare', this, formData) != false){
26971 this.xhr.send(formData);
26981 * @class Roo.bootstrap.DocumentViewer
26982 * @extends Roo.bootstrap.Component
26983 * Bootstrap DocumentViewer class
26986 * Create a new DocumentViewer
26987 * @param {Object} config The config object
26990 Roo.bootstrap.DocumentViewer = function(config){
26991 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26996 * Fire after initEvent
26997 * @param {Roo.bootstrap.DocumentViewer} this
27003 * @param {Roo.bootstrap.DocumentViewer} this
27008 * Fire after trash button
27009 * @param {Roo.bootstrap.DocumentViewer} this
27016 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
27018 getAutoCreate : function()
27022 cls : 'roo-document-viewer',
27026 cls : 'roo-document-viewer-body',
27030 cls : 'roo-document-viewer-thumb',
27034 cls : 'roo-document-viewer-image'
27042 cls : 'roo-document-viewer-footer',
27045 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27053 cls : 'btn btn-default roo-document-viewer-trash',
27054 html : '<i class="fa fa-trash"></i>'
27067 initEvents : function()
27070 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27071 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27073 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27074 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27076 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27077 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27079 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27080 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27082 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27083 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27085 this.bodyEl.on('click', this.onClick, this);
27087 this.trashBtn.on('click', this.onTrash, this);
27091 initial : function()
27093 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27096 this.fireEvent('initial', this);
27100 onClick : function(e)
27102 e.preventDefault();
27104 this.fireEvent('click', this);
27107 onTrash : function(e)
27109 e.preventDefault();
27111 this.fireEvent('trash', this);
27123 * @class Roo.bootstrap.NavProgressBar
27124 * @extends Roo.bootstrap.Component
27125 * Bootstrap NavProgressBar class
27128 * Create a new nav progress bar
27129 * @param {Object} config The config object
27132 Roo.bootstrap.NavProgressBar = function(config){
27133 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27135 this.bullets = this.bullets || [];
27137 // Roo.bootstrap.NavProgressBar.register(this);
27141 * Fires when the active item changes
27142 * @param {Roo.bootstrap.NavProgressBar} this
27143 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27144 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
27151 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
27156 getAutoCreate : function()
27158 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27162 cls : 'roo-navigation-bar-group',
27166 cls : 'roo-navigation-top-bar'
27170 cls : 'roo-navigation-bullets-bar',
27174 cls : 'roo-navigation-bar'
27181 cls : 'roo-navigation-bottom-bar'
27191 initEvents: function()
27196 onRender : function(ct, position)
27198 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27200 if(this.bullets.length){
27201 Roo.each(this.bullets, function(b){
27210 addItem : function(cfg)
27212 var item = new Roo.bootstrap.NavProgressItem(cfg);
27214 item.parentId = this.id;
27215 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27218 var top = new Roo.bootstrap.Element({
27220 cls : 'roo-navigation-bar-text'
27223 var bottom = new Roo.bootstrap.Element({
27225 cls : 'roo-navigation-bar-text'
27228 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27229 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27231 var topText = new Roo.bootstrap.Element({
27233 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27236 var bottomText = new Roo.bootstrap.Element({
27238 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27241 topText.onRender(top.el, null);
27242 bottomText.onRender(bottom.el, null);
27245 item.bottomEl = bottom;
27248 this.barItems.push(item);
27253 getActive : function()
27255 var active = false;
27257 Roo.each(this.barItems, function(v){
27259 if (!v.isActive()) {
27271 setActiveItem : function(item)
27275 Roo.each(this.barItems, function(v){
27276 if (v.rid == item.rid) {
27280 if (v.isActive()) {
27281 v.setActive(false);
27286 item.setActive(true);
27288 this.fireEvent('changed', this, item, prev);
27291 getBarItem: function(rid)
27295 Roo.each(this.barItems, function(e) {
27296 if (e.rid != rid) {
27307 indexOfItem : function(item)
27311 Roo.each(this.barItems, function(v, i){
27313 if (v.rid != item.rid) {
27324 setActiveNext : function()
27326 var i = this.indexOfItem(this.getActive());
27328 if (i > this.barItems.length) {
27332 this.setActiveItem(this.barItems[i+1]);
27335 setActivePrev : function()
27337 var i = this.indexOfItem(this.getActive());
27343 this.setActiveItem(this.barItems[i-1]);
27346 format : function()
27348 if(!this.barItems.length){
27352 var width = 100 / this.barItems.length;
27354 Roo.each(this.barItems, function(i){
27355 i.el.setStyle('width', width + '%');
27356 i.topEl.el.setStyle('width', width + '%');
27357 i.bottomEl.el.setStyle('width', width + '%');
27366 * Nav Progress Item
27371 * @class Roo.bootstrap.NavProgressItem
27372 * @extends Roo.bootstrap.Component
27373 * Bootstrap NavProgressItem class
27374 * @cfg {String} rid the reference id
27375 * @cfg {Boolean} active (true|false) Is item active default false
27376 * @cfg {Boolean} disabled (true|false) Is item active default false
27377 * @cfg {String} html
27378 * @cfg {String} position (top|bottom) text position default bottom
27379 * @cfg {String} icon show icon instead of number
27382 * Create a new NavProgressItem
27383 * @param {Object} config The config object
27385 Roo.bootstrap.NavProgressItem = function(config){
27386 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27391 * The raw click event for the entire grid.
27392 * @param {Roo.bootstrap.NavProgressItem} this
27393 * @param {Roo.EventObject} e
27400 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
27406 position : 'bottom',
27409 getAutoCreate : function()
27411 var iconCls = 'roo-navigation-bar-item-icon';
27413 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27417 cls: 'roo-navigation-bar-item',
27427 cfg.cls += ' active';
27430 cfg.cls += ' disabled';
27436 disable : function()
27438 this.setDisabled(true);
27441 enable : function()
27443 this.setDisabled(false);
27446 initEvents: function()
27448 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27450 this.iconEl.on('click', this.onClick, this);
27453 onClick : function(e)
27455 e.preventDefault();
27461 if(this.fireEvent('click', this, e) === false){
27465 this.parent().setActiveItem(this);
27468 isActive: function ()
27470 return this.active;
27473 setActive : function(state)
27475 if(this.active == state){
27479 this.active = state;
27482 this.el.addClass('active');
27486 this.el.removeClass('active');
27491 setDisabled : function(state)
27493 if(this.disabled == state){
27497 this.disabled = state;
27500 this.el.addClass('disabled');
27504 this.el.removeClass('disabled');
27507 tooltipEl : function()
27509 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27522 * @class Roo.bootstrap.FieldLabel
27523 * @extends Roo.bootstrap.Component
27524 * Bootstrap FieldLabel class
27525 * @cfg {String} html contents of the element
27526 * @cfg {String} tag tag of the element default label
27527 * @cfg {String} cls class of the element
27528 * @cfg {String} target label target
27529 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27530 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27531 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27532 * @cfg {String} iconTooltip default "This field is required"
27535 * Create a new FieldLabel
27536 * @param {Object} config The config object
27539 Roo.bootstrap.FieldLabel = function(config){
27540 Roo.bootstrap.Element.superclass.constructor.call(this, config);
27545 * Fires after the field has been marked as invalid.
27546 * @param {Roo.form.FieldLabel} this
27547 * @param {String} msg The validation message
27552 * Fires after the field has been validated with no errors.
27553 * @param {Roo.form.FieldLabel} this
27559 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
27566 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27567 validClass : 'text-success fa fa-lg fa-check',
27568 iconTooltip : 'This field is required',
27570 getAutoCreate : function(){
27574 cls : 'roo-bootstrap-field-label ' + this.cls,
27580 tooltip : this.iconTooltip
27592 initEvents: function()
27594 Roo.bootstrap.Element.superclass.initEvents.call(this);
27596 this.iconEl = this.el.select('i', true).first();
27598 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27600 Roo.bootstrap.FieldLabel.register(this);
27604 * Mark this field as valid
27606 markValid : function()
27608 this.iconEl.show();
27610 this.iconEl.removeClass(this.invalidClass);
27612 this.iconEl.addClass(this.validClass);
27614 this.fireEvent('valid', this);
27618 * Mark this field as invalid
27619 * @param {String} msg The validation message
27621 markInvalid : function(msg)
27623 this.iconEl.show();
27625 this.iconEl.removeClass(this.validClass);
27627 this.iconEl.addClass(this.invalidClass);
27629 this.fireEvent('invalid', this, msg);
27635 Roo.apply(Roo.bootstrap.FieldLabel, {
27640 * register a FieldLabel Group
27641 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27643 register : function(label)
27645 if(this.groups.hasOwnProperty(label.target)){
27649 this.groups[label.target] = label;
27653 * fetch a FieldLabel Group based on the target
27654 * @param {string} target
27655 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27657 get: function(target) {
27658 if (typeof(this.groups[target]) == 'undefined') {
27662 return this.groups[target] ;
27671 * page DateSplitField.
27677 * @class Roo.bootstrap.DateSplitField
27678 * @extends Roo.bootstrap.Component
27679 * Bootstrap DateSplitField class
27680 * @cfg {string} fieldLabel - the label associated
27681 * @cfg {Number} labelWidth set the width of label (0-12)
27682 * @cfg {String} labelAlign (top|left)
27683 * @cfg {Boolean} dayAllowBlank (true|false) default false
27684 * @cfg {Boolean} monthAllowBlank (true|false) default false
27685 * @cfg {Boolean} yearAllowBlank (true|false) default false
27686 * @cfg {string} dayPlaceholder
27687 * @cfg {string} monthPlaceholder
27688 * @cfg {string} yearPlaceholder
27689 * @cfg {string} dayFormat default 'd'
27690 * @cfg {string} monthFormat default 'm'
27691 * @cfg {string} yearFormat default 'Y'
27695 * Create a new DateSplitField
27696 * @param {Object} config The config object
27699 Roo.bootstrap.DateSplitField = function(config){
27700 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27706 * getting the data of years
27707 * @param {Roo.bootstrap.DateSplitField} this
27708 * @param {Object} years
27713 * getting the data of days
27714 * @param {Roo.bootstrap.DateSplitField} this
27715 * @param {Object} days
27720 * Fires after the field has been marked as invalid.
27721 * @param {Roo.form.Field} this
27722 * @param {String} msg The validation message
27727 * Fires after the field has been validated with no errors.
27728 * @param {Roo.form.Field} this
27734 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
27737 labelAlign : 'top',
27739 dayAllowBlank : false,
27740 monthAllowBlank : false,
27741 yearAllowBlank : false,
27742 dayPlaceholder : '',
27743 monthPlaceholder : '',
27744 yearPlaceholder : '',
27748 isFormField : true,
27750 getAutoCreate : function()
27754 cls : 'row roo-date-split-field-group',
27759 cls : 'form-hidden-field roo-date-split-field-group-value',
27765 if(this.fieldLabel){
27768 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27772 html : this.fieldLabel
27778 Roo.each(['day', 'month', 'year'], function(t){
27781 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27788 inputEl: function ()
27790 return this.el.select('.roo-date-split-field-group-value', true).first();
27793 onRender : function(ct, position)
27797 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27799 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27801 this.dayField = new Roo.bootstrap.ComboBox({
27802 allowBlank : this.dayAllowBlank,
27803 alwaysQuery : true,
27804 displayField : 'value',
27807 forceSelection : true,
27809 placeholder : this.dayPlaceholder,
27810 selectOnFocus : true,
27811 tpl : '<div class="select2-result"><b>{value}</b></div>',
27812 triggerAction : 'all',
27814 valueField : 'value',
27815 store : new Roo.data.SimpleStore({
27816 data : (function() {
27818 _this.fireEvent('days', _this, days);
27821 fields : [ 'value' ]
27824 select : function (_self, record, index)
27826 _this.setValue(_this.getValue());
27831 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27833 this.monthField = new Roo.bootstrap.MonthField({
27834 after : '<i class=\"fa fa-calendar\"></i>',
27835 allowBlank : this.monthAllowBlank,
27836 placeholder : this.monthPlaceholder,
27839 render : function (_self)
27841 this.el.select('span.input-group-addon', true).first().on('click', function(e){
27842 e.preventDefault();
27846 select : function (_self, oldvalue, newvalue)
27848 _this.setValue(_this.getValue());
27853 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27855 this.yearField = new Roo.bootstrap.ComboBox({
27856 allowBlank : this.yearAllowBlank,
27857 alwaysQuery : true,
27858 displayField : 'value',
27861 forceSelection : true,
27863 placeholder : this.yearPlaceholder,
27864 selectOnFocus : true,
27865 tpl : '<div class="select2-result"><b>{value}</b></div>',
27866 triggerAction : 'all',
27868 valueField : 'value',
27869 store : new Roo.data.SimpleStore({
27870 data : (function() {
27872 _this.fireEvent('years', _this, years);
27875 fields : [ 'value' ]
27878 select : function (_self, record, index)
27880 _this.setValue(_this.getValue());
27885 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
27888 setValue : function(v, format)
27890 this.inputEl.dom.value = v;
27892 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
27894 var d = Date.parseDate(v, f);
27901 this.setDay(d.format(this.dayFormat));
27902 this.setMonth(d.format(this.monthFormat));
27903 this.setYear(d.format(this.yearFormat));
27910 setDay : function(v)
27912 this.dayField.setValue(v);
27913 this.inputEl.dom.value = this.getValue();
27918 setMonth : function(v)
27920 this.monthField.setValue(v, true);
27921 this.inputEl.dom.value = this.getValue();
27926 setYear : function(v)
27928 this.yearField.setValue(v);
27929 this.inputEl.dom.value = this.getValue();
27934 getDay : function()
27936 return this.dayField.getValue();
27939 getMonth : function()
27941 return this.monthField.getValue();
27944 getYear : function()
27946 return this.yearField.getValue();
27949 getValue : function()
27951 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
27953 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
27963 this.inputEl.dom.value = '';
27968 validate : function()
27970 var d = this.dayField.validate();
27971 var m = this.monthField.validate();
27972 var y = this.yearField.validate();
27977 (!this.dayAllowBlank && !d) ||
27978 (!this.monthAllowBlank && !m) ||
27979 (!this.yearAllowBlank && !y)
27984 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
27993 this.markInvalid();
27998 markValid : function()
28001 var label = this.el.select('label', true).first();
28002 var icon = this.el.select('i.fa-star', true).first();
28008 this.fireEvent('valid', this);
28012 * Mark this field as invalid
28013 * @param {String} msg The validation message
28015 markInvalid : function(msg)
28018 var label = this.el.select('label', true).first();
28019 var icon = this.el.select('i.fa-star', true).first();
28021 if(label && !icon){
28022 this.el.select('.roo-date-split-field-label', true).createChild({
28024 cls : 'text-danger fa fa-lg fa-star',
28025 tooltip : 'This field is required',
28026 style : 'margin-right:5px;'
28030 this.fireEvent('invalid', this, msg);
28033 clearInvalid : function()
28035 var label = this.el.select('label', true).first();
28036 var icon = this.el.select('i.fa-star', true).first();
28042 this.fireEvent('valid', this);
28045 getName: function()