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) {
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 Roo.log('-------- init events on tab group ---------');
16103 if(Roo.isTouch && this.slideOnTouch){
16104 this.el.on("touchstart", this.onTouchStart, this);
16107 if(this.autoslide){
16110 this.slideFn = window.setInterval(function() {
16111 _this.showPanelNext();
16117 onTouchStart : function(e, el, o)
16119 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16123 this.showPanelNext();
16126 getChildContainer : function()
16128 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16132 * register a Navigation item
16133 * @param {Roo.bootstrap.NavItem} the navitem to add
16135 register : function(item)
16137 this.tabs.push( item);
16138 item.navId = this.navId; // not really needed..
16143 getActivePanel : function()
16146 Roo.each(this.tabs, function(t) {
16156 getPanelByName : function(n)
16159 Roo.each(this.tabs, function(t) {
16160 if (t.tabId == n) {
16168 indexOfPanel : function(p)
16171 Roo.each(this.tabs, function(t,i) {
16172 if (t.tabId == p.tabId) {
16181 * show a specific panel
16182 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16183 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16185 showPanel : function (pan)
16187 if(this.transition || typeof(pan) == 'undefined'){
16188 Roo.log("waiting for the transitionend");
16192 if (typeof(pan) == 'number') {
16193 pan = this.tabs[pan];
16196 if (typeof(pan) == 'string') {
16197 pan = this.getPanelByName(pan);
16200 var cur = this.getActivePanel();
16203 Roo.log('pan or acitve pan is undefined');
16207 if (pan.tabId == this.getActivePanel().tabId) {
16211 if (false === cur.fireEvent('beforedeactivate')) {
16215 if(this.bullets > 0 && !Roo.isTouch){
16216 this.setActiveBullet(this.indexOfPanel(pan));
16219 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16221 this.transition = true;
16222 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16223 var lr = dir == 'next' ? 'left' : 'right';
16224 pan.el.addClass(dir); // or prev
16225 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16226 cur.el.addClass(lr); // or right
16227 pan.el.addClass(lr);
16230 cur.el.on('transitionend', function() {
16231 Roo.log("trans end?");
16233 pan.el.removeClass([lr,dir]);
16234 pan.setActive(true);
16236 cur.el.removeClass([lr]);
16237 cur.setActive(false);
16239 _this.transition = false;
16241 }, this, { single: true } );
16246 cur.setActive(false);
16247 pan.setActive(true);
16252 showPanelNext : function()
16254 var i = this.indexOfPanel(this.getActivePanel());
16256 if (i >= this.tabs.length - 1 && !this.autoslide) {
16260 if (i >= this.tabs.length - 1 && this.autoslide) {
16264 this.showPanel(this.tabs[i+1]);
16267 showPanelPrev : function()
16269 var i = this.indexOfPanel(this.getActivePanel());
16271 if (i < 1 && !this.autoslide) {
16275 if (i < 1 && this.autoslide) {
16276 i = this.tabs.length;
16279 this.showPanel(this.tabs[i-1]);
16283 addBullet: function()
16285 if(!this.bullets || Roo.isTouch){
16288 var ctr = this.el.select('.carousel-bullets',true).first();
16289 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16290 var bullet = ctr.createChild({
16291 cls : 'bullet bullet-' + i
16292 },ctr.dom.lastChild);
16297 bullet.on('click', (function(e, el, o, ii, t){
16299 e.preventDefault();
16301 this.showPanel(ii);
16303 if(this.autoslide && this.slideFn){
16304 clearInterval(this.slideFn);
16305 this.slideFn = window.setInterval(function() {
16306 _this.showPanelNext();
16310 }).createDelegate(this, [i, bullet], true));
16315 setActiveBullet : function(i)
16321 Roo.each(this.el.select('.bullet', true).elements, function(el){
16322 el.removeClass('selected');
16325 var bullet = this.el.select('.bullet-' + i, true).first();
16331 bullet.addClass('selected');
16342 Roo.apply(Roo.bootstrap.TabGroup, {
16346 * register a Navigation Group
16347 * @param {Roo.bootstrap.NavGroup} the navgroup to add
16349 register : function(navgrp)
16351 this.groups[navgrp.navId] = navgrp;
16355 * fetch a Navigation Group based on the navigation ID
16356 * if one does not exist , it will get created.
16357 * @param {string} the navgroup to add
16358 * @returns {Roo.bootstrap.NavGroup} the navgroup
16360 get: function(navId) {
16361 if (typeof(this.groups[navId]) == 'undefined') {
16362 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16364 return this.groups[navId] ;
16379 * @class Roo.bootstrap.TabPanel
16380 * @extends Roo.bootstrap.Component
16381 * Bootstrap TabPanel class
16382 * @cfg {Boolean} active panel active
16383 * @cfg {String} html panel content
16384 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16385 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16389 * Create a new TabPanel
16390 * @param {Object} config The config object
16393 Roo.bootstrap.TabPanel = function(config){
16394 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16398 * Fires when the active status changes
16399 * @param {Roo.bootstrap.TabPanel} this
16400 * @param {Boolean} state the new state
16405 * @event beforedeactivate
16406 * Fires before a tab is de-activated - can be used to do validation on a form.
16407 * @param {Roo.bootstrap.TabPanel} this
16408 * @return {Boolean} false if there is an error
16411 'beforedeactivate': true
16414 this.tabId = this.tabId || Roo.id();
16418 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16425 getAutoCreate : function(){
16428 // item is needed for carousel - not sure if it has any effect otherwise
16429 cls: 'tab-pane item',
16430 html: this.html || ''
16434 cfg.cls += ' active';
16438 cfg.tabId = this.tabId;
16445 initEvents: function()
16447 Roo.log('-------- init events on tab panel ---------');
16449 var p = this.parent();
16450 this.navId = this.navId || p.navId;
16452 if (typeof(this.navId) != 'undefined') {
16453 // not really needed.. but just in case.. parent should be a NavGroup.
16454 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16458 var i = tg.tabs.length - 1;
16460 if(this.active && tg.bullets > 0 && i < tg.bullets){
16461 tg.setActiveBullet(i);
16468 onRender : function(ct, position)
16470 // Roo.log("Call onRender: " + this.xtype);
16472 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16480 setActive: function(state)
16482 Roo.log("panel - set active " + this.tabId + "=" + state);
16484 this.active = state;
16486 this.el.removeClass('active');
16488 } else if (!this.el.hasClass('active')) {
16489 this.el.addClass('active');
16492 this.fireEvent('changed', this, state);
16509 * @class Roo.bootstrap.DateField
16510 * @extends Roo.bootstrap.Input
16511 * Bootstrap DateField class
16512 * @cfg {Number} weekStart default 0
16513 * @cfg {String} viewMode default empty, (months|years)
16514 * @cfg {String} minViewMode default empty, (months|years)
16515 * @cfg {Number} startDate default -Infinity
16516 * @cfg {Number} endDate default Infinity
16517 * @cfg {Boolean} todayHighlight default false
16518 * @cfg {Boolean} todayBtn default false
16519 * @cfg {Boolean} calendarWeeks default false
16520 * @cfg {Object} daysOfWeekDisabled default empty
16521 * @cfg {Boolean} singleMode default false (true | false)
16523 * @cfg {Boolean} keyboardNavigation default true
16524 * @cfg {String} language default en
16527 * Create a new DateField
16528 * @param {Object} config The config object
16531 Roo.bootstrap.DateField = function(config){
16532 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16536 * Fires when this field show.
16537 * @param {Roo.bootstrap.DateField} this
16538 * @param {Mixed} date The date value
16543 * Fires when this field hide.
16544 * @param {Roo.bootstrap.DateField} this
16545 * @param {Mixed} date The date value
16550 * Fires when select a date.
16551 * @param {Roo.bootstrap.DateField} this
16552 * @param {Mixed} date The date value
16556 * @event beforeselect
16557 * Fires when before select a date.
16558 * @param {Roo.bootstrap.DateField} this
16559 * @param {Mixed} date The date value
16561 beforeselect : true
16565 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16568 * @cfg {String} format
16569 * The default date format string which can be overriden for localization support. The format must be
16570 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16574 * @cfg {String} altFormats
16575 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16576 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16578 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16586 todayHighlight : false,
16592 keyboardNavigation: true,
16594 calendarWeeks: false,
16596 startDate: -Infinity,
16600 daysOfWeekDisabled: [],
16604 singleMode : false,
16606 UTCDate: function()
16608 return new Date(Date.UTC.apply(Date, arguments));
16611 UTCToday: function()
16613 var today = new Date();
16614 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16617 getDate: function() {
16618 var d = this.getUTCDate();
16619 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16622 getUTCDate: function() {
16626 setDate: function(d) {
16627 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16630 setUTCDate: function(d) {
16632 this.setValue(this.formatDate(this.date));
16635 onRender: function(ct, position)
16638 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16640 this.language = this.language || 'en';
16641 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16642 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16644 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16645 this.format = this.format || 'm/d/y';
16646 this.isInline = false;
16647 this.isInput = true;
16648 this.component = this.el.select('.add-on', true).first() || false;
16649 this.component = (this.component && this.component.length === 0) ? false : this.component;
16650 this.hasInput = this.component && this.inputEL().length;
16652 if (typeof(this.minViewMode === 'string')) {
16653 switch (this.minViewMode) {
16655 this.minViewMode = 1;
16658 this.minViewMode = 2;
16661 this.minViewMode = 0;
16666 if (typeof(this.viewMode === 'string')) {
16667 switch (this.viewMode) {
16680 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16682 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16684 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16686 this.picker().on('mousedown', this.onMousedown, this);
16687 this.picker().on('click', this.onClick, this);
16689 this.picker().addClass('datepicker-dropdown');
16691 this.startViewMode = this.viewMode;
16693 if(this.singleMode){
16694 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16695 v.setVisibilityMode(Roo.Element.DISPLAY);
16699 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16700 v.setStyle('width', '189px');
16704 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16705 if(!this.calendarWeeks){
16710 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16711 v.attr('colspan', function(i, val){
16712 return parseInt(val) + 1;
16717 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16719 this.setStartDate(this.startDate);
16720 this.setEndDate(this.endDate);
16722 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16729 if(this.isInline) {
16734 picker : function()
16736 return this.pickerEl;
16737 // return this.el.select('.datepicker', true).first();
16740 fillDow: function()
16742 var dowCnt = this.weekStart;
16751 if(this.calendarWeeks){
16759 while (dowCnt < this.weekStart + 7) {
16763 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16767 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16770 fillMonths: function()
16773 var months = this.picker().select('>.datepicker-months td', true).first();
16775 months.dom.innerHTML = '';
16781 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16784 months.createChild(month);
16791 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;
16793 if (this.date < this.startDate) {
16794 this.viewDate = new Date(this.startDate);
16795 } else if (this.date > this.endDate) {
16796 this.viewDate = new Date(this.endDate);
16798 this.viewDate = new Date(this.date);
16806 var d = new Date(this.viewDate),
16807 year = d.getUTCFullYear(),
16808 month = d.getUTCMonth(),
16809 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16810 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16811 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16812 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16813 currentDate = this.date && this.date.valueOf(),
16814 today = this.UTCToday();
16816 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16818 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16820 // this.picker.select('>tfoot th.today').
16821 // .text(dates[this.language].today)
16822 // .toggle(this.todayBtn !== false);
16824 this.updateNavArrows();
16827 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16829 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16831 prevMonth.setUTCDate(day);
16833 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16835 var nextMonth = new Date(prevMonth);
16837 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16839 nextMonth = nextMonth.valueOf();
16841 var fillMonths = false;
16843 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16845 while(prevMonth.valueOf() < nextMonth) {
16848 if (prevMonth.getUTCDay() === this.weekStart) {
16850 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16858 if(this.calendarWeeks){
16859 // ISO 8601: First week contains first thursday.
16860 // ISO also states week starts on Monday, but we can be more abstract here.
16862 // Start of current week: based on weekstart/current date
16863 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16864 // Thursday of this week
16865 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16866 // First Thursday of year, year from thursday
16867 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16868 // Calendar week: ms between thursdays, div ms per day, div 7 days
16869 calWeek = (th - yth) / 864e5 / 7 + 1;
16871 fillMonths.cn.push({
16879 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16881 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16884 if (this.todayHighlight &&
16885 prevMonth.getUTCFullYear() == today.getFullYear() &&
16886 prevMonth.getUTCMonth() == today.getMonth() &&
16887 prevMonth.getUTCDate() == today.getDate()) {
16888 clsName += ' today';
16891 if (currentDate && prevMonth.valueOf() === currentDate) {
16892 clsName += ' active';
16895 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16896 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16897 clsName += ' disabled';
16900 fillMonths.cn.push({
16902 cls: 'day ' + clsName,
16903 html: prevMonth.getDate()
16906 prevMonth.setDate(prevMonth.getDate()+1);
16909 var currentYear = this.date && this.date.getUTCFullYear();
16910 var currentMonth = this.date && this.date.getUTCMonth();
16912 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16914 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16915 v.removeClass('active');
16917 if(currentYear === year && k === currentMonth){
16918 v.addClass('active');
16921 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16922 v.addClass('disabled');
16928 year = parseInt(year/10, 10) * 10;
16930 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16932 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16935 for (var i = -1; i < 11; i++) {
16936 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16938 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16946 showMode: function(dir)
16949 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16952 Roo.each(this.picker().select('>div',true).elements, function(v){
16953 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16956 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16961 if(this.isInline) {
16965 this.picker().removeClass(['bottom', 'top']);
16967 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16969 * place to the top of element!
16973 this.picker().addClass('top');
16974 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16979 this.picker().addClass('bottom');
16981 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16984 parseDate : function(value)
16986 if(!value || value instanceof Date){
16989 var v = Date.parseDate(value, this.format);
16990 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16991 v = Date.parseDate(value, 'Y-m-d');
16993 if(!v && this.altFormats){
16994 if(!this.altFormatsArray){
16995 this.altFormatsArray = this.altFormats.split("|");
16997 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16998 v = Date.parseDate(value, this.altFormatsArray[i]);
17004 formatDate : function(date, fmt)
17006 return (!date || !(date instanceof Date)) ?
17007 date : date.dateFormat(fmt || this.format);
17010 onFocus : function()
17012 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17016 onBlur : function()
17018 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17020 var d = this.inputEl().getValue();
17029 this.picker().show();
17033 this.fireEvent('show', this, this.date);
17038 if(this.isInline) {
17041 this.picker().hide();
17042 this.viewMode = this.startViewMode;
17045 this.fireEvent('hide', this, this.date);
17049 onMousedown: function(e)
17051 e.stopPropagation();
17052 e.preventDefault();
17057 Roo.bootstrap.DateField.superclass.keyup.call(this);
17061 setValue: function(v)
17063 if(this.fireEvent('beforeselect', this, v) !== false){
17064 var d = new Date(this.parseDate(v) ).clearTime();
17066 if(isNaN(d.getTime())){
17067 this.date = this.viewDate = '';
17068 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17072 v = this.formatDate(d);
17074 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17076 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17080 this.fireEvent('select', this, this.date);
17084 getValue: function()
17086 return this.formatDate(this.date);
17089 fireKey: function(e)
17091 if (!this.picker().isVisible()){
17092 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17098 var dateChanged = false,
17100 newDate, newViewDate;
17105 e.preventDefault();
17109 if (!this.keyboardNavigation) {
17112 dir = e.keyCode == 37 ? -1 : 1;
17115 newDate = this.moveYear(this.date, dir);
17116 newViewDate = this.moveYear(this.viewDate, dir);
17117 } else if (e.shiftKey){
17118 newDate = this.moveMonth(this.date, dir);
17119 newViewDate = this.moveMonth(this.viewDate, dir);
17121 newDate = new Date(this.date);
17122 newDate.setUTCDate(this.date.getUTCDate() + dir);
17123 newViewDate = new Date(this.viewDate);
17124 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17126 if (this.dateWithinRange(newDate)){
17127 this.date = newDate;
17128 this.viewDate = newViewDate;
17129 this.setValue(this.formatDate(this.date));
17131 e.preventDefault();
17132 dateChanged = true;
17137 if (!this.keyboardNavigation) {
17140 dir = e.keyCode == 38 ? -1 : 1;
17142 newDate = this.moveYear(this.date, dir);
17143 newViewDate = this.moveYear(this.viewDate, dir);
17144 } else if (e.shiftKey){
17145 newDate = this.moveMonth(this.date, dir);
17146 newViewDate = this.moveMonth(this.viewDate, dir);
17148 newDate = new Date(this.date);
17149 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17150 newViewDate = new Date(this.viewDate);
17151 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17153 if (this.dateWithinRange(newDate)){
17154 this.date = newDate;
17155 this.viewDate = newViewDate;
17156 this.setValue(this.formatDate(this.date));
17158 e.preventDefault();
17159 dateChanged = true;
17163 this.setValue(this.formatDate(this.date));
17165 e.preventDefault();
17168 this.setValue(this.formatDate(this.date));
17182 onClick: function(e)
17184 e.stopPropagation();
17185 e.preventDefault();
17187 var target = e.getTarget();
17189 if(target.nodeName.toLowerCase() === 'i'){
17190 target = Roo.get(target).dom.parentNode;
17193 var nodeName = target.nodeName;
17194 var className = target.className;
17195 var html = target.innerHTML;
17196 //Roo.log(nodeName);
17198 switch(nodeName.toLowerCase()) {
17200 switch(className) {
17206 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17207 switch(this.viewMode){
17209 this.viewDate = this.moveMonth(this.viewDate, dir);
17213 this.viewDate = this.moveYear(this.viewDate, dir);
17219 var date = new Date();
17220 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17222 this.setValue(this.formatDate(this.date));
17229 if (className.indexOf('disabled') < 0) {
17230 this.viewDate.setUTCDate(1);
17231 if (className.indexOf('month') > -1) {
17232 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17234 var year = parseInt(html, 10) || 0;
17235 this.viewDate.setUTCFullYear(year);
17239 if(this.singleMode){
17240 this.setValue(this.formatDate(this.viewDate));
17251 //Roo.log(className);
17252 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17253 var day = parseInt(html, 10) || 1;
17254 var year = this.viewDate.getUTCFullYear(),
17255 month = this.viewDate.getUTCMonth();
17257 if (className.indexOf('old') > -1) {
17264 } else if (className.indexOf('new') > -1) {
17272 //Roo.log([year,month,day]);
17273 this.date = this.UTCDate(year, month, day,0,0,0,0);
17274 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17276 //Roo.log(this.formatDate(this.date));
17277 this.setValue(this.formatDate(this.date));
17284 setStartDate: function(startDate)
17286 this.startDate = startDate || -Infinity;
17287 if (this.startDate !== -Infinity) {
17288 this.startDate = this.parseDate(this.startDate);
17291 this.updateNavArrows();
17294 setEndDate: function(endDate)
17296 this.endDate = endDate || Infinity;
17297 if (this.endDate !== Infinity) {
17298 this.endDate = this.parseDate(this.endDate);
17301 this.updateNavArrows();
17304 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17306 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17307 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17308 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17310 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17311 return parseInt(d, 10);
17314 this.updateNavArrows();
17317 updateNavArrows: function()
17319 if(this.singleMode){
17323 var d = new Date(this.viewDate),
17324 year = d.getUTCFullYear(),
17325 month = d.getUTCMonth();
17327 Roo.each(this.picker().select('.prev', true).elements, function(v){
17329 switch (this.viewMode) {
17332 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17338 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17345 Roo.each(this.picker().select('.next', true).elements, function(v){
17347 switch (this.viewMode) {
17350 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17356 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17364 moveMonth: function(date, dir)
17369 var new_date = new Date(date.valueOf()),
17370 day = new_date.getUTCDate(),
17371 month = new_date.getUTCMonth(),
17372 mag = Math.abs(dir),
17374 dir = dir > 0 ? 1 : -1;
17377 // If going back one month, make sure month is not current month
17378 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17380 return new_date.getUTCMonth() == month;
17382 // If going forward one month, make sure month is as expected
17383 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17385 return new_date.getUTCMonth() != new_month;
17387 new_month = month + dir;
17388 new_date.setUTCMonth(new_month);
17389 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17390 if (new_month < 0 || new_month > 11) {
17391 new_month = (new_month + 12) % 12;
17394 // For magnitudes >1, move one month at a time...
17395 for (var i=0; i<mag; i++) {
17396 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17397 new_date = this.moveMonth(new_date, dir);
17399 // ...then reset the day, keeping it in the new month
17400 new_month = new_date.getUTCMonth();
17401 new_date.setUTCDate(day);
17403 return new_month != new_date.getUTCMonth();
17406 // Common date-resetting loop -- if date is beyond end of month, make it
17409 new_date.setUTCDate(--day);
17410 new_date.setUTCMonth(new_month);
17415 moveYear: function(date, dir)
17417 return this.moveMonth(date, dir*12);
17420 dateWithinRange: function(date)
17422 return date >= this.startDate && date <= this.endDate;
17428 this.picker().remove();
17433 Roo.apply(Roo.bootstrap.DateField, {
17444 html: '<i class="fa fa-arrow-left"/>'
17454 html: '<i class="fa fa-arrow-right"/>'
17496 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17497 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17498 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17499 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17500 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17513 navFnc: 'FullYear',
17518 navFnc: 'FullYear',
17523 Roo.apply(Roo.bootstrap.DateField, {
17527 cls: 'datepicker dropdown-menu roo-dynamic',
17531 cls: 'datepicker-days',
17535 cls: 'table-condensed',
17537 Roo.bootstrap.DateField.head,
17541 Roo.bootstrap.DateField.footer
17548 cls: 'datepicker-months',
17552 cls: 'table-condensed',
17554 Roo.bootstrap.DateField.head,
17555 Roo.bootstrap.DateField.content,
17556 Roo.bootstrap.DateField.footer
17563 cls: 'datepicker-years',
17567 cls: 'table-condensed',
17569 Roo.bootstrap.DateField.head,
17570 Roo.bootstrap.DateField.content,
17571 Roo.bootstrap.DateField.footer
17590 * @class Roo.bootstrap.TimeField
17591 * @extends Roo.bootstrap.Input
17592 * Bootstrap DateField class
17596 * Create a new TimeField
17597 * @param {Object} config The config object
17600 Roo.bootstrap.TimeField = function(config){
17601 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17605 * Fires when this field show.
17606 * @param {Roo.bootstrap.DateField} thisthis
17607 * @param {Mixed} date The date value
17612 * Fires when this field hide.
17613 * @param {Roo.bootstrap.DateField} this
17614 * @param {Mixed} date The date value
17619 * Fires when select a date.
17620 * @param {Roo.bootstrap.DateField} this
17621 * @param {Mixed} date The date value
17627 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17630 * @cfg {String} format
17631 * The default time format string which can be overriden for localization support. The format must be
17632 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17636 onRender: function(ct, position)
17639 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17641 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17643 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17645 this.pop = this.picker().select('>.datepicker-time',true).first();
17646 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17648 this.picker().on('mousedown', this.onMousedown, this);
17649 this.picker().on('click', this.onClick, this);
17651 this.picker().addClass('datepicker-dropdown');
17656 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17657 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17658 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17659 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17660 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17661 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17665 fireKey: function(e){
17666 if (!this.picker().isVisible()){
17667 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17673 e.preventDefault();
17681 this.onTogglePeriod();
17684 this.onIncrementMinutes();
17687 this.onDecrementMinutes();
17696 onClick: function(e) {
17697 e.stopPropagation();
17698 e.preventDefault();
17701 picker : function()
17703 return this.el.select('.datepicker', true).first();
17706 fillTime: function()
17708 var time = this.pop.select('tbody', true).first();
17710 time.dom.innerHTML = '';
17725 cls: 'hours-up glyphicon glyphicon-chevron-up'
17745 cls: 'minutes-up glyphicon glyphicon-chevron-up'
17766 cls: 'timepicker-hour',
17781 cls: 'timepicker-minute',
17796 cls: 'btn btn-primary period',
17818 cls: 'hours-down glyphicon glyphicon-chevron-down'
17838 cls: 'minutes-down glyphicon glyphicon-chevron-down'
17856 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17863 var hours = this.time.getHours();
17864 var minutes = this.time.getMinutes();
17877 hours = hours - 12;
17881 hours = '0' + hours;
17885 minutes = '0' + minutes;
17888 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17889 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17890 this.pop.select('button', true).first().dom.innerHTML = period;
17896 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17898 var cls = ['bottom'];
17900 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17907 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17912 this.picker().addClass(cls.join('-'));
17916 Roo.each(cls, function(c){
17918 _this.picker().setTop(_this.inputEl().getHeight());
17922 _this.picker().setTop(0 - _this.picker().getHeight());
17927 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17931 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17938 onFocus : function()
17940 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17944 onBlur : function()
17946 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17952 this.picker().show();
17957 this.fireEvent('show', this, this.date);
17962 this.picker().hide();
17965 this.fireEvent('hide', this, this.date);
17968 setTime : function()
17971 this.setValue(this.time.format(this.format));
17973 this.fireEvent('select', this, this.date);
17978 onMousedown: function(e){
17979 e.stopPropagation();
17980 e.preventDefault();
17983 onIncrementHours: function()
17985 Roo.log('onIncrementHours');
17986 this.time = this.time.add(Date.HOUR, 1);
17991 onDecrementHours: function()
17993 Roo.log('onDecrementHours');
17994 this.time = this.time.add(Date.HOUR, -1);
17998 onIncrementMinutes: function()
18000 Roo.log('onIncrementMinutes');
18001 this.time = this.time.add(Date.MINUTE, 1);
18005 onDecrementMinutes: function()
18007 Roo.log('onDecrementMinutes');
18008 this.time = this.time.add(Date.MINUTE, -1);
18012 onTogglePeriod: function()
18014 Roo.log('onTogglePeriod');
18015 this.time = this.time.add(Date.HOUR, 12);
18022 Roo.apply(Roo.bootstrap.TimeField, {
18052 cls: 'btn btn-info ok',
18064 Roo.apply(Roo.bootstrap.TimeField, {
18068 cls: 'datepicker dropdown-menu',
18072 cls: 'datepicker-time',
18076 cls: 'table-condensed',
18078 Roo.bootstrap.TimeField.content,
18079 Roo.bootstrap.TimeField.footer
18098 * @class Roo.bootstrap.MonthField
18099 * @extends Roo.bootstrap.Input
18100 * Bootstrap MonthField class
18102 * @cfg {String} language default en
18105 * Create a new MonthField
18106 * @param {Object} config The config object
18109 Roo.bootstrap.MonthField = function(config){
18110 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18115 * Fires when this field show.
18116 * @param {Roo.bootstrap.MonthField} this
18117 * @param {Mixed} date The date value
18122 * Fires when this field hide.
18123 * @param {Roo.bootstrap.MonthField} this
18124 * @param {Mixed} date The date value
18129 * Fires when select a date.
18130 * @param {Roo.bootstrap.MonthField} this
18131 * @param {String} oldvalue The old value
18132 * @param {String} newvalue The new value
18138 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
18140 onRender: function(ct, position)
18143 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18145 this.language = this.language || 'en';
18146 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18147 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18149 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18150 this.isInline = false;
18151 this.isInput = true;
18152 this.component = this.el.select('.add-on', true).first() || false;
18153 this.component = (this.component && this.component.length === 0) ? false : this.component;
18154 this.hasInput = this.component && this.inputEL().length;
18156 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18158 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18160 this.picker().on('mousedown', this.onMousedown, this);
18161 this.picker().on('click', this.onClick, this);
18163 this.picker().addClass('datepicker-dropdown');
18165 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18166 v.setStyle('width', '189px');
18173 if(this.isInline) {
18179 setValue: function(v, suppressEvent)
18181 var o = this.getValue();
18183 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18187 if(suppressEvent !== true){
18188 this.fireEvent('select', this, o, v);
18193 getValue: function()
18198 onClick: function(e)
18200 e.stopPropagation();
18201 e.preventDefault();
18203 var target = e.getTarget();
18205 if(target.nodeName.toLowerCase() === 'i'){
18206 target = Roo.get(target).dom.parentNode;
18209 var nodeName = target.nodeName;
18210 var className = target.className;
18211 var html = target.innerHTML;
18213 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18217 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18219 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18225 picker : function()
18227 return this.pickerEl;
18230 fillMonths: function()
18233 var months = this.picker().select('>.datepicker-months td', true).first();
18235 months.dom.innerHTML = '';
18241 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18244 months.createChild(month);
18253 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18254 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18257 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18258 e.removeClass('active');
18260 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18261 e.addClass('active');
18268 if(this.isInline) {
18272 this.picker().removeClass(['bottom', 'top']);
18274 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18276 * place to the top of element!
18280 this.picker().addClass('top');
18281 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18286 this.picker().addClass('bottom');
18288 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18291 onFocus : function()
18293 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18297 onBlur : function()
18299 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18301 var d = this.inputEl().getValue();
18310 this.picker().show();
18311 this.picker().select('>.datepicker-months', true).first().show();
18315 this.fireEvent('show', this, this.date);
18320 if(this.isInline) {
18323 this.picker().hide();
18324 this.fireEvent('hide', this, this.date);
18328 onMousedown: function(e)
18330 e.stopPropagation();
18331 e.preventDefault();
18336 Roo.bootstrap.MonthField.superclass.keyup.call(this);
18340 fireKey: function(e)
18342 if (!this.picker().isVisible()){
18343 if (e.keyCode == 27) {// allow escape to hide and re-show picker
18354 e.preventDefault();
18358 dir = e.keyCode == 37 ? -1 : 1;
18360 this.vIndex = this.vIndex + dir;
18362 if(this.vIndex < 0){
18366 if(this.vIndex > 11){
18370 if(isNaN(this.vIndex)){
18374 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18380 dir = e.keyCode == 38 ? -1 : 1;
18382 this.vIndex = this.vIndex + dir * 4;
18384 if(this.vIndex < 0){
18388 if(this.vIndex > 11){
18392 if(isNaN(this.vIndex)){
18396 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18401 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18402 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18406 e.preventDefault();
18409 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18410 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18426 this.picker().remove();
18431 Roo.apply(Roo.bootstrap.MonthField, {
18450 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18451 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18456 Roo.apply(Roo.bootstrap.MonthField, {
18460 cls: 'datepicker dropdown-menu roo-dynamic',
18464 cls: 'datepicker-months',
18468 cls: 'table-condensed',
18470 Roo.bootstrap.DateField.content
18490 * @class Roo.bootstrap.CheckBox
18491 * @extends Roo.bootstrap.Input
18492 * Bootstrap CheckBox class
18494 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18495 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18496 * @cfg {String} boxLabel The text that appears beside the checkbox
18497 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18498 * @cfg {Boolean} checked initnal the element
18499 * @cfg {Boolean} inline inline the element (default false)
18500 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18503 * Create a new CheckBox
18504 * @param {Object} config The config object
18507 Roo.bootstrap.CheckBox = function(config){
18508 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18513 * Fires when the element is checked or unchecked.
18514 * @param {Roo.bootstrap.CheckBox} this This input
18515 * @param {Boolean} checked The new checked value
18522 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18524 inputType: 'checkbox',
18532 getAutoCreate : function()
18534 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18540 cfg.cls = 'form-group ' + this.inputType; //input-group
18543 cfg.cls += ' ' + this.inputType + '-inline';
18549 type : this.inputType,
18550 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18551 cls : 'roo-' + this.inputType, //'form-box',
18552 placeholder : this.placeholder || ''
18556 if (this.weight) { // Validity check?
18557 cfg.cls += " " + this.inputType + "-" + this.weight;
18560 if (this.disabled) {
18561 input.disabled=true;
18565 input.checked = this.checked;
18569 input.name = this.name;
18573 input.cls += ' input-' + this.size;
18578 ['xs','sm','md','lg'].map(function(size){
18579 if (settings[size]) {
18580 cfg.cls += ' col-' + size + '-' + settings[size];
18584 var inputblock = input;
18586 if (this.before || this.after) {
18589 cls : 'input-group',
18594 inputblock.cn.push({
18596 cls : 'input-group-addon',
18601 inputblock.cn.push(input);
18604 inputblock.cn.push({
18606 cls : 'input-group-addon',
18613 if (align ==='left' && this.fieldLabel.length) {
18614 Roo.log("left and has label");
18620 cls : 'control-label col-md-' + this.labelWidth,
18621 html : this.fieldLabel
18625 cls : "col-md-" + (12 - this.labelWidth),
18632 } else if ( this.fieldLabel.length) {
18637 tag: this.boxLabel ? 'span' : 'label',
18639 cls: 'control-label box-input-label',
18640 //cls : 'input-group-addon',
18641 html : this.fieldLabel
18651 Roo.log(" no label && no align");
18652 cfg.cn = [ inputblock ] ;
18657 var boxLabelCfg = {
18659 //'for': id, // box label is handled by onclick - so no for...
18661 html: this.boxLabel
18665 boxLabelCfg.tooltip = this.tooltip;
18668 cfg.cn.push(boxLabelCfg);
18678 * return the real input element.
18680 inputEl: function ()
18682 return this.el.select('input.roo-' + this.inputType,true).first();
18685 labelEl: function()
18687 return this.el.select('label.control-label',true).first();
18689 /* depricated... */
18693 return this.labelEl();
18696 initEvents : function()
18698 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18700 this.inputEl().on('click', this.onClick, this);
18702 if (this.boxLabel) {
18703 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18706 this.startValue = this.getValue();
18709 Roo.bootstrap.CheckBox.register(this);
18713 onClick : function()
18715 this.setChecked(!this.checked);
18718 setChecked : function(state,suppressEvent)
18720 this.startValue = this.getValue();
18722 if(this.inputType == 'radio'){
18724 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18725 e.dom.checked = false;
18728 this.inputEl().dom.checked = true;
18730 this.inputEl().dom.value = this.inputValue;
18732 if(suppressEvent !== true){
18733 this.fireEvent('check', this, true);
18741 this.checked = state;
18743 this.inputEl().dom.checked = state;
18745 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18747 if(suppressEvent !== true){
18748 this.fireEvent('check', this, state);
18754 getValue : function()
18756 if(this.inputType == 'radio'){
18757 return this.getGroupValue();
18760 return this.inputEl().getValue();
18764 getGroupValue : function()
18766 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18770 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18773 setValue : function(v,suppressEvent)
18775 if(this.inputType == 'radio'){
18776 this.setGroupValue(v, suppressEvent);
18780 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18785 setGroupValue : function(v, suppressEvent)
18787 this.startValue = this.getValue();
18789 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18790 e.dom.checked = false;
18792 if(e.dom.value == v){
18793 e.dom.checked = true;
18797 if(suppressEvent !== true){
18798 this.fireEvent('check', this, true);
18806 validate : function()
18810 (this.inputType == 'radio' && this.validateRadio()) ||
18811 (this.inputType == 'checkbox' && this.validateCheckbox())
18817 this.markInvalid();
18821 validateRadio : function()
18825 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18826 if(!e.dom.checked){
18838 validateCheckbox : function()
18841 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18844 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18852 for(var i in group){
18857 r = (group[i].getValue() == group[i].inputValue) ? true : false;
18864 * Mark this field as valid
18866 markValid : function()
18868 if(this.allowBlank){
18874 this.fireEvent('valid', this);
18876 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18879 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18886 if(this.inputType == 'radio'){
18887 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18888 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18889 e.findParent('.form-group', false, true).addClass(_this.validClass);
18896 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18897 this.el.findParent('.form-group', false, true).addClass(this.validClass);
18901 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18907 for(var i in group){
18908 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18909 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18914 * Mark this field as invalid
18915 * @param {String} msg The validation message
18917 markInvalid : function(msg)
18919 if(this.allowBlank){
18925 this.fireEvent('invalid', this, msg);
18927 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18930 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18934 label.markInvalid();
18937 if(this.inputType == 'radio'){
18938 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18939 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18940 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18947 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18948 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18952 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18958 for(var i in group){
18959 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18960 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18967 Roo.apply(Roo.bootstrap.CheckBox, {
18972 * register a CheckBox Group
18973 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18975 register : function(checkbox)
18977 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18978 this.groups[checkbox.groupId] = {};
18981 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18985 this.groups[checkbox.groupId][checkbox.name] = checkbox;
18989 * fetch a CheckBox Group based on the group ID
18990 * @param {string} the group ID
18991 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18993 get: function(groupId) {
18994 if (typeof(this.groups[groupId]) == 'undefined') {
18998 return this.groups[groupId] ;
19010 *<div class="radio">
19012 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19013 Option one is this and that—be sure to include why it's great
19020 *<label class="radio-inline">fieldLabel</label>
19021 *<label class="radio-inline">
19022 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19030 * @class Roo.bootstrap.Radio
19031 * @extends Roo.bootstrap.CheckBox
19032 * Bootstrap Radio class
19035 * Create a new Radio
19036 * @param {Object} config The config object
19039 Roo.bootstrap.Radio = function(config){
19040 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19044 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
19046 inputType: 'radio',
19050 getAutoCreate : function()
19052 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19053 align = align || 'left'; // default...
19060 tag : this.inline ? 'span' : 'div',
19065 var inline = this.inline ? ' radio-inline' : '';
19069 // does not need for, as we wrap the input with it..
19071 cls : 'control-label box-label' + inline,
19074 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19078 //cls : 'control-label' + inline,
19079 html : this.fieldLabel,
19080 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19089 type : this.inputType,
19090 //value : (!this.checked) ? this.valueOff : this.inputValue,
19091 value : this.inputValue,
19093 placeholder : this.placeholder || '' // ?? needed????
19096 if (this.weight) { // Validity check?
19097 input.cls += " radio-" + this.weight;
19099 if (this.disabled) {
19100 input.disabled=true;
19104 input.checked = this.checked;
19108 input.name = this.name;
19112 input.cls += ' input-' + this.size;
19115 //?? can span's inline have a width??
19118 ['xs','sm','md','lg'].map(function(size){
19119 if (settings[size]) {
19120 cfg.cls += ' col-' + size + '-' + settings[size];
19124 var inputblock = input;
19126 if (this.before || this.after) {
19129 cls : 'input-group',
19134 inputblock.cn.push({
19136 cls : 'input-group-addon',
19140 inputblock.cn.push(input);
19142 inputblock.cn.push({
19144 cls : 'input-group-addon',
19152 if (this.fieldLabel && this.fieldLabel.length) {
19153 cfg.cn.push(fieldLabel);
19156 // normal bootstrap puts the input inside the label.
19157 // however with our styled version - it has to go after the input.
19159 //lbl.cn.push(inputblock);
19163 cls: 'radio' + inline,
19170 cfg.cn.push( lblwrap);
19175 html: this.boxLabel
19184 initEvents : function()
19186 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19188 this.inputEl().on('click', this.onClick, this);
19189 if (this.boxLabel) {
19190 //Roo.log('find label');
19191 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
19196 inputEl: function ()
19198 return this.el.select('input.roo-radio',true).first();
19200 onClick : function()
19203 this.setChecked(true);
19206 setChecked : function(state,suppressEvent)
19209 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19210 v.dom.checked = false;
19213 Roo.log(this.inputEl().dom);
19214 this.checked = state;
19215 this.inputEl().dom.checked = state;
19217 if(suppressEvent !== true){
19218 this.fireEvent('check', this, state);
19221 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19225 getGroupValue : function()
19228 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19229 if(v.dom.checked == true){
19230 value = v.dom.value;
19238 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
19239 * @return {Mixed} value The field value
19241 getValue : function(){
19242 return this.getGroupValue();
19248 //<script type="text/javascript">
19251 * Based Ext JS Library 1.1.1
19252 * Copyright(c) 2006-2007, Ext JS, LLC.
19258 * @class Roo.HtmlEditorCore
19259 * @extends Roo.Component
19260 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19262 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19265 Roo.HtmlEditorCore = function(config){
19268 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19273 * @event initialize
19274 * Fires when the editor is fully initialized (including the iframe)
19275 * @param {Roo.HtmlEditorCore} this
19280 * Fires when the editor is first receives the focus. Any insertion must wait
19281 * until after this event.
19282 * @param {Roo.HtmlEditorCore} this
19286 * @event beforesync
19287 * Fires before the textarea is updated with content from the editor iframe. Return false
19288 * to cancel the sync.
19289 * @param {Roo.HtmlEditorCore} this
19290 * @param {String} html
19294 * @event beforepush
19295 * Fires before the iframe editor is updated with content from the textarea. Return false
19296 * to cancel the push.
19297 * @param {Roo.HtmlEditorCore} this
19298 * @param {String} html
19303 * Fires when the textarea is updated with content from the editor iframe.
19304 * @param {Roo.HtmlEditorCore} this
19305 * @param {String} html
19310 * Fires when the iframe editor is updated with content from the textarea.
19311 * @param {Roo.HtmlEditorCore} this
19312 * @param {String} html
19317 * @event editorevent
19318 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19319 * @param {Roo.HtmlEditorCore} this
19325 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19327 // defaults : white / black...
19328 this.applyBlacklists();
19335 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
19339 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
19345 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
19350 * @cfg {Number} height (in pixels)
19354 * @cfg {Number} width (in pixels)
19359 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19362 stylesheets: false,
19367 // private properties
19368 validationEvent : false,
19370 initialized : false,
19372 sourceEditMode : false,
19373 onFocus : Roo.emptyFn,
19375 hideMode:'offsets',
19379 // blacklist + whitelisted elements..
19386 * Protected method that will not generally be called directly. It
19387 * is called when the editor initializes the iframe with HTML contents. Override this method if you
19388 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19390 getDocMarkup : function(){
19394 // inherit styels from page...??
19395 if (this.stylesheets === false) {
19397 Roo.get(document.head).select('style').each(function(node) {
19398 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19401 Roo.get(document.head).select('link').each(function(node) {
19402 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19405 } else if (!this.stylesheets.length) {
19407 st = '<style type="text/css">' +
19408 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19414 st += '<style type="text/css">' +
19415 'IMG { cursor: pointer } ' +
19419 return '<html><head>' + st +
19420 //<style type="text/css">' +
19421 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19423 ' </head><body class="roo-htmleditor-body"></body></html>';
19427 onRender : function(ct, position)
19430 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19431 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19434 this.el.dom.style.border = '0 none';
19435 this.el.dom.setAttribute('tabIndex', -1);
19436 this.el.addClass('x-hidden hide');
19440 if(Roo.isIE){ // fix IE 1px bogus margin
19441 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19445 this.frameId = Roo.id();
19449 var iframe = this.owner.wrap.createChild({
19451 cls: 'form-control', // bootstrap..
19453 name: this.frameId,
19454 frameBorder : 'no',
19455 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19460 this.iframe = iframe.dom;
19462 this.assignDocWin();
19464 this.doc.designMode = 'on';
19467 this.doc.write(this.getDocMarkup());
19471 var task = { // must defer to wait for browser to be ready
19473 //console.log("run task?" + this.doc.readyState);
19474 this.assignDocWin();
19475 if(this.doc.body || this.doc.readyState == 'complete'){
19477 this.doc.designMode="on";
19481 Roo.TaskMgr.stop(task);
19482 this.initEditor.defer(10, this);
19489 Roo.TaskMgr.start(task);
19494 onResize : function(w, h)
19496 Roo.log('resize: ' +w + ',' + h );
19497 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19501 if(typeof w == 'number'){
19503 this.iframe.style.width = w + 'px';
19505 if(typeof h == 'number'){
19507 this.iframe.style.height = h + 'px';
19509 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19516 * Toggles the editor between standard and source edit mode.
19517 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19519 toggleSourceEdit : function(sourceEditMode){
19521 this.sourceEditMode = sourceEditMode === true;
19523 if(this.sourceEditMode){
19525 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19528 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19529 //this.iframe.className = '';
19532 //this.setSize(this.owner.wrap.getSize());
19533 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19540 * Protected method that will not generally be called directly. If you need/want
19541 * custom HTML cleanup, this is the method you should override.
19542 * @param {String} html The HTML to be cleaned
19543 * return {String} The cleaned HTML
19545 cleanHtml : function(html){
19546 html = String(html);
19547 if(html.length > 5){
19548 if(Roo.isSafari){ // strip safari nonsense
19549 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19552 if(html == ' '){
19559 * HTML Editor -> Textarea
19560 * Protected method that will not generally be called directly. Syncs the contents
19561 * of the editor iframe with the textarea.
19563 syncValue : function(){
19564 if(this.initialized){
19565 var bd = (this.doc.body || this.doc.documentElement);
19566 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19567 var html = bd.innerHTML;
19569 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19570 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19572 html = '<div style="'+m[0]+'">' + html + '</div>';
19575 html = this.cleanHtml(html);
19576 // fix up the special chars.. normaly like back quotes in word...
19577 // however we do not want to do this with chinese..
19578 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19579 var cc = b.charCodeAt();
19581 (cc >= 0x4E00 && cc < 0xA000 ) ||
19582 (cc >= 0x3400 && cc < 0x4E00 ) ||
19583 (cc >= 0xf900 && cc < 0xfb00 )
19589 if(this.owner.fireEvent('beforesync', this, html) !== false){
19590 this.el.dom.value = html;
19591 this.owner.fireEvent('sync', this, html);
19597 * Protected method that will not generally be called directly. Pushes the value of the textarea
19598 * into the iframe editor.
19600 pushValue : function(){
19601 if(this.initialized){
19602 var v = this.el.dom.value.trim();
19604 // if(v.length < 1){
19608 if(this.owner.fireEvent('beforepush', this, v) !== false){
19609 var d = (this.doc.body || this.doc.documentElement);
19611 this.cleanUpPaste();
19612 this.el.dom.value = d.innerHTML;
19613 this.owner.fireEvent('push', this, v);
19619 deferFocus : function(){
19620 this.focus.defer(10, this);
19624 focus : function(){
19625 if(this.win && !this.sourceEditMode){
19632 assignDocWin: function()
19634 var iframe = this.iframe;
19637 this.doc = iframe.contentWindow.document;
19638 this.win = iframe.contentWindow;
19640 // if (!Roo.get(this.frameId)) {
19643 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19644 // this.win = Roo.get(this.frameId).dom.contentWindow;
19646 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19650 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19651 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19656 initEditor : function(){
19657 //console.log("INIT EDITOR");
19658 this.assignDocWin();
19662 this.doc.designMode="on";
19664 this.doc.write(this.getDocMarkup());
19667 var dbody = (this.doc.body || this.doc.documentElement);
19668 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19669 // this copies styles from the containing element into thsi one..
19670 // not sure why we need all of this..
19671 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19673 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19674 //ss['background-attachment'] = 'fixed'; // w3c
19675 dbody.bgProperties = 'fixed'; // ie
19676 //Roo.DomHelper.applyStyles(dbody, ss);
19677 Roo.EventManager.on(this.doc, {
19678 //'mousedown': this.onEditorEvent,
19679 'mouseup': this.onEditorEvent,
19680 'dblclick': this.onEditorEvent,
19681 'click': this.onEditorEvent,
19682 'keyup': this.onEditorEvent,
19687 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19689 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19690 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19692 this.initialized = true;
19694 this.owner.fireEvent('initialize', this);
19699 onDestroy : function(){
19705 //for (var i =0; i < this.toolbars.length;i++) {
19706 // // fixme - ask toolbars for heights?
19707 // this.toolbars[i].onDestroy();
19710 //this.wrap.dom.innerHTML = '';
19711 //this.wrap.remove();
19716 onFirstFocus : function(){
19718 this.assignDocWin();
19721 this.activated = true;
19724 if(Roo.isGecko){ // prevent silly gecko errors
19726 var s = this.win.getSelection();
19727 if(!s.focusNode || s.focusNode.nodeType != 3){
19728 var r = s.getRangeAt(0);
19729 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19734 this.execCmd('useCSS', true);
19735 this.execCmd('styleWithCSS', false);
19738 this.owner.fireEvent('activate', this);
19742 adjustFont: function(btn){
19743 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19744 //if(Roo.isSafari){ // safari
19747 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19748 if(Roo.isSafari){ // safari
19749 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19750 v = (v < 10) ? 10 : v;
19751 v = (v > 48) ? 48 : v;
19752 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19757 v = Math.max(1, v+adjust);
19759 this.execCmd('FontSize', v );
19762 onEditorEvent : function(e)
19764 this.owner.fireEvent('editorevent', this, e);
19765 // this.updateToolbar();
19766 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19769 insertTag : function(tg)
19771 // could be a bit smarter... -> wrap the current selected tRoo..
19772 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19774 range = this.createRange(this.getSelection());
19775 var wrappingNode = this.doc.createElement(tg.toLowerCase());
19776 wrappingNode.appendChild(range.extractContents());
19777 range.insertNode(wrappingNode);
19784 this.execCmd("formatblock", tg);
19788 insertText : function(txt)
19792 var range = this.createRange();
19793 range.deleteContents();
19794 //alert(Sender.getAttribute('label'));
19796 range.insertNode(this.doc.createTextNode(txt));
19802 * Executes a Midas editor command on the editor document and performs necessary focus and
19803 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19804 * @param {String} cmd The Midas command
19805 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19807 relayCmd : function(cmd, value){
19809 this.execCmd(cmd, value);
19810 this.owner.fireEvent('editorevent', this);
19811 //this.updateToolbar();
19812 this.owner.deferFocus();
19816 * Executes a Midas editor command directly on the editor document.
19817 * For visual commands, you should use {@link #relayCmd} instead.
19818 * <b>This should only be called after the editor is initialized.</b>
19819 * @param {String} cmd The Midas command
19820 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19822 execCmd : function(cmd, value){
19823 this.doc.execCommand(cmd, false, value === undefined ? null : value);
19830 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19832 * @param {String} text | dom node..
19834 insertAtCursor : function(text)
19839 if(!this.activated){
19845 var r = this.doc.selection.createRange();
19856 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19860 // from jquery ui (MIT licenced)
19862 var win = this.win;
19864 if (win.getSelection && win.getSelection().getRangeAt) {
19865 range = win.getSelection().getRangeAt(0);
19866 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19867 range.insertNode(node);
19868 } else if (win.document.selection && win.document.selection.createRange) {
19869 // no firefox support
19870 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19871 win.document.selection.createRange().pasteHTML(txt);
19873 // no firefox support
19874 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19875 this.execCmd('InsertHTML', txt);
19884 mozKeyPress : function(e){
19886 var c = e.getCharCode(), cmd;
19889 c = String.fromCharCode(c).toLowerCase();
19903 this.cleanUpPaste.defer(100, this);
19911 e.preventDefault();
19919 fixKeys : function(){ // load time branching for fastest keydown performance
19921 return function(e){
19922 var k = e.getKey(), r;
19925 r = this.doc.selection.createRange();
19928 r.pasteHTML('    ');
19935 r = this.doc.selection.createRange();
19937 var target = r.parentElement();
19938 if(!target || target.tagName.toLowerCase() != 'li'){
19940 r.pasteHTML('<br />');
19946 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19947 this.cleanUpPaste.defer(100, this);
19953 }else if(Roo.isOpera){
19954 return function(e){
19955 var k = e.getKey();
19959 this.execCmd('InsertHTML','    ');
19962 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19963 this.cleanUpPaste.defer(100, this);
19968 }else if(Roo.isSafari){
19969 return function(e){
19970 var k = e.getKey();
19974 this.execCmd('InsertText','\t');
19978 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19979 this.cleanUpPaste.defer(100, this);
19987 getAllAncestors: function()
19989 var p = this.getSelectedNode();
19992 a.push(p); // push blank onto stack..
19993 p = this.getParentElement();
19997 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20001 a.push(this.doc.body);
20005 lastSelNode : false,
20008 getSelection : function()
20010 this.assignDocWin();
20011 return Roo.isIE ? this.doc.selection : this.win.getSelection();
20014 getSelectedNode: function()
20016 // this may only work on Gecko!!!
20018 // should we cache this!!!!
20023 var range = this.createRange(this.getSelection()).cloneRange();
20026 var parent = range.parentElement();
20028 var testRange = range.duplicate();
20029 testRange.moveToElementText(parent);
20030 if (testRange.inRange(range)) {
20033 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20036 parent = parent.parentElement;
20041 // is ancestor a text element.
20042 var ac = range.commonAncestorContainer;
20043 if (ac.nodeType == 3) {
20044 ac = ac.parentNode;
20047 var ar = ac.childNodes;
20050 var other_nodes = [];
20051 var has_other_nodes = false;
20052 for (var i=0;i<ar.length;i++) {
20053 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
20056 // fullly contained node.
20058 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20063 // probably selected..
20064 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20065 other_nodes.push(ar[i]);
20069 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
20074 has_other_nodes = true;
20076 if (!nodes.length && other_nodes.length) {
20077 nodes= other_nodes;
20079 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20085 createRange: function(sel)
20087 // this has strange effects when using with
20088 // top toolbar - not sure if it's a great idea.
20089 //this.editor.contentWindow.focus();
20090 if (typeof sel != "undefined") {
20092 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20094 return this.doc.createRange();
20097 return this.doc.createRange();
20100 getParentElement: function()
20103 this.assignDocWin();
20104 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20106 var range = this.createRange(sel);
20109 var p = range.commonAncestorContainer;
20110 while (p.nodeType == 3) { // text node
20121 * Range intersection.. the hard stuff...
20125 * [ -- selected range --- ]
20129 * if end is before start or hits it. fail.
20130 * if start is after end or hits it fail.
20132 * if either hits (but other is outside. - then it's not
20138 // @see http://www.thismuchiknow.co.uk/?p=64.
20139 rangeIntersectsNode : function(range, node)
20141 var nodeRange = node.ownerDocument.createRange();
20143 nodeRange.selectNode(node);
20145 nodeRange.selectNodeContents(node);
20148 var rangeStartRange = range.cloneRange();
20149 rangeStartRange.collapse(true);
20151 var rangeEndRange = range.cloneRange();
20152 rangeEndRange.collapse(false);
20154 var nodeStartRange = nodeRange.cloneRange();
20155 nodeStartRange.collapse(true);
20157 var nodeEndRange = nodeRange.cloneRange();
20158 nodeEndRange.collapse(false);
20160 return rangeStartRange.compareBoundaryPoints(
20161 Range.START_TO_START, nodeEndRange) == -1 &&
20162 rangeEndRange.compareBoundaryPoints(
20163 Range.START_TO_START, nodeStartRange) == 1;
20167 rangeCompareNode : function(range, node)
20169 var nodeRange = node.ownerDocument.createRange();
20171 nodeRange.selectNode(node);
20173 nodeRange.selectNodeContents(node);
20177 range.collapse(true);
20179 nodeRange.collapse(true);
20181 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20182 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
20184 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20186 var nodeIsBefore = ss == 1;
20187 var nodeIsAfter = ee == -1;
20189 if (nodeIsBefore && nodeIsAfter) {
20192 if (!nodeIsBefore && nodeIsAfter) {
20193 return 1; //right trailed.
20196 if (nodeIsBefore && !nodeIsAfter) {
20197 return 2; // left trailed.
20203 // private? - in a new class?
20204 cleanUpPaste : function()
20206 // cleans up the whole document..
20207 Roo.log('cleanuppaste');
20209 this.cleanUpChildren(this.doc.body);
20210 var clean = this.cleanWordChars(this.doc.body.innerHTML);
20211 if (clean != this.doc.body.innerHTML) {
20212 this.doc.body.innerHTML = clean;
20217 cleanWordChars : function(input) {// change the chars to hex code
20218 var he = Roo.HtmlEditorCore;
20220 var output = input;
20221 Roo.each(he.swapCodes, function(sw) {
20222 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20224 output = output.replace(swapper, sw[1]);
20231 cleanUpChildren : function (n)
20233 if (!n.childNodes.length) {
20236 for (var i = n.childNodes.length-1; i > -1 ; i--) {
20237 this.cleanUpChild(n.childNodes[i]);
20244 cleanUpChild : function (node)
20247 //console.log(node);
20248 if (node.nodeName == "#text") {
20249 // clean up silly Windows -- stuff?
20252 if (node.nodeName == "#comment") {
20253 node.parentNode.removeChild(node);
20254 // clean up silly Windows -- stuff?
20257 var lcname = node.tagName.toLowerCase();
20258 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20259 // whitelist of tags..
20261 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20263 node.parentNode.removeChild(node);
20268 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20270 // remove <a name=....> as rendering on yahoo mailer is borked with this.
20271 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20273 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20274 // remove_keep_children = true;
20277 if (remove_keep_children) {
20278 this.cleanUpChildren(node);
20279 // inserts everything just before this node...
20280 while (node.childNodes.length) {
20281 var cn = node.childNodes[0];
20282 node.removeChild(cn);
20283 node.parentNode.insertBefore(cn, node);
20285 node.parentNode.removeChild(node);
20289 if (!node.attributes || !node.attributes.length) {
20290 this.cleanUpChildren(node);
20294 function cleanAttr(n,v)
20297 if (v.match(/^\./) || v.match(/^\//)) {
20300 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20303 if (v.match(/^#/)) {
20306 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20307 node.removeAttribute(n);
20311 var cwhite = this.cwhite;
20312 var cblack = this.cblack;
20314 function cleanStyle(n,v)
20316 if (v.match(/expression/)) { //XSS?? should we even bother..
20317 node.removeAttribute(n);
20321 var parts = v.split(/;/);
20324 Roo.each(parts, function(p) {
20325 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20329 var l = p.split(':').shift().replace(/\s+/g,'');
20330 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20332 if ( cwhite.length && cblack.indexOf(l) > -1) {
20333 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20334 //node.removeAttribute(n);
20338 // only allow 'c whitelisted system attributes'
20339 if ( cwhite.length && cwhite.indexOf(l) < 0) {
20340 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20341 //node.removeAttribute(n);
20351 if (clean.length) {
20352 node.setAttribute(n, clean.join(';'));
20354 node.removeAttribute(n);
20360 for (var i = node.attributes.length-1; i > -1 ; i--) {
20361 var a = node.attributes[i];
20364 if (a.name.toLowerCase().substr(0,2)=='on') {
20365 node.removeAttribute(a.name);
20368 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20369 node.removeAttribute(a.name);
20372 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20373 cleanAttr(a.name,a.value); // fixme..
20376 if (a.name == 'style') {
20377 cleanStyle(a.name,a.value);
20380 /// clean up MS crap..
20381 // tecnically this should be a list of valid class'es..
20384 if (a.name == 'class') {
20385 if (a.value.match(/^Mso/)) {
20386 node.className = '';
20389 if (a.value.match(/body/)) {
20390 node.className = '';
20401 this.cleanUpChildren(node);
20407 * Clean up MS wordisms...
20409 cleanWord : function(node)
20414 this.cleanWord(this.doc.body);
20417 if (node.nodeName == "#text") {
20418 // clean up silly Windows -- stuff?
20421 if (node.nodeName == "#comment") {
20422 node.parentNode.removeChild(node);
20423 // clean up silly Windows -- stuff?
20427 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20428 node.parentNode.removeChild(node);
20432 // remove - but keep children..
20433 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20434 while (node.childNodes.length) {
20435 var cn = node.childNodes[0];
20436 node.removeChild(cn);
20437 node.parentNode.insertBefore(cn, node);
20439 node.parentNode.removeChild(node);
20440 this.iterateChildren(node, this.cleanWord);
20444 if (node.className.length) {
20446 var cn = node.className.split(/\W+/);
20448 Roo.each(cn, function(cls) {
20449 if (cls.match(/Mso[a-zA-Z]+/)) {
20454 node.className = cna.length ? cna.join(' ') : '';
20456 node.removeAttribute("class");
20460 if (node.hasAttribute("lang")) {
20461 node.removeAttribute("lang");
20464 if (node.hasAttribute("style")) {
20466 var styles = node.getAttribute("style").split(";");
20468 Roo.each(styles, function(s) {
20469 if (!s.match(/:/)) {
20472 var kv = s.split(":");
20473 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20476 // what ever is left... we allow.
20479 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20480 if (!nstyle.length) {
20481 node.removeAttribute('style');
20484 this.iterateChildren(node, this.cleanWord);
20490 * iterateChildren of a Node, calling fn each time, using this as the scole..
20491 * @param {DomNode} node node to iterate children of.
20492 * @param {Function} fn method of this class to call on each item.
20494 iterateChildren : function(node, fn)
20496 if (!node.childNodes.length) {
20499 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20500 fn.call(this, node.childNodes[i])
20506 * cleanTableWidths.
20508 * Quite often pasting from word etc.. results in tables with column and widths.
20509 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20512 cleanTableWidths : function(node)
20517 this.cleanTableWidths(this.doc.body);
20522 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20525 Roo.log(node.tagName);
20526 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20527 this.iterateChildren(node, this.cleanTableWidths);
20530 if (node.hasAttribute('width')) {
20531 node.removeAttribute('width');
20535 if (node.hasAttribute("style")) {
20538 var styles = node.getAttribute("style").split(";");
20540 Roo.each(styles, function(s) {
20541 if (!s.match(/:/)) {
20544 var kv = s.split(":");
20545 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20548 // what ever is left... we allow.
20551 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20552 if (!nstyle.length) {
20553 node.removeAttribute('style');
20557 this.iterateChildren(node, this.cleanTableWidths);
20565 domToHTML : function(currentElement, depth, nopadtext) {
20567 depth = depth || 0;
20568 nopadtext = nopadtext || false;
20570 if (!currentElement) {
20571 return this.domToHTML(this.doc.body);
20574 //Roo.log(currentElement);
20576 var allText = false;
20577 var nodeName = currentElement.nodeName;
20578 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20580 if (nodeName == '#text') {
20582 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20587 if (nodeName != 'BODY') {
20590 // Prints the node tagName, such as <A>, <IMG>, etc
20593 for(i = 0; i < currentElement.attributes.length;i++) {
20595 var aname = currentElement.attributes.item(i).name;
20596 if (!currentElement.attributes.item(i).value.length) {
20599 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20602 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20611 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20614 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20619 // Traverse the tree
20621 var currentElementChild = currentElement.childNodes.item(i);
20622 var allText = true;
20623 var innerHTML = '';
20625 while (currentElementChild) {
20626 // Formatting code (indent the tree so it looks nice on the screen)
20627 var nopad = nopadtext;
20628 if (lastnode == 'SPAN') {
20632 if (currentElementChild.nodeName == '#text') {
20633 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20634 toadd = nopadtext ? toadd : toadd.trim();
20635 if (!nopad && toadd.length > 80) {
20636 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20638 innerHTML += toadd;
20641 currentElementChild = currentElement.childNodes.item(i);
20647 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20649 // Recursively traverse the tree structure of the child node
20650 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20651 lastnode = currentElementChild.nodeName;
20653 currentElementChild=currentElement.childNodes.item(i);
20659 // The remaining code is mostly for formatting the tree
20660 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20665 ret+= "</"+tagName+">";
20671 applyBlacklists : function()
20673 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20674 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20678 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20679 if (b.indexOf(tag) > -1) {
20682 this.white.push(tag);
20686 Roo.each(w, function(tag) {
20687 if (b.indexOf(tag) > -1) {
20690 if (this.white.indexOf(tag) > -1) {
20693 this.white.push(tag);
20698 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20699 if (w.indexOf(tag) > -1) {
20702 this.black.push(tag);
20706 Roo.each(b, function(tag) {
20707 if (w.indexOf(tag) > -1) {
20710 if (this.black.indexOf(tag) > -1) {
20713 this.black.push(tag);
20718 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20719 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20723 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20724 if (b.indexOf(tag) > -1) {
20727 this.cwhite.push(tag);
20731 Roo.each(w, function(tag) {
20732 if (b.indexOf(tag) > -1) {
20735 if (this.cwhite.indexOf(tag) > -1) {
20738 this.cwhite.push(tag);
20743 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20744 if (w.indexOf(tag) > -1) {
20747 this.cblack.push(tag);
20751 Roo.each(b, function(tag) {
20752 if (w.indexOf(tag) > -1) {
20755 if (this.cblack.indexOf(tag) > -1) {
20758 this.cblack.push(tag);
20763 setStylesheets : function(stylesheets)
20765 if(typeof(stylesheets) == 'string'){
20766 Roo.get(this.iframe.contentDocument.head).createChild({
20768 rel : 'stylesheet',
20777 Roo.each(stylesheets, function(s) {
20782 Roo.get(_this.iframe.contentDocument.head).createChild({
20784 rel : 'stylesheet',
20793 removeStylesheets : function()
20797 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20802 // hide stuff that is not compatible
20816 * @event specialkey
20820 * @cfg {String} fieldClass @hide
20823 * @cfg {String} focusClass @hide
20826 * @cfg {String} autoCreate @hide
20829 * @cfg {String} inputType @hide
20832 * @cfg {String} invalidClass @hide
20835 * @cfg {String} invalidText @hide
20838 * @cfg {String} msgFx @hide
20841 * @cfg {String} validateOnBlur @hide
20845 Roo.HtmlEditorCore.white = [
20846 'area', 'br', 'img', 'input', 'hr', 'wbr',
20848 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
20849 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
20850 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
20851 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
20852 'table', 'ul', 'xmp',
20854 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
20857 'dir', 'menu', 'ol', 'ul', 'dl',
20863 Roo.HtmlEditorCore.black = [
20864 // 'embed', 'object', // enable - backend responsiblity to clean thiese
20866 'base', 'basefont', 'bgsound', 'blink', 'body',
20867 'frame', 'frameset', 'head', 'html', 'ilayer',
20868 'iframe', 'layer', 'link', 'meta', 'object',
20869 'script', 'style' ,'title', 'xml' // clean later..
20871 Roo.HtmlEditorCore.clean = [
20872 'script', 'style', 'title', 'xml'
20874 Roo.HtmlEditorCore.remove = [
20879 Roo.HtmlEditorCore.ablack = [
20883 Roo.HtmlEditorCore.aclean = [
20884 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
20888 Roo.HtmlEditorCore.pwhite= [
20889 'http', 'https', 'mailto'
20892 // white listed style attributes.
20893 Roo.HtmlEditorCore.cwhite= [
20894 // 'text-align', /// default is to allow most things..
20900 // black listed style attributes.
20901 Roo.HtmlEditorCore.cblack= [
20902 // 'font-size' -- this can be set by the project
20906 Roo.HtmlEditorCore.swapCodes =[
20925 * @class Roo.bootstrap.HtmlEditor
20926 * @extends Roo.bootstrap.TextArea
20927 * Bootstrap HtmlEditor class
20930 * Create a new HtmlEditor
20931 * @param {Object} config The config object
20934 Roo.bootstrap.HtmlEditor = function(config){
20935 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20936 if (!this.toolbars) {
20937 this.toolbars = [];
20939 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20942 * @event initialize
20943 * Fires when the editor is fully initialized (including the iframe)
20944 * @param {HtmlEditor} this
20949 * Fires when the editor is first receives the focus. Any insertion must wait
20950 * until after this event.
20951 * @param {HtmlEditor} this
20955 * @event beforesync
20956 * Fires before the textarea is updated with content from the editor iframe. Return false
20957 * to cancel the sync.
20958 * @param {HtmlEditor} this
20959 * @param {String} html
20963 * @event beforepush
20964 * Fires before the iframe editor is updated with content from the textarea. Return false
20965 * to cancel the push.
20966 * @param {HtmlEditor} this
20967 * @param {String} html
20972 * Fires when the textarea is updated with content from the editor iframe.
20973 * @param {HtmlEditor} this
20974 * @param {String} html
20979 * Fires when the iframe editor is updated with content from the textarea.
20980 * @param {HtmlEditor} this
20981 * @param {String} html
20985 * @event editmodechange
20986 * Fires when the editor switches edit modes
20987 * @param {HtmlEditor} this
20988 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20990 editmodechange: true,
20992 * @event editorevent
20993 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20994 * @param {HtmlEditor} this
20998 * @event firstfocus
20999 * Fires when on first focus - needed by toolbars..
21000 * @param {HtmlEditor} this
21005 * Auto save the htmlEditor value as a file into Events
21006 * @param {HtmlEditor} this
21010 * @event savedpreview
21011 * preview the saved version of htmlEditor
21012 * @param {HtmlEditor} this
21019 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
21023 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21028 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21033 * @cfg {Number} height (in pixels)
21037 * @cfg {Number} width (in pixels)
21042 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21045 stylesheets: false,
21050 // private properties
21051 validationEvent : false,
21053 initialized : false,
21056 onFocus : Roo.emptyFn,
21058 hideMode:'offsets',
21061 tbContainer : false,
21063 toolbarContainer :function() {
21064 return this.wrap.select('.x-html-editor-tb',true).first();
21068 * Protected method that will not generally be called directly. It
21069 * is called when the editor creates its toolbar. Override this method if you need to
21070 * add custom toolbar buttons.
21071 * @param {HtmlEditor} editor
21073 createToolbar : function(){
21075 Roo.log("create toolbars");
21077 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21078 this.toolbars[0].render(this.toolbarContainer());
21082 // if (!editor.toolbars || !editor.toolbars.length) {
21083 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21086 // for (var i =0 ; i < editor.toolbars.length;i++) {
21087 // editor.toolbars[i] = Roo.factory(
21088 // typeof(editor.toolbars[i]) == 'string' ?
21089 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
21090 // Roo.bootstrap.HtmlEditor);
21091 // editor.toolbars[i].init(editor);
21097 onRender : function(ct, position)
21099 // Roo.log("Call onRender: " + this.xtype);
21101 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21103 this.wrap = this.inputEl().wrap({
21104 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21107 this.editorcore.onRender(ct, position);
21109 if (this.resizable) {
21110 this.resizeEl = new Roo.Resizable(this.wrap, {
21114 minHeight : this.height,
21115 height: this.height,
21116 handles : this.resizable,
21119 resize : function(r, w, h) {
21120 _t.onResize(w,h); // -something
21126 this.createToolbar(this);
21129 if(!this.width && this.resizable){
21130 this.setSize(this.wrap.getSize());
21132 if (this.resizeEl) {
21133 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21134 // should trigger onReize..
21140 onResize : function(w, h)
21142 Roo.log('resize: ' +w + ',' + h );
21143 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21147 if(this.inputEl() ){
21148 if(typeof w == 'number'){
21149 var aw = w - this.wrap.getFrameWidth('lr');
21150 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21153 if(typeof h == 'number'){
21154 var tbh = -11; // fixme it needs to tool bar size!
21155 for (var i =0; i < this.toolbars.length;i++) {
21156 // fixme - ask toolbars for heights?
21157 tbh += this.toolbars[i].el.getHeight();
21158 //if (this.toolbars[i].footer) {
21159 // tbh += this.toolbars[i].footer.el.getHeight();
21167 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21168 ah -= 5; // knock a few pixes off for look..
21169 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21173 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21174 this.editorcore.onResize(ew,eh);
21179 * Toggles the editor between standard and source edit mode.
21180 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21182 toggleSourceEdit : function(sourceEditMode)
21184 this.editorcore.toggleSourceEdit(sourceEditMode);
21186 if(this.editorcore.sourceEditMode){
21187 Roo.log('editor - showing textarea');
21190 // Roo.log(this.syncValue());
21192 this.inputEl().removeClass(['hide', 'x-hidden']);
21193 this.inputEl().dom.removeAttribute('tabIndex');
21194 this.inputEl().focus();
21196 Roo.log('editor - hiding textarea');
21198 // Roo.log(this.pushValue());
21201 this.inputEl().addClass(['hide', 'x-hidden']);
21202 this.inputEl().dom.setAttribute('tabIndex', -1);
21203 //this.deferFocus();
21206 if(this.resizable){
21207 this.setSize(this.wrap.getSize());
21210 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21213 // private (for BoxComponent)
21214 adjustSize : Roo.BoxComponent.prototype.adjustSize,
21216 // private (for BoxComponent)
21217 getResizeEl : function(){
21221 // private (for BoxComponent)
21222 getPositionEl : function(){
21227 initEvents : function(){
21228 this.originalValue = this.getValue();
21232 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21235 // markInvalid : Roo.emptyFn,
21237 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21240 // clearInvalid : Roo.emptyFn,
21242 setValue : function(v){
21243 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21244 this.editorcore.pushValue();
21249 deferFocus : function(){
21250 this.focus.defer(10, this);
21254 focus : function(){
21255 this.editorcore.focus();
21261 onDestroy : function(){
21267 for (var i =0; i < this.toolbars.length;i++) {
21268 // fixme - ask toolbars for heights?
21269 this.toolbars[i].onDestroy();
21272 this.wrap.dom.innerHTML = '';
21273 this.wrap.remove();
21278 onFirstFocus : function(){
21279 //Roo.log("onFirstFocus");
21280 this.editorcore.onFirstFocus();
21281 for (var i =0; i < this.toolbars.length;i++) {
21282 this.toolbars[i].onFirstFocus();
21288 syncValue : function()
21290 this.editorcore.syncValue();
21293 pushValue : function()
21295 this.editorcore.pushValue();
21299 // hide stuff that is not compatible
21313 * @event specialkey
21317 * @cfg {String} fieldClass @hide
21320 * @cfg {String} focusClass @hide
21323 * @cfg {String} autoCreate @hide
21326 * @cfg {String} inputType @hide
21329 * @cfg {String} invalidClass @hide
21332 * @cfg {String} invalidText @hide
21335 * @cfg {String} msgFx @hide
21338 * @cfg {String} validateOnBlur @hide
21347 Roo.namespace('Roo.bootstrap.htmleditor');
21349 * @class Roo.bootstrap.HtmlEditorToolbar1
21354 new Roo.bootstrap.HtmlEditor({
21357 new Roo.bootstrap.HtmlEditorToolbar1({
21358 disable : { fonts: 1 , format: 1, ..., ... , ...],
21364 * @cfg {Object} disable List of elements to disable..
21365 * @cfg {Array} btns List of additional buttons.
21369 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21372 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21375 Roo.apply(this, config);
21377 // default disabled, based on 'good practice'..
21378 this.disable = this.disable || {};
21379 Roo.applyIf(this.disable, {
21382 specialElements : true
21384 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21386 this.editor = config.editor;
21387 this.editorcore = config.editor.editorcore;
21389 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21391 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21392 // dont call parent... till later.
21394 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
21399 editorcore : false,
21404 "h1","h2","h3","h4","h5","h6",
21406 "abbr", "acronym", "address", "cite", "samp", "var",
21410 onRender : function(ct, position)
21412 // Roo.log("Call onRender: " + this.xtype);
21414 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21416 this.el.dom.style.marginBottom = '0';
21418 var editorcore = this.editorcore;
21419 var editor= this.editor;
21422 var btn = function(id,cmd , toggle, handler){
21424 var event = toggle ? 'toggle' : 'click';
21429 xns: Roo.bootstrap,
21432 enableToggle:toggle !== false,
21434 pressed : toggle ? false : null,
21437 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21438 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21447 xns: Roo.bootstrap,
21448 glyphicon : 'font',
21452 xns: Roo.bootstrap,
21456 Roo.each(this.formats, function(f) {
21457 style.menu.items.push({
21459 xns: Roo.bootstrap,
21460 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21465 editorcore.insertTag(this.tagname);
21472 children.push(style);
21475 btn('bold',false,true);
21476 btn('italic',false,true);
21477 btn('align-left', 'justifyleft',true);
21478 btn('align-center', 'justifycenter',true);
21479 btn('align-right' , 'justifyright',true);
21480 btn('link', false, false, function(btn) {
21481 //Roo.log("create link?");
21482 var url = prompt(this.createLinkText, this.defaultLinkValue);
21483 if(url && url != 'http:/'+'/'){
21484 this.editorcore.relayCmd('createlink', url);
21487 btn('list','insertunorderedlist',true);
21488 btn('pencil', false,true, function(btn){
21491 this.toggleSourceEdit(btn.pressed);
21497 xns: Roo.bootstrap,
21502 xns: Roo.bootstrap,
21507 cog.menu.items.push({
21509 xns: Roo.bootstrap,
21510 html : Clean styles,
21515 editorcore.insertTag(this.tagname);
21524 this.xtype = 'NavSimplebar';
21526 for(var i=0;i< children.length;i++) {
21528 this.buttons.add(this.addxtypeChild(children[i]));
21532 editor.on('editorevent', this.updateToolbar, this);
21534 onBtnClick : function(id)
21536 this.editorcore.relayCmd(id);
21537 this.editorcore.focus();
21541 * Protected method that will not generally be called directly. It triggers
21542 * a toolbar update by reading the markup state of the current selection in the editor.
21544 updateToolbar: function(){
21546 if(!this.editorcore.activated){
21547 this.editor.onFirstFocus(); // is this neeed?
21551 var btns = this.buttons;
21552 var doc = this.editorcore.doc;
21553 btns.get('bold').setActive(doc.queryCommandState('bold'));
21554 btns.get('italic').setActive(doc.queryCommandState('italic'));
21555 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21557 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21558 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21559 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21561 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21562 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21565 var ans = this.editorcore.getAllAncestors();
21566 if (this.formatCombo) {
21569 var store = this.formatCombo.store;
21570 this.formatCombo.setValue("");
21571 for (var i =0; i < ans.length;i++) {
21572 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21574 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21582 // hides menus... - so this cant be on a menu...
21583 Roo.bootstrap.MenuMgr.hideAll();
21585 Roo.bootstrap.MenuMgr.hideAll();
21586 //this.editorsyncValue();
21588 onFirstFocus: function() {
21589 this.buttons.each(function(item){
21593 toggleSourceEdit : function(sourceEditMode){
21596 if(sourceEditMode){
21597 Roo.log("disabling buttons");
21598 this.buttons.each( function(item){
21599 if(item.cmd != 'pencil'){
21605 Roo.log("enabling buttons");
21606 if(this.editorcore.initialized){
21607 this.buttons.each( function(item){
21613 Roo.log("calling toggole on editor");
21614 // tell the editor that it's been pressed..
21615 this.editor.toggleSourceEdit(sourceEditMode);
21625 * @class Roo.bootstrap.Table.AbstractSelectionModel
21626 * @extends Roo.util.Observable
21627 * Abstract base class for grid SelectionModels. It provides the interface that should be
21628 * implemented by descendant classes. This class should not be directly instantiated.
21631 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21632 this.locked = false;
21633 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21637 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21638 /** @ignore Called by the grid automatically. Do not call directly. */
21639 init : function(grid){
21645 * Locks the selections.
21648 this.locked = true;
21652 * Unlocks the selections.
21654 unlock : function(){
21655 this.locked = false;
21659 * Returns true if the selections are locked.
21660 * @return {Boolean}
21662 isLocked : function(){
21663 return this.locked;
21667 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21668 * @class Roo.bootstrap.Table.RowSelectionModel
21669 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21670 * It supports multiple selections and keyboard selection/navigation.
21672 * @param {Object} config
21675 Roo.bootstrap.Table.RowSelectionModel = function(config){
21676 Roo.apply(this, config);
21677 this.selections = new Roo.util.MixedCollection(false, function(o){
21682 this.lastActive = false;
21686 * @event selectionchange
21687 * Fires when the selection changes
21688 * @param {SelectionModel} this
21690 "selectionchange" : true,
21692 * @event afterselectionchange
21693 * Fires after the selection changes (eg. by key press or clicking)
21694 * @param {SelectionModel} this
21696 "afterselectionchange" : true,
21698 * @event beforerowselect
21699 * Fires when a row is selected being selected, return false to cancel.
21700 * @param {SelectionModel} this
21701 * @param {Number} rowIndex The selected index
21702 * @param {Boolean} keepExisting False if other selections will be cleared
21704 "beforerowselect" : true,
21707 * Fires when a row is selected.
21708 * @param {SelectionModel} this
21709 * @param {Number} rowIndex The selected index
21710 * @param {Roo.data.Record} r The record
21712 "rowselect" : true,
21714 * @event rowdeselect
21715 * Fires when a row is deselected.
21716 * @param {SelectionModel} this
21717 * @param {Number} rowIndex The selected index
21719 "rowdeselect" : true
21721 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21722 this.locked = false;
21725 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21727 * @cfg {Boolean} singleSelect
21728 * True to allow selection of only one row at a time (defaults to false)
21730 singleSelect : false,
21733 initEvents : function(){
21735 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21736 this.grid.on("mousedown", this.handleMouseDown, this);
21737 }else{ // allow click to work like normal
21738 this.grid.on("rowclick", this.handleDragableRowClick, this);
21741 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21742 "up" : function(e){
21744 this.selectPrevious(e.shiftKey);
21745 }else if(this.last !== false && this.lastActive !== false){
21746 var last = this.last;
21747 this.selectRange(this.last, this.lastActive-1);
21748 this.grid.getView().focusRow(this.lastActive);
21749 if(last !== false){
21753 this.selectFirstRow();
21755 this.fireEvent("afterselectionchange", this);
21757 "down" : function(e){
21759 this.selectNext(e.shiftKey);
21760 }else if(this.last !== false && this.lastActive !== false){
21761 var last = this.last;
21762 this.selectRange(this.last, this.lastActive+1);
21763 this.grid.getView().focusRow(this.lastActive);
21764 if(last !== false){
21768 this.selectFirstRow();
21770 this.fireEvent("afterselectionchange", this);
21775 var view = this.grid.view;
21776 view.on("refresh", this.onRefresh, this);
21777 view.on("rowupdated", this.onRowUpdated, this);
21778 view.on("rowremoved", this.onRemove, this);
21782 onRefresh : function(){
21783 var ds = this.grid.dataSource, i, v = this.grid.view;
21784 var s = this.selections;
21785 s.each(function(r){
21786 if((i = ds.indexOfId(r.id)) != -1){
21795 onRemove : function(v, index, r){
21796 this.selections.remove(r);
21800 onRowUpdated : function(v, index, r){
21801 if(this.isSelected(r)){
21802 v.onRowSelect(index);
21808 * @param {Array} records The records to select
21809 * @param {Boolean} keepExisting (optional) True to keep existing selections
21811 selectRecords : function(records, keepExisting){
21813 this.clearSelections();
21815 var ds = this.grid.dataSource;
21816 for(var i = 0, len = records.length; i < len; i++){
21817 this.selectRow(ds.indexOf(records[i]), true);
21822 * Gets the number of selected rows.
21825 getCount : function(){
21826 return this.selections.length;
21830 * Selects the first row in the grid.
21832 selectFirstRow : function(){
21837 * Select the last row.
21838 * @param {Boolean} keepExisting (optional) True to keep existing selections
21840 selectLastRow : function(keepExisting){
21841 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21845 * Selects the row immediately following the last selected row.
21846 * @param {Boolean} keepExisting (optional) True to keep existing selections
21848 selectNext : function(keepExisting){
21849 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21850 this.selectRow(this.last+1, keepExisting);
21851 this.grid.getView().focusRow(this.last);
21856 * Selects the row that precedes the last selected row.
21857 * @param {Boolean} keepExisting (optional) True to keep existing selections
21859 selectPrevious : function(keepExisting){
21861 this.selectRow(this.last-1, keepExisting);
21862 this.grid.getView().focusRow(this.last);
21867 * Returns the selected records
21868 * @return {Array} Array of selected records
21870 getSelections : function(){
21871 return [].concat(this.selections.items);
21875 * Returns the first selected record.
21878 getSelected : function(){
21879 return this.selections.itemAt(0);
21884 * Clears all selections.
21886 clearSelections : function(fast){
21891 var ds = this.grid.dataSource;
21892 var s = this.selections;
21893 s.each(function(r){
21894 this.deselectRow(ds.indexOfId(r.id));
21898 this.selections.clear();
21905 * Selects all rows.
21907 selectAll : function(){
21911 this.selections.clear();
21912 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21913 this.selectRow(i, true);
21918 * Returns True if there is a selection.
21919 * @return {Boolean}
21921 hasSelection : function(){
21922 return this.selections.length > 0;
21926 * Returns True if the specified row is selected.
21927 * @param {Number/Record} record The record or index of the record to check
21928 * @return {Boolean}
21930 isSelected : function(index){
21931 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21932 return (r && this.selections.key(r.id) ? true : false);
21936 * Returns True if the specified record id is selected.
21937 * @param {String} id The id of record to check
21938 * @return {Boolean}
21940 isIdSelected : function(id){
21941 return (this.selections.key(id) ? true : false);
21945 handleMouseDown : function(e, t){
21946 var view = this.grid.getView(), rowIndex;
21947 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21950 if(e.shiftKey && this.last !== false){
21951 var last = this.last;
21952 this.selectRange(last, rowIndex, e.ctrlKey);
21953 this.last = last; // reset the last
21954 view.focusRow(rowIndex);
21956 var isSelected = this.isSelected(rowIndex);
21957 if(e.button !== 0 && isSelected){
21958 view.focusRow(rowIndex);
21959 }else if(e.ctrlKey && isSelected){
21960 this.deselectRow(rowIndex);
21961 }else if(!isSelected){
21962 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21963 view.focusRow(rowIndex);
21966 this.fireEvent("afterselectionchange", this);
21969 handleDragableRowClick : function(grid, rowIndex, e)
21971 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21972 this.selectRow(rowIndex, false);
21973 grid.view.focusRow(rowIndex);
21974 this.fireEvent("afterselectionchange", this);
21979 * Selects multiple rows.
21980 * @param {Array} rows Array of the indexes of the row to select
21981 * @param {Boolean} keepExisting (optional) True to keep existing selections
21983 selectRows : function(rows, keepExisting){
21985 this.clearSelections();
21987 for(var i = 0, len = rows.length; i < len; i++){
21988 this.selectRow(rows[i], true);
21993 * Selects a range of rows. All rows in between startRow and endRow are also selected.
21994 * @param {Number} startRow The index of the first row in the range
21995 * @param {Number} endRow The index of the last row in the range
21996 * @param {Boolean} keepExisting (optional) True to retain existing selections
21998 selectRange : function(startRow, endRow, keepExisting){
22003 this.clearSelections();
22005 if(startRow <= endRow){
22006 for(var i = startRow; i <= endRow; i++){
22007 this.selectRow(i, true);
22010 for(var i = startRow; i >= endRow; i--){
22011 this.selectRow(i, true);
22017 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22018 * @param {Number} startRow The index of the first row in the range
22019 * @param {Number} endRow The index of the last row in the range
22021 deselectRange : function(startRow, endRow, preventViewNotify){
22025 for(var i = startRow; i <= endRow; i++){
22026 this.deselectRow(i, preventViewNotify);
22032 * @param {Number} row The index of the row to select
22033 * @param {Boolean} keepExisting (optional) True to keep existing selections
22035 selectRow : function(index, keepExisting, preventViewNotify){
22036 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22039 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22040 if(!keepExisting || this.singleSelect){
22041 this.clearSelections();
22043 var r = this.grid.dataSource.getAt(index);
22044 this.selections.add(r);
22045 this.last = this.lastActive = index;
22046 if(!preventViewNotify){
22047 this.grid.getView().onRowSelect(index);
22049 this.fireEvent("rowselect", this, index, r);
22050 this.fireEvent("selectionchange", this);
22056 * @param {Number} row The index of the row to deselect
22058 deselectRow : function(index, preventViewNotify){
22062 if(this.last == index){
22065 if(this.lastActive == index){
22066 this.lastActive = false;
22068 var r = this.grid.dataSource.getAt(index);
22069 this.selections.remove(r);
22070 if(!preventViewNotify){
22071 this.grid.getView().onRowDeselect(index);
22073 this.fireEvent("rowdeselect", this, index);
22074 this.fireEvent("selectionchange", this);
22078 restoreLast : function(){
22080 this.last = this._last;
22085 acceptsNav : function(row, col, cm){
22086 return !cm.isHidden(col) && cm.isCellEditable(col, row);
22090 onEditorKey : function(field, e){
22091 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22096 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22098 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22100 }else if(k == e.ENTER && !e.ctrlKey){
22104 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22106 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22108 }else if(k == e.ESC){
22112 g.startEditing(newCell[0], newCell[1]);
22117 * Ext JS Library 1.1.1
22118 * Copyright(c) 2006-2007, Ext JS, LLC.
22120 * Originally Released Under LGPL - original licence link has changed is not relivant.
22123 * <script type="text/javascript">
22127 * @class Roo.bootstrap.PagingToolbar
22128 * @extends Roo.bootstrap.NavSimplebar
22129 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22131 * Create a new PagingToolbar
22132 * @param {Object} config The config object
22133 * @param {Roo.data.Store} store
22135 Roo.bootstrap.PagingToolbar = function(config)
22137 // old args format still supported... - xtype is prefered..
22138 // created from xtype...
22140 this.ds = config.dataSource;
22142 if (config.store && !this.ds) {
22143 this.store= Roo.factory(config.store, Roo.data);
22144 this.ds = this.store;
22145 this.ds.xmodule = this.xmodule || false;
22148 this.toolbarItems = [];
22149 if (config.items) {
22150 this.toolbarItems = config.items;
22153 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22158 this.bind(this.ds);
22161 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22165 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22167 * @cfg {Roo.data.Store} dataSource
22168 * The underlying data store providing the paged data
22171 * @cfg {String/HTMLElement/Element} container
22172 * container The id or element that will contain the toolbar
22175 * @cfg {Boolean} displayInfo
22176 * True to display the displayMsg (defaults to false)
22179 * @cfg {Number} pageSize
22180 * The number of records to display per page (defaults to 20)
22184 * @cfg {String} displayMsg
22185 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22187 displayMsg : 'Displaying {0} - {1} of {2}',
22189 * @cfg {String} emptyMsg
22190 * The message to display when no records are found (defaults to "No data to display")
22192 emptyMsg : 'No data to display',
22194 * Customizable piece of the default paging text (defaults to "Page")
22197 beforePageText : "Page",
22199 * Customizable piece of the default paging text (defaults to "of %0")
22202 afterPageText : "of {0}",
22204 * Customizable piece of the default paging text (defaults to "First Page")
22207 firstText : "First Page",
22209 * Customizable piece of the default paging text (defaults to "Previous Page")
22212 prevText : "Previous Page",
22214 * Customizable piece of the default paging text (defaults to "Next Page")
22217 nextText : "Next Page",
22219 * Customizable piece of the default paging text (defaults to "Last Page")
22222 lastText : "Last Page",
22224 * Customizable piece of the default paging text (defaults to "Refresh")
22227 refreshText : "Refresh",
22231 onRender : function(ct, position)
22233 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22234 this.navgroup.parentId = this.id;
22235 this.navgroup.onRender(this.el, null);
22236 // add the buttons to the navgroup
22238 if(this.displayInfo){
22239 Roo.log(this.el.select('ul.navbar-nav',true).first());
22240 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22241 this.displayEl = this.el.select('.x-paging-info', true).first();
22242 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22243 // this.displayEl = navel.el.select('span',true).first();
22249 Roo.each(_this.buttons, function(e){ // this might need to use render????
22250 Roo.factory(e).onRender(_this.el, null);
22254 Roo.each(_this.toolbarItems, function(e) {
22255 _this.navgroup.addItem(e);
22259 this.first = this.navgroup.addItem({
22260 tooltip: this.firstText,
22262 icon : 'fa fa-backward',
22264 preventDefault: true,
22265 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22268 this.prev = this.navgroup.addItem({
22269 tooltip: this.prevText,
22271 icon : 'fa fa-step-backward',
22273 preventDefault: true,
22274 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
22276 //this.addSeparator();
22279 var field = this.navgroup.addItem( {
22281 cls : 'x-paging-position',
22283 html : this.beforePageText +
22284 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22285 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
22288 this.field = field.el.select('input', true).first();
22289 this.field.on("keydown", this.onPagingKeydown, this);
22290 this.field.on("focus", function(){this.dom.select();});
22293 this.afterTextEl = field.el.select('.x-paging-after',true).first();
22294 //this.field.setHeight(18);
22295 //this.addSeparator();
22296 this.next = this.navgroup.addItem({
22297 tooltip: this.nextText,
22299 html : ' <i class="fa fa-step-forward">',
22301 preventDefault: true,
22302 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
22304 this.last = this.navgroup.addItem({
22305 tooltip: this.lastText,
22306 icon : 'fa fa-forward',
22309 preventDefault: true,
22310 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
22312 //this.addSeparator();
22313 this.loading = this.navgroup.addItem({
22314 tooltip: this.refreshText,
22315 icon: 'fa fa-refresh',
22316 preventDefault: true,
22317 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22323 updateInfo : function(){
22324 if(this.displayEl){
22325 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22326 var msg = count == 0 ?
22330 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
22332 this.displayEl.update(msg);
22337 onLoad : function(ds, r, o){
22338 this.cursor = o.params ? o.params.start : 0;
22339 var d = this.getPageData(),
22343 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22344 this.field.dom.value = ap;
22345 this.first.setDisabled(ap == 1);
22346 this.prev.setDisabled(ap == 1);
22347 this.next.setDisabled(ap == ps);
22348 this.last.setDisabled(ap == ps);
22349 this.loading.enable();
22354 getPageData : function(){
22355 var total = this.ds.getTotalCount();
22358 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22359 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22364 onLoadError : function(){
22365 this.loading.enable();
22369 onPagingKeydown : function(e){
22370 var k = e.getKey();
22371 var d = this.getPageData();
22373 var v = this.field.dom.value, pageNum;
22374 if(!v || isNaN(pageNum = parseInt(v, 10))){
22375 this.field.dom.value = d.activePage;
22378 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22379 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22382 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))
22384 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22385 this.field.dom.value = pageNum;
22386 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22389 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22391 var v = this.field.dom.value, pageNum;
22392 var increment = (e.shiftKey) ? 10 : 1;
22393 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22396 if(!v || isNaN(pageNum = parseInt(v, 10))) {
22397 this.field.dom.value = d.activePage;
22400 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22402 this.field.dom.value = parseInt(v, 10) + increment;
22403 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22404 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22411 beforeLoad : function(){
22413 this.loading.disable();
22418 onClick : function(which){
22427 ds.load({params:{start: 0, limit: this.pageSize}});
22430 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22433 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22436 var total = ds.getTotalCount();
22437 var extra = total % this.pageSize;
22438 var lastStart = extra ? (total - extra) : total-this.pageSize;
22439 ds.load({params:{start: lastStart, limit: this.pageSize}});
22442 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22448 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22449 * @param {Roo.data.Store} store The data store to unbind
22451 unbind : function(ds){
22452 ds.un("beforeload", this.beforeLoad, this);
22453 ds.un("load", this.onLoad, this);
22454 ds.un("loadexception", this.onLoadError, this);
22455 ds.un("remove", this.updateInfo, this);
22456 ds.un("add", this.updateInfo, this);
22457 this.ds = undefined;
22461 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22462 * @param {Roo.data.Store} store The data store to bind
22464 bind : function(ds){
22465 ds.on("beforeload", this.beforeLoad, this);
22466 ds.on("load", this.onLoad, this);
22467 ds.on("loadexception", this.onLoadError, this);
22468 ds.on("remove", this.updateInfo, this);
22469 ds.on("add", this.updateInfo, this);
22480 * @class Roo.bootstrap.MessageBar
22481 * @extends Roo.bootstrap.Component
22482 * Bootstrap MessageBar class
22483 * @cfg {String} html contents of the MessageBar
22484 * @cfg {String} weight (info | success | warning | danger) default info
22485 * @cfg {String} beforeClass insert the bar before the given class
22486 * @cfg {Boolean} closable (true | false) default false
22487 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22490 * Create a new Element
22491 * @param {Object} config The config object
22494 Roo.bootstrap.MessageBar = function(config){
22495 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22498 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22504 beforeClass: 'bootstrap-sticky-wrap',
22506 getAutoCreate : function(){
22510 cls: 'alert alert-dismissable alert-' + this.weight,
22515 html: this.html || ''
22521 cfg.cls += ' alert-messages-fixed';
22535 onRender : function(ct, position)
22537 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22540 var cfg = Roo.apply({}, this.getAutoCreate());
22544 cfg.cls += ' ' + this.cls;
22547 cfg.style = this.style;
22549 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22551 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22554 this.el.select('>button.close').on('click', this.hide, this);
22560 if (!this.rendered) {
22566 this.fireEvent('show', this);
22572 if (!this.rendered) {
22578 this.fireEvent('hide', this);
22581 update : function()
22583 // var e = this.el.dom.firstChild;
22585 // if(this.closable){
22586 // e = e.nextSibling;
22589 // e.data = this.html || '';
22591 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22607 * @class Roo.bootstrap.Graph
22608 * @extends Roo.bootstrap.Component
22609 * Bootstrap Graph class
22613 @cfg {String} graphtype bar | vbar | pie
22614 @cfg {number} g_x coodinator | centre x (pie)
22615 @cfg {number} g_y coodinator | centre y (pie)
22616 @cfg {number} g_r radius (pie)
22617 @cfg {number} g_height height of the chart (respected by all elements in the set)
22618 @cfg {number} g_width width of the chart (respected by all elements in the set)
22619 @cfg {Object} title The title of the chart
22622 -opts (object) options for the chart
22624 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22625 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22627 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.
22628 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22630 o stretch (boolean)
22632 -opts (object) options for the pie
22635 o startAngle (number)
22636 o endAngle (number)
22640 * Create a new Input
22641 * @param {Object} config The config object
22644 Roo.bootstrap.Graph = function(config){
22645 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22651 * The img click event for the img.
22652 * @param {Roo.EventObject} e
22658 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22669 //g_colors: this.colors,
22676 getAutoCreate : function(){
22687 onRender : function(ct,position){
22688 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22689 this.raphael = Raphael(this.el.dom);
22691 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22692 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22693 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22694 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22696 r.text(160, 10, "Single Series Chart").attr(txtattr);
22697 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22698 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22699 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22701 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22702 r.barchart(330, 10, 300, 220, data1);
22703 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22704 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22707 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22708 // r.barchart(30, 30, 560, 250, xdata, {
22709 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22710 // axis : "0 0 1 1",
22711 // axisxlabels : xdata
22712 // //yvalues : cols,
22715 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22717 // this.load(null,xdata,{
22718 // axis : "0 0 1 1",
22719 // axisxlabels : xdata
22724 load : function(graphtype,xdata,opts){
22725 this.raphael.clear();
22727 graphtype = this.graphtype;
22732 var r = this.raphael,
22733 fin = function () {
22734 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22736 fout = function () {
22737 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22739 pfin = function() {
22740 this.sector.stop();
22741 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22744 this.label[0].stop();
22745 this.label[0].attr({ r: 7.5 });
22746 this.label[1].attr({ "font-weight": 800 });
22749 pfout = function() {
22750 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22753 this.label[0].animate({ r: 5 }, 500, "bounce");
22754 this.label[1].attr({ "font-weight": 400 });
22760 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22763 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22766 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
22767 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22769 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22776 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22781 setTitle: function(o)
22786 initEvents: function() {
22789 this.el.on('click', this.onClick, this);
22793 onClick : function(e)
22795 Roo.log('img onclick');
22796 this.fireEvent('click', this, e);
22808 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22811 * @class Roo.bootstrap.dash.NumberBox
22812 * @extends Roo.bootstrap.Component
22813 * Bootstrap NumberBox class
22814 * @cfg {String} headline Box headline
22815 * @cfg {String} content Box content
22816 * @cfg {String} icon Box icon
22817 * @cfg {String} footer Footer text
22818 * @cfg {String} fhref Footer href
22821 * Create a new NumberBox
22822 * @param {Object} config The config object
22826 Roo.bootstrap.dash.NumberBox = function(config){
22827 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22831 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
22840 getAutoCreate : function(){
22844 cls : 'small-box ',
22852 cls : 'roo-headline',
22853 html : this.headline
22857 cls : 'roo-content',
22858 html : this.content
22872 cls : 'ion ' + this.icon
22881 cls : 'small-box-footer',
22882 href : this.fhref || '#',
22886 cfg.cn.push(footer);
22893 onRender : function(ct,position){
22894 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22901 setHeadline: function (value)
22903 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22906 setFooter: function (value, href)
22908 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22911 this.el.select('a.small-box-footer',true).first().attr('href', href);
22916 setContent: function (value)
22918 this.el.select('.roo-content',true).first().dom.innerHTML = value;
22921 initEvents: function()
22935 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22938 * @class Roo.bootstrap.dash.TabBox
22939 * @extends Roo.bootstrap.Component
22940 * Bootstrap TabBox class
22941 * @cfg {String} title Title of the TabBox
22942 * @cfg {String} icon Icon of the TabBox
22943 * @cfg {Boolean} showtabs (true|false) show the tabs default true
22944 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22947 * Create a new TabBox
22948 * @param {Object} config The config object
22952 Roo.bootstrap.dash.TabBox = function(config){
22953 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22958 * When a pane is added
22959 * @param {Roo.bootstrap.dash.TabPane} pane
22963 * @event activatepane
22964 * When a pane is activated
22965 * @param {Roo.bootstrap.dash.TabPane} pane
22967 "activatepane" : true
22975 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
22980 tabScrollable : false,
22982 getChildContainer : function()
22984 return this.el.select('.tab-content', true).first();
22987 getAutoCreate : function(){
22991 cls: 'pull-left header',
22999 cls: 'fa ' + this.icon
23005 cls: 'nav nav-tabs pull-right',
23011 if(this.tabScrollable){
23018 cls: 'nav nav-tabs pull-right',
23029 cls: 'nav-tabs-custom',
23034 cls: 'tab-content no-padding',
23042 initEvents : function()
23044 //Roo.log('add add pane handler');
23045 this.on('addpane', this.onAddPane, this);
23048 * Updates the box title
23049 * @param {String} html to set the title to.
23051 setTitle : function(value)
23053 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23055 onAddPane : function(pane)
23057 this.panes.push(pane);
23058 //Roo.log('addpane');
23060 // tabs are rendere left to right..
23061 if(!this.showtabs){
23065 var ctr = this.el.select('.nav-tabs', true).first();
23068 var existing = ctr.select('.nav-tab',true);
23069 var qty = existing.getCount();;
23072 var tab = ctr.createChild({
23074 cls : 'nav-tab' + (qty ? '' : ' active'),
23082 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23085 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23087 pane.el.addClass('active');
23092 onTabClick : function(ev,un,ob,pane)
23094 //Roo.log('tab - prev default');
23095 ev.preventDefault();
23098 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23099 pane.tab.addClass('active');
23100 //Roo.log(pane.title);
23101 this.getChildContainer().select('.tab-pane',true).removeClass('active');
23102 // technically we should have a deactivate event.. but maybe add later.
23103 // and it should not de-activate the selected tab...
23104 this.fireEvent('activatepane', pane);
23105 pane.el.addClass('active');
23106 pane.fireEvent('activate');
23111 getActivePane : function()
23114 Roo.each(this.panes, function(p) {
23115 if(p.el.hasClass('active')){
23136 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23138 * @class Roo.bootstrap.TabPane
23139 * @extends Roo.bootstrap.Component
23140 * Bootstrap TabPane class
23141 * @cfg {Boolean} active (false | true) Default false
23142 * @cfg {String} title title of panel
23146 * Create a new TabPane
23147 * @param {Object} config The config object
23150 Roo.bootstrap.dash.TabPane = function(config){
23151 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23157 * When a pane is activated
23158 * @param {Roo.bootstrap.dash.TabPane} pane
23165 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
23170 // the tabBox that this is attached to.
23173 getAutoCreate : function()
23181 cfg.cls += ' active';
23186 initEvents : function()
23188 //Roo.log('trigger add pane handler');
23189 this.parent().fireEvent('addpane', this)
23193 * Updates the tab title
23194 * @param {String} html to set the title to.
23196 setTitle: function(str)
23202 this.tab.select('a', true).first().dom.innerHTML = str;
23219 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23222 * @class Roo.bootstrap.menu.Menu
23223 * @extends Roo.bootstrap.Component
23224 * Bootstrap Menu class - container for Menu
23225 * @cfg {String} html Text of the menu
23226 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23227 * @cfg {String} icon Font awesome icon
23228 * @cfg {String} pos Menu align to (top | bottom) default bottom
23232 * Create a new Menu
23233 * @param {Object} config The config object
23237 Roo.bootstrap.menu.Menu = function(config){
23238 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23242 * @event beforeshow
23243 * Fires before this menu is displayed
23244 * @param {Roo.bootstrap.menu.Menu} this
23248 * @event beforehide
23249 * Fires before this menu is hidden
23250 * @param {Roo.bootstrap.menu.Menu} this
23255 * Fires after this menu is displayed
23256 * @param {Roo.bootstrap.menu.Menu} this
23261 * Fires after this menu is hidden
23262 * @param {Roo.bootstrap.menu.Menu} this
23267 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23268 * @param {Roo.bootstrap.menu.Menu} this
23269 * @param {Roo.EventObject} e
23276 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
23280 weight : 'default',
23285 getChildContainer : function() {
23286 if(this.isSubMenu){
23290 return this.el.select('ul.dropdown-menu', true).first();
23293 getAutoCreate : function()
23298 cls : 'roo-menu-text',
23306 cls : 'fa ' + this.icon
23317 cls : 'dropdown-button btn btn-' + this.weight,
23322 cls : 'dropdown-toggle btn btn-' + this.weight,
23332 cls : 'dropdown-menu'
23338 if(this.pos == 'top'){
23339 cfg.cls += ' dropup';
23342 if(this.isSubMenu){
23345 cls : 'dropdown-menu'
23352 onRender : function(ct, position)
23354 this.isSubMenu = ct.hasClass('dropdown-submenu');
23356 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23359 initEvents : function()
23361 if(this.isSubMenu){
23365 this.hidden = true;
23367 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23368 this.triggerEl.on('click', this.onTriggerPress, this);
23370 this.buttonEl = this.el.select('button.dropdown-button', true).first();
23371 this.buttonEl.on('click', this.onClick, this);
23377 if(this.isSubMenu){
23381 return this.el.select('ul.dropdown-menu', true).first();
23384 onClick : function(e)
23386 this.fireEvent("click", this, e);
23389 onTriggerPress : function(e)
23391 if (this.isVisible()) {
23398 isVisible : function(){
23399 return !this.hidden;
23404 this.fireEvent("beforeshow", this);
23406 this.hidden = false;
23407 this.el.addClass('open');
23409 Roo.get(document).on("mouseup", this.onMouseUp, this);
23411 this.fireEvent("show", this);
23418 this.fireEvent("beforehide", this);
23420 this.hidden = true;
23421 this.el.removeClass('open');
23423 Roo.get(document).un("mouseup", this.onMouseUp);
23425 this.fireEvent("hide", this);
23428 onMouseUp : function()
23442 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23445 * @class Roo.bootstrap.menu.Item
23446 * @extends Roo.bootstrap.Component
23447 * Bootstrap MenuItem class
23448 * @cfg {Boolean} submenu (true | false) default false
23449 * @cfg {String} html text of the item
23450 * @cfg {String} href the link
23451 * @cfg {Boolean} disable (true | false) default false
23452 * @cfg {Boolean} preventDefault (true | false) default true
23453 * @cfg {String} icon Font awesome icon
23454 * @cfg {String} pos Submenu align to (left | right) default right
23458 * Create a new Item
23459 * @param {Object} config The config object
23463 Roo.bootstrap.menu.Item = function(config){
23464 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23468 * Fires when the mouse is hovering over this menu
23469 * @param {Roo.bootstrap.menu.Item} this
23470 * @param {Roo.EventObject} e
23475 * Fires when the mouse exits this menu
23476 * @param {Roo.bootstrap.menu.Item} this
23477 * @param {Roo.EventObject} e
23483 * The raw click event for the entire grid.
23484 * @param {Roo.EventObject} e
23490 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23495 preventDefault: true,
23500 getAutoCreate : function()
23505 cls : 'roo-menu-item-text',
23513 cls : 'fa ' + this.icon
23522 href : this.href || '#',
23529 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23533 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23535 if(this.pos == 'left'){
23536 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23543 initEvents : function()
23545 this.el.on('mouseover', this.onMouseOver, this);
23546 this.el.on('mouseout', this.onMouseOut, this);
23548 this.el.select('a', true).first().on('click', this.onClick, this);
23552 onClick : function(e)
23554 if(this.preventDefault){
23555 e.preventDefault();
23558 this.fireEvent("click", this, e);
23561 onMouseOver : function(e)
23563 if(this.submenu && this.pos == 'left'){
23564 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23567 this.fireEvent("mouseover", this, e);
23570 onMouseOut : function(e)
23572 this.fireEvent("mouseout", this, e);
23584 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23587 * @class Roo.bootstrap.menu.Separator
23588 * @extends Roo.bootstrap.Component
23589 * Bootstrap Separator class
23592 * Create a new Separator
23593 * @param {Object} config The config object
23597 Roo.bootstrap.menu.Separator = function(config){
23598 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23601 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23603 getAutoCreate : function(){
23624 * @class Roo.bootstrap.Tooltip
23625 * Bootstrap Tooltip class
23626 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23627 * to determine which dom element triggers the tooltip.
23629 * It needs to add support for additional attributes like tooltip-position
23632 * Create a new Toolti
23633 * @param {Object} config The config object
23636 Roo.bootstrap.Tooltip = function(config){
23637 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23640 Roo.apply(Roo.bootstrap.Tooltip, {
23642 * @function init initialize tooltip monitoring.
23646 currentTip : false,
23647 currentRegion : false,
23653 Roo.get(document).on('mouseover', this.enter ,this);
23654 Roo.get(document).on('mouseout', this.leave, this);
23657 this.currentTip = new Roo.bootstrap.Tooltip();
23660 enter : function(ev)
23662 var dom = ev.getTarget();
23664 //Roo.log(['enter',dom]);
23665 var el = Roo.fly(dom);
23666 if (this.currentEl) {
23668 //Roo.log(this.currentEl);
23669 //Roo.log(this.currentEl.contains(dom));
23670 if (this.currentEl == el) {
23673 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23679 if (this.currentTip.el) {
23680 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23685 // you can not look for children, as if el is the body.. then everythign is the child..
23686 if (!el.attr('tooltip')) { //
23687 if (!el.select("[tooltip]").elements.length) {
23690 // is the mouse over this child...?
23691 bindEl = el.select("[tooltip]").first();
23692 var xy = ev.getXY();
23693 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23694 //Roo.log("not in region.");
23697 //Roo.log("child element over..");
23700 this.currentEl = bindEl;
23701 this.currentTip.bind(bindEl);
23702 this.currentRegion = Roo.lib.Region.getRegion(dom);
23703 this.currentTip.enter();
23706 leave : function(ev)
23708 var dom = ev.getTarget();
23709 //Roo.log(['leave',dom]);
23710 if (!this.currentEl) {
23715 if (dom != this.currentEl.dom) {
23718 var xy = ev.getXY();
23719 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23722 // only activate leave if mouse cursor is outside... bounding box..
23727 if (this.currentTip) {
23728 this.currentTip.leave();
23730 //Roo.log('clear currentEl');
23731 this.currentEl = false;
23736 'left' : ['r-l', [-2,0], 'right'],
23737 'right' : ['l-r', [2,0], 'left'],
23738 'bottom' : ['t-b', [0,2], 'top'],
23739 'top' : [ 'b-t', [0,-2], 'bottom']
23745 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
23750 delay : null, // can be { show : 300 , hide: 500}
23754 hoverState : null, //???
23756 placement : 'bottom',
23758 getAutoCreate : function(){
23765 cls : 'tooltip-arrow'
23768 cls : 'tooltip-inner'
23775 bind : function(el)
23781 enter : function () {
23783 if (this.timeout != null) {
23784 clearTimeout(this.timeout);
23787 this.hoverState = 'in';
23788 //Roo.log("enter - show");
23789 if (!this.delay || !this.delay.show) {
23794 this.timeout = setTimeout(function () {
23795 if (_t.hoverState == 'in') {
23798 }, this.delay.show);
23802 clearTimeout(this.timeout);
23804 this.hoverState = 'out';
23805 if (!this.delay || !this.delay.hide) {
23811 this.timeout = setTimeout(function () {
23812 //Roo.log("leave - timeout");
23814 if (_t.hoverState == 'out') {
23816 Roo.bootstrap.Tooltip.currentEl = false;
23824 this.render(document.body);
23827 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23829 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23831 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23833 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23835 var placement = typeof this.placement == 'function' ?
23836 this.placement.call(this, this.el, on_el) :
23839 var autoToken = /\s?auto?\s?/i;
23840 var autoPlace = autoToken.test(placement);
23842 placement = placement.replace(autoToken, '') || 'top';
23846 //this.el.setXY([0,0]);
23848 //this.el.dom.style.display='block';
23850 //this.el.appendTo(on_el);
23852 var p = this.getPosition();
23853 var box = this.el.getBox();
23859 var align = Roo.bootstrap.Tooltip.alignment[placement];
23861 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23863 if(placement == 'top' || placement == 'bottom'){
23865 placement = 'right';
23868 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23869 placement = 'left';
23873 align = Roo.bootstrap.Tooltip.alignment[placement];
23875 this.el.alignTo(this.bindEl, align[0],align[1]);
23876 //var arrow = this.el.select('.arrow',true).first();
23877 //arrow.set(align[2],
23879 this.el.addClass(placement);
23881 this.el.addClass('in fade');
23883 this.hoverState = null;
23885 if (this.el.hasClass('fade')) {
23896 //this.el.setXY([0,0]);
23897 this.el.removeClass('in');
23913 * @class Roo.bootstrap.LocationPicker
23914 * @extends Roo.bootstrap.Component
23915 * Bootstrap LocationPicker class
23916 * @cfg {Number} latitude Position when init default 0
23917 * @cfg {Number} longitude Position when init default 0
23918 * @cfg {Number} zoom default 15
23919 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23920 * @cfg {Boolean} mapTypeControl default false
23921 * @cfg {Boolean} disableDoubleClickZoom default false
23922 * @cfg {Boolean} scrollwheel default true
23923 * @cfg {Boolean} streetViewControl default false
23924 * @cfg {Number} radius default 0
23925 * @cfg {String} locationName
23926 * @cfg {Boolean} draggable default true
23927 * @cfg {Boolean} enableAutocomplete default false
23928 * @cfg {Boolean} enableReverseGeocode default true
23929 * @cfg {String} markerTitle
23932 * Create a new LocationPicker
23933 * @param {Object} config The config object
23937 Roo.bootstrap.LocationPicker = function(config){
23939 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23944 * Fires when the picker initialized.
23945 * @param {Roo.bootstrap.LocationPicker} this
23946 * @param {Google Location} location
23950 * @event positionchanged
23951 * Fires when the picker position changed.
23952 * @param {Roo.bootstrap.LocationPicker} this
23953 * @param {Google Location} location
23955 positionchanged : true,
23958 * Fires when the map resize.
23959 * @param {Roo.bootstrap.LocationPicker} this
23964 * Fires when the map show.
23965 * @param {Roo.bootstrap.LocationPicker} this
23970 * Fires when the map hide.
23971 * @param {Roo.bootstrap.LocationPicker} this
23976 * Fires when click the map.
23977 * @param {Roo.bootstrap.LocationPicker} this
23978 * @param {Map event} e
23982 * @event mapRightClick
23983 * Fires when right click the map.
23984 * @param {Roo.bootstrap.LocationPicker} this
23985 * @param {Map event} e
23987 mapRightClick : true,
23989 * @event markerClick
23990 * Fires when click the marker.
23991 * @param {Roo.bootstrap.LocationPicker} this
23992 * @param {Map event} e
23994 markerClick : true,
23996 * @event markerRightClick
23997 * Fires when right click the marker.
23998 * @param {Roo.bootstrap.LocationPicker} this
23999 * @param {Map event} e
24001 markerRightClick : true,
24003 * @event OverlayViewDraw
24004 * Fires when OverlayView Draw
24005 * @param {Roo.bootstrap.LocationPicker} this
24007 OverlayViewDraw : true,
24009 * @event OverlayViewOnAdd
24010 * Fires when OverlayView Draw
24011 * @param {Roo.bootstrap.LocationPicker} this
24013 OverlayViewOnAdd : true,
24015 * @event OverlayViewOnRemove
24016 * Fires when OverlayView Draw
24017 * @param {Roo.bootstrap.LocationPicker} this
24019 OverlayViewOnRemove : true,
24021 * @event OverlayViewShow
24022 * Fires when OverlayView Draw
24023 * @param {Roo.bootstrap.LocationPicker} this
24024 * @param {Pixel} cpx
24026 OverlayViewShow : true,
24028 * @event OverlayViewHide
24029 * Fires when OverlayView Draw
24030 * @param {Roo.bootstrap.LocationPicker} this
24032 OverlayViewHide : true,
24034 * @event loadexception
24035 * Fires when load google lib failed.
24036 * @param {Roo.bootstrap.LocationPicker} this
24038 loadexception : true
24043 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
24045 gMapContext: false,
24051 mapTypeControl: false,
24052 disableDoubleClickZoom: false,
24054 streetViewControl: false,
24058 enableAutocomplete: false,
24059 enableReverseGeocode: true,
24062 getAutoCreate: function()
24067 cls: 'roo-location-picker'
24073 initEvents: function(ct, position)
24075 if(!this.el.getWidth() || this.isApplied()){
24079 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24084 initial: function()
24086 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24087 this.fireEvent('loadexception', this);
24091 if(!this.mapTypeId){
24092 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24095 this.gMapContext = this.GMapContext();
24097 this.initOverlayView();
24099 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24103 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24104 _this.setPosition(_this.gMapContext.marker.position);
24107 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24108 _this.fireEvent('mapClick', this, event);
24112 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24113 _this.fireEvent('mapRightClick', this, event);
24117 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24118 _this.fireEvent('markerClick', this, event);
24122 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24123 _this.fireEvent('markerRightClick', this, event);
24127 this.setPosition(this.gMapContext.location);
24129 this.fireEvent('initial', this, this.gMapContext.location);
24132 initOverlayView: function()
24136 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24140 _this.fireEvent('OverlayViewDraw', _this);
24145 _this.fireEvent('OverlayViewOnAdd', _this);
24148 onRemove: function()
24150 _this.fireEvent('OverlayViewOnRemove', _this);
24153 show: function(cpx)
24155 _this.fireEvent('OverlayViewShow', _this, cpx);
24160 _this.fireEvent('OverlayViewHide', _this);
24166 fromLatLngToContainerPixel: function(event)
24168 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24171 isApplied: function()
24173 return this.getGmapContext() == false ? false : true;
24176 getGmapContext: function()
24178 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24181 GMapContext: function()
24183 var position = new google.maps.LatLng(this.latitude, this.longitude);
24185 var _map = new google.maps.Map(this.el.dom, {
24188 mapTypeId: this.mapTypeId,
24189 mapTypeControl: this.mapTypeControl,
24190 disableDoubleClickZoom: this.disableDoubleClickZoom,
24191 scrollwheel: this.scrollwheel,
24192 streetViewControl: this.streetViewControl,
24193 locationName: this.locationName,
24194 draggable: this.draggable,
24195 enableAutocomplete: this.enableAutocomplete,
24196 enableReverseGeocode: this.enableReverseGeocode
24199 var _marker = new google.maps.Marker({
24200 position: position,
24202 title: this.markerTitle,
24203 draggable: this.draggable
24210 location: position,
24211 radius: this.radius,
24212 locationName: this.locationName,
24213 addressComponents: {
24214 formatted_address: null,
24215 addressLine1: null,
24216 addressLine2: null,
24218 streetNumber: null,
24222 stateOrProvince: null
24225 domContainer: this.el.dom,
24226 geodecoder: new google.maps.Geocoder()
24230 drawCircle: function(center, radius, options)
24232 if (this.gMapContext.circle != null) {
24233 this.gMapContext.circle.setMap(null);
24237 options = Roo.apply({}, options, {
24238 strokeColor: "#0000FF",
24239 strokeOpacity: .35,
24241 fillColor: "#0000FF",
24245 options.map = this.gMapContext.map;
24246 options.radius = radius;
24247 options.center = center;
24248 this.gMapContext.circle = new google.maps.Circle(options);
24249 return this.gMapContext.circle;
24255 setPosition: function(location)
24257 this.gMapContext.location = location;
24258 this.gMapContext.marker.setPosition(location);
24259 this.gMapContext.map.panTo(location);
24260 this.drawCircle(location, this.gMapContext.radius, {});
24264 if (this.gMapContext.settings.enableReverseGeocode) {
24265 this.gMapContext.geodecoder.geocode({
24266 latLng: this.gMapContext.location
24267 }, function(results, status) {
24269 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24270 _this.gMapContext.locationName = results[0].formatted_address;
24271 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24273 _this.fireEvent('positionchanged', this, location);
24280 this.fireEvent('positionchanged', this, location);
24285 google.maps.event.trigger(this.gMapContext.map, "resize");
24287 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24289 this.fireEvent('resize', this);
24292 setPositionByLatLng: function(latitude, longitude)
24294 this.setPosition(new google.maps.LatLng(latitude, longitude));
24297 getCurrentPosition: function()
24300 latitude: this.gMapContext.location.lat(),
24301 longitude: this.gMapContext.location.lng()
24305 getAddressName: function()
24307 return this.gMapContext.locationName;
24310 getAddressComponents: function()
24312 return this.gMapContext.addressComponents;
24315 address_component_from_google_geocode: function(address_components)
24319 for (var i = 0; i < address_components.length; i++) {
24320 var component = address_components[i];
24321 if (component.types.indexOf("postal_code") >= 0) {
24322 result.postalCode = component.short_name;
24323 } else if (component.types.indexOf("street_number") >= 0) {
24324 result.streetNumber = component.short_name;
24325 } else if (component.types.indexOf("route") >= 0) {
24326 result.streetName = component.short_name;
24327 } else if (component.types.indexOf("neighborhood") >= 0) {
24328 result.city = component.short_name;
24329 } else if (component.types.indexOf("locality") >= 0) {
24330 result.city = component.short_name;
24331 } else if (component.types.indexOf("sublocality") >= 0) {
24332 result.district = component.short_name;
24333 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24334 result.stateOrProvince = component.short_name;
24335 } else if (component.types.indexOf("country") >= 0) {
24336 result.country = component.short_name;
24340 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24341 result.addressLine2 = "";
24345 setZoomLevel: function(zoom)
24347 this.gMapContext.map.setZoom(zoom);
24360 this.fireEvent('show', this);
24371 this.fireEvent('hide', this);
24376 Roo.apply(Roo.bootstrap.LocationPicker, {
24378 OverlayView : function(map, options)
24380 options = options || {};
24394 * @class Roo.bootstrap.Alert
24395 * @extends Roo.bootstrap.Component
24396 * Bootstrap Alert class
24397 * @cfg {String} title The title of alert
24398 * @cfg {String} html The content of alert
24399 * @cfg {String} weight ( success | info | warning | danger )
24400 * @cfg {String} faicon font-awesomeicon
24403 * Create a new alert
24404 * @param {Object} config The config object
24408 Roo.bootstrap.Alert = function(config){
24409 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24413 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
24420 getAutoCreate : function()
24429 cls : 'roo-alert-icon'
24434 cls : 'roo-alert-title',
24439 cls : 'roo-alert-text',
24446 cfg.cn[0].cls += ' fa ' + this.faicon;
24450 cfg.cls += ' alert-' + this.weight;
24456 initEvents: function()
24458 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24461 setTitle : function(str)
24463 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24466 setText : function(str)
24468 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24471 setWeight : function(weight)
24474 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24477 this.weight = weight;
24479 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24482 setIcon : function(icon)
24485 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24488 this.faicon = icon;
24490 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24511 * @class Roo.bootstrap.UploadCropbox
24512 * @extends Roo.bootstrap.Component
24513 * Bootstrap UploadCropbox class
24514 * @cfg {String} emptyText show when image has been loaded
24515 * @cfg {String} rotateNotify show when image too small to rotate
24516 * @cfg {Number} errorTimeout default 3000
24517 * @cfg {Number} minWidth default 300
24518 * @cfg {Number} minHeight default 300
24519 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24520 * @cfg {Boolean} isDocument (true|false) default false
24521 * @cfg {String} url action url
24522 * @cfg {String} paramName default 'imageUpload'
24523 * @cfg {String} method default POST
24524 * @cfg {Boolean} loadMask (true|false) default true
24525 * @cfg {Boolean} loadingText default 'Loading...'
24528 * Create a new UploadCropbox
24529 * @param {Object} config The config object
24532 Roo.bootstrap.UploadCropbox = function(config){
24533 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24537 * @event beforeselectfile
24538 * Fire before select file
24539 * @param {Roo.bootstrap.UploadCropbox} this
24541 "beforeselectfile" : true,
24544 * Fire after initEvent
24545 * @param {Roo.bootstrap.UploadCropbox} this
24550 * Fire after initEvent
24551 * @param {Roo.bootstrap.UploadCropbox} this
24552 * @param {String} data
24557 * Fire when preparing the file data
24558 * @param {Roo.bootstrap.UploadCropbox} this
24559 * @param {Object} file
24564 * Fire when get exception
24565 * @param {Roo.bootstrap.UploadCropbox} this
24566 * @param {XMLHttpRequest} xhr
24568 "exception" : true,
24570 * @event beforeloadcanvas
24571 * Fire before load the canvas
24572 * @param {Roo.bootstrap.UploadCropbox} this
24573 * @param {String} src
24575 "beforeloadcanvas" : true,
24578 * Fire when trash image
24579 * @param {Roo.bootstrap.UploadCropbox} this
24584 * Fire when download the image
24585 * @param {Roo.bootstrap.UploadCropbox} this
24589 * @event footerbuttonclick
24590 * Fire when footerbuttonclick
24591 * @param {Roo.bootstrap.UploadCropbox} this
24592 * @param {String} type
24594 "footerbuttonclick" : true,
24598 * @param {Roo.bootstrap.UploadCropbox} this
24603 * Fire when rotate the image
24604 * @param {Roo.bootstrap.UploadCropbox} this
24605 * @param {String} pos
24610 * Fire when inspect the file
24611 * @param {Roo.bootstrap.UploadCropbox} this
24612 * @param {Object} file
24617 * Fire when xhr upload the file
24618 * @param {Roo.bootstrap.UploadCropbox} this
24619 * @param {Object} data
24624 * Fire when arrange the file data
24625 * @param {Roo.bootstrap.UploadCropbox} this
24626 * @param {Object} formData
24631 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24634 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24636 emptyText : 'Click to upload image',
24637 rotateNotify : 'Image is too small to rotate',
24638 errorTimeout : 3000,
24652 cropType : 'image/jpeg',
24654 canvasLoaded : false,
24655 isDocument : false,
24657 paramName : 'imageUpload',
24659 loadingText : 'Loading...',
24662 getAutoCreate : function()
24666 cls : 'roo-upload-cropbox',
24670 cls : 'roo-upload-cropbox-selector',
24675 cls : 'roo-upload-cropbox-body',
24676 style : 'cursor:pointer',
24680 cls : 'roo-upload-cropbox-preview'
24684 cls : 'roo-upload-cropbox-thumb'
24688 cls : 'roo-upload-cropbox-empty-notify',
24689 html : this.emptyText
24693 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24694 html : this.rotateNotify
24700 cls : 'roo-upload-cropbox-footer',
24703 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24713 onRender : function(ct, position)
24715 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24717 if (this.buttons.length) {
24719 Roo.each(this.buttons, function(bb) {
24721 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24723 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24729 this.maskEl = this.el;
24733 initEvents : function()
24735 this.urlAPI = (window.createObjectURL && window) ||
24736 (window.URL && URL.revokeObjectURL && URL) ||
24737 (window.webkitURL && webkitURL);
24739 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24740 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24742 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24743 this.selectorEl.hide();
24745 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24746 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24748 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24749 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24750 this.thumbEl.hide();
24752 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24753 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24755 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24756 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24757 this.errorEl.hide();
24759 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24760 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24761 this.footerEl.hide();
24763 this.setThumbBoxSize();
24769 this.fireEvent('initial', this);
24776 window.addEventListener("resize", function() { _this.resize(); } );
24778 this.bodyEl.on('click', this.beforeSelectFile, this);
24781 this.bodyEl.on('touchstart', this.onTouchStart, this);
24782 this.bodyEl.on('touchmove', this.onTouchMove, this);
24783 this.bodyEl.on('touchend', this.onTouchEnd, this);
24787 this.bodyEl.on('mousedown', this.onMouseDown, this);
24788 this.bodyEl.on('mousemove', this.onMouseMove, this);
24789 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24790 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24791 Roo.get(document).on('mouseup', this.onMouseUp, this);
24794 this.selectorEl.on('change', this.onFileSelected, this);
24800 this.baseScale = 1;
24802 this.baseRotate = 1;
24803 this.dragable = false;
24804 this.pinching = false;
24807 this.cropData = false;
24808 this.notifyEl.dom.innerHTML = this.emptyText;
24810 this.selectorEl.dom.value = '';
24814 resize : function()
24816 if(this.fireEvent('resize', this) != false){
24817 this.setThumbBoxPosition();
24818 this.setCanvasPosition();
24822 onFooterButtonClick : function(e, el, o, type)
24825 case 'rotate-left' :
24826 this.onRotateLeft(e);
24828 case 'rotate-right' :
24829 this.onRotateRight(e);
24832 this.beforeSelectFile(e);
24847 this.fireEvent('footerbuttonclick', this, type);
24850 beforeSelectFile : function(e)
24852 e.preventDefault();
24854 if(this.fireEvent('beforeselectfile', this) != false){
24855 this.selectorEl.dom.click();
24859 onFileSelected : function(e)
24861 e.preventDefault();
24863 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24867 var file = this.selectorEl.dom.files[0];
24869 if(this.fireEvent('inspect', this, file) != false){
24870 this.prepare(file);
24875 trash : function(e)
24877 this.fireEvent('trash', this);
24880 download : function(e)
24882 this.fireEvent('download', this);
24885 loadCanvas : function(src)
24887 if(this.fireEvent('beforeloadcanvas', this, src) != false){
24891 this.imageEl = document.createElement('img');
24895 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24897 this.imageEl.src = src;
24901 onLoadCanvas : function()
24903 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24904 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24906 this.bodyEl.un('click', this.beforeSelectFile, this);
24908 this.notifyEl.hide();
24909 this.thumbEl.show();
24910 this.footerEl.show();
24912 this.baseRotateLevel();
24914 if(this.isDocument){
24915 this.setThumbBoxSize();
24918 this.setThumbBoxPosition();
24920 this.baseScaleLevel();
24926 this.canvasLoaded = true;
24929 this.maskEl.unmask();
24934 setCanvasPosition : function()
24936 if(!this.canvasEl){
24940 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24941 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24943 this.previewEl.setLeft(pw);
24944 this.previewEl.setTop(ph);
24948 onMouseDown : function(e)
24952 this.dragable = true;
24953 this.pinching = false;
24955 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24956 this.dragable = false;
24960 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24961 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24965 onMouseMove : function(e)
24969 if(!this.canvasLoaded){
24973 if (!this.dragable){
24977 var minX = Math.ceil(this.thumbEl.getLeft(true));
24978 var minY = Math.ceil(this.thumbEl.getTop(true));
24980 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24981 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24983 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24984 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24986 x = x - this.mouseX;
24987 y = y - this.mouseY;
24989 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24990 var bgY = Math.ceil(y + this.previewEl.getTop(true));
24992 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24993 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24995 this.previewEl.setLeft(bgX);
24996 this.previewEl.setTop(bgY);
24998 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24999 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25002 onMouseUp : function(e)
25006 this.dragable = false;
25009 onMouseWheel : function(e)
25013 this.startScale = this.scale;
25015 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25017 if(!this.zoomable()){
25018 this.scale = this.startScale;
25027 zoomable : function()
25029 var minScale = this.thumbEl.getWidth() / this.minWidth;
25031 if(this.minWidth < this.minHeight){
25032 minScale = this.thumbEl.getHeight() / this.minHeight;
25035 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25036 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25040 (this.rotate == 0 || this.rotate == 180) &&
25042 width > this.imageEl.OriginWidth ||
25043 height > this.imageEl.OriginHeight ||
25044 (width < this.minWidth && height < this.minHeight)
25052 (this.rotate == 90 || this.rotate == 270) &&
25054 width > this.imageEl.OriginWidth ||
25055 height > this.imageEl.OriginHeight ||
25056 (width < this.minHeight && height < this.minWidth)
25063 !this.isDocument &&
25064 (this.rotate == 0 || this.rotate == 180) &&
25066 width < this.minWidth ||
25067 width > this.imageEl.OriginWidth ||
25068 height < this.minHeight ||
25069 height > this.imageEl.OriginHeight
25076 !this.isDocument &&
25077 (this.rotate == 90 || this.rotate == 270) &&
25079 width < this.minHeight ||
25080 width > this.imageEl.OriginWidth ||
25081 height < this.minWidth ||
25082 height > this.imageEl.OriginHeight
25092 onRotateLeft : function(e)
25094 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25096 var minScale = this.thumbEl.getWidth() / this.minWidth;
25098 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25099 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25101 this.startScale = this.scale;
25103 while (this.getScaleLevel() < minScale){
25105 this.scale = this.scale + 1;
25107 if(!this.zoomable()){
25112 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25113 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25118 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25125 this.scale = this.startScale;
25127 this.onRotateFail();
25132 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25134 if(this.isDocument){
25135 this.setThumbBoxSize();
25136 this.setThumbBoxPosition();
25137 this.setCanvasPosition();
25142 this.fireEvent('rotate', this, 'left');
25146 onRotateRight : function(e)
25148 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25150 var minScale = this.thumbEl.getWidth() / this.minWidth;
25152 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25153 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25155 this.startScale = this.scale;
25157 while (this.getScaleLevel() < minScale){
25159 this.scale = this.scale + 1;
25161 if(!this.zoomable()){
25166 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25167 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25172 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25179 this.scale = this.startScale;
25181 this.onRotateFail();
25186 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25188 if(this.isDocument){
25189 this.setThumbBoxSize();
25190 this.setThumbBoxPosition();
25191 this.setCanvasPosition();
25196 this.fireEvent('rotate', this, 'right');
25199 onRotateFail : function()
25201 this.errorEl.show(true);
25205 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25210 this.previewEl.dom.innerHTML = '';
25212 var canvasEl = document.createElement("canvas");
25214 var contextEl = canvasEl.getContext("2d");
25216 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25217 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25218 var center = this.imageEl.OriginWidth / 2;
25220 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25221 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25222 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25223 center = this.imageEl.OriginHeight / 2;
25226 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25228 contextEl.translate(center, center);
25229 contextEl.rotate(this.rotate * Math.PI / 180);
25231 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25233 this.canvasEl = document.createElement("canvas");
25235 this.contextEl = this.canvasEl.getContext("2d");
25237 switch (this.rotate) {
25240 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25241 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25243 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25248 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25249 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25251 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25252 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);
25256 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25261 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25262 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25264 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25265 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);
25269 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);
25274 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25275 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25277 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25278 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25282 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);
25289 this.previewEl.appendChild(this.canvasEl);
25291 this.setCanvasPosition();
25296 if(!this.canvasLoaded){
25300 var imageCanvas = document.createElement("canvas");
25302 var imageContext = imageCanvas.getContext("2d");
25304 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25305 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25307 var center = imageCanvas.width / 2;
25309 imageContext.translate(center, center);
25311 imageContext.rotate(this.rotate * Math.PI / 180);
25313 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25315 var canvas = document.createElement("canvas");
25317 var context = canvas.getContext("2d");
25319 canvas.width = this.minWidth;
25320 canvas.height = this.minHeight;
25322 switch (this.rotate) {
25325 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25326 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25328 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25329 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25331 var targetWidth = this.minWidth - 2 * x;
25332 var targetHeight = this.minHeight - 2 * y;
25336 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25337 scale = targetWidth / width;
25340 if(x > 0 && y == 0){
25341 scale = targetHeight / height;
25344 if(x > 0 && y > 0){
25345 scale = targetWidth / width;
25347 if(width < height){
25348 scale = targetHeight / height;
25352 context.scale(scale, scale);
25354 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25355 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25357 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25358 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25360 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25365 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25366 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25368 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25369 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25371 var targetWidth = this.minWidth - 2 * x;
25372 var targetHeight = this.minHeight - 2 * y;
25376 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25377 scale = targetWidth / width;
25380 if(x > 0 && y == 0){
25381 scale = targetHeight / height;
25384 if(x > 0 && y > 0){
25385 scale = targetWidth / width;
25387 if(width < height){
25388 scale = targetHeight / height;
25392 context.scale(scale, scale);
25394 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25395 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25397 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25398 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25400 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25402 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25407 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25408 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25410 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25411 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25413 var targetWidth = this.minWidth - 2 * x;
25414 var targetHeight = this.minHeight - 2 * y;
25418 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25419 scale = targetWidth / width;
25422 if(x > 0 && y == 0){
25423 scale = targetHeight / height;
25426 if(x > 0 && y > 0){
25427 scale = targetWidth / width;
25429 if(width < height){
25430 scale = targetHeight / height;
25434 context.scale(scale, scale);
25436 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25437 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25439 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25440 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25442 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25443 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25445 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25450 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25451 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25453 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25454 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25456 var targetWidth = this.minWidth - 2 * x;
25457 var targetHeight = this.minHeight - 2 * y;
25461 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25462 scale = targetWidth / width;
25465 if(x > 0 && y == 0){
25466 scale = targetHeight / height;
25469 if(x > 0 && y > 0){
25470 scale = targetWidth / width;
25472 if(width < height){
25473 scale = targetHeight / height;
25477 context.scale(scale, scale);
25479 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25480 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25482 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25483 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25485 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25487 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25494 this.cropData = canvas.toDataURL(this.cropType);
25496 if(this.fireEvent('crop', this, this.cropData) !== false){
25497 this.process(this.file, this.cropData);
25504 setThumbBoxSize : function()
25508 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25509 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25510 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25512 this.minWidth = width;
25513 this.minHeight = height;
25515 if(this.rotate == 90 || this.rotate == 270){
25516 this.minWidth = height;
25517 this.minHeight = width;
25522 width = Math.ceil(this.minWidth * height / this.minHeight);
25524 if(this.minWidth > this.minHeight){
25526 height = Math.ceil(this.minHeight * width / this.minWidth);
25529 this.thumbEl.setStyle({
25530 width : width + 'px',
25531 height : height + 'px'
25538 setThumbBoxPosition : function()
25540 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25541 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25543 this.thumbEl.setLeft(x);
25544 this.thumbEl.setTop(y);
25548 baseRotateLevel : function()
25550 this.baseRotate = 1;
25553 typeof(this.exif) != 'undefined' &&
25554 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25555 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25557 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25560 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25564 baseScaleLevel : function()
25568 if(this.isDocument){
25570 if(this.baseRotate == 6 || this.baseRotate == 8){
25572 height = this.thumbEl.getHeight();
25573 this.baseScale = height / this.imageEl.OriginWidth;
25575 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25576 width = this.thumbEl.getWidth();
25577 this.baseScale = width / this.imageEl.OriginHeight;
25583 height = this.thumbEl.getHeight();
25584 this.baseScale = height / this.imageEl.OriginHeight;
25586 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25587 width = this.thumbEl.getWidth();
25588 this.baseScale = width / this.imageEl.OriginWidth;
25594 if(this.baseRotate == 6 || this.baseRotate == 8){
25596 width = this.thumbEl.getHeight();
25597 this.baseScale = width / this.imageEl.OriginHeight;
25599 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25600 height = this.thumbEl.getWidth();
25601 this.baseScale = height / this.imageEl.OriginHeight;
25604 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25605 height = this.thumbEl.getWidth();
25606 this.baseScale = height / this.imageEl.OriginHeight;
25608 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25609 width = this.thumbEl.getHeight();
25610 this.baseScale = width / this.imageEl.OriginWidth;
25617 width = this.thumbEl.getWidth();
25618 this.baseScale = width / this.imageEl.OriginWidth;
25620 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25621 height = this.thumbEl.getHeight();
25622 this.baseScale = height / this.imageEl.OriginHeight;
25625 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25627 height = this.thumbEl.getHeight();
25628 this.baseScale = height / this.imageEl.OriginHeight;
25630 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25631 width = this.thumbEl.getWidth();
25632 this.baseScale = width / this.imageEl.OriginWidth;
25640 getScaleLevel : function()
25642 return this.baseScale * Math.pow(1.1, this.scale);
25645 onTouchStart : function(e)
25647 if(!this.canvasLoaded){
25648 this.beforeSelectFile(e);
25652 var touches = e.browserEvent.touches;
25658 if(touches.length == 1){
25659 this.onMouseDown(e);
25663 if(touches.length != 2){
25669 for(var i = 0, finger; finger = touches[i]; i++){
25670 coords.push(finger.pageX, finger.pageY);
25673 var x = Math.pow(coords[0] - coords[2], 2);
25674 var y = Math.pow(coords[1] - coords[3], 2);
25676 this.startDistance = Math.sqrt(x + y);
25678 this.startScale = this.scale;
25680 this.pinching = true;
25681 this.dragable = false;
25685 onTouchMove : function(e)
25687 if(!this.pinching && !this.dragable){
25691 var touches = e.browserEvent.touches;
25698 this.onMouseMove(e);
25704 for(var i = 0, finger; finger = touches[i]; i++){
25705 coords.push(finger.pageX, finger.pageY);
25708 var x = Math.pow(coords[0] - coords[2], 2);
25709 var y = Math.pow(coords[1] - coords[3], 2);
25711 this.endDistance = Math.sqrt(x + y);
25713 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25715 if(!this.zoomable()){
25716 this.scale = this.startScale;
25724 onTouchEnd : function(e)
25726 this.pinching = false;
25727 this.dragable = false;
25731 process : function(file, crop)
25734 this.maskEl.mask(this.loadingText);
25737 this.xhr = new XMLHttpRequest();
25739 file.xhr = this.xhr;
25741 this.xhr.open(this.method, this.url, true);
25744 "Accept": "application/json",
25745 "Cache-Control": "no-cache",
25746 "X-Requested-With": "XMLHttpRequest"
25749 for (var headerName in headers) {
25750 var headerValue = headers[headerName];
25752 this.xhr.setRequestHeader(headerName, headerValue);
25758 this.xhr.onload = function()
25760 _this.xhrOnLoad(_this.xhr);
25763 this.xhr.onerror = function()
25765 _this.xhrOnError(_this.xhr);
25768 var formData = new FormData();
25770 formData.append('returnHTML', 'NO');
25773 formData.append('crop', crop);
25776 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25777 formData.append(this.paramName, file, file.name);
25780 if(typeof(file.filename) != 'undefined'){
25781 formData.append('filename', file.filename);
25784 if(typeof(file.mimetype) != 'undefined'){
25785 formData.append('mimetype', file.mimetype);
25788 if(this.fireEvent('arrange', this, formData) != false){
25789 this.xhr.send(formData);
25793 xhrOnLoad : function(xhr)
25796 this.maskEl.unmask();
25799 if (xhr.readyState !== 4) {
25800 this.fireEvent('exception', this, xhr);
25804 var response = Roo.decode(xhr.responseText);
25806 if(!response.success){
25807 this.fireEvent('exception', this, xhr);
25811 var response = Roo.decode(xhr.responseText);
25813 this.fireEvent('upload', this, response);
25817 xhrOnError : function()
25820 this.maskEl.unmask();
25823 Roo.log('xhr on error');
25825 var response = Roo.decode(xhr.responseText);
25831 prepare : function(file)
25834 this.maskEl.mask(this.loadingText);
25840 if(typeof(file) === 'string'){
25841 this.loadCanvas(file);
25845 if(!file || !this.urlAPI){
25850 this.cropType = file.type;
25854 if(this.fireEvent('prepare', this, this.file) != false){
25856 var reader = new FileReader();
25858 reader.onload = function (e) {
25859 if (e.target.error) {
25860 Roo.log(e.target.error);
25864 var buffer = e.target.result,
25865 dataView = new DataView(buffer),
25867 maxOffset = dataView.byteLength - 4,
25871 if (dataView.getUint16(0) === 0xffd8) {
25872 while (offset < maxOffset) {
25873 markerBytes = dataView.getUint16(offset);
25875 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25876 markerLength = dataView.getUint16(offset + 2) + 2;
25877 if (offset + markerLength > dataView.byteLength) {
25878 Roo.log('Invalid meta data: Invalid segment size.');
25882 if(markerBytes == 0xffe1){
25883 _this.parseExifData(
25890 offset += markerLength;
25900 var url = _this.urlAPI.createObjectURL(_this.file);
25902 _this.loadCanvas(url);
25907 reader.readAsArrayBuffer(this.file);
25913 parseExifData : function(dataView, offset, length)
25915 var tiffOffset = offset + 10,
25919 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25920 // No Exif data, might be XMP data instead
25924 // Check for the ASCII code for "Exif" (0x45786966):
25925 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25926 // No Exif data, might be XMP data instead
25929 if (tiffOffset + 8 > dataView.byteLength) {
25930 Roo.log('Invalid Exif data: Invalid segment size.');
25933 // Check for the two null bytes:
25934 if (dataView.getUint16(offset + 8) !== 0x0000) {
25935 Roo.log('Invalid Exif data: Missing byte alignment offset.');
25938 // Check the byte alignment:
25939 switch (dataView.getUint16(tiffOffset)) {
25941 littleEndian = true;
25944 littleEndian = false;
25947 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25950 // Check for the TIFF tag marker (0x002A):
25951 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25952 Roo.log('Invalid Exif data: Missing TIFF marker.');
25955 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25956 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25958 this.parseExifTags(
25961 tiffOffset + dirOffset,
25966 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25971 if (dirOffset + 6 > dataView.byteLength) {
25972 Roo.log('Invalid Exif data: Invalid directory offset.');
25975 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25976 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25977 if (dirEndOffset + 4 > dataView.byteLength) {
25978 Roo.log('Invalid Exif data: Invalid directory size.');
25981 for (i = 0; i < tagsNumber; i += 1) {
25985 dirOffset + 2 + 12 * i, // tag offset
25989 // Return the offset to the next directory:
25990 return dataView.getUint32(dirEndOffset, littleEndian);
25993 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
25995 var tag = dataView.getUint16(offset, littleEndian);
25997 this.exif[tag] = this.getExifValue(
26001 dataView.getUint16(offset + 2, littleEndian), // tag type
26002 dataView.getUint32(offset + 4, littleEndian), // tag length
26007 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26009 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26018 Roo.log('Invalid Exif data: Invalid tag type.');
26022 tagSize = tagType.size * length;
26023 // Determine if the value is contained in the dataOffset bytes,
26024 // or if the value at the dataOffset is a pointer to the actual data:
26025 dataOffset = tagSize > 4 ?
26026 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26027 if (dataOffset + tagSize > dataView.byteLength) {
26028 Roo.log('Invalid Exif data: Invalid data offset.');
26031 if (length === 1) {
26032 return tagType.getValue(dataView, dataOffset, littleEndian);
26035 for (i = 0; i < length; i += 1) {
26036 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26039 if (tagType.ascii) {
26041 // Concatenate the chars:
26042 for (i = 0; i < values.length; i += 1) {
26044 // Ignore the terminating NULL byte(s):
26045 if (c === '\u0000') {
26057 Roo.apply(Roo.bootstrap.UploadCropbox, {
26059 'Orientation': 0x0112
26063 1: 0, //'top-left',
26065 3: 180, //'bottom-right',
26066 // 4: 'bottom-left',
26068 6: 90, //'right-top',
26069 // 7: 'right-bottom',
26070 8: 270 //'left-bottom'
26074 // byte, 8-bit unsigned int:
26076 getValue: function (dataView, dataOffset) {
26077 return dataView.getUint8(dataOffset);
26081 // ascii, 8-bit byte:
26083 getValue: function (dataView, dataOffset) {
26084 return String.fromCharCode(dataView.getUint8(dataOffset));
26089 // short, 16 bit int:
26091 getValue: function (dataView, dataOffset, littleEndian) {
26092 return dataView.getUint16(dataOffset, littleEndian);
26096 // long, 32 bit int:
26098 getValue: function (dataView, dataOffset, littleEndian) {
26099 return dataView.getUint32(dataOffset, littleEndian);
26103 // rational = two long values, first is numerator, second is denominator:
26105 getValue: function (dataView, dataOffset, littleEndian) {
26106 return dataView.getUint32(dataOffset, littleEndian) /
26107 dataView.getUint32(dataOffset + 4, littleEndian);
26111 // slong, 32 bit signed int:
26113 getValue: function (dataView, dataOffset, littleEndian) {
26114 return dataView.getInt32(dataOffset, littleEndian);
26118 // srational, two slongs, first is numerator, second is denominator:
26120 getValue: function (dataView, dataOffset, littleEndian) {
26121 return dataView.getInt32(dataOffset, littleEndian) /
26122 dataView.getInt32(dataOffset + 4, littleEndian);
26132 cls : 'btn-group roo-upload-cropbox-rotate-left',
26133 action : 'rotate-left',
26137 cls : 'btn btn-default',
26138 html : '<i class="fa fa-undo"></i>'
26144 cls : 'btn-group roo-upload-cropbox-picture',
26145 action : 'picture',
26149 cls : 'btn btn-default',
26150 html : '<i class="fa fa-picture-o"></i>'
26156 cls : 'btn-group roo-upload-cropbox-rotate-right',
26157 action : 'rotate-right',
26161 cls : 'btn btn-default',
26162 html : '<i class="fa fa-repeat"></i>'
26170 cls : 'btn-group roo-upload-cropbox-rotate-left',
26171 action : 'rotate-left',
26175 cls : 'btn btn-default',
26176 html : '<i class="fa fa-undo"></i>'
26182 cls : 'btn-group roo-upload-cropbox-download',
26183 action : 'download',
26187 cls : 'btn btn-default',
26188 html : '<i class="fa fa-download"></i>'
26194 cls : 'btn-group roo-upload-cropbox-crop',
26199 cls : 'btn btn-default',
26200 html : '<i class="fa fa-crop"></i>'
26206 cls : 'btn-group roo-upload-cropbox-trash',
26211 cls : 'btn btn-default',
26212 html : '<i class="fa fa-trash"></i>'
26218 cls : 'btn-group roo-upload-cropbox-rotate-right',
26219 action : 'rotate-right',
26223 cls : 'btn btn-default',
26224 html : '<i class="fa fa-repeat"></i>'
26232 cls : 'btn-group roo-upload-cropbox-rotate-left',
26233 action : 'rotate-left',
26237 cls : 'btn btn-default',
26238 html : '<i class="fa fa-undo"></i>'
26244 cls : 'btn-group roo-upload-cropbox-rotate-right',
26245 action : 'rotate-right',
26249 cls : 'btn btn-default',
26250 html : '<i class="fa fa-repeat"></i>'
26263 * @class Roo.bootstrap.DocumentManager
26264 * @extends Roo.bootstrap.Component
26265 * Bootstrap DocumentManager class
26266 * @cfg {String} paramName default 'imageUpload'
26267 * @cfg {String} method default POST
26268 * @cfg {String} url action url
26269 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26270 * @cfg {Boolean} multiple multiple upload default true
26271 * @cfg {Number} thumbSize default 300
26272 * @cfg {String} fieldLabel
26273 * @cfg {Number} labelWidth default 4
26274 * @cfg {String} labelAlign (left|top) default left
26275 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26278 * Create a new DocumentManager
26279 * @param {Object} config The config object
26282 Roo.bootstrap.DocumentManager = function(config){
26283 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26288 * Fire when initial the DocumentManager
26289 * @param {Roo.bootstrap.DocumentManager} this
26294 * inspect selected file
26295 * @param {Roo.bootstrap.DocumentManager} this
26296 * @param {File} file
26301 * Fire when xhr load exception
26302 * @param {Roo.bootstrap.DocumentManager} this
26303 * @param {XMLHttpRequest} xhr
26305 "exception" : true,
26308 * prepare the form data
26309 * @param {Roo.bootstrap.DocumentManager} this
26310 * @param {Object} formData
26315 * Fire when remove the file
26316 * @param {Roo.bootstrap.DocumentManager} this
26317 * @param {Object} file
26322 * Fire after refresh the file
26323 * @param {Roo.bootstrap.DocumentManager} this
26328 * Fire after click the image
26329 * @param {Roo.bootstrap.DocumentManager} this
26330 * @param {Object} file
26335 * Fire when upload a image and editable set to true
26336 * @param {Roo.bootstrap.DocumentManager} this
26337 * @param {Object} file
26341 * @event beforeselectfile
26342 * Fire before select file
26343 * @param {Roo.bootstrap.DocumentManager} this
26345 "beforeselectfile" : true,
26348 * Fire before process file
26349 * @param {Roo.bootstrap.DocumentManager} this
26350 * @param {Object} file
26357 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
26366 paramName : 'imageUpload',
26369 labelAlign : 'left',
26376 getAutoCreate : function()
26378 var managerWidget = {
26380 cls : 'roo-document-manager',
26384 cls : 'roo-document-manager-selector',
26389 cls : 'roo-document-manager-uploader',
26393 cls : 'roo-document-manager-upload-btn',
26394 html : '<i class="fa fa-plus"></i>'
26405 cls : 'column col-md-12',
26410 if(this.fieldLabel.length){
26415 cls : 'column col-md-12',
26416 html : this.fieldLabel
26420 cls : 'column col-md-12',
26425 if(this.labelAlign == 'left'){
26429 cls : 'column col-md-' + this.labelWidth,
26430 html : this.fieldLabel
26434 cls : 'column col-md-' + (12 - this.labelWidth),
26444 cls : 'row clearfix',
26452 initEvents : function()
26454 this.managerEl = this.el.select('.roo-document-manager', true).first();
26455 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26457 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26458 this.selectorEl.hide();
26461 this.selectorEl.attr('multiple', 'multiple');
26464 this.selectorEl.on('change', this.onFileSelected, this);
26466 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26467 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26469 this.uploader.on('click', this.onUploaderClick, this);
26471 this.renderProgressDialog();
26475 window.addEventListener("resize", function() { _this.refresh(); } );
26477 this.fireEvent('initial', this);
26480 renderProgressDialog : function()
26484 this.progressDialog = new Roo.bootstrap.Modal({
26485 cls : 'roo-document-manager-progress-dialog',
26486 allow_close : false,
26496 btnclick : function() {
26497 _this.uploadCancel();
26503 this.progressDialog.render(Roo.get(document.body));
26505 this.progress = new Roo.bootstrap.Progress({
26506 cls : 'roo-document-manager-progress',
26511 this.progress.render(this.progressDialog.getChildContainer());
26513 this.progressBar = new Roo.bootstrap.ProgressBar({
26514 cls : 'roo-document-manager-progress-bar',
26517 aria_valuemax : 12,
26521 this.progressBar.render(this.progress.getChildContainer());
26524 onUploaderClick : function(e)
26526 e.preventDefault();
26528 if(this.fireEvent('beforeselectfile', this) != false){
26529 this.selectorEl.dom.click();
26534 onFileSelected : function(e)
26536 e.preventDefault();
26538 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26542 Roo.each(this.selectorEl.dom.files, function(file){
26543 if(this.fireEvent('inspect', this, file) != false){
26544 this.files.push(file);
26554 this.selectorEl.dom.value = '';
26556 if(!this.files.length){
26560 if(this.boxes > 0 && this.files.length > this.boxes){
26561 this.files = this.files.slice(0, this.boxes);
26564 this.uploader.show();
26566 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26567 this.uploader.hide();
26576 Roo.each(this.files, function(file){
26578 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26579 var f = this.renderPreview(file);
26584 if(file.type.indexOf('image') != -1){
26585 this.delegates.push(
26587 _this.process(file);
26588 }).createDelegate(this)
26596 _this.process(file);
26597 }).createDelegate(this)
26602 this.files = files;
26604 this.delegates = this.delegates.concat(docs);
26606 if(!this.delegates.length){
26611 this.progressBar.aria_valuemax = this.delegates.length;
26618 arrange : function()
26620 if(!this.delegates.length){
26621 this.progressDialog.hide();
26626 var delegate = this.delegates.shift();
26628 this.progressDialog.show();
26630 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26632 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26637 refresh : function()
26639 this.uploader.show();
26641 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26642 this.uploader.hide();
26645 Roo.isTouch ? this.closable(false) : this.closable(true);
26647 this.fireEvent('refresh', this);
26650 onRemove : function(e, el, o)
26652 e.preventDefault();
26654 this.fireEvent('remove', this, o);
26658 remove : function(o)
26662 Roo.each(this.files, function(file){
26663 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26672 this.files = files;
26679 Roo.each(this.files, function(file){
26684 file.target.remove();
26693 onClick : function(e, el, o)
26695 e.preventDefault();
26697 this.fireEvent('click', this, o);
26701 closable : function(closable)
26703 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26705 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26717 xhrOnLoad : function(xhr)
26719 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26723 if (xhr.readyState !== 4) {
26725 this.fireEvent('exception', this, xhr);
26729 var response = Roo.decode(xhr.responseText);
26731 if(!response.success){
26733 this.fireEvent('exception', this, xhr);
26737 var file = this.renderPreview(response.data);
26739 this.files.push(file);
26745 xhrOnError : function()
26747 Roo.log('xhr on error');
26749 var response = Roo.decode(xhr.responseText);
26756 process : function(file)
26758 if(this.fireEvent('process', this, file) !== false){
26759 if(this.editable && file.type.indexOf('image') != -1){
26760 this.fireEvent('edit', this, file);
26764 this.uploadStart(file, false);
26771 uploadStart : function(file, crop)
26773 this.xhr = new XMLHttpRequest();
26775 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26780 file.xhr = this.xhr;
26782 this.managerEl.createChild({
26784 cls : 'roo-document-manager-loading',
26788 tooltip : file.name,
26789 cls : 'roo-document-manager-thumb',
26790 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26796 this.xhr.open(this.method, this.url, true);
26799 "Accept": "application/json",
26800 "Cache-Control": "no-cache",
26801 "X-Requested-With": "XMLHttpRequest"
26804 for (var headerName in headers) {
26805 var headerValue = headers[headerName];
26807 this.xhr.setRequestHeader(headerName, headerValue);
26813 this.xhr.onload = function()
26815 _this.xhrOnLoad(_this.xhr);
26818 this.xhr.onerror = function()
26820 _this.xhrOnError(_this.xhr);
26823 var formData = new FormData();
26825 formData.append('returnHTML', 'NO');
26828 formData.append('crop', crop);
26831 formData.append(this.paramName, file, file.name);
26833 if(this.fireEvent('prepare', this, formData) != false){
26834 this.xhr.send(formData);
26838 uploadCancel : function()
26845 this.delegates = [];
26847 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26854 renderPreview : function(file)
26856 if(typeof(file.target) != 'undefined' && file.target){
26860 var previewEl = this.managerEl.createChild({
26862 cls : 'roo-document-manager-preview',
26866 tooltip : file.filename,
26867 cls : 'roo-document-manager-thumb',
26868 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26873 html : '<i class="fa fa-times-circle"></i>'
26878 var close = previewEl.select('button.close', true).first();
26880 close.on('click', this.onRemove, this, file);
26882 file.target = previewEl;
26884 var image = previewEl.select('img', true).first();
26888 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26890 image.on('click', this.onClick, this, file);
26896 onPreviewLoad : function(file, image)
26898 if(typeof(file.target) == 'undefined' || !file.target){
26902 var width = image.dom.naturalWidth || image.dom.width;
26903 var height = image.dom.naturalHeight || image.dom.height;
26905 if(width > height){
26906 file.target.addClass('wide');
26910 file.target.addClass('tall');
26915 uploadFromSource : function(file, crop)
26917 this.xhr = new XMLHttpRequest();
26919 this.managerEl.createChild({
26921 cls : 'roo-document-manager-loading',
26925 tooltip : file.name,
26926 cls : 'roo-document-manager-thumb',
26927 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26933 this.xhr.open(this.method, this.url, true);
26936 "Accept": "application/json",
26937 "Cache-Control": "no-cache",
26938 "X-Requested-With": "XMLHttpRequest"
26941 for (var headerName in headers) {
26942 var headerValue = headers[headerName];
26944 this.xhr.setRequestHeader(headerName, headerValue);
26950 this.xhr.onload = function()
26952 _this.xhrOnLoad(_this.xhr);
26955 this.xhr.onerror = function()
26957 _this.xhrOnError(_this.xhr);
26960 var formData = new FormData();
26962 formData.append('returnHTML', 'NO');
26964 formData.append('crop', crop);
26966 if(typeof(file.filename) != 'undefined'){
26967 formData.append('filename', file.filename);
26970 if(typeof(file.mimetype) != 'undefined'){
26971 formData.append('mimetype', file.mimetype);
26974 if(this.fireEvent('prepare', this, formData) != false){
26975 this.xhr.send(formData);
26985 * @class Roo.bootstrap.DocumentViewer
26986 * @extends Roo.bootstrap.Component
26987 * Bootstrap DocumentViewer class
26990 * Create a new DocumentViewer
26991 * @param {Object} config The config object
26994 Roo.bootstrap.DocumentViewer = function(config){
26995 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27000 * Fire after initEvent
27001 * @param {Roo.bootstrap.DocumentViewer} this
27007 * @param {Roo.bootstrap.DocumentViewer} this
27012 * Fire after trash button
27013 * @param {Roo.bootstrap.DocumentViewer} this
27020 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
27022 getAutoCreate : function()
27026 cls : 'roo-document-viewer',
27030 cls : 'roo-document-viewer-body',
27034 cls : 'roo-document-viewer-thumb',
27038 cls : 'roo-document-viewer-image'
27046 cls : 'roo-document-viewer-footer',
27049 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27057 cls : 'btn btn-default roo-document-viewer-trash',
27058 html : '<i class="fa fa-trash"></i>'
27071 initEvents : function()
27074 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27075 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27077 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27078 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27080 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27081 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27083 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27084 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27086 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27087 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27089 this.bodyEl.on('click', this.onClick, this);
27091 this.trashBtn.on('click', this.onTrash, this);
27095 initial : function()
27097 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27100 this.fireEvent('initial', this);
27104 onClick : function(e)
27106 e.preventDefault();
27108 this.fireEvent('click', this);
27111 onTrash : function(e)
27113 e.preventDefault();
27115 this.fireEvent('trash', this);
27127 * @class Roo.bootstrap.NavProgressBar
27128 * @extends Roo.bootstrap.Component
27129 * Bootstrap NavProgressBar class
27132 * Create a new nav progress bar
27133 * @param {Object} config The config object
27136 Roo.bootstrap.NavProgressBar = function(config){
27137 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27139 this.bullets = this.bullets || [];
27141 // Roo.bootstrap.NavProgressBar.register(this);
27145 * Fires when the active item changes
27146 * @param {Roo.bootstrap.NavProgressBar} this
27147 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27148 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
27155 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
27160 getAutoCreate : function()
27162 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27166 cls : 'roo-navigation-bar-group',
27170 cls : 'roo-navigation-top-bar'
27174 cls : 'roo-navigation-bullets-bar',
27178 cls : 'roo-navigation-bar'
27185 cls : 'roo-navigation-bottom-bar'
27195 initEvents: function()
27200 onRender : function(ct, position)
27202 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27204 if(this.bullets.length){
27205 Roo.each(this.bullets, function(b){
27214 addItem : function(cfg)
27216 var item = new Roo.bootstrap.NavProgressItem(cfg);
27218 item.parentId = this.id;
27219 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27222 var top = new Roo.bootstrap.Element({
27224 cls : 'roo-navigation-bar-text'
27227 var bottom = new Roo.bootstrap.Element({
27229 cls : 'roo-navigation-bar-text'
27232 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27233 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27235 var topText = new Roo.bootstrap.Element({
27237 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27240 var bottomText = new Roo.bootstrap.Element({
27242 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27245 topText.onRender(top.el, null);
27246 bottomText.onRender(bottom.el, null);
27249 item.bottomEl = bottom;
27252 this.barItems.push(item);
27257 getActive : function()
27259 var active = false;
27261 Roo.each(this.barItems, function(v){
27263 if (!v.isActive()) {
27275 setActiveItem : function(item)
27279 Roo.each(this.barItems, function(v){
27280 if (v.rid == item.rid) {
27284 if (v.isActive()) {
27285 v.setActive(false);
27290 item.setActive(true);
27292 this.fireEvent('changed', this, item, prev);
27295 getBarItem: function(rid)
27299 Roo.each(this.barItems, function(e) {
27300 if (e.rid != rid) {
27311 indexOfItem : function(item)
27315 Roo.each(this.barItems, function(v, i){
27317 if (v.rid != item.rid) {
27328 setActiveNext : function()
27330 var i = this.indexOfItem(this.getActive());
27332 if (i > this.barItems.length) {
27336 this.setActiveItem(this.barItems[i+1]);
27339 setActivePrev : function()
27341 var i = this.indexOfItem(this.getActive());
27347 this.setActiveItem(this.barItems[i-1]);
27350 format : function()
27352 if(!this.barItems.length){
27356 var width = 100 / this.barItems.length;
27358 Roo.each(this.barItems, function(i){
27359 i.el.setStyle('width', width + '%');
27360 i.topEl.el.setStyle('width', width + '%');
27361 i.bottomEl.el.setStyle('width', width + '%');
27370 * Nav Progress Item
27375 * @class Roo.bootstrap.NavProgressItem
27376 * @extends Roo.bootstrap.Component
27377 * Bootstrap NavProgressItem class
27378 * @cfg {String} rid the reference id
27379 * @cfg {Boolean} active (true|false) Is item active default false
27380 * @cfg {Boolean} disabled (true|false) Is item active default false
27381 * @cfg {String} html
27382 * @cfg {String} position (top|bottom) text position default bottom
27383 * @cfg {String} icon show icon instead of number
27386 * Create a new NavProgressItem
27387 * @param {Object} config The config object
27389 Roo.bootstrap.NavProgressItem = function(config){
27390 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27395 * The raw click event for the entire grid.
27396 * @param {Roo.bootstrap.NavProgressItem} this
27397 * @param {Roo.EventObject} e
27404 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
27410 position : 'bottom',
27413 getAutoCreate : function()
27415 var iconCls = 'roo-navigation-bar-item-icon';
27417 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27421 cls: 'roo-navigation-bar-item',
27431 cfg.cls += ' active';
27434 cfg.cls += ' disabled';
27440 disable : function()
27442 this.setDisabled(true);
27445 enable : function()
27447 this.setDisabled(false);
27450 initEvents: function()
27452 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27454 this.iconEl.on('click', this.onClick, this);
27457 onClick : function(e)
27459 e.preventDefault();
27465 if(this.fireEvent('click', this, e) === false){
27469 this.parent().setActiveItem(this);
27472 isActive: function ()
27474 return this.active;
27477 setActive : function(state)
27479 if(this.active == state){
27483 this.active = state;
27486 this.el.addClass('active');
27490 this.el.removeClass('active');
27495 setDisabled : function(state)
27497 if(this.disabled == state){
27501 this.disabled = state;
27504 this.el.addClass('disabled');
27508 this.el.removeClass('disabled');
27511 tooltipEl : function()
27513 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27526 * @class Roo.bootstrap.FieldLabel
27527 * @extends Roo.bootstrap.Component
27528 * Bootstrap FieldLabel class
27529 * @cfg {String} html contents of the element
27530 * @cfg {String} tag tag of the element default label
27531 * @cfg {String} cls class of the element
27532 * @cfg {String} target label target
27533 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27534 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27535 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27536 * @cfg {String} iconTooltip default "This field is required"
27539 * Create a new FieldLabel
27540 * @param {Object} config The config object
27543 Roo.bootstrap.FieldLabel = function(config){
27544 Roo.bootstrap.Element.superclass.constructor.call(this, config);
27549 * Fires after the field has been marked as invalid.
27550 * @param {Roo.form.FieldLabel} this
27551 * @param {String} msg The validation message
27556 * Fires after the field has been validated with no errors.
27557 * @param {Roo.form.FieldLabel} this
27563 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
27570 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27571 validClass : 'text-success fa fa-lg fa-check',
27572 iconTooltip : 'This field is required',
27574 getAutoCreate : function(){
27578 cls : 'roo-bootstrap-field-label ' + this.cls,
27584 tooltip : this.iconTooltip
27596 initEvents: function()
27598 Roo.bootstrap.Element.superclass.initEvents.call(this);
27600 this.iconEl = this.el.select('i', true).first();
27602 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27604 Roo.bootstrap.FieldLabel.register(this);
27608 * Mark this field as valid
27610 markValid : function()
27612 this.iconEl.show();
27614 this.iconEl.removeClass(this.invalidClass);
27616 this.iconEl.addClass(this.validClass);
27618 this.fireEvent('valid', this);
27622 * Mark this field as invalid
27623 * @param {String} msg The validation message
27625 markInvalid : function(msg)
27627 this.iconEl.show();
27629 this.iconEl.removeClass(this.validClass);
27631 this.iconEl.addClass(this.invalidClass);
27633 this.fireEvent('invalid', this, msg);
27639 Roo.apply(Roo.bootstrap.FieldLabel, {
27644 * register a FieldLabel Group
27645 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27647 register : function(label)
27649 if(this.groups.hasOwnProperty(label.target)){
27653 this.groups[label.target] = label;
27657 * fetch a FieldLabel Group based on the target
27658 * @param {string} target
27659 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27661 get: function(target) {
27662 if (typeof(this.groups[target]) == 'undefined') {
27666 return this.groups[target] ;
27675 * page DateSplitField.
27681 * @class Roo.bootstrap.DateSplitField
27682 * @extends Roo.bootstrap.Component
27683 * Bootstrap DateSplitField class
27684 * @cfg {string} fieldLabel - the label associated
27685 * @cfg {Number} labelWidth set the width of label (0-12)
27686 * @cfg {String} labelAlign (top|left)
27687 * @cfg {Boolean} dayAllowBlank (true|false) default false
27688 * @cfg {Boolean} monthAllowBlank (true|false) default false
27689 * @cfg {Boolean} yearAllowBlank (true|false) default false
27690 * @cfg {string} dayPlaceholder
27691 * @cfg {string} monthPlaceholder
27692 * @cfg {string} yearPlaceholder
27693 * @cfg {string} dayFormat default 'd'
27694 * @cfg {string} monthFormat default 'm'
27695 * @cfg {string} yearFormat default 'Y'
27699 * Create a new DateSplitField
27700 * @param {Object} config The config object
27703 Roo.bootstrap.DateSplitField = function(config){
27704 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27710 * getting the data of years
27711 * @param {Roo.bootstrap.DateSplitField} this
27712 * @param {Object} years
27717 * getting the data of days
27718 * @param {Roo.bootstrap.DateSplitField} this
27719 * @param {Object} days
27724 * Fires after the field has been marked as invalid.
27725 * @param {Roo.form.Field} this
27726 * @param {String} msg The validation message
27731 * Fires after the field has been validated with no errors.
27732 * @param {Roo.form.Field} this
27738 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
27741 labelAlign : 'top',
27743 dayAllowBlank : false,
27744 monthAllowBlank : false,
27745 yearAllowBlank : false,
27746 dayPlaceholder : '',
27747 monthPlaceholder : '',
27748 yearPlaceholder : '',
27752 isFormField : true,
27754 getAutoCreate : function()
27758 cls : 'row roo-date-split-field-group',
27763 cls : 'form-hidden-field roo-date-split-field-group-value',
27769 if(this.fieldLabel){
27772 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27776 html : this.fieldLabel
27782 Roo.each(['day', 'month', 'year'], function(t){
27785 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27792 inputEl: function ()
27794 return this.el.select('.roo-date-split-field-group-value', true).first();
27797 onRender : function(ct, position)
27801 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27803 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27805 this.dayField = new Roo.bootstrap.ComboBox({
27806 allowBlank : this.dayAllowBlank,
27807 alwaysQuery : true,
27808 displayField : 'value',
27811 forceSelection : true,
27813 placeholder : this.dayPlaceholder,
27814 selectOnFocus : true,
27815 tpl : '<div class="select2-result"><b>{value}</b></div>',
27816 triggerAction : 'all',
27818 valueField : 'value',
27819 store : new Roo.data.SimpleStore({
27820 data : (function() {
27822 _this.fireEvent('days', _this, days);
27825 fields : [ 'value' ]
27828 select : function (_self, record, index)
27830 _this.setValue(_this.getValue());
27835 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27837 this.monthField = new Roo.bootstrap.MonthField({
27838 after : '<i class=\"fa fa-calendar\"></i>',
27839 allowBlank : this.monthAllowBlank,
27840 placeholder : this.monthPlaceholder,
27843 render : function (_self)
27845 this.el.select('span.input-group-addon', true).first().on('click', function(e){
27846 e.preventDefault();
27850 select : function (_self, oldvalue, newvalue)
27852 _this.setValue(_this.getValue());
27857 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27859 this.yearField = new Roo.bootstrap.ComboBox({
27860 allowBlank : this.yearAllowBlank,
27861 alwaysQuery : true,
27862 displayField : 'value',
27865 forceSelection : true,
27867 placeholder : this.yearPlaceholder,
27868 selectOnFocus : true,
27869 tpl : '<div class="select2-result"><b>{value}</b></div>',
27870 triggerAction : 'all',
27872 valueField : 'value',
27873 store : new Roo.data.SimpleStore({
27874 data : (function() {
27876 _this.fireEvent('years', _this, years);
27879 fields : [ 'value' ]
27882 select : function (_self, record, index)
27884 _this.setValue(_this.getValue());
27889 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
27892 setValue : function(v, format)
27894 this.inputEl.dom.value = v;
27896 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
27898 var d = Date.parseDate(v, f);
27905 this.setDay(d.format(this.dayFormat));
27906 this.setMonth(d.format(this.monthFormat));
27907 this.setYear(d.format(this.yearFormat));
27914 setDay : function(v)
27916 this.dayField.setValue(v);
27917 this.inputEl.dom.value = this.getValue();
27922 setMonth : function(v)
27924 this.monthField.setValue(v, true);
27925 this.inputEl.dom.value = this.getValue();
27930 setYear : function(v)
27932 this.yearField.setValue(v);
27933 this.inputEl.dom.value = this.getValue();
27938 getDay : function()
27940 return this.dayField.getValue();
27943 getMonth : function()
27945 return this.monthField.getValue();
27948 getYear : function()
27950 return this.yearField.getValue();
27953 getValue : function()
27955 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
27957 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
27967 this.inputEl.dom.value = '';
27972 validate : function()
27974 var d = this.dayField.validate();
27975 var m = this.monthField.validate();
27976 var y = this.yearField.validate();
27981 (!this.dayAllowBlank && !d) ||
27982 (!this.monthAllowBlank && !m) ||
27983 (!this.yearAllowBlank && !y)
27988 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
27997 this.markInvalid();
28002 markValid : function()
28005 var label = this.el.select('label', true).first();
28006 var icon = this.el.select('i.fa-star', true).first();
28012 this.fireEvent('valid', this);
28016 * Mark this field as invalid
28017 * @param {String} msg The validation message
28019 markInvalid : function(msg)
28022 var label = this.el.select('label', true).first();
28023 var icon = this.el.select('i.fa-star', true).first();
28025 if(label && !icon){
28026 this.el.select('.roo-date-split-field-label', true).createChild({
28028 cls : 'text-danger fa fa-lg fa-star',
28029 tooltip : 'This field is required',
28030 style : 'margin-right:5px;'
28034 this.fireEvent('invalid', this, msg);
28037 clearInvalid : function()
28039 var label = this.el.select('label', true).first();
28040 var icon = this.el.select('i.fa-star', true).first();
28046 this.fireEvent('valid', this);
28049 getName: function()