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(){
15668 Roo.log('make popover?');
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);
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 Roo.log('get auto create...............');
16066 if (this.carousel) {
16067 cfg.cls += ' carousel slide';
16070 cls : 'carousel-inner'
16073 if(this.bullets && !Roo.isTouch){
16076 cls : 'carousel-bullets',
16080 if(this.bullets_cls){
16081 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16084 for (var i = 0; i < this.bullets; i++){
16086 cls : 'bullet bullet-' + i
16094 cfg.cn[0].cn = bullets;
16101 initEvents: function()
16103 Roo.log('-------- init events on tab group ---------');
16105 if(Roo.isTouch && this.slideOnTouch){
16106 this.el.on("touchstart", this.onTouchStart, this);
16109 if(this.autoslide){
16112 this.slideFn = window.setInterval(function() {
16113 _this.showPanelNext();
16119 onTouchStart : function(e, el, o)
16121 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16125 this.showPanelNext();
16128 getChildContainer : function()
16130 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16134 * register a Navigation item
16135 * @param {Roo.bootstrap.NavItem} the navitem to add
16137 register : function(item)
16139 this.tabs.push( item);
16140 item.navId = this.navId; // not really needed..
16145 getActivePanel : function()
16148 Roo.each(this.tabs, function(t) {
16158 getPanelByName : function(n)
16161 Roo.each(this.tabs, function(t) {
16162 if (t.tabId == n) {
16170 indexOfPanel : function(p)
16173 Roo.each(this.tabs, function(t,i) {
16174 if (t.tabId == p.tabId) {
16183 * show a specific panel
16184 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16185 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16187 showPanel : function (pan)
16189 if(this.transition || typeof(pan) == 'undefined'){
16190 Roo.log("waiting for the transitionend");
16194 if (typeof(pan) == 'number') {
16195 pan = this.tabs[pan];
16198 if (typeof(pan) == 'string') {
16199 pan = this.getPanelByName(pan);
16202 var cur = this.getActivePanel();
16205 Roo.log('pan or acitve pan is undefined');
16209 if (pan.tabId == this.getActivePanel().tabId) {
16213 if (false === cur.fireEvent('beforedeactivate')) {
16217 if(this.bullets > 0 && !Roo.isTouch){
16218 this.setActiveBullet(this.indexOfPanel(pan));
16221 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16223 this.transition = true;
16224 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16225 var lr = dir == 'next' ? 'left' : 'right';
16226 pan.el.addClass(dir); // or prev
16227 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16228 cur.el.addClass(lr); // or right
16229 pan.el.addClass(lr);
16232 cur.el.on('transitionend', function() {
16233 Roo.log("trans end?");
16235 pan.el.removeClass([lr,dir]);
16236 pan.setActive(true);
16238 cur.el.removeClass([lr]);
16239 cur.setActive(false);
16241 _this.transition = false;
16243 }, this, { single: true } );
16248 cur.setActive(false);
16249 pan.setActive(true);
16254 showPanelNext : function()
16256 var i = this.indexOfPanel(this.getActivePanel());
16258 if (i >= this.tabs.length - 1 && !this.autoslide) {
16262 if (i >= this.tabs.length - 1 && this.autoslide) {
16266 this.showPanel(this.tabs[i+1]);
16269 showPanelPrev : function()
16271 var i = this.indexOfPanel(this.getActivePanel());
16273 if (i < 1 && !this.autoslide) {
16277 if (i < 1 && this.autoslide) {
16278 i = this.tabs.length;
16281 this.showPanel(this.tabs[i-1]);
16285 addBullet: function()
16287 if(!this.bullets || Roo.isTouch){
16290 var ctr = this.el.select('.carousel-bullets',true).first();
16291 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16292 var bullet = ctr.createChild({
16293 cls : 'bullet bullet-' + i
16294 },ctr.dom.lastChild);
16299 bullet.on('click', (function(e, el, o, ii, t){
16301 e.preventDefault();
16303 this.showPanel(ii);
16305 if(this.autoslide && this.slideFn){
16306 clearInterval(this.slideFn);
16307 this.slideFn = window.setInterval(function() {
16308 _this.showPanelNext();
16312 }).createDelegate(this, [i, bullet], true));
16317 setActiveBullet : function(i)
16323 Roo.each(this.el.select('.bullet', true).elements, function(el){
16324 el.removeClass('selected');
16327 var bullet = this.el.select('.bullet-' + i, true).first();
16333 bullet.addClass('selected');
16344 Roo.apply(Roo.bootstrap.TabGroup, {
16348 * register a Navigation Group
16349 * @param {Roo.bootstrap.NavGroup} the navgroup to add
16351 register : function(navgrp)
16353 this.groups[navgrp.navId] = navgrp;
16357 * fetch a Navigation Group based on the navigation ID
16358 * if one does not exist , it will get created.
16359 * @param {string} the navgroup to add
16360 * @returns {Roo.bootstrap.NavGroup} the navgroup
16362 get: function(navId) {
16363 if (typeof(this.groups[navId]) == 'undefined') {
16364 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16366 return this.groups[navId] ;
16381 * @class Roo.bootstrap.TabPanel
16382 * @extends Roo.bootstrap.Component
16383 * Bootstrap TabPanel class
16384 * @cfg {Boolean} active panel active
16385 * @cfg {String} html panel content
16386 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16387 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16391 * Create a new TabPanel
16392 * @param {Object} config The config object
16395 Roo.bootstrap.TabPanel = function(config){
16396 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16400 * Fires when the active status changes
16401 * @param {Roo.bootstrap.TabPanel} this
16402 * @param {Boolean} state the new state
16407 * @event beforedeactivate
16408 * Fires before a tab is de-activated - can be used to do validation on a form.
16409 * @param {Roo.bootstrap.TabPanel} this
16410 * @return {Boolean} false if there is an error
16413 'beforedeactivate': true
16416 this.tabId = this.tabId || Roo.id();
16420 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16427 getAutoCreate : function(){
16430 // item is needed for carousel - not sure if it has any effect otherwise
16431 cls: 'tab-pane item',
16432 html: this.html || ''
16436 cfg.cls += ' active';
16440 cfg.tabId = this.tabId;
16447 initEvents: function()
16449 Roo.log('-------- init events on tab panel ---------');
16451 var p = this.parent();
16452 this.navId = this.navId || p.navId;
16454 if (typeof(this.navId) != 'undefined') {
16455 // not really needed.. but just in case.. parent should be a NavGroup.
16456 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16460 var i = tg.tabs.length - 1;
16462 if(this.active && tg.bullets > 0 && i < tg.bullets){
16463 tg.setActiveBullet(i);
16470 onRender : function(ct, position)
16472 // Roo.log("Call onRender: " + this.xtype);
16474 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16482 setActive: function(state)
16484 Roo.log("panel - set active " + this.tabId + "=" + state);
16486 this.active = state;
16488 this.el.removeClass('active');
16490 } else if (!this.el.hasClass('active')) {
16491 this.el.addClass('active');
16494 this.fireEvent('changed', this, state);
16511 * @class Roo.bootstrap.DateField
16512 * @extends Roo.bootstrap.Input
16513 * Bootstrap DateField class
16514 * @cfg {Number} weekStart default 0
16515 * @cfg {String} viewMode default empty, (months|years)
16516 * @cfg {String} minViewMode default empty, (months|years)
16517 * @cfg {Number} startDate default -Infinity
16518 * @cfg {Number} endDate default Infinity
16519 * @cfg {Boolean} todayHighlight default false
16520 * @cfg {Boolean} todayBtn default false
16521 * @cfg {Boolean} calendarWeeks default false
16522 * @cfg {Object} daysOfWeekDisabled default empty
16523 * @cfg {Boolean} singleMode default false (true | false)
16525 * @cfg {Boolean} keyboardNavigation default true
16526 * @cfg {String} language default en
16529 * Create a new DateField
16530 * @param {Object} config The config object
16533 Roo.bootstrap.DateField = function(config){
16534 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16538 * Fires when this field show.
16539 * @param {Roo.bootstrap.DateField} this
16540 * @param {Mixed} date The date value
16545 * Fires when this field hide.
16546 * @param {Roo.bootstrap.DateField} this
16547 * @param {Mixed} date The date value
16552 * Fires when select a date.
16553 * @param {Roo.bootstrap.DateField} this
16554 * @param {Mixed} date The date value
16558 * @event beforeselect
16559 * Fires when before select a date.
16560 * @param {Roo.bootstrap.DateField} this
16561 * @param {Mixed} date The date value
16563 beforeselect : true
16567 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16570 * @cfg {String} format
16571 * The default date format string which can be overriden for localization support. The format must be
16572 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16576 * @cfg {String} altFormats
16577 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16578 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16580 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16588 todayHighlight : false,
16594 keyboardNavigation: true,
16596 calendarWeeks: false,
16598 startDate: -Infinity,
16602 daysOfWeekDisabled: [],
16606 singleMode : false,
16608 UTCDate: function()
16610 return new Date(Date.UTC.apply(Date, arguments));
16613 UTCToday: function()
16615 var today = new Date();
16616 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16619 getDate: function() {
16620 var d = this.getUTCDate();
16621 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16624 getUTCDate: function() {
16628 setDate: function(d) {
16629 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16632 setUTCDate: function(d) {
16634 this.setValue(this.formatDate(this.date));
16637 onRender: function(ct, position)
16640 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16642 this.language = this.language || 'en';
16643 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16644 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16646 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16647 this.format = this.format || 'm/d/y';
16648 this.isInline = false;
16649 this.isInput = true;
16650 this.component = this.el.select('.add-on', true).first() || false;
16651 this.component = (this.component && this.component.length === 0) ? false : this.component;
16652 this.hasInput = this.component && this.inputEL().length;
16654 if (typeof(this.minViewMode === 'string')) {
16655 switch (this.minViewMode) {
16657 this.minViewMode = 1;
16660 this.minViewMode = 2;
16663 this.minViewMode = 0;
16668 if (typeof(this.viewMode === 'string')) {
16669 switch (this.viewMode) {
16682 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16684 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16686 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16688 this.picker().on('mousedown', this.onMousedown, this);
16689 this.picker().on('click', this.onClick, this);
16691 this.picker().addClass('datepicker-dropdown');
16693 this.startViewMode = this.viewMode;
16695 if(this.singleMode){
16696 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16697 v.setVisibilityMode(Roo.Element.DISPLAY);
16701 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16702 v.setStyle('width', '189px');
16706 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16707 if(!this.calendarWeeks){
16712 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16713 v.attr('colspan', function(i, val){
16714 return parseInt(val) + 1;
16719 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16721 this.setStartDate(this.startDate);
16722 this.setEndDate(this.endDate);
16724 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16731 if(this.isInline) {
16736 picker : function()
16738 return this.pickerEl;
16739 // return this.el.select('.datepicker', true).first();
16742 fillDow: function()
16744 var dowCnt = this.weekStart;
16753 if(this.calendarWeeks){
16761 while (dowCnt < this.weekStart + 7) {
16765 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16769 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16772 fillMonths: function()
16775 var months = this.picker().select('>.datepicker-months td', true).first();
16777 months.dom.innerHTML = '';
16783 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16786 months.createChild(month);
16793 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;
16795 if (this.date < this.startDate) {
16796 this.viewDate = new Date(this.startDate);
16797 } else if (this.date > this.endDate) {
16798 this.viewDate = new Date(this.endDate);
16800 this.viewDate = new Date(this.date);
16808 var d = new Date(this.viewDate),
16809 year = d.getUTCFullYear(),
16810 month = d.getUTCMonth(),
16811 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16812 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16813 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16814 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16815 currentDate = this.date && this.date.valueOf(),
16816 today = this.UTCToday();
16818 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16820 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16822 // this.picker.select('>tfoot th.today').
16823 // .text(dates[this.language].today)
16824 // .toggle(this.todayBtn !== false);
16826 this.updateNavArrows();
16829 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16831 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16833 prevMonth.setUTCDate(day);
16835 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16837 var nextMonth = new Date(prevMonth);
16839 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16841 nextMonth = nextMonth.valueOf();
16843 var fillMonths = false;
16845 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16847 while(prevMonth.valueOf() < nextMonth) {
16850 if (prevMonth.getUTCDay() === this.weekStart) {
16852 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16860 if(this.calendarWeeks){
16861 // ISO 8601: First week contains first thursday.
16862 // ISO also states week starts on Monday, but we can be more abstract here.
16864 // Start of current week: based on weekstart/current date
16865 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16866 // Thursday of this week
16867 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16868 // First Thursday of year, year from thursday
16869 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16870 // Calendar week: ms between thursdays, div ms per day, div 7 days
16871 calWeek = (th - yth) / 864e5 / 7 + 1;
16873 fillMonths.cn.push({
16881 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16883 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16886 if (this.todayHighlight &&
16887 prevMonth.getUTCFullYear() == today.getFullYear() &&
16888 prevMonth.getUTCMonth() == today.getMonth() &&
16889 prevMonth.getUTCDate() == today.getDate()) {
16890 clsName += ' today';
16893 if (currentDate && prevMonth.valueOf() === currentDate) {
16894 clsName += ' active';
16897 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16898 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16899 clsName += ' disabled';
16902 fillMonths.cn.push({
16904 cls: 'day ' + clsName,
16905 html: prevMonth.getDate()
16908 prevMonth.setDate(prevMonth.getDate()+1);
16911 var currentYear = this.date && this.date.getUTCFullYear();
16912 var currentMonth = this.date && this.date.getUTCMonth();
16914 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16916 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16917 v.removeClass('active');
16919 if(currentYear === year && k === currentMonth){
16920 v.addClass('active');
16923 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16924 v.addClass('disabled');
16930 year = parseInt(year/10, 10) * 10;
16932 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16934 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16937 for (var i = -1; i < 11; i++) {
16938 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16940 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16948 showMode: function(dir)
16951 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16954 Roo.each(this.picker().select('>div',true).elements, function(v){
16955 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16958 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16963 if(this.isInline) {
16967 this.picker().removeClass(['bottom', 'top']);
16969 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16971 * place to the top of element!
16975 this.picker().addClass('top');
16976 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16981 this.picker().addClass('bottom');
16983 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16986 parseDate : function(value)
16988 if(!value || value instanceof Date){
16991 var v = Date.parseDate(value, this.format);
16992 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16993 v = Date.parseDate(value, 'Y-m-d');
16995 if(!v && this.altFormats){
16996 if(!this.altFormatsArray){
16997 this.altFormatsArray = this.altFormats.split("|");
16999 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17000 v = Date.parseDate(value, this.altFormatsArray[i]);
17006 formatDate : function(date, fmt)
17008 return (!date || !(date instanceof Date)) ?
17009 date : date.dateFormat(fmt || this.format);
17012 onFocus : function()
17014 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17018 onBlur : function()
17020 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17022 var d = this.inputEl().getValue();
17031 this.picker().show();
17035 this.fireEvent('show', this, this.date);
17040 if(this.isInline) {
17043 this.picker().hide();
17044 this.viewMode = this.startViewMode;
17047 this.fireEvent('hide', this, this.date);
17051 onMousedown: function(e)
17053 e.stopPropagation();
17054 e.preventDefault();
17059 Roo.bootstrap.DateField.superclass.keyup.call(this);
17063 setValue: function(v)
17065 if(this.fireEvent('beforeselect', this, v) !== false){
17066 var d = new Date(this.parseDate(v) ).clearTime();
17068 if(isNaN(d.getTime())){
17069 this.date = this.viewDate = '';
17070 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17074 v = this.formatDate(d);
17076 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17078 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17082 this.fireEvent('select', this, this.date);
17086 getValue: function()
17088 return this.formatDate(this.date);
17091 fireKey: function(e)
17093 if (!this.picker().isVisible()){
17094 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17100 var dateChanged = false,
17102 newDate, newViewDate;
17107 e.preventDefault();
17111 if (!this.keyboardNavigation) {
17114 dir = e.keyCode == 37 ? -1 : 1;
17117 newDate = this.moveYear(this.date, dir);
17118 newViewDate = this.moveYear(this.viewDate, dir);
17119 } else if (e.shiftKey){
17120 newDate = this.moveMonth(this.date, dir);
17121 newViewDate = this.moveMonth(this.viewDate, dir);
17123 newDate = new Date(this.date);
17124 newDate.setUTCDate(this.date.getUTCDate() + dir);
17125 newViewDate = new Date(this.viewDate);
17126 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17128 if (this.dateWithinRange(newDate)){
17129 this.date = newDate;
17130 this.viewDate = newViewDate;
17131 this.setValue(this.formatDate(this.date));
17133 e.preventDefault();
17134 dateChanged = true;
17139 if (!this.keyboardNavigation) {
17142 dir = e.keyCode == 38 ? -1 : 1;
17144 newDate = this.moveYear(this.date, dir);
17145 newViewDate = this.moveYear(this.viewDate, dir);
17146 } else if (e.shiftKey){
17147 newDate = this.moveMonth(this.date, dir);
17148 newViewDate = this.moveMonth(this.viewDate, dir);
17150 newDate = new Date(this.date);
17151 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17152 newViewDate = new Date(this.viewDate);
17153 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17155 if (this.dateWithinRange(newDate)){
17156 this.date = newDate;
17157 this.viewDate = newViewDate;
17158 this.setValue(this.formatDate(this.date));
17160 e.preventDefault();
17161 dateChanged = true;
17165 this.setValue(this.formatDate(this.date));
17167 e.preventDefault();
17170 this.setValue(this.formatDate(this.date));
17184 onClick: function(e)
17186 e.stopPropagation();
17187 e.preventDefault();
17189 var target = e.getTarget();
17191 if(target.nodeName.toLowerCase() === 'i'){
17192 target = Roo.get(target).dom.parentNode;
17195 var nodeName = target.nodeName;
17196 var className = target.className;
17197 var html = target.innerHTML;
17198 //Roo.log(nodeName);
17200 switch(nodeName.toLowerCase()) {
17202 switch(className) {
17208 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17209 switch(this.viewMode){
17211 this.viewDate = this.moveMonth(this.viewDate, dir);
17215 this.viewDate = this.moveYear(this.viewDate, dir);
17221 var date = new Date();
17222 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17224 this.setValue(this.formatDate(this.date));
17231 if (className.indexOf('disabled') < 0) {
17232 this.viewDate.setUTCDate(1);
17233 if (className.indexOf('month') > -1) {
17234 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17236 var year = parseInt(html, 10) || 0;
17237 this.viewDate.setUTCFullYear(year);
17241 if(this.singleMode){
17242 this.setValue(this.formatDate(this.viewDate));
17253 //Roo.log(className);
17254 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17255 var day = parseInt(html, 10) || 1;
17256 var year = this.viewDate.getUTCFullYear(),
17257 month = this.viewDate.getUTCMonth();
17259 if (className.indexOf('old') > -1) {
17266 } else if (className.indexOf('new') > -1) {
17274 //Roo.log([year,month,day]);
17275 this.date = this.UTCDate(year, month, day,0,0,0,0);
17276 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17278 //Roo.log(this.formatDate(this.date));
17279 this.setValue(this.formatDate(this.date));
17286 setStartDate: function(startDate)
17288 this.startDate = startDate || -Infinity;
17289 if (this.startDate !== -Infinity) {
17290 this.startDate = this.parseDate(this.startDate);
17293 this.updateNavArrows();
17296 setEndDate: function(endDate)
17298 this.endDate = endDate || Infinity;
17299 if (this.endDate !== Infinity) {
17300 this.endDate = this.parseDate(this.endDate);
17303 this.updateNavArrows();
17306 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17308 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17309 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17310 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17312 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17313 return parseInt(d, 10);
17316 this.updateNavArrows();
17319 updateNavArrows: function()
17321 if(this.singleMode){
17325 var d = new Date(this.viewDate),
17326 year = d.getUTCFullYear(),
17327 month = d.getUTCMonth();
17329 Roo.each(this.picker().select('.prev', true).elements, function(v){
17331 switch (this.viewMode) {
17334 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17340 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17347 Roo.each(this.picker().select('.next', true).elements, function(v){
17349 switch (this.viewMode) {
17352 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17358 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17366 moveMonth: function(date, dir)
17371 var new_date = new Date(date.valueOf()),
17372 day = new_date.getUTCDate(),
17373 month = new_date.getUTCMonth(),
17374 mag = Math.abs(dir),
17376 dir = dir > 0 ? 1 : -1;
17379 // If going back one month, make sure month is not current month
17380 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17382 return new_date.getUTCMonth() == month;
17384 // If going forward one month, make sure month is as expected
17385 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17387 return new_date.getUTCMonth() != new_month;
17389 new_month = month + dir;
17390 new_date.setUTCMonth(new_month);
17391 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17392 if (new_month < 0 || new_month > 11) {
17393 new_month = (new_month + 12) % 12;
17396 // For magnitudes >1, move one month at a time...
17397 for (var i=0; i<mag; i++) {
17398 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17399 new_date = this.moveMonth(new_date, dir);
17401 // ...then reset the day, keeping it in the new month
17402 new_month = new_date.getUTCMonth();
17403 new_date.setUTCDate(day);
17405 return new_month != new_date.getUTCMonth();
17408 // Common date-resetting loop -- if date is beyond end of month, make it
17411 new_date.setUTCDate(--day);
17412 new_date.setUTCMonth(new_month);
17417 moveYear: function(date, dir)
17419 return this.moveMonth(date, dir*12);
17422 dateWithinRange: function(date)
17424 return date >= this.startDate && date <= this.endDate;
17430 this.picker().remove();
17435 Roo.apply(Roo.bootstrap.DateField, {
17446 html: '<i class="fa fa-arrow-left"/>'
17456 html: '<i class="fa fa-arrow-right"/>'
17498 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17499 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17500 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17501 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17502 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17515 navFnc: 'FullYear',
17520 navFnc: 'FullYear',
17525 Roo.apply(Roo.bootstrap.DateField, {
17529 cls: 'datepicker dropdown-menu roo-dynamic',
17533 cls: 'datepicker-days',
17537 cls: 'table-condensed',
17539 Roo.bootstrap.DateField.head,
17543 Roo.bootstrap.DateField.footer
17550 cls: 'datepicker-months',
17554 cls: 'table-condensed',
17556 Roo.bootstrap.DateField.head,
17557 Roo.bootstrap.DateField.content,
17558 Roo.bootstrap.DateField.footer
17565 cls: 'datepicker-years',
17569 cls: 'table-condensed',
17571 Roo.bootstrap.DateField.head,
17572 Roo.bootstrap.DateField.content,
17573 Roo.bootstrap.DateField.footer
17592 * @class Roo.bootstrap.TimeField
17593 * @extends Roo.bootstrap.Input
17594 * Bootstrap DateField class
17598 * Create a new TimeField
17599 * @param {Object} config The config object
17602 Roo.bootstrap.TimeField = function(config){
17603 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17607 * Fires when this field show.
17608 * @param {Roo.bootstrap.DateField} thisthis
17609 * @param {Mixed} date The date value
17614 * Fires when this field hide.
17615 * @param {Roo.bootstrap.DateField} this
17616 * @param {Mixed} date The date value
17621 * Fires when select a date.
17622 * @param {Roo.bootstrap.DateField} this
17623 * @param {Mixed} date The date value
17629 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17632 * @cfg {String} format
17633 * The default time format string which can be overriden for localization support. The format must be
17634 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17638 onRender: function(ct, position)
17641 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17643 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17645 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17647 this.pop = this.picker().select('>.datepicker-time',true).first();
17648 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17650 this.picker().on('mousedown', this.onMousedown, this);
17651 this.picker().on('click', this.onClick, this);
17653 this.picker().addClass('datepicker-dropdown');
17658 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17659 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17660 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17661 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17662 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17663 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17667 fireKey: function(e){
17668 if (!this.picker().isVisible()){
17669 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17675 e.preventDefault();
17683 this.onTogglePeriod();
17686 this.onIncrementMinutes();
17689 this.onDecrementMinutes();
17698 onClick: function(e) {
17699 e.stopPropagation();
17700 e.preventDefault();
17703 picker : function()
17705 return this.el.select('.datepicker', true).first();
17708 fillTime: function()
17710 var time = this.pop.select('tbody', true).first();
17712 time.dom.innerHTML = '';
17727 cls: 'hours-up glyphicon glyphicon-chevron-up'
17747 cls: 'minutes-up glyphicon glyphicon-chevron-up'
17768 cls: 'timepicker-hour',
17783 cls: 'timepicker-minute',
17798 cls: 'btn btn-primary period',
17820 cls: 'hours-down glyphicon glyphicon-chevron-down'
17840 cls: 'minutes-down glyphicon glyphicon-chevron-down'
17858 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17865 var hours = this.time.getHours();
17866 var minutes = this.time.getMinutes();
17879 hours = hours - 12;
17883 hours = '0' + hours;
17887 minutes = '0' + minutes;
17890 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17891 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17892 this.pop.select('button', true).first().dom.innerHTML = period;
17898 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17900 var cls = ['bottom'];
17902 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17909 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17914 this.picker().addClass(cls.join('-'));
17918 Roo.each(cls, function(c){
17920 _this.picker().setTop(_this.inputEl().getHeight());
17924 _this.picker().setTop(0 - _this.picker().getHeight());
17929 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17933 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17940 onFocus : function()
17942 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17946 onBlur : function()
17948 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17954 this.picker().show();
17959 this.fireEvent('show', this, this.date);
17964 this.picker().hide();
17967 this.fireEvent('hide', this, this.date);
17970 setTime : function()
17973 this.setValue(this.time.format(this.format));
17975 this.fireEvent('select', this, this.date);
17980 onMousedown: function(e){
17981 e.stopPropagation();
17982 e.preventDefault();
17985 onIncrementHours: function()
17987 Roo.log('onIncrementHours');
17988 this.time = this.time.add(Date.HOUR, 1);
17993 onDecrementHours: function()
17995 Roo.log('onDecrementHours');
17996 this.time = this.time.add(Date.HOUR, -1);
18000 onIncrementMinutes: function()
18002 Roo.log('onIncrementMinutes');
18003 this.time = this.time.add(Date.MINUTE, 1);
18007 onDecrementMinutes: function()
18009 Roo.log('onDecrementMinutes');
18010 this.time = this.time.add(Date.MINUTE, -1);
18014 onTogglePeriod: function()
18016 Roo.log('onTogglePeriod');
18017 this.time = this.time.add(Date.HOUR, 12);
18024 Roo.apply(Roo.bootstrap.TimeField, {
18054 cls: 'btn btn-info ok',
18066 Roo.apply(Roo.bootstrap.TimeField, {
18070 cls: 'datepicker dropdown-menu',
18074 cls: 'datepicker-time',
18078 cls: 'table-condensed',
18080 Roo.bootstrap.TimeField.content,
18081 Roo.bootstrap.TimeField.footer
18100 * @class Roo.bootstrap.MonthField
18101 * @extends Roo.bootstrap.Input
18102 * Bootstrap MonthField class
18104 * @cfg {String} language default en
18107 * Create a new MonthField
18108 * @param {Object} config The config object
18111 Roo.bootstrap.MonthField = function(config){
18112 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18117 * Fires when this field show.
18118 * @param {Roo.bootstrap.MonthField} this
18119 * @param {Mixed} date The date value
18124 * Fires when this field hide.
18125 * @param {Roo.bootstrap.MonthField} this
18126 * @param {Mixed} date The date value
18131 * Fires when select a date.
18132 * @param {Roo.bootstrap.MonthField} this
18133 * @param {String} oldvalue The old value
18134 * @param {String} newvalue The new value
18140 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
18142 onRender: function(ct, position)
18145 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18147 this.language = this.language || 'en';
18148 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18149 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18151 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18152 this.isInline = false;
18153 this.isInput = true;
18154 this.component = this.el.select('.add-on', true).first() || false;
18155 this.component = (this.component && this.component.length === 0) ? false : this.component;
18156 this.hasInput = this.component && this.inputEL().length;
18158 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18160 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18162 this.picker().on('mousedown', this.onMousedown, this);
18163 this.picker().on('click', this.onClick, this);
18165 this.picker().addClass('datepicker-dropdown');
18167 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18168 v.setStyle('width', '189px');
18175 if(this.isInline) {
18181 setValue: function(v, suppressEvent)
18183 var o = this.getValue();
18185 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18189 if(suppressEvent !== true){
18190 this.fireEvent('select', this, o, v);
18195 getValue: function()
18200 onClick: function(e)
18202 e.stopPropagation();
18203 e.preventDefault();
18205 var target = e.getTarget();
18207 if(target.nodeName.toLowerCase() === 'i'){
18208 target = Roo.get(target).dom.parentNode;
18211 var nodeName = target.nodeName;
18212 var className = target.className;
18213 var html = target.innerHTML;
18215 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18219 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18221 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18227 picker : function()
18229 return this.pickerEl;
18232 fillMonths: function()
18235 var months = this.picker().select('>.datepicker-months td', true).first();
18237 months.dom.innerHTML = '';
18243 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18246 months.createChild(month);
18255 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18256 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18259 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18260 e.removeClass('active');
18262 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18263 e.addClass('active');
18270 if(this.isInline) {
18274 this.picker().removeClass(['bottom', 'top']);
18276 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18278 * place to the top of element!
18282 this.picker().addClass('top');
18283 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18288 this.picker().addClass('bottom');
18290 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18293 onFocus : function()
18295 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18299 onBlur : function()
18301 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18303 var d = this.inputEl().getValue();
18312 this.picker().show();
18313 this.picker().select('>.datepicker-months', true).first().show();
18317 this.fireEvent('show', this, this.date);
18322 if(this.isInline) {
18325 this.picker().hide();
18326 this.fireEvent('hide', this, this.date);
18330 onMousedown: function(e)
18332 e.stopPropagation();
18333 e.preventDefault();
18338 Roo.bootstrap.MonthField.superclass.keyup.call(this);
18342 fireKey: function(e)
18344 if (!this.picker().isVisible()){
18345 if (e.keyCode == 27) {// allow escape to hide and re-show picker
18356 e.preventDefault();
18360 dir = e.keyCode == 37 ? -1 : 1;
18362 this.vIndex = this.vIndex + dir;
18364 if(this.vIndex < 0){
18368 if(this.vIndex > 11){
18372 if(isNaN(this.vIndex)){
18376 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18382 dir = e.keyCode == 38 ? -1 : 1;
18384 this.vIndex = this.vIndex + dir * 4;
18386 if(this.vIndex < 0){
18390 if(this.vIndex > 11){
18394 if(isNaN(this.vIndex)){
18398 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18403 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18404 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18408 e.preventDefault();
18411 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18412 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18428 this.picker().remove();
18433 Roo.apply(Roo.bootstrap.MonthField, {
18452 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18453 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18458 Roo.apply(Roo.bootstrap.MonthField, {
18462 cls: 'datepicker dropdown-menu roo-dynamic',
18466 cls: 'datepicker-months',
18470 cls: 'table-condensed',
18472 Roo.bootstrap.DateField.content
18492 * @class Roo.bootstrap.CheckBox
18493 * @extends Roo.bootstrap.Input
18494 * Bootstrap CheckBox class
18496 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18497 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18498 * @cfg {String} boxLabel The text that appears beside the checkbox
18499 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18500 * @cfg {Boolean} checked initnal the element
18501 * @cfg {Boolean} inline inline the element (default false)
18502 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18505 * Create a new CheckBox
18506 * @param {Object} config The config object
18509 Roo.bootstrap.CheckBox = function(config){
18510 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18515 * Fires when the element is checked or unchecked.
18516 * @param {Roo.bootstrap.CheckBox} this This input
18517 * @param {Boolean} checked The new checked value
18524 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18526 inputType: 'checkbox',
18534 getAutoCreate : function()
18536 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18542 cfg.cls = 'form-group ' + this.inputType; //input-group
18545 cfg.cls += ' ' + this.inputType + '-inline';
18551 type : this.inputType,
18552 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18553 cls : 'roo-' + this.inputType, //'form-box',
18554 placeholder : this.placeholder || ''
18558 if (this.weight) { // Validity check?
18559 cfg.cls += " " + this.inputType + "-" + this.weight;
18562 if (this.disabled) {
18563 input.disabled=true;
18567 input.checked = this.checked;
18571 input.name = this.name;
18575 input.cls += ' input-' + this.size;
18580 ['xs','sm','md','lg'].map(function(size){
18581 if (settings[size]) {
18582 cfg.cls += ' col-' + size + '-' + settings[size];
18586 var inputblock = input;
18588 if (this.before || this.after) {
18591 cls : 'input-group',
18596 inputblock.cn.push({
18598 cls : 'input-group-addon',
18603 inputblock.cn.push(input);
18606 inputblock.cn.push({
18608 cls : 'input-group-addon',
18615 if (align ==='left' && this.fieldLabel.length) {
18616 Roo.log("left and has label");
18622 cls : 'control-label col-md-' + this.labelWidth,
18623 html : this.fieldLabel
18627 cls : "col-md-" + (12 - this.labelWidth),
18634 } else if ( this.fieldLabel.length) {
18639 tag: this.boxLabel ? 'span' : 'label',
18641 cls: 'control-label box-input-label',
18642 //cls : 'input-group-addon',
18643 html : this.fieldLabel
18653 Roo.log(" no label && no align");
18654 cfg.cn = [ inputblock ] ;
18659 var boxLabelCfg = {
18661 //'for': id, // box label is handled by onclick - so no for...
18663 html: this.boxLabel
18667 boxLabelCfg.tooltip = this.tooltip;
18670 cfg.cn.push(boxLabelCfg);
18680 * return the real input element.
18682 inputEl: function ()
18684 return this.el.select('input.roo-' + this.inputType,true).first();
18687 labelEl: function()
18689 return this.el.select('label.control-label',true).first();
18691 /* depricated... */
18695 return this.labelEl();
18698 initEvents : function()
18700 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18702 this.inputEl().on('click', this.onClick, this);
18704 if (this.boxLabel) {
18705 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18708 this.startValue = this.getValue();
18711 Roo.bootstrap.CheckBox.register(this);
18715 onClick : function()
18717 this.setChecked(!this.checked);
18720 setChecked : function(state,suppressEvent)
18722 this.startValue = this.getValue();
18724 if(this.inputType == 'radio'){
18726 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18727 e.dom.checked = false;
18730 this.inputEl().dom.checked = true;
18732 this.inputEl().dom.value = this.inputValue;
18734 if(suppressEvent !== true){
18735 this.fireEvent('check', this, true);
18743 this.checked = state;
18745 this.inputEl().dom.checked = state;
18747 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18749 if(suppressEvent !== true){
18750 this.fireEvent('check', this, state);
18756 getValue : function()
18758 if(this.inputType == 'radio'){
18759 return this.getGroupValue();
18762 return this.inputEl().getValue();
18766 getGroupValue : function()
18768 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18772 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18775 setValue : function(v,suppressEvent)
18777 if(this.inputType == 'radio'){
18778 this.setGroupValue(v, suppressEvent);
18782 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18787 setGroupValue : function(v, suppressEvent)
18789 this.startValue = this.getValue();
18791 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18792 e.dom.checked = false;
18794 if(e.dom.value == v){
18795 e.dom.checked = true;
18799 if(suppressEvent !== true){
18800 this.fireEvent('check', this, true);
18808 validate : function()
18812 (this.inputType == 'radio' && this.validateRadio()) ||
18813 (this.inputType == 'checkbox' && this.validateCheckbox())
18819 this.markInvalid();
18823 validateRadio : function()
18827 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18828 if(!e.dom.checked){
18840 validateCheckbox : function()
18843 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18846 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18854 for(var i in group){
18859 r = (group[i].getValue() == group[i].inputValue) ? true : false;
18866 * Mark this field as valid
18868 markValid : function()
18870 if(this.allowBlank){
18876 this.fireEvent('valid', this);
18878 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18881 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18888 if(this.inputType == 'radio'){
18889 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18890 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18891 e.findParent('.form-group', false, true).addClass(_this.validClass);
18898 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18899 this.el.findParent('.form-group', false, true).addClass(this.validClass);
18903 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18909 for(var i in group){
18910 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18911 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18916 * Mark this field as invalid
18917 * @param {String} msg The validation message
18919 markInvalid : function(msg)
18921 if(this.allowBlank){
18927 this.fireEvent('invalid', this, msg);
18929 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18932 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18936 label.markInvalid();
18939 if(this.inputType == 'radio'){
18940 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18941 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18942 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18949 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18950 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18954 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18960 for(var i in group){
18961 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18962 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18969 Roo.apply(Roo.bootstrap.CheckBox, {
18974 * register a CheckBox Group
18975 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18977 register : function(checkbox)
18979 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18980 this.groups[checkbox.groupId] = {};
18983 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18987 this.groups[checkbox.groupId][checkbox.name] = checkbox;
18991 * fetch a CheckBox Group based on the group ID
18992 * @param {string} the group ID
18993 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18995 get: function(groupId) {
18996 if (typeof(this.groups[groupId]) == 'undefined') {
19000 return this.groups[groupId] ;
19012 *<div class="radio">
19014 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19015 Option one is this and that—be sure to include why it's great
19022 *<label class="radio-inline">fieldLabel</label>
19023 *<label class="radio-inline">
19024 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19032 * @class Roo.bootstrap.Radio
19033 * @extends Roo.bootstrap.CheckBox
19034 * Bootstrap Radio class
19037 * Create a new Radio
19038 * @param {Object} config The config object
19041 Roo.bootstrap.Radio = function(config){
19042 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19046 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
19048 inputType: 'radio',
19052 getAutoCreate : function()
19054 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19055 align = align || 'left'; // default...
19062 tag : this.inline ? 'span' : 'div',
19067 var inline = this.inline ? ' radio-inline' : '';
19071 // does not need for, as we wrap the input with it..
19073 cls : 'control-label box-label' + inline,
19076 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19080 //cls : 'control-label' + inline,
19081 html : this.fieldLabel,
19082 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19091 type : this.inputType,
19092 //value : (!this.checked) ? this.valueOff : this.inputValue,
19093 value : this.inputValue,
19095 placeholder : this.placeholder || '' // ?? needed????
19098 if (this.weight) { // Validity check?
19099 input.cls += " radio-" + this.weight;
19101 if (this.disabled) {
19102 input.disabled=true;
19106 input.checked = this.checked;
19110 input.name = this.name;
19114 input.cls += ' input-' + this.size;
19117 //?? can span's inline have a width??
19120 ['xs','sm','md','lg'].map(function(size){
19121 if (settings[size]) {
19122 cfg.cls += ' col-' + size + '-' + settings[size];
19126 var inputblock = input;
19128 if (this.before || this.after) {
19131 cls : 'input-group',
19136 inputblock.cn.push({
19138 cls : 'input-group-addon',
19142 inputblock.cn.push(input);
19144 inputblock.cn.push({
19146 cls : 'input-group-addon',
19154 if (this.fieldLabel && this.fieldLabel.length) {
19155 cfg.cn.push(fieldLabel);
19158 // normal bootstrap puts the input inside the label.
19159 // however with our styled version - it has to go after the input.
19161 //lbl.cn.push(inputblock);
19165 cls: 'radio' + inline,
19172 cfg.cn.push( lblwrap);
19177 html: this.boxLabel
19186 initEvents : function()
19188 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19190 this.inputEl().on('click', this.onClick, this);
19191 if (this.boxLabel) {
19192 //Roo.log('find label');
19193 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
19198 inputEl: function ()
19200 return this.el.select('input.roo-radio',true).first();
19202 onClick : function()
19205 this.setChecked(true);
19208 setChecked : function(state,suppressEvent)
19211 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19212 v.dom.checked = false;
19215 Roo.log(this.inputEl().dom);
19216 this.checked = state;
19217 this.inputEl().dom.checked = state;
19219 if(suppressEvent !== true){
19220 this.fireEvent('check', this, state);
19223 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19227 getGroupValue : function()
19230 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19231 if(v.dom.checked == true){
19232 value = v.dom.value;
19240 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
19241 * @return {Mixed} value The field value
19243 getValue : function(){
19244 return this.getGroupValue();
19250 //<script type="text/javascript">
19253 * Based Ext JS Library 1.1.1
19254 * Copyright(c) 2006-2007, Ext JS, LLC.
19260 * @class Roo.HtmlEditorCore
19261 * @extends Roo.Component
19262 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19264 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19267 Roo.HtmlEditorCore = function(config){
19270 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19275 * @event initialize
19276 * Fires when the editor is fully initialized (including the iframe)
19277 * @param {Roo.HtmlEditorCore} this
19282 * Fires when the editor is first receives the focus. Any insertion must wait
19283 * until after this event.
19284 * @param {Roo.HtmlEditorCore} this
19288 * @event beforesync
19289 * Fires before the textarea is updated with content from the editor iframe. Return false
19290 * to cancel the sync.
19291 * @param {Roo.HtmlEditorCore} this
19292 * @param {String} html
19296 * @event beforepush
19297 * Fires before the iframe editor is updated with content from the textarea. Return false
19298 * to cancel the push.
19299 * @param {Roo.HtmlEditorCore} this
19300 * @param {String} html
19305 * Fires when the textarea is updated with content from the editor iframe.
19306 * @param {Roo.HtmlEditorCore} this
19307 * @param {String} html
19312 * Fires when the iframe editor is updated with content from the textarea.
19313 * @param {Roo.HtmlEditorCore} this
19314 * @param {String} html
19319 * @event editorevent
19320 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19321 * @param {Roo.HtmlEditorCore} this
19327 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19329 // defaults : white / black...
19330 this.applyBlacklists();
19337 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
19341 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
19347 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
19352 * @cfg {Number} height (in pixels)
19356 * @cfg {Number} width (in pixels)
19361 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19364 stylesheets: false,
19369 // private properties
19370 validationEvent : false,
19372 initialized : false,
19374 sourceEditMode : false,
19375 onFocus : Roo.emptyFn,
19377 hideMode:'offsets',
19381 // blacklist + whitelisted elements..
19388 * Protected method that will not generally be called directly. It
19389 * is called when the editor initializes the iframe with HTML contents. Override this method if you
19390 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19392 getDocMarkup : function(){
19396 // inherit styels from page...??
19397 if (this.stylesheets === false) {
19399 Roo.get(document.head).select('style').each(function(node) {
19400 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19403 Roo.get(document.head).select('link').each(function(node) {
19404 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19407 } else if (!this.stylesheets.length) {
19409 st = '<style type="text/css">' +
19410 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19416 st += '<style type="text/css">' +
19417 'IMG { cursor: pointer } ' +
19421 return '<html><head>' + st +
19422 //<style type="text/css">' +
19423 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19425 ' </head><body class="roo-htmleditor-body"></body></html>';
19429 onRender : function(ct, position)
19432 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19433 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19436 this.el.dom.style.border = '0 none';
19437 this.el.dom.setAttribute('tabIndex', -1);
19438 this.el.addClass('x-hidden hide');
19442 if(Roo.isIE){ // fix IE 1px bogus margin
19443 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19447 this.frameId = Roo.id();
19451 var iframe = this.owner.wrap.createChild({
19453 cls: 'form-control', // bootstrap..
19455 name: this.frameId,
19456 frameBorder : 'no',
19457 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19462 this.iframe = iframe.dom;
19464 this.assignDocWin();
19466 this.doc.designMode = 'on';
19469 this.doc.write(this.getDocMarkup());
19473 var task = { // must defer to wait for browser to be ready
19475 //console.log("run task?" + this.doc.readyState);
19476 this.assignDocWin();
19477 if(this.doc.body || this.doc.readyState == 'complete'){
19479 this.doc.designMode="on";
19483 Roo.TaskMgr.stop(task);
19484 this.initEditor.defer(10, this);
19491 Roo.TaskMgr.start(task);
19496 onResize : function(w, h)
19498 Roo.log('resize: ' +w + ',' + h );
19499 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19503 if(typeof w == 'number'){
19505 this.iframe.style.width = w + 'px';
19507 if(typeof h == 'number'){
19509 this.iframe.style.height = h + 'px';
19511 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19518 * Toggles the editor between standard and source edit mode.
19519 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19521 toggleSourceEdit : function(sourceEditMode){
19523 this.sourceEditMode = sourceEditMode === true;
19525 if(this.sourceEditMode){
19527 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19530 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19531 //this.iframe.className = '';
19534 //this.setSize(this.owner.wrap.getSize());
19535 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19542 * Protected method that will not generally be called directly. If you need/want
19543 * custom HTML cleanup, this is the method you should override.
19544 * @param {String} html The HTML to be cleaned
19545 * return {String} The cleaned HTML
19547 cleanHtml : function(html){
19548 html = String(html);
19549 if(html.length > 5){
19550 if(Roo.isSafari){ // strip safari nonsense
19551 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19554 if(html == ' '){
19561 * HTML Editor -> Textarea
19562 * Protected method that will not generally be called directly. Syncs the contents
19563 * of the editor iframe with the textarea.
19565 syncValue : function(){
19566 if(this.initialized){
19567 var bd = (this.doc.body || this.doc.documentElement);
19568 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19569 var html = bd.innerHTML;
19571 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19572 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19574 html = '<div style="'+m[0]+'">' + html + '</div>';
19577 html = this.cleanHtml(html);
19578 // fix up the special chars.. normaly like back quotes in word...
19579 // however we do not want to do this with chinese..
19580 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19581 var cc = b.charCodeAt();
19583 (cc >= 0x4E00 && cc < 0xA000 ) ||
19584 (cc >= 0x3400 && cc < 0x4E00 ) ||
19585 (cc >= 0xf900 && cc < 0xfb00 )
19591 if(this.owner.fireEvent('beforesync', this, html) !== false){
19592 this.el.dom.value = html;
19593 this.owner.fireEvent('sync', this, html);
19599 * Protected method that will not generally be called directly. Pushes the value of the textarea
19600 * into the iframe editor.
19602 pushValue : function(){
19603 if(this.initialized){
19604 var v = this.el.dom.value.trim();
19606 // if(v.length < 1){
19610 if(this.owner.fireEvent('beforepush', this, v) !== false){
19611 var d = (this.doc.body || this.doc.documentElement);
19613 this.cleanUpPaste();
19614 this.el.dom.value = d.innerHTML;
19615 this.owner.fireEvent('push', this, v);
19621 deferFocus : function(){
19622 this.focus.defer(10, this);
19626 focus : function(){
19627 if(this.win && !this.sourceEditMode){
19634 assignDocWin: function()
19636 var iframe = this.iframe;
19639 this.doc = iframe.contentWindow.document;
19640 this.win = iframe.contentWindow;
19642 // if (!Roo.get(this.frameId)) {
19645 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19646 // this.win = Roo.get(this.frameId).dom.contentWindow;
19648 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19652 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19653 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19658 initEditor : function(){
19659 //console.log("INIT EDITOR");
19660 this.assignDocWin();
19664 this.doc.designMode="on";
19666 this.doc.write(this.getDocMarkup());
19669 var dbody = (this.doc.body || this.doc.documentElement);
19670 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19671 // this copies styles from the containing element into thsi one..
19672 // not sure why we need all of this..
19673 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19675 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19676 //ss['background-attachment'] = 'fixed'; // w3c
19677 dbody.bgProperties = 'fixed'; // ie
19678 //Roo.DomHelper.applyStyles(dbody, ss);
19679 Roo.EventManager.on(this.doc, {
19680 //'mousedown': this.onEditorEvent,
19681 'mouseup': this.onEditorEvent,
19682 'dblclick': this.onEditorEvent,
19683 'click': this.onEditorEvent,
19684 'keyup': this.onEditorEvent,
19689 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19691 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19692 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19694 this.initialized = true;
19696 this.owner.fireEvent('initialize', this);
19701 onDestroy : function(){
19707 //for (var i =0; i < this.toolbars.length;i++) {
19708 // // fixme - ask toolbars for heights?
19709 // this.toolbars[i].onDestroy();
19712 //this.wrap.dom.innerHTML = '';
19713 //this.wrap.remove();
19718 onFirstFocus : function(){
19720 this.assignDocWin();
19723 this.activated = true;
19726 if(Roo.isGecko){ // prevent silly gecko errors
19728 var s = this.win.getSelection();
19729 if(!s.focusNode || s.focusNode.nodeType != 3){
19730 var r = s.getRangeAt(0);
19731 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19736 this.execCmd('useCSS', true);
19737 this.execCmd('styleWithCSS', false);
19740 this.owner.fireEvent('activate', this);
19744 adjustFont: function(btn){
19745 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19746 //if(Roo.isSafari){ // safari
19749 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19750 if(Roo.isSafari){ // safari
19751 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19752 v = (v < 10) ? 10 : v;
19753 v = (v > 48) ? 48 : v;
19754 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19759 v = Math.max(1, v+adjust);
19761 this.execCmd('FontSize', v );
19764 onEditorEvent : function(e)
19766 this.owner.fireEvent('editorevent', this, e);
19767 // this.updateToolbar();
19768 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19771 insertTag : function(tg)
19773 // could be a bit smarter... -> wrap the current selected tRoo..
19774 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19776 range = this.createRange(this.getSelection());
19777 var wrappingNode = this.doc.createElement(tg.toLowerCase());
19778 wrappingNode.appendChild(range.extractContents());
19779 range.insertNode(wrappingNode);
19786 this.execCmd("formatblock", tg);
19790 insertText : function(txt)
19794 var range = this.createRange();
19795 range.deleteContents();
19796 //alert(Sender.getAttribute('label'));
19798 range.insertNode(this.doc.createTextNode(txt));
19804 * Executes a Midas editor command on the editor document and performs necessary focus and
19805 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19806 * @param {String} cmd The Midas command
19807 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19809 relayCmd : function(cmd, value){
19811 this.execCmd(cmd, value);
19812 this.owner.fireEvent('editorevent', this);
19813 //this.updateToolbar();
19814 this.owner.deferFocus();
19818 * Executes a Midas editor command directly on the editor document.
19819 * For visual commands, you should use {@link #relayCmd} instead.
19820 * <b>This should only be called after the editor is initialized.</b>
19821 * @param {String} cmd The Midas command
19822 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19824 execCmd : function(cmd, value){
19825 this.doc.execCommand(cmd, false, value === undefined ? null : value);
19832 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19834 * @param {String} text | dom node..
19836 insertAtCursor : function(text)
19841 if(!this.activated){
19847 var r = this.doc.selection.createRange();
19858 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19862 // from jquery ui (MIT licenced)
19864 var win = this.win;
19866 if (win.getSelection && win.getSelection().getRangeAt) {
19867 range = win.getSelection().getRangeAt(0);
19868 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19869 range.insertNode(node);
19870 } else if (win.document.selection && win.document.selection.createRange) {
19871 // no firefox support
19872 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19873 win.document.selection.createRange().pasteHTML(txt);
19875 // no firefox support
19876 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19877 this.execCmd('InsertHTML', txt);
19886 mozKeyPress : function(e){
19888 var c = e.getCharCode(), cmd;
19891 c = String.fromCharCode(c).toLowerCase();
19905 this.cleanUpPaste.defer(100, this);
19913 e.preventDefault();
19921 fixKeys : function(){ // load time branching for fastest keydown performance
19923 return function(e){
19924 var k = e.getKey(), r;
19927 r = this.doc.selection.createRange();
19930 r.pasteHTML('    ');
19937 r = this.doc.selection.createRange();
19939 var target = r.parentElement();
19940 if(!target || target.tagName.toLowerCase() != 'li'){
19942 r.pasteHTML('<br />');
19948 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19949 this.cleanUpPaste.defer(100, this);
19955 }else if(Roo.isOpera){
19956 return function(e){
19957 var k = e.getKey();
19961 this.execCmd('InsertHTML','    ');
19964 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19965 this.cleanUpPaste.defer(100, this);
19970 }else if(Roo.isSafari){
19971 return function(e){
19972 var k = e.getKey();
19976 this.execCmd('InsertText','\t');
19980 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19981 this.cleanUpPaste.defer(100, this);
19989 getAllAncestors: function()
19991 var p = this.getSelectedNode();
19994 a.push(p); // push blank onto stack..
19995 p = this.getParentElement();
19999 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20003 a.push(this.doc.body);
20007 lastSelNode : false,
20010 getSelection : function()
20012 this.assignDocWin();
20013 return Roo.isIE ? this.doc.selection : this.win.getSelection();
20016 getSelectedNode: function()
20018 // this may only work on Gecko!!!
20020 // should we cache this!!!!
20025 var range = this.createRange(this.getSelection()).cloneRange();
20028 var parent = range.parentElement();
20030 var testRange = range.duplicate();
20031 testRange.moveToElementText(parent);
20032 if (testRange.inRange(range)) {
20035 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20038 parent = parent.parentElement;
20043 // is ancestor a text element.
20044 var ac = range.commonAncestorContainer;
20045 if (ac.nodeType == 3) {
20046 ac = ac.parentNode;
20049 var ar = ac.childNodes;
20052 var other_nodes = [];
20053 var has_other_nodes = false;
20054 for (var i=0;i<ar.length;i++) {
20055 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
20058 // fullly contained node.
20060 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20065 // probably selected..
20066 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20067 other_nodes.push(ar[i]);
20071 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
20076 has_other_nodes = true;
20078 if (!nodes.length && other_nodes.length) {
20079 nodes= other_nodes;
20081 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20087 createRange: function(sel)
20089 // this has strange effects when using with
20090 // top toolbar - not sure if it's a great idea.
20091 //this.editor.contentWindow.focus();
20092 if (typeof sel != "undefined") {
20094 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20096 return this.doc.createRange();
20099 return this.doc.createRange();
20102 getParentElement: function()
20105 this.assignDocWin();
20106 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20108 var range = this.createRange(sel);
20111 var p = range.commonAncestorContainer;
20112 while (p.nodeType == 3) { // text node
20123 * Range intersection.. the hard stuff...
20127 * [ -- selected range --- ]
20131 * if end is before start or hits it. fail.
20132 * if start is after end or hits it fail.
20134 * if either hits (but other is outside. - then it's not
20140 // @see http://www.thismuchiknow.co.uk/?p=64.
20141 rangeIntersectsNode : function(range, node)
20143 var nodeRange = node.ownerDocument.createRange();
20145 nodeRange.selectNode(node);
20147 nodeRange.selectNodeContents(node);
20150 var rangeStartRange = range.cloneRange();
20151 rangeStartRange.collapse(true);
20153 var rangeEndRange = range.cloneRange();
20154 rangeEndRange.collapse(false);
20156 var nodeStartRange = nodeRange.cloneRange();
20157 nodeStartRange.collapse(true);
20159 var nodeEndRange = nodeRange.cloneRange();
20160 nodeEndRange.collapse(false);
20162 return rangeStartRange.compareBoundaryPoints(
20163 Range.START_TO_START, nodeEndRange) == -1 &&
20164 rangeEndRange.compareBoundaryPoints(
20165 Range.START_TO_START, nodeStartRange) == 1;
20169 rangeCompareNode : function(range, node)
20171 var nodeRange = node.ownerDocument.createRange();
20173 nodeRange.selectNode(node);
20175 nodeRange.selectNodeContents(node);
20179 range.collapse(true);
20181 nodeRange.collapse(true);
20183 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20184 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
20186 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20188 var nodeIsBefore = ss == 1;
20189 var nodeIsAfter = ee == -1;
20191 if (nodeIsBefore && nodeIsAfter) {
20194 if (!nodeIsBefore && nodeIsAfter) {
20195 return 1; //right trailed.
20198 if (nodeIsBefore && !nodeIsAfter) {
20199 return 2; // left trailed.
20205 // private? - in a new class?
20206 cleanUpPaste : function()
20208 // cleans up the whole document..
20209 Roo.log('cleanuppaste');
20211 this.cleanUpChildren(this.doc.body);
20212 var clean = this.cleanWordChars(this.doc.body.innerHTML);
20213 if (clean != this.doc.body.innerHTML) {
20214 this.doc.body.innerHTML = clean;
20219 cleanWordChars : function(input) {// change the chars to hex code
20220 var he = Roo.HtmlEditorCore;
20222 var output = input;
20223 Roo.each(he.swapCodes, function(sw) {
20224 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20226 output = output.replace(swapper, sw[1]);
20233 cleanUpChildren : function (n)
20235 if (!n.childNodes.length) {
20238 for (var i = n.childNodes.length-1; i > -1 ; i--) {
20239 this.cleanUpChild(n.childNodes[i]);
20246 cleanUpChild : function (node)
20249 //console.log(node);
20250 if (node.nodeName == "#text") {
20251 // clean up silly Windows -- stuff?
20254 if (node.nodeName == "#comment") {
20255 node.parentNode.removeChild(node);
20256 // clean up silly Windows -- stuff?
20259 var lcname = node.tagName.toLowerCase();
20260 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20261 // whitelist of tags..
20263 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20265 node.parentNode.removeChild(node);
20270 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20272 // remove <a name=....> as rendering on yahoo mailer is borked with this.
20273 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20275 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20276 // remove_keep_children = true;
20279 if (remove_keep_children) {
20280 this.cleanUpChildren(node);
20281 // inserts everything just before this node...
20282 while (node.childNodes.length) {
20283 var cn = node.childNodes[0];
20284 node.removeChild(cn);
20285 node.parentNode.insertBefore(cn, node);
20287 node.parentNode.removeChild(node);
20291 if (!node.attributes || !node.attributes.length) {
20292 this.cleanUpChildren(node);
20296 function cleanAttr(n,v)
20299 if (v.match(/^\./) || v.match(/^\//)) {
20302 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20305 if (v.match(/^#/)) {
20308 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20309 node.removeAttribute(n);
20313 var cwhite = this.cwhite;
20314 var cblack = this.cblack;
20316 function cleanStyle(n,v)
20318 if (v.match(/expression/)) { //XSS?? should we even bother..
20319 node.removeAttribute(n);
20323 var parts = v.split(/;/);
20326 Roo.each(parts, function(p) {
20327 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20331 var l = p.split(':').shift().replace(/\s+/g,'');
20332 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20334 if ( cwhite.length && cblack.indexOf(l) > -1) {
20335 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20336 //node.removeAttribute(n);
20340 // only allow 'c whitelisted system attributes'
20341 if ( cwhite.length && cwhite.indexOf(l) < 0) {
20342 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20343 //node.removeAttribute(n);
20353 if (clean.length) {
20354 node.setAttribute(n, clean.join(';'));
20356 node.removeAttribute(n);
20362 for (var i = node.attributes.length-1; i > -1 ; i--) {
20363 var a = node.attributes[i];
20366 if (a.name.toLowerCase().substr(0,2)=='on') {
20367 node.removeAttribute(a.name);
20370 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20371 node.removeAttribute(a.name);
20374 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20375 cleanAttr(a.name,a.value); // fixme..
20378 if (a.name == 'style') {
20379 cleanStyle(a.name,a.value);
20382 /// clean up MS crap..
20383 // tecnically this should be a list of valid class'es..
20386 if (a.name == 'class') {
20387 if (a.value.match(/^Mso/)) {
20388 node.className = '';
20391 if (a.value.match(/body/)) {
20392 node.className = '';
20403 this.cleanUpChildren(node);
20409 * Clean up MS wordisms...
20411 cleanWord : function(node)
20416 this.cleanWord(this.doc.body);
20419 if (node.nodeName == "#text") {
20420 // clean up silly Windows -- stuff?
20423 if (node.nodeName == "#comment") {
20424 node.parentNode.removeChild(node);
20425 // clean up silly Windows -- stuff?
20429 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20430 node.parentNode.removeChild(node);
20434 // remove - but keep children..
20435 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20436 while (node.childNodes.length) {
20437 var cn = node.childNodes[0];
20438 node.removeChild(cn);
20439 node.parentNode.insertBefore(cn, node);
20441 node.parentNode.removeChild(node);
20442 this.iterateChildren(node, this.cleanWord);
20446 if (node.className.length) {
20448 var cn = node.className.split(/\W+/);
20450 Roo.each(cn, function(cls) {
20451 if (cls.match(/Mso[a-zA-Z]+/)) {
20456 node.className = cna.length ? cna.join(' ') : '';
20458 node.removeAttribute("class");
20462 if (node.hasAttribute("lang")) {
20463 node.removeAttribute("lang");
20466 if (node.hasAttribute("style")) {
20468 var styles = node.getAttribute("style").split(";");
20470 Roo.each(styles, function(s) {
20471 if (!s.match(/:/)) {
20474 var kv = s.split(":");
20475 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20478 // what ever is left... we allow.
20481 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20482 if (!nstyle.length) {
20483 node.removeAttribute('style');
20486 this.iterateChildren(node, this.cleanWord);
20492 * iterateChildren of a Node, calling fn each time, using this as the scole..
20493 * @param {DomNode} node node to iterate children of.
20494 * @param {Function} fn method of this class to call on each item.
20496 iterateChildren : function(node, fn)
20498 if (!node.childNodes.length) {
20501 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20502 fn.call(this, node.childNodes[i])
20508 * cleanTableWidths.
20510 * Quite often pasting from word etc.. results in tables with column and widths.
20511 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20514 cleanTableWidths : function(node)
20519 this.cleanTableWidths(this.doc.body);
20524 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20527 Roo.log(node.tagName);
20528 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20529 this.iterateChildren(node, this.cleanTableWidths);
20532 if (node.hasAttribute('width')) {
20533 node.removeAttribute('width');
20537 if (node.hasAttribute("style")) {
20540 var styles = node.getAttribute("style").split(";");
20542 Roo.each(styles, function(s) {
20543 if (!s.match(/:/)) {
20546 var kv = s.split(":");
20547 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20550 // what ever is left... we allow.
20553 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20554 if (!nstyle.length) {
20555 node.removeAttribute('style');
20559 this.iterateChildren(node, this.cleanTableWidths);
20567 domToHTML : function(currentElement, depth, nopadtext) {
20569 depth = depth || 0;
20570 nopadtext = nopadtext || false;
20572 if (!currentElement) {
20573 return this.domToHTML(this.doc.body);
20576 //Roo.log(currentElement);
20578 var allText = false;
20579 var nodeName = currentElement.nodeName;
20580 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20582 if (nodeName == '#text') {
20584 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20589 if (nodeName != 'BODY') {
20592 // Prints the node tagName, such as <A>, <IMG>, etc
20595 for(i = 0; i < currentElement.attributes.length;i++) {
20597 var aname = currentElement.attributes.item(i).name;
20598 if (!currentElement.attributes.item(i).value.length) {
20601 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20604 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20613 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20616 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20621 // Traverse the tree
20623 var currentElementChild = currentElement.childNodes.item(i);
20624 var allText = true;
20625 var innerHTML = '';
20627 while (currentElementChild) {
20628 // Formatting code (indent the tree so it looks nice on the screen)
20629 var nopad = nopadtext;
20630 if (lastnode == 'SPAN') {
20634 if (currentElementChild.nodeName == '#text') {
20635 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20636 toadd = nopadtext ? toadd : toadd.trim();
20637 if (!nopad && toadd.length > 80) {
20638 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20640 innerHTML += toadd;
20643 currentElementChild = currentElement.childNodes.item(i);
20649 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20651 // Recursively traverse the tree structure of the child node
20652 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20653 lastnode = currentElementChild.nodeName;
20655 currentElementChild=currentElement.childNodes.item(i);
20661 // The remaining code is mostly for formatting the tree
20662 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20667 ret+= "</"+tagName+">";
20673 applyBlacklists : function()
20675 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20676 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20680 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20681 if (b.indexOf(tag) > -1) {
20684 this.white.push(tag);
20688 Roo.each(w, function(tag) {
20689 if (b.indexOf(tag) > -1) {
20692 if (this.white.indexOf(tag) > -1) {
20695 this.white.push(tag);
20700 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20701 if (w.indexOf(tag) > -1) {
20704 this.black.push(tag);
20708 Roo.each(b, function(tag) {
20709 if (w.indexOf(tag) > -1) {
20712 if (this.black.indexOf(tag) > -1) {
20715 this.black.push(tag);
20720 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20721 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20725 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20726 if (b.indexOf(tag) > -1) {
20729 this.cwhite.push(tag);
20733 Roo.each(w, function(tag) {
20734 if (b.indexOf(tag) > -1) {
20737 if (this.cwhite.indexOf(tag) > -1) {
20740 this.cwhite.push(tag);
20745 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20746 if (w.indexOf(tag) > -1) {
20749 this.cblack.push(tag);
20753 Roo.each(b, function(tag) {
20754 if (w.indexOf(tag) > -1) {
20757 if (this.cblack.indexOf(tag) > -1) {
20760 this.cblack.push(tag);
20765 setStylesheets : function(stylesheets)
20767 if(typeof(stylesheets) == 'string'){
20768 Roo.get(this.iframe.contentDocument.head).createChild({
20770 rel : 'stylesheet',
20779 Roo.each(stylesheets, function(s) {
20784 Roo.get(_this.iframe.contentDocument.head).createChild({
20786 rel : 'stylesheet',
20795 removeStylesheets : function()
20799 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20804 // hide stuff that is not compatible
20818 * @event specialkey
20822 * @cfg {String} fieldClass @hide
20825 * @cfg {String} focusClass @hide
20828 * @cfg {String} autoCreate @hide
20831 * @cfg {String} inputType @hide
20834 * @cfg {String} invalidClass @hide
20837 * @cfg {String} invalidText @hide
20840 * @cfg {String} msgFx @hide
20843 * @cfg {String} validateOnBlur @hide
20847 Roo.HtmlEditorCore.white = [
20848 'area', 'br', 'img', 'input', 'hr', 'wbr',
20850 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
20851 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
20852 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
20853 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
20854 'table', 'ul', 'xmp',
20856 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
20859 'dir', 'menu', 'ol', 'ul', 'dl',
20865 Roo.HtmlEditorCore.black = [
20866 // 'embed', 'object', // enable - backend responsiblity to clean thiese
20868 'base', 'basefont', 'bgsound', 'blink', 'body',
20869 'frame', 'frameset', 'head', 'html', 'ilayer',
20870 'iframe', 'layer', 'link', 'meta', 'object',
20871 'script', 'style' ,'title', 'xml' // clean later..
20873 Roo.HtmlEditorCore.clean = [
20874 'script', 'style', 'title', 'xml'
20876 Roo.HtmlEditorCore.remove = [
20881 Roo.HtmlEditorCore.ablack = [
20885 Roo.HtmlEditorCore.aclean = [
20886 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
20890 Roo.HtmlEditorCore.pwhite= [
20891 'http', 'https', 'mailto'
20894 // white listed style attributes.
20895 Roo.HtmlEditorCore.cwhite= [
20896 // 'text-align', /// default is to allow most things..
20902 // black listed style attributes.
20903 Roo.HtmlEditorCore.cblack= [
20904 // 'font-size' -- this can be set by the project
20908 Roo.HtmlEditorCore.swapCodes =[
20927 * @class Roo.bootstrap.HtmlEditor
20928 * @extends Roo.bootstrap.TextArea
20929 * Bootstrap HtmlEditor class
20932 * Create a new HtmlEditor
20933 * @param {Object} config The config object
20936 Roo.bootstrap.HtmlEditor = function(config){
20937 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20938 if (!this.toolbars) {
20939 this.toolbars = [];
20941 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20944 * @event initialize
20945 * Fires when the editor is fully initialized (including the iframe)
20946 * @param {HtmlEditor} this
20951 * Fires when the editor is first receives the focus. Any insertion must wait
20952 * until after this event.
20953 * @param {HtmlEditor} this
20957 * @event beforesync
20958 * Fires before the textarea is updated with content from the editor iframe. Return false
20959 * to cancel the sync.
20960 * @param {HtmlEditor} this
20961 * @param {String} html
20965 * @event beforepush
20966 * Fires before the iframe editor is updated with content from the textarea. Return false
20967 * to cancel the push.
20968 * @param {HtmlEditor} this
20969 * @param {String} html
20974 * Fires when the textarea is updated with content from the editor iframe.
20975 * @param {HtmlEditor} this
20976 * @param {String} html
20981 * Fires when the iframe editor is updated with content from the textarea.
20982 * @param {HtmlEditor} this
20983 * @param {String} html
20987 * @event editmodechange
20988 * Fires when the editor switches edit modes
20989 * @param {HtmlEditor} this
20990 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20992 editmodechange: true,
20994 * @event editorevent
20995 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20996 * @param {HtmlEditor} this
21000 * @event firstfocus
21001 * Fires when on first focus - needed by toolbars..
21002 * @param {HtmlEditor} this
21007 * Auto save the htmlEditor value as a file into Events
21008 * @param {HtmlEditor} this
21012 * @event savedpreview
21013 * preview the saved version of htmlEditor
21014 * @param {HtmlEditor} this
21021 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
21025 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21030 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21035 * @cfg {Number} height (in pixels)
21039 * @cfg {Number} width (in pixels)
21044 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21047 stylesheets: false,
21052 // private properties
21053 validationEvent : false,
21055 initialized : false,
21058 onFocus : Roo.emptyFn,
21060 hideMode:'offsets',
21063 tbContainer : false,
21065 toolbarContainer :function() {
21066 return this.wrap.select('.x-html-editor-tb',true).first();
21070 * Protected method that will not generally be called directly. It
21071 * is called when the editor creates its toolbar. Override this method if you need to
21072 * add custom toolbar buttons.
21073 * @param {HtmlEditor} editor
21075 createToolbar : function(){
21077 Roo.log("create toolbars");
21079 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21080 this.toolbars[0].render(this.toolbarContainer());
21084 // if (!editor.toolbars || !editor.toolbars.length) {
21085 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21088 // for (var i =0 ; i < editor.toolbars.length;i++) {
21089 // editor.toolbars[i] = Roo.factory(
21090 // typeof(editor.toolbars[i]) == 'string' ?
21091 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
21092 // Roo.bootstrap.HtmlEditor);
21093 // editor.toolbars[i].init(editor);
21099 onRender : function(ct, position)
21101 // Roo.log("Call onRender: " + this.xtype);
21103 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21105 this.wrap = this.inputEl().wrap({
21106 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21109 this.editorcore.onRender(ct, position);
21111 if (this.resizable) {
21112 this.resizeEl = new Roo.Resizable(this.wrap, {
21116 minHeight : this.height,
21117 height: this.height,
21118 handles : this.resizable,
21121 resize : function(r, w, h) {
21122 _t.onResize(w,h); // -something
21128 this.createToolbar(this);
21131 if(!this.width && this.resizable){
21132 this.setSize(this.wrap.getSize());
21134 if (this.resizeEl) {
21135 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21136 // should trigger onReize..
21142 onResize : function(w, h)
21144 Roo.log('resize: ' +w + ',' + h );
21145 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21149 if(this.inputEl() ){
21150 if(typeof w == 'number'){
21151 var aw = w - this.wrap.getFrameWidth('lr');
21152 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21155 if(typeof h == 'number'){
21156 var tbh = -11; // fixme it needs to tool bar size!
21157 for (var i =0; i < this.toolbars.length;i++) {
21158 // fixme - ask toolbars for heights?
21159 tbh += this.toolbars[i].el.getHeight();
21160 //if (this.toolbars[i].footer) {
21161 // tbh += this.toolbars[i].footer.el.getHeight();
21169 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21170 ah -= 5; // knock a few pixes off for look..
21171 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21175 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21176 this.editorcore.onResize(ew,eh);
21181 * Toggles the editor between standard and source edit mode.
21182 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21184 toggleSourceEdit : function(sourceEditMode)
21186 this.editorcore.toggleSourceEdit(sourceEditMode);
21188 if(this.editorcore.sourceEditMode){
21189 Roo.log('editor - showing textarea');
21192 // Roo.log(this.syncValue());
21194 this.inputEl().removeClass(['hide', 'x-hidden']);
21195 this.inputEl().dom.removeAttribute('tabIndex');
21196 this.inputEl().focus();
21198 Roo.log('editor - hiding textarea');
21200 // Roo.log(this.pushValue());
21203 this.inputEl().addClass(['hide', 'x-hidden']);
21204 this.inputEl().dom.setAttribute('tabIndex', -1);
21205 //this.deferFocus();
21208 if(this.resizable){
21209 this.setSize(this.wrap.getSize());
21212 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21215 // private (for BoxComponent)
21216 adjustSize : Roo.BoxComponent.prototype.adjustSize,
21218 // private (for BoxComponent)
21219 getResizeEl : function(){
21223 // private (for BoxComponent)
21224 getPositionEl : function(){
21229 initEvents : function(){
21230 this.originalValue = this.getValue();
21234 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21237 // markInvalid : Roo.emptyFn,
21239 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21242 // clearInvalid : Roo.emptyFn,
21244 setValue : function(v){
21245 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21246 this.editorcore.pushValue();
21251 deferFocus : function(){
21252 this.focus.defer(10, this);
21256 focus : function(){
21257 this.editorcore.focus();
21263 onDestroy : function(){
21269 for (var i =0; i < this.toolbars.length;i++) {
21270 // fixme - ask toolbars for heights?
21271 this.toolbars[i].onDestroy();
21274 this.wrap.dom.innerHTML = '';
21275 this.wrap.remove();
21280 onFirstFocus : function(){
21281 //Roo.log("onFirstFocus");
21282 this.editorcore.onFirstFocus();
21283 for (var i =0; i < this.toolbars.length;i++) {
21284 this.toolbars[i].onFirstFocus();
21290 syncValue : function()
21292 this.editorcore.syncValue();
21295 pushValue : function()
21297 this.editorcore.pushValue();
21301 // hide stuff that is not compatible
21315 * @event specialkey
21319 * @cfg {String} fieldClass @hide
21322 * @cfg {String} focusClass @hide
21325 * @cfg {String} autoCreate @hide
21328 * @cfg {String} inputType @hide
21331 * @cfg {String} invalidClass @hide
21334 * @cfg {String} invalidText @hide
21337 * @cfg {String} msgFx @hide
21340 * @cfg {String} validateOnBlur @hide
21349 Roo.namespace('Roo.bootstrap.htmleditor');
21351 * @class Roo.bootstrap.HtmlEditorToolbar1
21356 new Roo.bootstrap.HtmlEditor({
21359 new Roo.bootstrap.HtmlEditorToolbar1({
21360 disable : { fonts: 1 , format: 1, ..., ... , ...],
21366 * @cfg {Object} disable List of elements to disable..
21367 * @cfg {Array} btns List of additional buttons.
21371 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21374 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21377 Roo.apply(this, config);
21379 // default disabled, based on 'good practice'..
21380 this.disable = this.disable || {};
21381 Roo.applyIf(this.disable, {
21384 specialElements : true
21386 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21388 this.editor = config.editor;
21389 this.editorcore = config.editor.editorcore;
21391 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21393 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21394 // dont call parent... till later.
21396 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
21401 editorcore : false,
21406 "h1","h2","h3","h4","h5","h6",
21408 "abbr", "acronym", "address", "cite", "samp", "var",
21412 onRender : function(ct, position)
21414 // Roo.log("Call onRender: " + this.xtype);
21416 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21418 this.el.dom.style.marginBottom = '0';
21420 var editorcore = this.editorcore;
21421 var editor= this.editor;
21424 var btn = function(id,cmd , toggle, handler){
21426 var event = toggle ? 'toggle' : 'click';
21431 xns: Roo.bootstrap,
21434 enableToggle:toggle !== false,
21436 pressed : toggle ? false : null,
21439 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21440 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21449 xns: Roo.bootstrap,
21450 glyphicon : 'font',
21454 xns: Roo.bootstrap,
21458 Roo.each(this.formats, function(f) {
21459 style.menu.items.push({
21461 xns: Roo.bootstrap,
21462 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21467 editorcore.insertTag(this.tagname);
21474 children.push(style);
21477 btn('bold',false,true);
21478 btn('italic',false,true);
21479 btn('align-left', 'justifyleft',true);
21480 btn('align-center', 'justifycenter',true);
21481 btn('align-right' , 'justifyright',true);
21482 btn('link', false, false, function(btn) {
21483 //Roo.log("create link?");
21484 var url = prompt(this.createLinkText, this.defaultLinkValue);
21485 if(url && url != 'http:/'+'/'){
21486 this.editorcore.relayCmd('createlink', url);
21489 btn('list','insertunorderedlist',true);
21490 btn('pencil', false,true, function(btn){
21493 this.toggleSourceEdit(btn.pressed);
21499 xns: Roo.bootstrap,
21504 xns: Roo.bootstrap,
21509 cog.menu.items.push({
21511 xns: Roo.bootstrap,
21512 html : Clean styles,
21517 editorcore.insertTag(this.tagname);
21526 this.xtype = 'NavSimplebar';
21528 for(var i=0;i< children.length;i++) {
21530 this.buttons.add(this.addxtypeChild(children[i]));
21534 editor.on('editorevent', this.updateToolbar, this);
21536 onBtnClick : function(id)
21538 this.editorcore.relayCmd(id);
21539 this.editorcore.focus();
21543 * Protected method that will not generally be called directly. It triggers
21544 * a toolbar update by reading the markup state of the current selection in the editor.
21546 updateToolbar: function(){
21548 if(!this.editorcore.activated){
21549 this.editor.onFirstFocus(); // is this neeed?
21553 var btns = this.buttons;
21554 var doc = this.editorcore.doc;
21555 btns.get('bold').setActive(doc.queryCommandState('bold'));
21556 btns.get('italic').setActive(doc.queryCommandState('italic'));
21557 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21559 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21560 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21561 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21563 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21564 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21567 var ans = this.editorcore.getAllAncestors();
21568 if (this.formatCombo) {
21571 var store = this.formatCombo.store;
21572 this.formatCombo.setValue("");
21573 for (var i =0; i < ans.length;i++) {
21574 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21576 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21584 // hides menus... - so this cant be on a menu...
21585 Roo.bootstrap.MenuMgr.hideAll();
21587 Roo.bootstrap.MenuMgr.hideAll();
21588 //this.editorsyncValue();
21590 onFirstFocus: function() {
21591 this.buttons.each(function(item){
21595 toggleSourceEdit : function(sourceEditMode){
21598 if(sourceEditMode){
21599 Roo.log("disabling buttons");
21600 this.buttons.each( function(item){
21601 if(item.cmd != 'pencil'){
21607 Roo.log("enabling buttons");
21608 if(this.editorcore.initialized){
21609 this.buttons.each( function(item){
21615 Roo.log("calling toggole on editor");
21616 // tell the editor that it's been pressed..
21617 this.editor.toggleSourceEdit(sourceEditMode);
21627 * @class Roo.bootstrap.Table.AbstractSelectionModel
21628 * @extends Roo.util.Observable
21629 * Abstract base class for grid SelectionModels. It provides the interface that should be
21630 * implemented by descendant classes. This class should not be directly instantiated.
21633 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21634 this.locked = false;
21635 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21639 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21640 /** @ignore Called by the grid automatically. Do not call directly. */
21641 init : function(grid){
21647 * Locks the selections.
21650 this.locked = true;
21654 * Unlocks the selections.
21656 unlock : function(){
21657 this.locked = false;
21661 * Returns true if the selections are locked.
21662 * @return {Boolean}
21664 isLocked : function(){
21665 return this.locked;
21669 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21670 * @class Roo.bootstrap.Table.RowSelectionModel
21671 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21672 * It supports multiple selections and keyboard selection/navigation.
21674 * @param {Object} config
21677 Roo.bootstrap.Table.RowSelectionModel = function(config){
21678 Roo.apply(this, config);
21679 this.selections = new Roo.util.MixedCollection(false, function(o){
21684 this.lastActive = false;
21688 * @event selectionchange
21689 * Fires when the selection changes
21690 * @param {SelectionModel} this
21692 "selectionchange" : true,
21694 * @event afterselectionchange
21695 * Fires after the selection changes (eg. by key press or clicking)
21696 * @param {SelectionModel} this
21698 "afterselectionchange" : true,
21700 * @event beforerowselect
21701 * Fires when a row is selected being selected, return false to cancel.
21702 * @param {SelectionModel} this
21703 * @param {Number} rowIndex The selected index
21704 * @param {Boolean} keepExisting False if other selections will be cleared
21706 "beforerowselect" : true,
21709 * Fires when a row is selected.
21710 * @param {SelectionModel} this
21711 * @param {Number} rowIndex The selected index
21712 * @param {Roo.data.Record} r The record
21714 "rowselect" : true,
21716 * @event rowdeselect
21717 * Fires when a row is deselected.
21718 * @param {SelectionModel} this
21719 * @param {Number} rowIndex The selected index
21721 "rowdeselect" : true
21723 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21724 this.locked = false;
21727 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21729 * @cfg {Boolean} singleSelect
21730 * True to allow selection of only one row at a time (defaults to false)
21732 singleSelect : false,
21735 initEvents : function(){
21737 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21738 this.grid.on("mousedown", this.handleMouseDown, this);
21739 }else{ // allow click to work like normal
21740 this.grid.on("rowclick", this.handleDragableRowClick, this);
21743 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21744 "up" : function(e){
21746 this.selectPrevious(e.shiftKey);
21747 }else if(this.last !== false && this.lastActive !== false){
21748 var last = this.last;
21749 this.selectRange(this.last, this.lastActive-1);
21750 this.grid.getView().focusRow(this.lastActive);
21751 if(last !== false){
21755 this.selectFirstRow();
21757 this.fireEvent("afterselectionchange", this);
21759 "down" : function(e){
21761 this.selectNext(e.shiftKey);
21762 }else if(this.last !== false && this.lastActive !== false){
21763 var last = this.last;
21764 this.selectRange(this.last, this.lastActive+1);
21765 this.grid.getView().focusRow(this.lastActive);
21766 if(last !== false){
21770 this.selectFirstRow();
21772 this.fireEvent("afterselectionchange", this);
21777 var view = this.grid.view;
21778 view.on("refresh", this.onRefresh, this);
21779 view.on("rowupdated", this.onRowUpdated, this);
21780 view.on("rowremoved", this.onRemove, this);
21784 onRefresh : function(){
21785 var ds = this.grid.dataSource, i, v = this.grid.view;
21786 var s = this.selections;
21787 s.each(function(r){
21788 if((i = ds.indexOfId(r.id)) != -1){
21797 onRemove : function(v, index, r){
21798 this.selections.remove(r);
21802 onRowUpdated : function(v, index, r){
21803 if(this.isSelected(r)){
21804 v.onRowSelect(index);
21810 * @param {Array} records The records to select
21811 * @param {Boolean} keepExisting (optional) True to keep existing selections
21813 selectRecords : function(records, keepExisting){
21815 this.clearSelections();
21817 var ds = this.grid.dataSource;
21818 for(var i = 0, len = records.length; i < len; i++){
21819 this.selectRow(ds.indexOf(records[i]), true);
21824 * Gets the number of selected rows.
21827 getCount : function(){
21828 return this.selections.length;
21832 * Selects the first row in the grid.
21834 selectFirstRow : function(){
21839 * Select the last row.
21840 * @param {Boolean} keepExisting (optional) True to keep existing selections
21842 selectLastRow : function(keepExisting){
21843 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21847 * Selects the row immediately following the last selected row.
21848 * @param {Boolean} keepExisting (optional) True to keep existing selections
21850 selectNext : function(keepExisting){
21851 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21852 this.selectRow(this.last+1, keepExisting);
21853 this.grid.getView().focusRow(this.last);
21858 * Selects the row that precedes the last selected row.
21859 * @param {Boolean} keepExisting (optional) True to keep existing selections
21861 selectPrevious : function(keepExisting){
21863 this.selectRow(this.last-1, keepExisting);
21864 this.grid.getView().focusRow(this.last);
21869 * Returns the selected records
21870 * @return {Array} Array of selected records
21872 getSelections : function(){
21873 return [].concat(this.selections.items);
21877 * Returns the first selected record.
21880 getSelected : function(){
21881 return this.selections.itemAt(0);
21886 * Clears all selections.
21888 clearSelections : function(fast){
21893 var ds = this.grid.dataSource;
21894 var s = this.selections;
21895 s.each(function(r){
21896 this.deselectRow(ds.indexOfId(r.id));
21900 this.selections.clear();
21907 * Selects all rows.
21909 selectAll : function(){
21913 this.selections.clear();
21914 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21915 this.selectRow(i, true);
21920 * Returns True if there is a selection.
21921 * @return {Boolean}
21923 hasSelection : function(){
21924 return this.selections.length > 0;
21928 * Returns True if the specified row is selected.
21929 * @param {Number/Record} record The record or index of the record to check
21930 * @return {Boolean}
21932 isSelected : function(index){
21933 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21934 return (r && this.selections.key(r.id) ? true : false);
21938 * Returns True if the specified record id is selected.
21939 * @param {String} id The id of record to check
21940 * @return {Boolean}
21942 isIdSelected : function(id){
21943 return (this.selections.key(id) ? true : false);
21947 handleMouseDown : function(e, t){
21948 var view = this.grid.getView(), rowIndex;
21949 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21952 if(e.shiftKey && this.last !== false){
21953 var last = this.last;
21954 this.selectRange(last, rowIndex, e.ctrlKey);
21955 this.last = last; // reset the last
21956 view.focusRow(rowIndex);
21958 var isSelected = this.isSelected(rowIndex);
21959 if(e.button !== 0 && isSelected){
21960 view.focusRow(rowIndex);
21961 }else if(e.ctrlKey && isSelected){
21962 this.deselectRow(rowIndex);
21963 }else if(!isSelected){
21964 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21965 view.focusRow(rowIndex);
21968 this.fireEvent("afterselectionchange", this);
21971 handleDragableRowClick : function(grid, rowIndex, e)
21973 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21974 this.selectRow(rowIndex, false);
21975 grid.view.focusRow(rowIndex);
21976 this.fireEvent("afterselectionchange", this);
21981 * Selects multiple rows.
21982 * @param {Array} rows Array of the indexes of the row to select
21983 * @param {Boolean} keepExisting (optional) True to keep existing selections
21985 selectRows : function(rows, keepExisting){
21987 this.clearSelections();
21989 for(var i = 0, len = rows.length; i < len; i++){
21990 this.selectRow(rows[i], true);
21995 * Selects a range of rows. All rows in between startRow and endRow are also selected.
21996 * @param {Number} startRow The index of the first row in the range
21997 * @param {Number} endRow The index of the last row in the range
21998 * @param {Boolean} keepExisting (optional) True to retain existing selections
22000 selectRange : function(startRow, endRow, keepExisting){
22005 this.clearSelections();
22007 if(startRow <= endRow){
22008 for(var i = startRow; i <= endRow; i++){
22009 this.selectRow(i, true);
22012 for(var i = startRow; i >= endRow; i--){
22013 this.selectRow(i, true);
22019 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22020 * @param {Number} startRow The index of the first row in the range
22021 * @param {Number} endRow The index of the last row in the range
22023 deselectRange : function(startRow, endRow, preventViewNotify){
22027 for(var i = startRow; i <= endRow; i++){
22028 this.deselectRow(i, preventViewNotify);
22034 * @param {Number} row The index of the row to select
22035 * @param {Boolean} keepExisting (optional) True to keep existing selections
22037 selectRow : function(index, keepExisting, preventViewNotify){
22038 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22041 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22042 if(!keepExisting || this.singleSelect){
22043 this.clearSelections();
22045 var r = this.grid.dataSource.getAt(index);
22046 this.selections.add(r);
22047 this.last = this.lastActive = index;
22048 if(!preventViewNotify){
22049 this.grid.getView().onRowSelect(index);
22051 this.fireEvent("rowselect", this, index, r);
22052 this.fireEvent("selectionchange", this);
22058 * @param {Number} row The index of the row to deselect
22060 deselectRow : function(index, preventViewNotify){
22064 if(this.last == index){
22067 if(this.lastActive == index){
22068 this.lastActive = false;
22070 var r = this.grid.dataSource.getAt(index);
22071 this.selections.remove(r);
22072 if(!preventViewNotify){
22073 this.grid.getView().onRowDeselect(index);
22075 this.fireEvent("rowdeselect", this, index);
22076 this.fireEvent("selectionchange", this);
22080 restoreLast : function(){
22082 this.last = this._last;
22087 acceptsNav : function(row, col, cm){
22088 return !cm.isHidden(col) && cm.isCellEditable(col, row);
22092 onEditorKey : function(field, e){
22093 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22098 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22100 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22102 }else if(k == e.ENTER && !e.ctrlKey){
22106 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22108 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22110 }else if(k == e.ESC){
22114 g.startEditing(newCell[0], newCell[1]);
22119 * Ext JS Library 1.1.1
22120 * Copyright(c) 2006-2007, Ext JS, LLC.
22122 * Originally Released Under LGPL - original licence link has changed is not relivant.
22125 * <script type="text/javascript">
22129 * @class Roo.bootstrap.PagingToolbar
22130 * @extends Roo.bootstrap.NavSimplebar
22131 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22133 * Create a new PagingToolbar
22134 * @param {Object} config The config object
22135 * @param {Roo.data.Store} store
22137 Roo.bootstrap.PagingToolbar = function(config)
22139 // old args format still supported... - xtype is prefered..
22140 // created from xtype...
22142 this.ds = config.dataSource;
22144 if (config.store && !this.ds) {
22145 this.store= Roo.factory(config.store, Roo.data);
22146 this.ds = this.store;
22147 this.ds.xmodule = this.xmodule || false;
22150 this.toolbarItems = [];
22151 if (config.items) {
22152 this.toolbarItems = config.items;
22155 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22160 this.bind(this.ds);
22163 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22167 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22169 * @cfg {Roo.data.Store} dataSource
22170 * The underlying data store providing the paged data
22173 * @cfg {String/HTMLElement/Element} container
22174 * container The id or element that will contain the toolbar
22177 * @cfg {Boolean} displayInfo
22178 * True to display the displayMsg (defaults to false)
22181 * @cfg {Number} pageSize
22182 * The number of records to display per page (defaults to 20)
22186 * @cfg {String} displayMsg
22187 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22189 displayMsg : 'Displaying {0} - {1} of {2}',
22191 * @cfg {String} emptyMsg
22192 * The message to display when no records are found (defaults to "No data to display")
22194 emptyMsg : 'No data to display',
22196 * Customizable piece of the default paging text (defaults to "Page")
22199 beforePageText : "Page",
22201 * Customizable piece of the default paging text (defaults to "of %0")
22204 afterPageText : "of {0}",
22206 * Customizable piece of the default paging text (defaults to "First Page")
22209 firstText : "First Page",
22211 * Customizable piece of the default paging text (defaults to "Previous Page")
22214 prevText : "Previous Page",
22216 * Customizable piece of the default paging text (defaults to "Next Page")
22219 nextText : "Next Page",
22221 * Customizable piece of the default paging text (defaults to "Last Page")
22224 lastText : "Last Page",
22226 * Customizable piece of the default paging text (defaults to "Refresh")
22229 refreshText : "Refresh",
22233 onRender : function(ct, position)
22235 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22236 this.navgroup.parentId = this.id;
22237 this.navgroup.onRender(this.el, null);
22238 // add the buttons to the navgroup
22240 if(this.displayInfo){
22241 Roo.log(this.el.select('ul.navbar-nav',true).first());
22242 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22243 this.displayEl = this.el.select('.x-paging-info', true).first();
22244 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22245 // this.displayEl = navel.el.select('span',true).first();
22251 Roo.each(_this.buttons, function(e){ // this might need to use render????
22252 Roo.factory(e).onRender(_this.el, null);
22256 Roo.each(_this.toolbarItems, function(e) {
22257 _this.navgroup.addItem(e);
22261 this.first = this.navgroup.addItem({
22262 tooltip: this.firstText,
22264 icon : 'fa fa-backward',
22266 preventDefault: true,
22267 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22270 this.prev = this.navgroup.addItem({
22271 tooltip: this.prevText,
22273 icon : 'fa fa-step-backward',
22275 preventDefault: true,
22276 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
22278 //this.addSeparator();
22281 var field = this.navgroup.addItem( {
22283 cls : 'x-paging-position',
22285 html : this.beforePageText +
22286 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22287 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
22290 this.field = field.el.select('input', true).first();
22291 this.field.on("keydown", this.onPagingKeydown, this);
22292 this.field.on("focus", function(){this.dom.select();});
22295 this.afterTextEl = field.el.select('.x-paging-after',true).first();
22296 //this.field.setHeight(18);
22297 //this.addSeparator();
22298 this.next = this.navgroup.addItem({
22299 tooltip: this.nextText,
22301 html : ' <i class="fa fa-step-forward">',
22303 preventDefault: true,
22304 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
22306 this.last = this.navgroup.addItem({
22307 tooltip: this.lastText,
22308 icon : 'fa fa-forward',
22311 preventDefault: true,
22312 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
22314 //this.addSeparator();
22315 this.loading = this.navgroup.addItem({
22316 tooltip: this.refreshText,
22317 icon: 'fa fa-refresh',
22318 preventDefault: true,
22319 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22325 updateInfo : function(){
22326 if(this.displayEl){
22327 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22328 var msg = count == 0 ?
22332 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
22334 this.displayEl.update(msg);
22339 onLoad : function(ds, r, o){
22340 this.cursor = o.params ? o.params.start : 0;
22341 var d = this.getPageData(),
22345 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22346 this.field.dom.value = ap;
22347 this.first.setDisabled(ap == 1);
22348 this.prev.setDisabled(ap == 1);
22349 this.next.setDisabled(ap == ps);
22350 this.last.setDisabled(ap == ps);
22351 this.loading.enable();
22356 getPageData : function(){
22357 var total = this.ds.getTotalCount();
22360 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22361 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22366 onLoadError : function(){
22367 this.loading.enable();
22371 onPagingKeydown : function(e){
22372 var k = e.getKey();
22373 var d = this.getPageData();
22375 var v = this.field.dom.value, pageNum;
22376 if(!v || isNaN(pageNum = parseInt(v, 10))){
22377 this.field.dom.value = d.activePage;
22380 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22381 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22384 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))
22386 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22387 this.field.dom.value = pageNum;
22388 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22391 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22393 var v = this.field.dom.value, pageNum;
22394 var increment = (e.shiftKey) ? 10 : 1;
22395 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22398 if(!v || isNaN(pageNum = parseInt(v, 10))) {
22399 this.field.dom.value = d.activePage;
22402 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22404 this.field.dom.value = parseInt(v, 10) + increment;
22405 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22406 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22413 beforeLoad : function(){
22415 this.loading.disable();
22420 onClick : function(which){
22429 ds.load({params:{start: 0, limit: this.pageSize}});
22432 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22435 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22438 var total = ds.getTotalCount();
22439 var extra = total % this.pageSize;
22440 var lastStart = extra ? (total - extra) : total-this.pageSize;
22441 ds.load({params:{start: lastStart, limit: this.pageSize}});
22444 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22450 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22451 * @param {Roo.data.Store} store The data store to unbind
22453 unbind : function(ds){
22454 ds.un("beforeload", this.beforeLoad, this);
22455 ds.un("load", this.onLoad, this);
22456 ds.un("loadexception", this.onLoadError, this);
22457 ds.un("remove", this.updateInfo, this);
22458 ds.un("add", this.updateInfo, this);
22459 this.ds = undefined;
22463 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22464 * @param {Roo.data.Store} store The data store to bind
22466 bind : function(ds){
22467 ds.on("beforeload", this.beforeLoad, this);
22468 ds.on("load", this.onLoad, this);
22469 ds.on("loadexception", this.onLoadError, this);
22470 ds.on("remove", this.updateInfo, this);
22471 ds.on("add", this.updateInfo, this);
22482 * @class Roo.bootstrap.MessageBar
22483 * @extends Roo.bootstrap.Component
22484 * Bootstrap MessageBar class
22485 * @cfg {String} html contents of the MessageBar
22486 * @cfg {String} weight (info | success | warning | danger) default info
22487 * @cfg {String} beforeClass insert the bar before the given class
22488 * @cfg {Boolean} closable (true | false) default false
22489 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22492 * Create a new Element
22493 * @param {Object} config The config object
22496 Roo.bootstrap.MessageBar = function(config){
22497 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22500 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22506 beforeClass: 'bootstrap-sticky-wrap',
22508 getAutoCreate : function(){
22512 cls: 'alert alert-dismissable alert-' + this.weight,
22517 html: this.html || ''
22523 cfg.cls += ' alert-messages-fixed';
22537 onRender : function(ct, position)
22539 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22542 var cfg = Roo.apply({}, this.getAutoCreate());
22546 cfg.cls += ' ' + this.cls;
22549 cfg.style = this.style;
22551 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22553 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22556 this.el.select('>button.close').on('click', this.hide, this);
22562 if (!this.rendered) {
22568 this.fireEvent('show', this);
22574 if (!this.rendered) {
22580 this.fireEvent('hide', this);
22583 update : function()
22585 // var e = this.el.dom.firstChild;
22587 // if(this.closable){
22588 // e = e.nextSibling;
22591 // e.data = this.html || '';
22593 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22609 * @class Roo.bootstrap.Graph
22610 * @extends Roo.bootstrap.Component
22611 * Bootstrap Graph class
22615 @cfg {String} graphtype bar | vbar | pie
22616 @cfg {number} g_x coodinator | centre x (pie)
22617 @cfg {number} g_y coodinator | centre y (pie)
22618 @cfg {number} g_r radius (pie)
22619 @cfg {number} g_height height of the chart (respected by all elements in the set)
22620 @cfg {number} g_width width of the chart (respected by all elements in the set)
22621 @cfg {Object} title The title of the chart
22624 -opts (object) options for the chart
22626 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22627 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22629 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.
22630 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22632 o stretch (boolean)
22634 -opts (object) options for the pie
22637 o startAngle (number)
22638 o endAngle (number)
22642 * Create a new Input
22643 * @param {Object} config The config object
22646 Roo.bootstrap.Graph = function(config){
22647 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22653 * The img click event for the img.
22654 * @param {Roo.EventObject} e
22660 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22671 //g_colors: this.colors,
22678 getAutoCreate : function(){
22689 onRender : function(ct,position){
22690 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22691 this.raphael = Raphael(this.el.dom);
22693 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22694 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22695 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22696 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22698 r.text(160, 10, "Single Series Chart").attr(txtattr);
22699 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22700 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22701 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22703 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22704 r.barchart(330, 10, 300, 220, data1);
22705 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22706 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22709 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22710 // r.barchart(30, 30, 560, 250, xdata, {
22711 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22712 // axis : "0 0 1 1",
22713 // axisxlabels : xdata
22714 // //yvalues : cols,
22717 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22719 // this.load(null,xdata,{
22720 // axis : "0 0 1 1",
22721 // axisxlabels : xdata
22726 load : function(graphtype,xdata,opts){
22727 this.raphael.clear();
22729 graphtype = this.graphtype;
22734 var r = this.raphael,
22735 fin = function () {
22736 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22738 fout = function () {
22739 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22741 pfin = function() {
22742 this.sector.stop();
22743 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22746 this.label[0].stop();
22747 this.label[0].attr({ r: 7.5 });
22748 this.label[1].attr({ "font-weight": 800 });
22751 pfout = function() {
22752 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22755 this.label[0].animate({ r: 5 }, 500, "bounce");
22756 this.label[1].attr({ "font-weight": 400 });
22762 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22765 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22768 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
22769 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22771 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22778 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22783 setTitle: function(o)
22788 initEvents: function() {
22791 this.el.on('click', this.onClick, this);
22795 onClick : function(e)
22797 Roo.log('img onclick');
22798 this.fireEvent('click', this, e);
22810 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22813 * @class Roo.bootstrap.dash.NumberBox
22814 * @extends Roo.bootstrap.Component
22815 * Bootstrap NumberBox class
22816 * @cfg {String} headline Box headline
22817 * @cfg {String} content Box content
22818 * @cfg {String} icon Box icon
22819 * @cfg {String} footer Footer text
22820 * @cfg {String} fhref Footer href
22823 * Create a new NumberBox
22824 * @param {Object} config The config object
22828 Roo.bootstrap.dash.NumberBox = function(config){
22829 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22833 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
22842 getAutoCreate : function(){
22846 cls : 'small-box ',
22854 cls : 'roo-headline',
22855 html : this.headline
22859 cls : 'roo-content',
22860 html : this.content
22874 cls : 'ion ' + this.icon
22883 cls : 'small-box-footer',
22884 href : this.fhref || '#',
22888 cfg.cn.push(footer);
22895 onRender : function(ct,position){
22896 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22903 setHeadline: function (value)
22905 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22908 setFooter: function (value, href)
22910 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22913 this.el.select('a.small-box-footer',true).first().attr('href', href);
22918 setContent: function (value)
22920 this.el.select('.roo-content',true).first().dom.innerHTML = value;
22923 initEvents: function()
22937 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22940 * @class Roo.bootstrap.dash.TabBox
22941 * @extends Roo.bootstrap.Component
22942 * Bootstrap TabBox class
22943 * @cfg {String} title Title of the TabBox
22944 * @cfg {String} icon Icon of the TabBox
22945 * @cfg {Boolean} showtabs (true|false) show the tabs default true
22946 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22949 * Create a new TabBox
22950 * @param {Object} config The config object
22954 Roo.bootstrap.dash.TabBox = function(config){
22955 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22960 * When a pane is added
22961 * @param {Roo.bootstrap.dash.TabPane} pane
22965 * @event activatepane
22966 * When a pane is activated
22967 * @param {Roo.bootstrap.dash.TabPane} pane
22969 "activatepane" : true
22977 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
22982 tabScrollable : false,
22984 getChildContainer : function()
22986 return this.el.select('.tab-content', true).first();
22989 getAutoCreate : function(){
22993 cls: 'pull-left header',
23001 cls: 'fa ' + this.icon
23007 cls: 'nav nav-tabs pull-right',
23013 if(this.tabScrollable){
23020 cls: 'nav nav-tabs pull-right',
23031 cls: 'nav-tabs-custom',
23036 cls: 'tab-content no-padding',
23044 initEvents : function()
23046 //Roo.log('add add pane handler');
23047 this.on('addpane', this.onAddPane, this);
23050 * Updates the box title
23051 * @param {String} html to set the title to.
23053 setTitle : function(value)
23055 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23057 onAddPane : function(pane)
23059 this.panes.push(pane);
23060 //Roo.log('addpane');
23062 // tabs are rendere left to right..
23063 if(!this.showtabs){
23067 var ctr = this.el.select('.nav-tabs', true).first();
23070 var existing = ctr.select('.nav-tab',true);
23071 var qty = existing.getCount();;
23074 var tab = ctr.createChild({
23076 cls : 'nav-tab' + (qty ? '' : ' active'),
23084 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23087 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23089 pane.el.addClass('active');
23094 onTabClick : function(ev,un,ob,pane)
23096 //Roo.log('tab - prev default');
23097 ev.preventDefault();
23100 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23101 pane.tab.addClass('active');
23102 //Roo.log(pane.title);
23103 this.getChildContainer().select('.tab-pane',true).removeClass('active');
23104 // technically we should have a deactivate event.. but maybe add later.
23105 // and it should not de-activate the selected tab...
23106 this.fireEvent('activatepane', pane);
23107 pane.el.addClass('active');
23108 pane.fireEvent('activate');
23113 getActivePane : function()
23116 Roo.each(this.panes, function(p) {
23117 if(p.el.hasClass('active')){
23138 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23140 * @class Roo.bootstrap.TabPane
23141 * @extends Roo.bootstrap.Component
23142 * Bootstrap TabPane class
23143 * @cfg {Boolean} active (false | true) Default false
23144 * @cfg {String} title title of panel
23148 * Create a new TabPane
23149 * @param {Object} config The config object
23152 Roo.bootstrap.dash.TabPane = function(config){
23153 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23159 * When a pane is activated
23160 * @param {Roo.bootstrap.dash.TabPane} pane
23167 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
23172 // the tabBox that this is attached to.
23175 getAutoCreate : function()
23183 cfg.cls += ' active';
23188 initEvents : function()
23190 //Roo.log('trigger add pane handler');
23191 this.parent().fireEvent('addpane', this)
23195 * Updates the tab title
23196 * @param {String} html to set the title to.
23198 setTitle: function(str)
23204 this.tab.select('a', true).first().dom.innerHTML = str;
23221 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23224 * @class Roo.bootstrap.menu.Menu
23225 * @extends Roo.bootstrap.Component
23226 * Bootstrap Menu class - container for Menu
23227 * @cfg {String} html Text of the menu
23228 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23229 * @cfg {String} icon Font awesome icon
23230 * @cfg {String} pos Menu align to (top | bottom) default bottom
23234 * Create a new Menu
23235 * @param {Object} config The config object
23239 Roo.bootstrap.menu.Menu = function(config){
23240 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23244 * @event beforeshow
23245 * Fires before this menu is displayed
23246 * @param {Roo.bootstrap.menu.Menu} this
23250 * @event beforehide
23251 * Fires before this menu is hidden
23252 * @param {Roo.bootstrap.menu.Menu} this
23257 * Fires after this menu is displayed
23258 * @param {Roo.bootstrap.menu.Menu} this
23263 * Fires after this menu is hidden
23264 * @param {Roo.bootstrap.menu.Menu} this
23269 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23270 * @param {Roo.bootstrap.menu.Menu} this
23271 * @param {Roo.EventObject} e
23278 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
23282 weight : 'default',
23287 getChildContainer : function() {
23288 if(this.isSubMenu){
23292 return this.el.select('ul.dropdown-menu', true).first();
23295 getAutoCreate : function()
23300 cls : 'roo-menu-text',
23308 cls : 'fa ' + this.icon
23319 cls : 'dropdown-button btn btn-' + this.weight,
23324 cls : 'dropdown-toggle btn btn-' + this.weight,
23334 cls : 'dropdown-menu'
23340 if(this.pos == 'top'){
23341 cfg.cls += ' dropup';
23344 if(this.isSubMenu){
23347 cls : 'dropdown-menu'
23354 onRender : function(ct, position)
23356 this.isSubMenu = ct.hasClass('dropdown-submenu');
23358 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23361 initEvents : function()
23363 if(this.isSubMenu){
23367 this.hidden = true;
23369 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23370 this.triggerEl.on('click', this.onTriggerPress, this);
23372 this.buttonEl = this.el.select('button.dropdown-button', true).first();
23373 this.buttonEl.on('click', this.onClick, this);
23379 if(this.isSubMenu){
23383 return this.el.select('ul.dropdown-menu', true).first();
23386 onClick : function(e)
23388 this.fireEvent("click", this, e);
23391 onTriggerPress : function(e)
23393 if (this.isVisible()) {
23400 isVisible : function(){
23401 return !this.hidden;
23406 this.fireEvent("beforeshow", this);
23408 this.hidden = false;
23409 this.el.addClass('open');
23411 Roo.get(document).on("mouseup", this.onMouseUp, this);
23413 this.fireEvent("show", this);
23420 this.fireEvent("beforehide", this);
23422 this.hidden = true;
23423 this.el.removeClass('open');
23425 Roo.get(document).un("mouseup", this.onMouseUp);
23427 this.fireEvent("hide", this);
23430 onMouseUp : function()
23444 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23447 * @class Roo.bootstrap.menu.Item
23448 * @extends Roo.bootstrap.Component
23449 * Bootstrap MenuItem class
23450 * @cfg {Boolean} submenu (true | false) default false
23451 * @cfg {String} html text of the item
23452 * @cfg {String} href the link
23453 * @cfg {Boolean} disable (true | false) default false
23454 * @cfg {Boolean} preventDefault (true | false) default true
23455 * @cfg {String} icon Font awesome icon
23456 * @cfg {String} pos Submenu align to (left | right) default right
23460 * Create a new Item
23461 * @param {Object} config The config object
23465 Roo.bootstrap.menu.Item = function(config){
23466 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23470 * Fires when the mouse is hovering over this menu
23471 * @param {Roo.bootstrap.menu.Item} this
23472 * @param {Roo.EventObject} e
23477 * Fires when the mouse exits this menu
23478 * @param {Roo.bootstrap.menu.Item} this
23479 * @param {Roo.EventObject} e
23485 * The raw click event for the entire grid.
23486 * @param {Roo.EventObject} e
23492 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23497 preventDefault: true,
23502 getAutoCreate : function()
23507 cls : 'roo-menu-item-text',
23515 cls : 'fa ' + this.icon
23524 href : this.href || '#',
23531 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23535 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23537 if(this.pos == 'left'){
23538 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23545 initEvents : function()
23547 this.el.on('mouseover', this.onMouseOver, this);
23548 this.el.on('mouseout', this.onMouseOut, this);
23550 this.el.select('a', true).first().on('click', this.onClick, this);
23554 onClick : function(e)
23556 if(this.preventDefault){
23557 e.preventDefault();
23560 this.fireEvent("click", this, e);
23563 onMouseOver : function(e)
23565 if(this.submenu && this.pos == 'left'){
23566 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23569 this.fireEvent("mouseover", this, e);
23572 onMouseOut : function(e)
23574 this.fireEvent("mouseout", this, e);
23586 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23589 * @class Roo.bootstrap.menu.Separator
23590 * @extends Roo.bootstrap.Component
23591 * Bootstrap Separator class
23594 * Create a new Separator
23595 * @param {Object} config The config object
23599 Roo.bootstrap.menu.Separator = function(config){
23600 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23603 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23605 getAutoCreate : function(){
23626 * @class Roo.bootstrap.Tooltip
23627 * Bootstrap Tooltip class
23628 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23629 * to determine which dom element triggers the tooltip.
23631 * It needs to add support for additional attributes like tooltip-position
23634 * Create a new Toolti
23635 * @param {Object} config The config object
23638 Roo.bootstrap.Tooltip = function(config){
23639 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23642 Roo.apply(Roo.bootstrap.Tooltip, {
23644 * @function init initialize tooltip monitoring.
23648 currentTip : false,
23649 currentRegion : false,
23655 Roo.get(document).on('mouseover', this.enter ,this);
23656 Roo.get(document).on('mouseout', this.leave, this);
23659 this.currentTip = new Roo.bootstrap.Tooltip();
23662 enter : function(ev)
23664 var dom = ev.getTarget();
23666 //Roo.log(['enter',dom]);
23667 var el = Roo.fly(dom);
23668 if (this.currentEl) {
23670 //Roo.log(this.currentEl);
23671 //Roo.log(this.currentEl.contains(dom));
23672 if (this.currentEl == el) {
23675 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23681 if (this.currentTip.el) {
23682 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23687 // you can not look for children, as if el is the body.. then everythign is the child..
23688 if (!el.attr('tooltip')) { //
23689 if (!el.select("[tooltip]").elements.length) {
23692 // is the mouse over this child...?
23693 bindEl = el.select("[tooltip]").first();
23694 var xy = ev.getXY();
23695 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23696 //Roo.log("not in region.");
23699 //Roo.log("child element over..");
23702 this.currentEl = bindEl;
23703 this.currentTip.bind(bindEl);
23704 this.currentRegion = Roo.lib.Region.getRegion(dom);
23705 this.currentTip.enter();
23708 leave : function(ev)
23710 var dom = ev.getTarget();
23711 //Roo.log(['leave',dom]);
23712 if (!this.currentEl) {
23717 if (dom != this.currentEl.dom) {
23720 var xy = ev.getXY();
23721 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23724 // only activate leave if mouse cursor is outside... bounding box..
23729 if (this.currentTip) {
23730 this.currentTip.leave();
23732 //Roo.log('clear currentEl');
23733 this.currentEl = false;
23738 'left' : ['r-l', [-2,0], 'right'],
23739 'right' : ['l-r', [2,0], 'left'],
23740 'bottom' : ['t-b', [0,2], 'top'],
23741 'top' : [ 'b-t', [0,-2], 'bottom']
23747 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
23752 delay : null, // can be { show : 300 , hide: 500}
23756 hoverState : null, //???
23758 placement : 'bottom',
23760 getAutoCreate : function(){
23767 cls : 'tooltip-arrow'
23770 cls : 'tooltip-inner'
23777 bind : function(el)
23783 enter : function () {
23785 if (this.timeout != null) {
23786 clearTimeout(this.timeout);
23789 this.hoverState = 'in';
23790 //Roo.log("enter - show");
23791 if (!this.delay || !this.delay.show) {
23796 this.timeout = setTimeout(function () {
23797 if (_t.hoverState == 'in') {
23800 }, this.delay.show);
23804 clearTimeout(this.timeout);
23806 this.hoverState = 'out';
23807 if (!this.delay || !this.delay.hide) {
23813 this.timeout = setTimeout(function () {
23814 //Roo.log("leave - timeout");
23816 if (_t.hoverState == 'out') {
23818 Roo.bootstrap.Tooltip.currentEl = false;
23826 this.render(document.body);
23829 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23831 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23833 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23835 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23837 var placement = typeof this.placement == 'function' ?
23838 this.placement.call(this, this.el, on_el) :
23841 var autoToken = /\s?auto?\s?/i;
23842 var autoPlace = autoToken.test(placement);
23844 placement = placement.replace(autoToken, '') || 'top';
23848 //this.el.setXY([0,0]);
23850 //this.el.dom.style.display='block';
23852 //this.el.appendTo(on_el);
23854 var p = this.getPosition();
23855 var box = this.el.getBox();
23861 var align = Roo.bootstrap.Tooltip.alignment[placement];
23863 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23865 if(placement == 'top' || placement == 'bottom'){
23867 placement = 'right';
23870 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23871 placement = 'left';
23875 align = Roo.bootstrap.Tooltip.alignment[placement];
23877 this.el.alignTo(this.bindEl, align[0],align[1]);
23878 //var arrow = this.el.select('.arrow',true).first();
23879 //arrow.set(align[2],
23881 this.el.addClass(placement);
23883 this.el.addClass('in fade');
23885 this.hoverState = null;
23887 if (this.el.hasClass('fade')) {
23898 //this.el.setXY([0,0]);
23899 this.el.removeClass('in');
23915 * @class Roo.bootstrap.LocationPicker
23916 * @extends Roo.bootstrap.Component
23917 * Bootstrap LocationPicker class
23918 * @cfg {Number} latitude Position when init default 0
23919 * @cfg {Number} longitude Position when init default 0
23920 * @cfg {Number} zoom default 15
23921 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23922 * @cfg {Boolean} mapTypeControl default false
23923 * @cfg {Boolean} disableDoubleClickZoom default false
23924 * @cfg {Boolean} scrollwheel default true
23925 * @cfg {Boolean} streetViewControl default false
23926 * @cfg {Number} radius default 0
23927 * @cfg {String} locationName
23928 * @cfg {Boolean} draggable default true
23929 * @cfg {Boolean} enableAutocomplete default false
23930 * @cfg {Boolean} enableReverseGeocode default true
23931 * @cfg {String} markerTitle
23934 * Create a new LocationPicker
23935 * @param {Object} config The config object
23939 Roo.bootstrap.LocationPicker = function(config){
23941 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23946 * Fires when the picker initialized.
23947 * @param {Roo.bootstrap.LocationPicker} this
23948 * @param {Google Location} location
23952 * @event positionchanged
23953 * Fires when the picker position changed.
23954 * @param {Roo.bootstrap.LocationPicker} this
23955 * @param {Google Location} location
23957 positionchanged : true,
23960 * Fires when the map resize.
23961 * @param {Roo.bootstrap.LocationPicker} this
23966 * Fires when the map show.
23967 * @param {Roo.bootstrap.LocationPicker} this
23972 * Fires when the map hide.
23973 * @param {Roo.bootstrap.LocationPicker} this
23978 * Fires when click the map.
23979 * @param {Roo.bootstrap.LocationPicker} this
23980 * @param {Map event} e
23984 * @event mapRightClick
23985 * Fires when right click the map.
23986 * @param {Roo.bootstrap.LocationPicker} this
23987 * @param {Map event} e
23989 mapRightClick : true,
23991 * @event markerClick
23992 * Fires when click the marker.
23993 * @param {Roo.bootstrap.LocationPicker} this
23994 * @param {Map event} e
23996 markerClick : true,
23998 * @event markerRightClick
23999 * Fires when right click the marker.
24000 * @param {Roo.bootstrap.LocationPicker} this
24001 * @param {Map event} e
24003 markerRightClick : true,
24005 * @event OverlayViewDraw
24006 * Fires when OverlayView Draw
24007 * @param {Roo.bootstrap.LocationPicker} this
24009 OverlayViewDraw : true,
24011 * @event OverlayViewOnAdd
24012 * Fires when OverlayView Draw
24013 * @param {Roo.bootstrap.LocationPicker} this
24015 OverlayViewOnAdd : true,
24017 * @event OverlayViewOnRemove
24018 * Fires when OverlayView Draw
24019 * @param {Roo.bootstrap.LocationPicker} this
24021 OverlayViewOnRemove : true,
24023 * @event OverlayViewShow
24024 * Fires when OverlayView Draw
24025 * @param {Roo.bootstrap.LocationPicker} this
24026 * @param {Pixel} cpx
24028 OverlayViewShow : true,
24030 * @event OverlayViewHide
24031 * Fires when OverlayView Draw
24032 * @param {Roo.bootstrap.LocationPicker} this
24034 OverlayViewHide : true,
24036 * @event loadexception
24037 * Fires when load google lib failed.
24038 * @param {Roo.bootstrap.LocationPicker} this
24040 loadexception : true
24045 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
24047 gMapContext: false,
24053 mapTypeControl: false,
24054 disableDoubleClickZoom: false,
24056 streetViewControl: false,
24060 enableAutocomplete: false,
24061 enableReverseGeocode: true,
24064 getAutoCreate: function()
24069 cls: 'roo-location-picker'
24075 initEvents: function(ct, position)
24077 if(!this.el.getWidth() || this.isApplied()){
24081 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24086 initial: function()
24088 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24089 this.fireEvent('loadexception', this);
24093 if(!this.mapTypeId){
24094 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24097 this.gMapContext = this.GMapContext();
24099 this.initOverlayView();
24101 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24105 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24106 _this.setPosition(_this.gMapContext.marker.position);
24109 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24110 _this.fireEvent('mapClick', this, event);
24114 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24115 _this.fireEvent('mapRightClick', this, event);
24119 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24120 _this.fireEvent('markerClick', this, event);
24124 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24125 _this.fireEvent('markerRightClick', this, event);
24129 this.setPosition(this.gMapContext.location);
24131 this.fireEvent('initial', this, this.gMapContext.location);
24134 initOverlayView: function()
24138 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24142 _this.fireEvent('OverlayViewDraw', _this);
24147 _this.fireEvent('OverlayViewOnAdd', _this);
24150 onRemove: function()
24152 _this.fireEvent('OverlayViewOnRemove', _this);
24155 show: function(cpx)
24157 _this.fireEvent('OverlayViewShow', _this, cpx);
24162 _this.fireEvent('OverlayViewHide', _this);
24168 fromLatLngToContainerPixel: function(event)
24170 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24173 isApplied: function()
24175 return this.getGmapContext() == false ? false : true;
24178 getGmapContext: function()
24180 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24183 GMapContext: function()
24185 var position = new google.maps.LatLng(this.latitude, this.longitude);
24187 var _map = new google.maps.Map(this.el.dom, {
24190 mapTypeId: this.mapTypeId,
24191 mapTypeControl: this.mapTypeControl,
24192 disableDoubleClickZoom: this.disableDoubleClickZoom,
24193 scrollwheel: this.scrollwheel,
24194 streetViewControl: this.streetViewControl,
24195 locationName: this.locationName,
24196 draggable: this.draggable,
24197 enableAutocomplete: this.enableAutocomplete,
24198 enableReverseGeocode: this.enableReverseGeocode
24201 var _marker = new google.maps.Marker({
24202 position: position,
24204 title: this.markerTitle,
24205 draggable: this.draggable
24212 location: position,
24213 radius: this.radius,
24214 locationName: this.locationName,
24215 addressComponents: {
24216 formatted_address: null,
24217 addressLine1: null,
24218 addressLine2: null,
24220 streetNumber: null,
24224 stateOrProvince: null
24227 domContainer: this.el.dom,
24228 geodecoder: new google.maps.Geocoder()
24232 drawCircle: function(center, radius, options)
24234 if (this.gMapContext.circle != null) {
24235 this.gMapContext.circle.setMap(null);
24239 options = Roo.apply({}, options, {
24240 strokeColor: "#0000FF",
24241 strokeOpacity: .35,
24243 fillColor: "#0000FF",
24247 options.map = this.gMapContext.map;
24248 options.radius = radius;
24249 options.center = center;
24250 this.gMapContext.circle = new google.maps.Circle(options);
24251 return this.gMapContext.circle;
24257 setPosition: function(location)
24259 this.gMapContext.location = location;
24260 this.gMapContext.marker.setPosition(location);
24261 this.gMapContext.map.panTo(location);
24262 this.drawCircle(location, this.gMapContext.radius, {});
24266 if (this.gMapContext.settings.enableReverseGeocode) {
24267 this.gMapContext.geodecoder.geocode({
24268 latLng: this.gMapContext.location
24269 }, function(results, status) {
24271 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24272 _this.gMapContext.locationName = results[0].formatted_address;
24273 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24275 _this.fireEvent('positionchanged', this, location);
24282 this.fireEvent('positionchanged', this, location);
24287 google.maps.event.trigger(this.gMapContext.map, "resize");
24289 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24291 this.fireEvent('resize', this);
24294 setPositionByLatLng: function(latitude, longitude)
24296 this.setPosition(new google.maps.LatLng(latitude, longitude));
24299 getCurrentPosition: function()
24302 latitude: this.gMapContext.location.lat(),
24303 longitude: this.gMapContext.location.lng()
24307 getAddressName: function()
24309 return this.gMapContext.locationName;
24312 getAddressComponents: function()
24314 return this.gMapContext.addressComponents;
24317 address_component_from_google_geocode: function(address_components)
24321 for (var i = 0; i < address_components.length; i++) {
24322 var component = address_components[i];
24323 if (component.types.indexOf("postal_code") >= 0) {
24324 result.postalCode = component.short_name;
24325 } else if (component.types.indexOf("street_number") >= 0) {
24326 result.streetNumber = component.short_name;
24327 } else if (component.types.indexOf("route") >= 0) {
24328 result.streetName = component.short_name;
24329 } else if (component.types.indexOf("neighborhood") >= 0) {
24330 result.city = component.short_name;
24331 } else if (component.types.indexOf("locality") >= 0) {
24332 result.city = component.short_name;
24333 } else if (component.types.indexOf("sublocality") >= 0) {
24334 result.district = component.short_name;
24335 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24336 result.stateOrProvince = component.short_name;
24337 } else if (component.types.indexOf("country") >= 0) {
24338 result.country = component.short_name;
24342 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24343 result.addressLine2 = "";
24347 setZoomLevel: function(zoom)
24349 this.gMapContext.map.setZoom(zoom);
24362 this.fireEvent('show', this);
24373 this.fireEvent('hide', this);
24378 Roo.apply(Roo.bootstrap.LocationPicker, {
24380 OverlayView : function(map, options)
24382 options = options || {};
24396 * @class Roo.bootstrap.Alert
24397 * @extends Roo.bootstrap.Component
24398 * Bootstrap Alert class
24399 * @cfg {String} title The title of alert
24400 * @cfg {String} html The content of alert
24401 * @cfg {String} weight ( success | info | warning | danger )
24402 * @cfg {String} faicon font-awesomeicon
24405 * Create a new alert
24406 * @param {Object} config The config object
24410 Roo.bootstrap.Alert = function(config){
24411 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24415 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
24422 getAutoCreate : function()
24431 cls : 'roo-alert-icon'
24436 cls : 'roo-alert-title',
24441 cls : 'roo-alert-text',
24448 cfg.cn[0].cls += ' fa ' + this.faicon;
24452 cfg.cls += ' alert-' + this.weight;
24458 initEvents: function()
24460 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24463 setTitle : function(str)
24465 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24468 setText : function(str)
24470 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24473 setWeight : function(weight)
24476 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24479 this.weight = weight;
24481 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24484 setIcon : function(icon)
24487 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24490 this.faicon = icon;
24492 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24513 * @class Roo.bootstrap.UploadCropbox
24514 * @extends Roo.bootstrap.Component
24515 * Bootstrap UploadCropbox class
24516 * @cfg {String} emptyText show when image has been loaded
24517 * @cfg {String} rotateNotify show when image too small to rotate
24518 * @cfg {Number} errorTimeout default 3000
24519 * @cfg {Number} minWidth default 300
24520 * @cfg {Number} minHeight default 300
24521 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24522 * @cfg {Boolean} isDocument (true|false) default false
24523 * @cfg {String} url action url
24524 * @cfg {String} paramName default 'imageUpload'
24525 * @cfg {String} method default POST
24526 * @cfg {Boolean} loadMask (true|false) default true
24527 * @cfg {Boolean} loadingText default 'Loading...'
24530 * Create a new UploadCropbox
24531 * @param {Object} config The config object
24534 Roo.bootstrap.UploadCropbox = function(config){
24535 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24539 * @event beforeselectfile
24540 * Fire before select file
24541 * @param {Roo.bootstrap.UploadCropbox} this
24543 "beforeselectfile" : true,
24546 * Fire after initEvent
24547 * @param {Roo.bootstrap.UploadCropbox} this
24552 * Fire after initEvent
24553 * @param {Roo.bootstrap.UploadCropbox} this
24554 * @param {String} data
24559 * Fire when preparing the file data
24560 * @param {Roo.bootstrap.UploadCropbox} this
24561 * @param {Object} file
24566 * Fire when get exception
24567 * @param {Roo.bootstrap.UploadCropbox} this
24568 * @param {XMLHttpRequest} xhr
24570 "exception" : true,
24572 * @event beforeloadcanvas
24573 * Fire before load the canvas
24574 * @param {Roo.bootstrap.UploadCropbox} this
24575 * @param {String} src
24577 "beforeloadcanvas" : true,
24580 * Fire when trash image
24581 * @param {Roo.bootstrap.UploadCropbox} this
24586 * Fire when download the image
24587 * @param {Roo.bootstrap.UploadCropbox} this
24591 * @event footerbuttonclick
24592 * Fire when footerbuttonclick
24593 * @param {Roo.bootstrap.UploadCropbox} this
24594 * @param {String} type
24596 "footerbuttonclick" : true,
24600 * @param {Roo.bootstrap.UploadCropbox} this
24605 * Fire when rotate the image
24606 * @param {Roo.bootstrap.UploadCropbox} this
24607 * @param {String} pos
24612 * Fire when inspect the file
24613 * @param {Roo.bootstrap.UploadCropbox} this
24614 * @param {Object} file
24619 * Fire when xhr upload the file
24620 * @param {Roo.bootstrap.UploadCropbox} this
24621 * @param {Object} data
24626 * Fire when arrange the file data
24627 * @param {Roo.bootstrap.UploadCropbox} this
24628 * @param {Object} formData
24633 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24636 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24638 emptyText : 'Click to upload image',
24639 rotateNotify : 'Image is too small to rotate',
24640 errorTimeout : 3000,
24654 cropType : 'image/jpeg',
24656 canvasLoaded : false,
24657 isDocument : false,
24659 paramName : 'imageUpload',
24661 loadingText : 'Loading...',
24664 getAutoCreate : function()
24668 cls : 'roo-upload-cropbox',
24672 cls : 'roo-upload-cropbox-selector',
24677 cls : 'roo-upload-cropbox-body',
24678 style : 'cursor:pointer',
24682 cls : 'roo-upload-cropbox-preview'
24686 cls : 'roo-upload-cropbox-thumb'
24690 cls : 'roo-upload-cropbox-empty-notify',
24691 html : this.emptyText
24695 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24696 html : this.rotateNotify
24702 cls : 'roo-upload-cropbox-footer',
24705 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24715 onRender : function(ct, position)
24717 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24719 if (this.buttons.length) {
24721 Roo.each(this.buttons, function(bb) {
24723 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24725 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24731 this.maskEl = this.el;
24735 initEvents : function()
24737 this.urlAPI = (window.createObjectURL && window) ||
24738 (window.URL && URL.revokeObjectURL && URL) ||
24739 (window.webkitURL && webkitURL);
24741 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24742 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24744 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24745 this.selectorEl.hide();
24747 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24748 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24750 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24751 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24752 this.thumbEl.hide();
24754 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24755 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24757 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24758 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24759 this.errorEl.hide();
24761 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24762 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24763 this.footerEl.hide();
24765 this.setThumbBoxSize();
24771 this.fireEvent('initial', this);
24778 window.addEventListener("resize", function() { _this.resize(); } );
24780 this.bodyEl.on('click', this.beforeSelectFile, this);
24783 this.bodyEl.on('touchstart', this.onTouchStart, this);
24784 this.bodyEl.on('touchmove', this.onTouchMove, this);
24785 this.bodyEl.on('touchend', this.onTouchEnd, this);
24789 this.bodyEl.on('mousedown', this.onMouseDown, this);
24790 this.bodyEl.on('mousemove', this.onMouseMove, this);
24791 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24792 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24793 Roo.get(document).on('mouseup', this.onMouseUp, this);
24796 this.selectorEl.on('change', this.onFileSelected, this);
24802 this.baseScale = 1;
24804 this.baseRotate = 1;
24805 this.dragable = false;
24806 this.pinching = false;
24809 this.cropData = false;
24810 this.notifyEl.dom.innerHTML = this.emptyText;
24812 this.selectorEl.dom.value = '';
24816 resize : function()
24818 if(this.fireEvent('resize', this) != false){
24819 this.setThumbBoxPosition();
24820 this.setCanvasPosition();
24824 onFooterButtonClick : function(e, el, o, type)
24827 case 'rotate-left' :
24828 this.onRotateLeft(e);
24830 case 'rotate-right' :
24831 this.onRotateRight(e);
24834 this.beforeSelectFile(e);
24849 this.fireEvent('footerbuttonclick', this, type);
24852 beforeSelectFile : function(e)
24854 e.preventDefault();
24856 if(this.fireEvent('beforeselectfile', this) != false){
24857 this.selectorEl.dom.click();
24861 onFileSelected : function(e)
24863 e.preventDefault();
24865 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24869 var file = this.selectorEl.dom.files[0];
24871 if(this.fireEvent('inspect', this, file) != false){
24872 this.prepare(file);
24877 trash : function(e)
24879 this.fireEvent('trash', this);
24882 download : function(e)
24884 this.fireEvent('download', this);
24887 loadCanvas : function(src)
24889 if(this.fireEvent('beforeloadcanvas', this, src) != false){
24893 this.imageEl = document.createElement('img');
24897 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24899 this.imageEl.src = src;
24903 onLoadCanvas : function()
24905 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24906 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24908 this.bodyEl.un('click', this.beforeSelectFile, this);
24910 this.notifyEl.hide();
24911 this.thumbEl.show();
24912 this.footerEl.show();
24914 this.baseRotateLevel();
24916 if(this.isDocument){
24917 this.setThumbBoxSize();
24920 this.setThumbBoxPosition();
24922 this.baseScaleLevel();
24928 this.canvasLoaded = true;
24931 this.maskEl.unmask();
24936 setCanvasPosition : function()
24938 if(!this.canvasEl){
24942 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24943 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24945 this.previewEl.setLeft(pw);
24946 this.previewEl.setTop(ph);
24950 onMouseDown : function(e)
24954 this.dragable = true;
24955 this.pinching = false;
24957 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24958 this.dragable = false;
24962 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24963 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24967 onMouseMove : function(e)
24971 if(!this.canvasLoaded){
24975 if (!this.dragable){
24979 var minX = Math.ceil(this.thumbEl.getLeft(true));
24980 var minY = Math.ceil(this.thumbEl.getTop(true));
24982 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24983 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24985 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24986 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24988 x = x - this.mouseX;
24989 y = y - this.mouseY;
24991 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24992 var bgY = Math.ceil(y + this.previewEl.getTop(true));
24994 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24995 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24997 this.previewEl.setLeft(bgX);
24998 this.previewEl.setTop(bgY);
25000 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25001 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25004 onMouseUp : function(e)
25008 this.dragable = false;
25011 onMouseWheel : function(e)
25015 this.startScale = this.scale;
25017 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25019 if(!this.zoomable()){
25020 this.scale = this.startScale;
25029 zoomable : function()
25031 var minScale = this.thumbEl.getWidth() / this.minWidth;
25033 if(this.minWidth < this.minHeight){
25034 minScale = this.thumbEl.getHeight() / this.minHeight;
25037 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25038 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25042 (this.rotate == 0 || this.rotate == 180) &&
25044 width > this.imageEl.OriginWidth ||
25045 height > this.imageEl.OriginHeight ||
25046 (width < this.minWidth && height < this.minHeight)
25054 (this.rotate == 90 || this.rotate == 270) &&
25056 width > this.imageEl.OriginWidth ||
25057 height > this.imageEl.OriginHeight ||
25058 (width < this.minHeight && height < this.minWidth)
25065 !this.isDocument &&
25066 (this.rotate == 0 || this.rotate == 180) &&
25068 width < this.minWidth ||
25069 width > this.imageEl.OriginWidth ||
25070 height < this.minHeight ||
25071 height > this.imageEl.OriginHeight
25078 !this.isDocument &&
25079 (this.rotate == 90 || this.rotate == 270) &&
25081 width < this.minHeight ||
25082 width > this.imageEl.OriginWidth ||
25083 height < this.minWidth ||
25084 height > this.imageEl.OriginHeight
25094 onRotateLeft : function(e)
25096 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25098 var minScale = this.thumbEl.getWidth() / this.minWidth;
25100 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25101 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25103 this.startScale = this.scale;
25105 while (this.getScaleLevel() < minScale){
25107 this.scale = this.scale + 1;
25109 if(!this.zoomable()){
25114 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25115 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25120 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25127 this.scale = this.startScale;
25129 this.onRotateFail();
25134 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25136 if(this.isDocument){
25137 this.setThumbBoxSize();
25138 this.setThumbBoxPosition();
25139 this.setCanvasPosition();
25144 this.fireEvent('rotate', this, 'left');
25148 onRotateRight : function(e)
25150 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25152 var minScale = this.thumbEl.getWidth() / this.minWidth;
25154 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25155 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25157 this.startScale = this.scale;
25159 while (this.getScaleLevel() < minScale){
25161 this.scale = this.scale + 1;
25163 if(!this.zoomable()){
25168 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25169 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25174 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25181 this.scale = this.startScale;
25183 this.onRotateFail();
25188 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25190 if(this.isDocument){
25191 this.setThumbBoxSize();
25192 this.setThumbBoxPosition();
25193 this.setCanvasPosition();
25198 this.fireEvent('rotate', this, 'right');
25201 onRotateFail : function()
25203 this.errorEl.show(true);
25207 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25212 this.previewEl.dom.innerHTML = '';
25214 var canvasEl = document.createElement("canvas");
25216 var contextEl = canvasEl.getContext("2d");
25218 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25219 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25220 var center = this.imageEl.OriginWidth / 2;
25222 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25223 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25224 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25225 center = this.imageEl.OriginHeight / 2;
25228 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25230 contextEl.translate(center, center);
25231 contextEl.rotate(this.rotate * Math.PI / 180);
25233 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25235 this.canvasEl = document.createElement("canvas");
25237 this.contextEl = this.canvasEl.getContext("2d");
25239 switch (this.rotate) {
25242 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25243 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25245 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25250 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25251 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25253 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25254 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);
25258 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25263 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25264 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25266 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25267 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);
25271 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);
25276 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25277 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25279 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25280 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25284 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);
25291 this.previewEl.appendChild(this.canvasEl);
25293 this.setCanvasPosition();
25298 if(!this.canvasLoaded){
25302 var imageCanvas = document.createElement("canvas");
25304 var imageContext = imageCanvas.getContext("2d");
25306 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25307 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25309 var center = imageCanvas.width / 2;
25311 imageContext.translate(center, center);
25313 imageContext.rotate(this.rotate * Math.PI / 180);
25315 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25317 var canvas = document.createElement("canvas");
25319 var context = canvas.getContext("2d");
25321 canvas.width = this.minWidth;
25322 canvas.height = this.minHeight;
25324 switch (this.rotate) {
25327 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25328 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25330 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25331 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25333 var targetWidth = this.minWidth - 2 * x;
25334 var targetHeight = this.minHeight - 2 * y;
25338 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25339 scale = targetWidth / width;
25342 if(x > 0 && y == 0){
25343 scale = targetHeight / height;
25346 if(x > 0 && y > 0){
25347 scale = targetWidth / width;
25349 if(width < height){
25350 scale = targetHeight / height;
25354 context.scale(scale, scale);
25356 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25357 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25359 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25360 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25362 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25367 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25368 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25370 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25371 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25373 var targetWidth = this.minWidth - 2 * x;
25374 var targetHeight = this.minHeight - 2 * y;
25378 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25379 scale = targetWidth / width;
25382 if(x > 0 && y == 0){
25383 scale = targetHeight / height;
25386 if(x > 0 && y > 0){
25387 scale = targetWidth / width;
25389 if(width < height){
25390 scale = targetHeight / height;
25394 context.scale(scale, scale);
25396 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25397 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25399 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25400 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25402 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25404 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25409 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25410 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25412 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25413 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25415 var targetWidth = this.minWidth - 2 * x;
25416 var targetHeight = this.minHeight - 2 * y;
25420 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25421 scale = targetWidth / width;
25424 if(x > 0 && y == 0){
25425 scale = targetHeight / height;
25428 if(x > 0 && y > 0){
25429 scale = targetWidth / width;
25431 if(width < height){
25432 scale = targetHeight / height;
25436 context.scale(scale, scale);
25438 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25439 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25441 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25442 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25444 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25445 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25447 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25452 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25453 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25455 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25456 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25458 var targetWidth = this.minWidth - 2 * x;
25459 var targetHeight = this.minHeight - 2 * y;
25463 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25464 scale = targetWidth / width;
25467 if(x > 0 && y == 0){
25468 scale = targetHeight / height;
25471 if(x > 0 && y > 0){
25472 scale = targetWidth / width;
25474 if(width < height){
25475 scale = targetHeight / height;
25479 context.scale(scale, scale);
25481 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25482 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25484 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25485 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25487 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25489 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25496 this.cropData = canvas.toDataURL(this.cropType);
25498 if(this.fireEvent('crop', this, this.cropData) !== false){
25499 this.process(this.file, this.cropData);
25506 setThumbBoxSize : function()
25510 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25511 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25512 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25514 this.minWidth = width;
25515 this.minHeight = height;
25517 if(this.rotate == 90 || this.rotate == 270){
25518 this.minWidth = height;
25519 this.minHeight = width;
25524 width = Math.ceil(this.minWidth * height / this.minHeight);
25526 if(this.minWidth > this.minHeight){
25528 height = Math.ceil(this.minHeight * width / this.minWidth);
25531 this.thumbEl.setStyle({
25532 width : width + 'px',
25533 height : height + 'px'
25540 setThumbBoxPosition : function()
25542 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25543 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25545 this.thumbEl.setLeft(x);
25546 this.thumbEl.setTop(y);
25550 baseRotateLevel : function()
25552 this.baseRotate = 1;
25555 typeof(this.exif) != 'undefined' &&
25556 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25557 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25559 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25562 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25566 baseScaleLevel : function()
25570 if(this.isDocument){
25572 if(this.baseRotate == 6 || this.baseRotate == 8){
25574 height = this.thumbEl.getHeight();
25575 this.baseScale = height / this.imageEl.OriginWidth;
25577 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25578 width = this.thumbEl.getWidth();
25579 this.baseScale = width / this.imageEl.OriginHeight;
25585 height = this.thumbEl.getHeight();
25586 this.baseScale = height / this.imageEl.OriginHeight;
25588 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25589 width = this.thumbEl.getWidth();
25590 this.baseScale = width / this.imageEl.OriginWidth;
25596 if(this.baseRotate == 6 || this.baseRotate == 8){
25598 width = this.thumbEl.getHeight();
25599 this.baseScale = width / this.imageEl.OriginHeight;
25601 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25602 height = this.thumbEl.getWidth();
25603 this.baseScale = height / this.imageEl.OriginHeight;
25606 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25607 height = this.thumbEl.getWidth();
25608 this.baseScale = height / this.imageEl.OriginHeight;
25610 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25611 width = this.thumbEl.getHeight();
25612 this.baseScale = width / this.imageEl.OriginWidth;
25619 width = this.thumbEl.getWidth();
25620 this.baseScale = width / this.imageEl.OriginWidth;
25622 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25623 height = this.thumbEl.getHeight();
25624 this.baseScale = height / this.imageEl.OriginHeight;
25627 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25629 height = this.thumbEl.getHeight();
25630 this.baseScale = height / this.imageEl.OriginHeight;
25632 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25633 width = this.thumbEl.getWidth();
25634 this.baseScale = width / this.imageEl.OriginWidth;
25642 getScaleLevel : function()
25644 return this.baseScale * Math.pow(1.1, this.scale);
25647 onTouchStart : function(e)
25649 if(!this.canvasLoaded){
25650 this.beforeSelectFile(e);
25654 var touches = e.browserEvent.touches;
25660 if(touches.length == 1){
25661 this.onMouseDown(e);
25665 if(touches.length != 2){
25671 for(var i = 0, finger; finger = touches[i]; i++){
25672 coords.push(finger.pageX, finger.pageY);
25675 var x = Math.pow(coords[0] - coords[2], 2);
25676 var y = Math.pow(coords[1] - coords[3], 2);
25678 this.startDistance = Math.sqrt(x + y);
25680 this.startScale = this.scale;
25682 this.pinching = true;
25683 this.dragable = false;
25687 onTouchMove : function(e)
25689 if(!this.pinching && !this.dragable){
25693 var touches = e.browserEvent.touches;
25700 this.onMouseMove(e);
25706 for(var i = 0, finger; finger = touches[i]; i++){
25707 coords.push(finger.pageX, finger.pageY);
25710 var x = Math.pow(coords[0] - coords[2], 2);
25711 var y = Math.pow(coords[1] - coords[3], 2);
25713 this.endDistance = Math.sqrt(x + y);
25715 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25717 if(!this.zoomable()){
25718 this.scale = this.startScale;
25726 onTouchEnd : function(e)
25728 this.pinching = false;
25729 this.dragable = false;
25733 process : function(file, crop)
25736 this.maskEl.mask(this.loadingText);
25739 this.xhr = new XMLHttpRequest();
25741 file.xhr = this.xhr;
25743 this.xhr.open(this.method, this.url, true);
25746 "Accept": "application/json",
25747 "Cache-Control": "no-cache",
25748 "X-Requested-With": "XMLHttpRequest"
25751 for (var headerName in headers) {
25752 var headerValue = headers[headerName];
25754 this.xhr.setRequestHeader(headerName, headerValue);
25760 this.xhr.onload = function()
25762 _this.xhrOnLoad(_this.xhr);
25765 this.xhr.onerror = function()
25767 _this.xhrOnError(_this.xhr);
25770 var formData = new FormData();
25772 formData.append('returnHTML', 'NO');
25775 formData.append('crop', crop);
25778 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25779 formData.append(this.paramName, file, file.name);
25782 if(typeof(file.filename) != 'undefined'){
25783 formData.append('filename', file.filename);
25786 if(typeof(file.mimetype) != 'undefined'){
25787 formData.append('mimetype', file.mimetype);
25790 if(this.fireEvent('arrange', this, formData) != false){
25791 this.xhr.send(formData);
25795 xhrOnLoad : function(xhr)
25798 this.maskEl.unmask();
25801 if (xhr.readyState !== 4) {
25802 this.fireEvent('exception', this, xhr);
25806 var response = Roo.decode(xhr.responseText);
25808 if(!response.success){
25809 this.fireEvent('exception', this, xhr);
25813 var response = Roo.decode(xhr.responseText);
25815 this.fireEvent('upload', this, response);
25819 xhrOnError : function()
25822 this.maskEl.unmask();
25825 Roo.log('xhr on error');
25827 var response = Roo.decode(xhr.responseText);
25833 prepare : function(file)
25836 this.maskEl.mask(this.loadingText);
25842 if(typeof(file) === 'string'){
25843 this.loadCanvas(file);
25847 if(!file || !this.urlAPI){
25852 this.cropType = file.type;
25856 if(this.fireEvent('prepare', this, this.file) != false){
25858 var reader = new FileReader();
25860 reader.onload = function (e) {
25861 if (e.target.error) {
25862 Roo.log(e.target.error);
25866 var buffer = e.target.result,
25867 dataView = new DataView(buffer),
25869 maxOffset = dataView.byteLength - 4,
25873 if (dataView.getUint16(0) === 0xffd8) {
25874 while (offset < maxOffset) {
25875 markerBytes = dataView.getUint16(offset);
25877 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25878 markerLength = dataView.getUint16(offset + 2) + 2;
25879 if (offset + markerLength > dataView.byteLength) {
25880 Roo.log('Invalid meta data: Invalid segment size.');
25884 if(markerBytes == 0xffe1){
25885 _this.parseExifData(
25892 offset += markerLength;
25902 var url = _this.urlAPI.createObjectURL(_this.file);
25904 _this.loadCanvas(url);
25909 reader.readAsArrayBuffer(this.file);
25915 parseExifData : function(dataView, offset, length)
25917 var tiffOffset = offset + 10,
25921 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25922 // No Exif data, might be XMP data instead
25926 // Check for the ASCII code for "Exif" (0x45786966):
25927 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25928 // No Exif data, might be XMP data instead
25931 if (tiffOffset + 8 > dataView.byteLength) {
25932 Roo.log('Invalid Exif data: Invalid segment size.');
25935 // Check for the two null bytes:
25936 if (dataView.getUint16(offset + 8) !== 0x0000) {
25937 Roo.log('Invalid Exif data: Missing byte alignment offset.');
25940 // Check the byte alignment:
25941 switch (dataView.getUint16(tiffOffset)) {
25943 littleEndian = true;
25946 littleEndian = false;
25949 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25952 // Check for the TIFF tag marker (0x002A):
25953 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25954 Roo.log('Invalid Exif data: Missing TIFF marker.');
25957 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25958 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25960 this.parseExifTags(
25963 tiffOffset + dirOffset,
25968 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25973 if (dirOffset + 6 > dataView.byteLength) {
25974 Roo.log('Invalid Exif data: Invalid directory offset.');
25977 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25978 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25979 if (dirEndOffset + 4 > dataView.byteLength) {
25980 Roo.log('Invalid Exif data: Invalid directory size.');
25983 for (i = 0; i < tagsNumber; i += 1) {
25987 dirOffset + 2 + 12 * i, // tag offset
25991 // Return the offset to the next directory:
25992 return dataView.getUint32(dirEndOffset, littleEndian);
25995 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
25997 var tag = dataView.getUint16(offset, littleEndian);
25999 this.exif[tag] = this.getExifValue(
26003 dataView.getUint16(offset + 2, littleEndian), // tag type
26004 dataView.getUint32(offset + 4, littleEndian), // tag length
26009 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26011 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26020 Roo.log('Invalid Exif data: Invalid tag type.');
26024 tagSize = tagType.size * length;
26025 // Determine if the value is contained in the dataOffset bytes,
26026 // or if the value at the dataOffset is a pointer to the actual data:
26027 dataOffset = tagSize > 4 ?
26028 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26029 if (dataOffset + tagSize > dataView.byteLength) {
26030 Roo.log('Invalid Exif data: Invalid data offset.');
26033 if (length === 1) {
26034 return tagType.getValue(dataView, dataOffset, littleEndian);
26037 for (i = 0; i < length; i += 1) {
26038 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26041 if (tagType.ascii) {
26043 // Concatenate the chars:
26044 for (i = 0; i < values.length; i += 1) {
26046 // Ignore the terminating NULL byte(s):
26047 if (c === '\u0000') {
26059 Roo.apply(Roo.bootstrap.UploadCropbox, {
26061 'Orientation': 0x0112
26065 1: 0, //'top-left',
26067 3: 180, //'bottom-right',
26068 // 4: 'bottom-left',
26070 6: 90, //'right-top',
26071 // 7: 'right-bottom',
26072 8: 270 //'left-bottom'
26076 // byte, 8-bit unsigned int:
26078 getValue: function (dataView, dataOffset) {
26079 return dataView.getUint8(dataOffset);
26083 // ascii, 8-bit byte:
26085 getValue: function (dataView, dataOffset) {
26086 return String.fromCharCode(dataView.getUint8(dataOffset));
26091 // short, 16 bit int:
26093 getValue: function (dataView, dataOffset, littleEndian) {
26094 return dataView.getUint16(dataOffset, littleEndian);
26098 // long, 32 bit int:
26100 getValue: function (dataView, dataOffset, littleEndian) {
26101 return dataView.getUint32(dataOffset, littleEndian);
26105 // rational = two long values, first is numerator, second is denominator:
26107 getValue: function (dataView, dataOffset, littleEndian) {
26108 return dataView.getUint32(dataOffset, littleEndian) /
26109 dataView.getUint32(dataOffset + 4, littleEndian);
26113 // slong, 32 bit signed int:
26115 getValue: function (dataView, dataOffset, littleEndian) {
26116 return dataView.getInt32(dataOffset, littleEndian);
26120 // srational, two slongs, first is numerator, second is denominator:
26122 getValue: function (dataView, dataOffset, littleEndian) {
26123 return dataView.getInt32(dataOffset, littleEndian) /
26124 dataView.getInt32(dataOffset + 4, littleEndian);
26134 cls : 'btn-group roo-upload-cropbox-rotate-left',
26135 action : 'rotate-left',
26139 cls : 'btn btn-default',
26140 html : '<i class="fa fa-undo"></i>'
26146 cls : 'btn-group roo-upload-cropbox-picture',
26147 action : 'picture',
26151 cls : 'btn btn-default',
26152 html : '<i class="fa fa-picture-o"></i>'
26158 cls : 'btn-group roo-upload-cropbox-rotate-right',
26159 action : 'rotate-right',
26163 cls : 'btn btn-default',
26164 html : '<i class="fa fa-repeat"></i>'
26172 cls : 'btn-group roo-upload-cropbox-rotate-left',
26173 action : 'rotate-left',
26177 cls : 'btn btn-default',
26178 html : '<i class="fa fa-undo"></i>'
26184 cls : 'btn-group roo-upload-cropbox-download',
26185 action : 'download',
26189 cls : 'btn btn-default',
26190 html : '<i class="fa fa-download"></i>'
26196 cls : 'btn-group roo-upload-cropbox-crop',
26201 cls : 'btn btn-default',
26202 html : '<i class="fa fa-crop"></i>'
26208 cls : 'btn-group roo-upload-cropbox-trash',
26213 cls : 'btn btn-default',
26214 html : '<i class="fa fa-trash"></i>'
26220 cls : 'btn-group roo-upload-cropbox-rotate-right',
26221 action : 'rotate-right',
26225 cls : 'btn btn-default',
26226 html : '<i class="fa fa-repeat"></i>'
26234 cls : 'btn-group roo-upload-cropbox-rotate-left',
26235 action : 'rotate-left',
26239 cls : 'btn btn-default',
26240 html : '<i class="fa fa-undo"></i>'
26246 cls : 'btn-group roo-upload-cropbox-rotate-right',
26247 action : 'rotate-right',
26251 cls : 'btn btn-default',
26252 html : '<i class="fa fa-repeat"></i>'
26265 * @class Roo.bootstrap.DocumentManager
26266 * @extends Roo.bootstrap.Component
26267 * Bootstrap DocumentManager class
26268 * @cfg {String} paramName default 'imageUpload'
26269 * @cfg {String} method default POST
26270 * @cfg {String} url action url
26271 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26272 * @cfg {Boolean} multiple multiple upload default true
26273 * @cfg {Number} thumbSize default 300
26274 * @cfg {String} fieldLabel
26275 * @cfg {Number} labelWidth default 4
26276 * @cfg {String} labelAlign (left|top) default left
26277 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26280 * Create a new DocumentManager
26281 * @param {Object} config The config object
26284 Roo.bootstrap.DocumentManager = function(config){
26285 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26290 * Fire when initial the DocumentManager
26291 * @param {Roo.bootstrap.DocumentManager} this
26296 * inspect selected file
26297 * @param {Roo.bootstrap.DocumentManager} this
26298 * @param {File} file
26303 * Fire when xhr load exception
26304 * @param {Roo.bootstrap.DocumentManager} this
26305 * @param {XMLHttpRequest} xhr
26307 "exception" : true,
26310 * prepare the form data
26311 * @param {Roo.bootstrap.DocumentManager} this
26312 * @param {Object} formData
26317 * Fire when remove the file
26318 * @param {Roo.bootstrap.DocumentManager} this
26319 * @param {Object} file
26324 * Fire after refresh the file
26325 * @param {Roo.bootstrap.DocumentManager} this
26330 * Fire after click the image
26331 * @param {Roo.bootstrap.DocumentManager} this
26332 * @param {Object} file
26337 * Fire when upload a image and editable set to true
26338 * @param {Roo.bootstrap.DocumentManager} this
26339 * @param {Object} file
26343 * @event beforeselectfile
26344 * Fire before select file
26345 * @param {Roo.bootstrap.DocumentManager} this
26347 "beforeselectfile" : true,
26350 * Fire before process file
26351 * @param {Roo.bootstrap.DocumentManager} this
26352 * @param {Object} file
26359 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
26368 paramName : 'imageUpload',
26371 labelAlign : 'left',
26378 getAutoCreate : function()
26380 var managerWidget = {
26382 cls : 'roo-document-manager',
26386 cls : 'roo-document-manager-selector',
26391 cls : 'roo-document-manager-uploader',
26395 cls : 'roo-document-manager-upload-btn',
26396 html : '<i class="fa fa-plus"></i>'
26407 cls : 'column col-md-12',
26412 if(this.fieldLabel.length){
26417 cls : 'column col-md-12',
26418 html : this.fieldLabel
26422 cls : 'column col-md-12',
26427 if(this.labelAlign == 'left'){
26431 cls : 'column col-md-' + this.labelWidth,
26432 html : this.fieldLabel
26436 cls : 'column col-md-' + (12 - this.labelWidth),
26446 cls : 'row clearfix',
26454 initEvents : function()
26456 this.managerEl = this.el.select('.roo-document-manager', true).first();
26457 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26459 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26460 this.selectorEl.hide();
26463 this.selectorEl.attr('multiple', 'multiple');
26466 this.selectorEl.on('change', this.onFileSelected, this);
26468 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26469 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26471 this.uploader.on('click', this.onUploaderClick, this);
26473 this.renderProgressDialog();
26477 window.addEventListener("resize", function() { _this.refresh(); } );
26479 this.fireEvent('initial', this);
26482 renderProgressDialog : function()
26486 this.progressDialog = new Roo.bootstrap.Modal({
26487 cls : 'roo-document-manager-progress-dialog',
26488 allow_close : false,
26498 btnclick : function() {
26499 _this.uploadCancel();
26505 this.progressDialog.render(Roo.get(document.body));
26507 this.progress = new Roo.bootstrap.Progress({
26508 cls : 'roo-document-manager-progress',
26513 this.progress.render(this.progressDialog.getChildContainer());
26515 this.progressBar = new Roo.bootstrap.ProgressBar({
26516 cls : 'roo-document-manager-progress-bar',
26519 aria_valuemax : 12,
26523 this.progressBar.render(this.progress.getChildContainer());
26526 onUploaderClick : function(e)
26528 e.preventDefault();
26530 if(this.fireEvent('beforeselectfile', this) != false){
26531 this.selectorEl.dom.click();
26536 onFileSelected : function(e)
26538 e.preventDefault();
26540 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26544 Roo.each(this.selectorEl.dom.files, function(file){
26545 if(this.fireEvent('inspect', this, file) != false){
26546 this.files.push(file);
26556 this.selectorEl.dom.value = '';
26558 if(!this.files.length){
26562 if(this.boxes > 0 && this.files.length > this.boxes){
26563 this.files = this.files.slice(0, this.boxes);
26566 this.uploader.show();
26568 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26569 this.uploader.hide();
26578 Roo.each(this.files, function(file){
26580 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26581 var f = this.renderPreview(file);
26586 if(file.type.indexOf('image') != -1){
26587 this.delegates.push(
26589 _this.process(file);
26590 }).createDelegate(this)
26598 _this.process(file);
26599 }).createDelegate(this)
26604 this.files = files;
26606 this.delegates = this.delegates.concat(docs);
26608 if(!this.delegates.length){
26613 this.progressBar.aria_valuemax = this.delegates.length;
26620 arrange : function()
26622 if(!this.delegates.length){
26623 this.progressDialog.hide();
26628 var delegate = this.delegates.shift();
26630 this.progressDialog.show();
26632 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26634 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26639 refresh : function()
26641 this.uploader.show();
26643 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26644 this.uploader.hide();
26647 Roo.isTouch ? this.closable(false) : this.closable(true);
26649 this.fireEvent('refresh', this);
26652 onRemove : function(e, el, o)
26654 e.preventDefault();
26656 this.fireEvent('remove', this, o);
26660 remove : function(o)
26664 Roo.each(this.files, function(file){
26665 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26674 this.files = files;
26681 Roo.each(this.files, function(file){
26686 file.target.remove();
26695 onClick : function(e, el, o)
26697 e.preventDefault();
26699 this.fireEvent('click', this, o);
26703 closable : function(closable)
26705 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26707 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26719 xhrOnLoad : function(xhr)
26721 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26725 if (xhr.readyState !== 4) {
26727 this.fireEvent('exception', this, xhr);
26731 var response = Roo.decode(xhr.responseText);
26733 if(!response.success){
26735 this.fireEvent('exception', this, xhr);
26739 var file = this.renderPreview(response.data);
26741 this.files.push(file);
26747 xhrOnError : function()
26749 Roo.log('xhr on error');
26751 var response = Roo.decode(xhr.responseText);
26758 process : function(file)
26760 if(this.fireEvent('process', this, file) !== false){
26761 if(this.editable && file.type.indexOf('image') != -1){
26762 this.fireEvent('edit', this, file);
26766 this.uploadStart(file, false);
26773 uploadStart : function(file, crop)
26775 this.xhr = new XMLHttpRequest();
26777 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26782 file.xhr = this.xhr;
26784 this.managerEl.createChild({
26786 cls : 'roo-document-manager-loading',
26790 tooltip : file.name,
26791 cls : 'roo-document-manager-thumb',
26792 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26798 this.xhr.open(this.method, this.url, true);
26801 "Accept": "application/json",
26802 "Cache-Control": "no-cache",
26803 "X-Requested-With": "XMLHttpRequest"
26806 for (var headerName in headers) {
26807 var headerValue = headers[headerName];
26809 this.xhr.setRequestHeader(headerName, headerValue);
26815 this.xhr.onload = function()
26817 _this.xhrOnLoad(_this.xhr);
26820 this.xhr.onerror = function()
26822 _this.xhrOnError(_this.xhr);
26825 var formData = new FormData();
26827 formData.append('returnHTML', 'NO');
26830 formData.append('crop', crop);
26833 formData.append(this.paramName, file, file.name);
26835 if(this.fireEvent('prepare', this, formData) != false){
26836 this.xhr.send(formData);
26840 uploadCancel : function()
26847 this.delegates = [];
26849 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26856 renderPreview : function(file)
26858 if(typeof(file.target) != 'undefined' && file.target){
26862 var previewEl = this.managerEl.createChild({
26864 cls : 'roo-document-manager-preview',
26868 tooltip : file.filename,
26869 cls : 'roo-document-manager-thumb',
26870 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26875 html : '<i class="fa fa-times-circle"></i>'
26880 var close = previewEl.select('button.close', true).first();
26882 close.on('click', this.onRemove, this, file);
26884 file.target = previewEl;
26886 var image = previewEl.select('img', true).first();
26890 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26892 image.on('click', this.onClick, this, file);
26898 onPreviewLoad : function(file, image)
26900 if(typeof(file.target) == 'undefined' || !file.target){
26904 var width = image.dom.naturalWidth || image.dom.width;
26905 var height = image.dom.naturalHeight || image.dom.height;
26907 if(width > height){
26908 file.target.addClass('wide');
26912 file.target.addClass('tall');
26917 uploadFromSource : function(file, crop)
26919 this.xhr = new XMLHttpRequest();
26921 this.managerEl.createChild({
26923 cls : 'roo-document-manager-loading',
26927 tooltip : file.name,
26928 cls : 'roo-document-manager-thumb',
26929 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26935 this.xhr.open(this.method, this.url, true);
26938 "Accept": "application/json",
26939 "Cache-Control": "no-cache",
26940 "X-Requested-With": "XMLHttpRequest"
26943 for (var headerName in headers) {
26944 var headerValue = headers[headerName];
26946 this.xhr.setRequestHeader(headerName, headerValue);
26952 this.xhr.onload = function()
26954 _this.xhrOnLoad(_this.xhr);
26957 this.xhr.onerror = function()
26959 _this.xhrOnError(_this.xhr);
26962 var formData = new FormData();
26964 formData.append('returnHTML', 'NO');
26966 formData.append('crop', crop);
26968 if(typeof(file.filename) != 'undefined'){
26969 formData.append('filename', file.filename);
26972 if(typeof(file.mimetype) != 'undefined'){
26973 formData.append('mimetype', file.mimetype);
26976 if(this.fireEvent('prepare', this, formData) != false){
26977 this.xhr.send(formData);
26987 * @class Roo.bootstrap.DocumentViewer
26988 * @extends Roo.bootstrap.Component
26989 * Bootstrap DocumentViewer class
26992 * Create a new DocumentViewer
26993 * @param {Object} config The config object
26996 Roo.bootstrap.DocumentViewer = function(config){
26997 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27002 * Fire after initEvent
27003 * @param {Roo.bootstrap.DocumentViewer} this
27009 * @param {Roo.bootstrap.DocumentViewer} this
27014 * Fire after trash button
27015 * @param {Roo.bootstrap.DocumentViewer} this
27022 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
27024 getAutoCreate : function()
27028 cls : 'roo-document-viewer',
27032 cls : 'roo-document-viewer-body',
27036 cls : 'roo-document-viewer-thumb',
27040 cls : 'roo-document-viewer-image'
27048 cls : 'roo-document-viewer-footer',
27051 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27059 cls : 'btn btn-default roo-document-viewer-trash',
27060 html : '<i class="fa fa-trash"></i>'
27073 initEvents : function()
27076 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27077 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27079 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27080 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27082 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27083 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27085 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27086 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27088 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27089 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27091 this.bodyEl.on('click', this.onClick, this);
27093 this.trashBtn.on('click', this.onTrash, this);
27097 initial : function()
27099 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27102 this.fireEvent('initial', this);
27106 onClick : function(e)
27108 e.preventDefault();
27110 this.fireEvent('click', this);
27113 onTrash : function(e)
27115 e.preventDefault();
27117 this.fireEvent('trash', this);
27129 * @class Roo.bootstrap.NavProgressBar
27130 * @extends Roo.bootstrap.Component
27131 * Bootstrap NavProgressBar class
27134 * Create a new nav progress bar
27135 * @param {Object} config The config object
27138 Roo.bootstrap.NavProgressBar = function(config){
27139 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27141 this.bullets = this.bullets || [];
27143 // Roo.bootstrap.NavProgressBar.register(this);
27147 * Fires when the active item changes
27148 * @param {Roo.bootstrap.NavProgressBar} this
27149 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27150 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
27157 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
27162 getAutoCreate : function()
27164 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27168 cls : 'roo-navigation-bar-group',
27172 cls : 'roo-navigation-top-bar'
27176 cls : 'roo-navigation-bullets-bar',
27180 cls : 'roo-navigation-bar'
27187 cls : 'roo-navigation-bottom-bar'
27197 initEvents: function()
27202 onRender : function(ct, position)
27204 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27206 if(this.bullets.length){
27207 Roo.each(this.bullets, function(b){
27216 addItem : function(cfg)
27218 var item = new Roo.bootstrap.NavProgressItem(cfg);
27220 item.parentId = this.id;
27221 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27224 var top = new Roo.bootstrap.Element({
27226 cls : 'roo-navigation-bar-text'
27229 var bottom = new Roo.bootstrap.Element({
27231 cls : 'roo-navigation-bar-text'
27234 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27235 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27237 var topText = new Roo.bootstrap.Element({
27239 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27242 var bottomText = new Roo.bootstrap.Element({
27244 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27247 topText.onRender(top.el, null);
27248 bottomText.onRender(bottom.el, null);
27251 item.bottomEl = bottom;
27254 this.barItems.push(item);
27259 getActive : function()
27261 var active = false;
27263 Roo.each(this.barItems, function(v){
27265 if (!v.isActive()) {
27277 setActiveItem : function(item)
27281 Roo.each(this.barItems, function(v){
27282 if (v.rid == item.rid) {
27286 if (v.isActive()) {
27287 v.setActive(false);
27292 item.setActive(true);
27294 this.fireEvent('changed', this, item, prev);
27297 getBarItem: function(rid)
27301 Roo.each(this.barItems, function(e) {
27302 if (e.rid != rid) {
27313 indexOfItem : function(item)
27317 Roo.each(this.barItems, function(v, i){
27319 if (v.rid != item.rid) {
27330 setActiveNext : function()
27332 var i = this.indexOfItem(this.getActive());
27334 if (i > this.barItems.length) {
27338 this.setActiveItem(this.barItems[i+1]);
27341 setActivePrev : function()
27343 var i = this.indexOfItem(this.getActive());
27349 this.setActiveItem(this.barItems[i-1]);
27352 format : function()
27354 if(!this.barItems.length){
27358 var width = 100 / this.barItems.length;
27360 Roo.each(this.barItems, function(i){
27361 i.el.setStyle('width', width + '%');
27362 i.topEl.el.setStyle('width', width + '%');
27363 i.bottomEl.el.setStyle('width', width + '%');
27372 * Nav Progress Item
27377 * @class Roo.bootstrap.NavProgressItem
27378 * @extends Roo.bootstrap.Component
27379 * Bootstrap NavProgressItem class
27380 * @cfg {String} rid the reference id
27381 * @cfg {Boolean} active (true|false) Is item active default false
27382 * @cfg {Boolean} disabled (true|false) Is item active default false
27383 * @cfg {String} html
27384 * @cfg {String} position (top|bottom) text position default bottom
27385 * @cfg {String} icon show icon instead of number
27388 * Create a new NavProgressItem
27389 * @param {Object} config The config object
27391 Roo.bootstrap.NavProgressItem = function(config){
27392 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27397 * The raw click event for the entire grid.
27398 * @param {Roo.bootstrap.NavProgressItem} this
27399 * @param {Roo.EventObject} e
27406 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
27412 position : 'bottom',
27415 getAutoCreate : function()
27417 var iconCls = 'roo-navigation-bar-item-icon';
27419 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27423 cls: 'roo-navigation-bar-item',
27433 cfg.cls += ' active';
27436 cfg.cls += ' disabled';
27442 disable : function()
27444 this.setDisabled(true);
27447 enable : function()
27449 this.setDisabled(false);
27452 initEvents: function()
27454 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27456 this.iconEl.on('click', this.onClick, this);
27459 onClick : function(e)
27461 e.preventDefault();
27467 if(this.fireEvent('click', this, e) === false){
27471 this.parent().setActiveItem(this);
27474 isActive: function ()
27476 return this.active;
27479 setActive : function(state)
27481 if(this.active == state){
27485 this.active = state;
27488 this.el.addClass('active');
27492 this.el.removeClass('active');
27497 setDisabled : function(state)
27499 if(this.disabled == state){
27503 this.disabled = state;
27506 this.el.addClass('disabled');
27510 this.el.removeClass('disabled');
27513 tooltipEl : function()
27515 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27528 * @class Roo.bootstrap.FieldLabel
27529 * @extends Roo.bootstrap.Component
27530 * Bootstrap FieldLabel class
27531 * @cfg {String} html contents of the element
27532 * @cfg {String} tag tag of the element default label
27533 * @cfg {String} cls class of the element
27534 * @cfg {String} target label target
27535 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27536 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27537 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27538 * @cfg {String} iconTooltip default "This field is required"
27541 * Create a new FieldLabel
27542 * @param {Object} config The config object
27545 Roo.bootstrap.FieldLabel = function(config){
27546 Roo.bootstrap.Element.superclass.constructor.call(this, config);
27551 * Fires after the field has been marked as invalid.
27552 * @param {Roo.form.FieldLabel} this
27553 * @param {String} msg The validation message
27558 * Fires after the field has been validated with no errors.
27559 * @param {Roo.form.FieldLabel} this
27565 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
27572 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27573 validClass : 'text-success fa fa-lg fa-check',
27574 iconTooltip : 'This field is required',
27576 getAutoCreate : function(){
27580 cls : 'roo-bootstrap-field-label ' + this.cls,
27586 tooltip : this.iconTooltip
27598 initEvents: function()
27600 Roo.bootstrap.Element.superclass.initEvents.call(this);
27602 this.iconEl = this.el.select('i', true).first();
27604 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27606 Roo.bootstrap.FieldLabel.register(this);
27610 * Mark this field as valid
27612 markValid : function()
27614 this.iconEl.show();
27616 this.iconEl.removeClass(this.invalidClass);
27618 this.iconEl.addClass(this.validClass);
27620 this.fireEvent('valid', this);
27624 * Mark this field as invalid
27625 * @param {String} msg The validation message
27627 markInvalid : function(msg)
27629 this.iconEl.show();
27631 this.iconEl.removeClass(this.validClass);
27633 this.iconEl.addClass(this.invalidClass);
27635 this.fireEvent('invalid', this, msg);
27641 Roo.apply(Roo.bootstrap.FieldLabel, {
27646 * register a FieldLabel Group
27647 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27649 register : function(label)
27651 if(this.groups.hasOwnProperty(label.target)){
27655 this.groups[label.target] = label;
27659 * fetch a FieldLabel Group based on the target
27660 * @param {string} target
27661 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27663 get: function(target) {
27664 if (typeof(this.groups[target]) == 'undefined') {
27668 return this.groups[target] ;
27677 * page DateSplitField.
27683 * @class Roo.bootstrap.DateSplitField
27684 * @extends Roo.bootstrap.Component
27685 * Bootstrap DateSplitField class
27686 * @cfg {string} fieldLabel - the label associated
27687 * @cfg {Number} labelWidth set the width of label (0-12)
27688 * @cfg {String} labelAlign (top|left)
27689 * @cfg {Boolean} dayAllowBlank (true|false) default false
27690 * @cfg {Boolean} monthAllowBlank (true|false) default false
27691 * @cfg {Boolean} yearAllowBlank (true|false) default false
27692 * @cfg {string} dayPlaceholder
27693 * @cfg {string} monthPlaceholder
27694 * @cfg {string} yearPlaceholder
27695 * @cfg {string} dayFormat default 'd'
27696 * @cfg {string} monthFormat default 'm'
27697 * @cfg {string} yearFormat default 'Y'
27701 * Create a new DateSplitField
27702 * @param {Object} config The config object
27705 Roo.bootstrap.DateSplitField = function(config){
27706 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27712 * getting the data of years
27713 * @param {Roo.bootstrap.DateSplitField} this
27714 * @param {Object} years
27719 * getting the data of days
27720 * @param {Roo.bootstrap.DateSplitField} this
27721 * @param {Object} days
27726 * Fires after the field has been marked as invalid.
27727 * @param {Roo.form.Field} this
27728 * @param {String} msg The validation message
27733 * Fires after the field has been validated with no errors.
27734 * @param {Roo.form.Field} this
27740 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
27743 labelAlign : 'top',
27745 dayAllowBlank : false,
27746 monthAllowBlank : false,
27747 yearAllowBlank : false,
27748 dayPlaceholder : '',
27749 monthPlaceholder : '',
27750 yearPlaceholder : '',
27754 isFormField : true,
27756 getAutoCreate : function()
27760 cls : 'row roo-date-split-field-group',
27765 cls : 'form-hidden-field roo-date-split-field-group-value',
27771 if(this.fieldLabel){
27774 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27778 html : this.fieldLabel
27784 Roo.each(['day', 'month', 'year'], function(t){
27787 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27794 inputEl: function ()
27796 return this.el.select('.roo-date-split-field-group-value', true).first();
27799 onRender : function(ct, position)
27803 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27805 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27807 this.dayField = new Roo.bootstrap.ComboBox({
27808 allowBlank : this.dayAllowBlank,
27809 alwaysQuery : true,
27810 displayField : 'value',
27813 forceSelection : true,
27815 placeholder : this.dayPlaceholder,
27816 selectOnFocus : true,
27817 tpl : '<div class="select2-result"><b>{value}</b></div>',
27818 triggerAction : 'all',
27820 valueField : 'value',
27821 store : new Roo.data.SimpleStore({
27822 data : (function() {
27824 _this.fireEvent('days', _this, days);
27827 fields : [ 'value' ]
27830 select : function (_self, record, index)
27832 _this.setValue(_this.getValue());
27837 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27839 this.monthField = new Roo.bootstrap.MonthField({
27840 after : '<i class=\"fa fa-calendar\"></i>',
27841 allowBlank : this.monthAllowBlank,
27842 placeholder : this.monthPlaceholder,
27845 render : function (_self)
27847 this.el.select('span.input-group-addon', true).first().on('click', function(e){
27848 e.preventDefault();
27852 select : function (_self, oldvalue, newvalue)
27854 _this.setValue(_this.getValue());
27859 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27861 this.yearField = new Roo.bootstrap.ComboBox({
27862 allowBlank : this.yearAllowBlank,
27863 alwaysQuery : true,
27864 displayField : 'value',
27867 forceSelection : true,
27869 placeholder : this.yearPlaceholder,
27870 selectOnFocus : true,
27871 tpl : '<div class="select2-result"><b>{value}</b></div>',
27872 triggerAction : 'all',
27874 valueField : 'value',
27875 store : new Roo.data.SimpleStore({
27876 data : (function() {
27878 _this.fireEvent('years', _this, years);
27881 fields : [ 'value' ]
27884 select : function (_self, record, index)
27886 _this.setValue(_this.getValue());
27891 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
27894 setValue : function(v, format)
27896 this.inputEl.dom.value = v;
27898 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
27900 var d = Date.parseDate(v, f);
27907 this.setDay(d.format(this.dayFormat));
27908 this.setMonth(d.format(this.monthFormat));
27909 this.setYear(d.format(this.yearFormat));
27916 setDay : function(v)
27918 this.dayField.setValue(v);
27919 this.inputEl.dom.value = this.getValue();
27924 setMonth : function(v)
27926 this.monthField.setValue(v, true);
27927 this.inputEl.dom.value = this.getValue();
27932 setYear : function(v)
27934 this.yearField.setValue(v);
27935 this.inputEl.dom.value = this.getValue();
27940 getDay : function()
27942 return this.dayField.getValue();
27945 getMonth : function()
27947 return this.monthField.getValue();
27950 getYear : function()
27952 return this.yearField.getValue();
27955 getValue : function()
27957 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
27959 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
27969 this.inputEl.dom.value = '';
27974 validate : function()
27976 var d = this.dayField.validate();
27977 var m = this.monthField.validate();
27978 var y = this.yearField.validate();
27983 (!this.dayAllowBlank && !d) ||
27984 (!this.monthAllowBlank && !m) ||
27985 (!this.yearAllowBlank && !y)
27990 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
27999 this.markInvalid();
28004 markValid : function()
28007 var label = this.el.select('label', true).first();
28008 var icon = this.el.select('i.fa-star', true).first();
28014 this.fireEvent('valid', this);
28018 * Mark this field as invalid
28019 * @param {String} msg The validation message
28021 markInvalid : function(msg)
28024 var label = this.el.select('label', true).first();
28025 var icon = this.el.select('i.fa-star', true).first();
28027 if(label && !icon){
28028 this.el.select('.roo-date-split-field-label', true).createChild({
28030 cls : 'text-danger fa fa-lg fa-star',
28031 tooltip : 'This field is required',
28032 style : 'margin-right:5px;'
28036 this.fireEvent('invalid', this, msg);
28039 clearInvalid : function()
28041 var label = this.el.select('label', true).first();
28042 var icon = this.el.select('i.fa-star', true).first();
28048 this.fireEvent('valid', this);
28051 getName: function()