4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
23 * Do not use directly - it does not do anything..
24 * @param {Object} config The config object
29 Roo.bootstrap.Component = function(config){
30 Roo.bootstrap.Component.superclass.constructor.call(this, config);
34 * @event childrenrendered
35 * Fires when the children have been rendered..
36 * @param {Roo.bootstrap.Component} this
38 "childrenrendered" : true
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
50 allowDomMove : false, // to stop relocations in parent onRender...
60 * Initialize Events for the element
62 initEvents : function() { },
68 can_build_overlaid : true,
70 container_method : false,
77 // returns the parent component..
78 return Roo.ComponentMgr.get(this.parentId)
84 onRender : function(ct, position)
86 // Roo.log("Call onRender: " + this.xtype);
88 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
91 if (this.el.attr('xtype')) {
92 this.el.attr('xtypex', this.el.attr('xtype'));
93 this.el.dom.removeAttribute('xtype');
103 var cfg = Roo.apply({}, this.getAutoCreate());
104 cfg.id = this.id || Roo.id();
106 // fill in the extra attributes
107 if (this.xattr && typeof(this.xattr) =='object') {
108 for (var i in this.xattr) {
109 cfg[i] = this.xattr[i];
114 cfg.dataId = this.dataId;
118 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121 if (this.style) { // fixme needs to support more complex style data.
122 cfg.style = this.style;
126 cfg.name = this.name;
129 this.el = ct.createChild(cfg, position);
132 this.tooltipEl().attr('tooltip', this.tooltip);
135 if(this.tabIndex !== undefined){
136 this.el.dom.setAttribute('tabIndex', this.tabIndex);
143 * Fetch the element to add children to
144 * @return {Roo.Element} defaults to this.el
146 getChildContainer : function()
151 * Fetch the element to display the tooltip on.
152 * @return {Roo.Element} defaults to this.el
154 tooltipEl : function()
159 addxtype : function(tree,cntr)
163 cn = Roo.factory(tree);
165 cn.parentType = this.xtype; //??
166 cn.parentId = this.id;
168 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
169 if (typeof(cn.container_method) == 'string') {
170 cntr = cn.container_method;
174 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
176 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
178 var build_from_html = Roo.XComponent.build_from_html;
180 var is_body = (tree.xtype == 'Body') ;
182 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
184 var self_cntr_el = Roo.get(this[cntr](false));
186 // do not try and build conditional elements
187 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
191 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
192 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
193 return this.addxtypeChild(tree,cntr);
196 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202 Roo.log('skipping render');
208 if (!build_from_html) {
212 // this i think handles overlaying multiple children of the same type
213 // with the sam eelement.. - which might be buggy..
215 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
221 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
225 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
230 addxtypeChild : function (tree, cntr)
232 Roo.debug && Roo.log('addxtypeChild:' + cntr);
234 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
237 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
238 (typeof(tree['flexy:foreach']) != 'undefined');
242 skip_children = false;
243 // render the element if it's not BODY.
244 if (tree.xtype != 'Body') {
246 cn = Roo.factory(tree);
248 cn.parentType = this.xtype; //??
249 cn.parentId = this.id;
251 var build_from_html = Roo.XComponent.build_from_html;
254 // does the container contain child eleemnts with 'xtype' attributes.
255 // that match this xtype..
256 // note - when we render we create these as well..
257 // so we should check to see if body has xtype set.
258 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
260 var self_cntr_el = Roo.get(this[cntr](false));
261 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
263 //Roo.log(Roo.XComponent.build_from_html);
264 //Roo.log("got echild:");
267 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
268 // and are not displayed -this causes this to use up the wrong element when matching.
269 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
272 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
273 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
279 //echild.dom.removeAttribute('xtype');
281 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
282 Roo.debug && Roo.log(self_cntr_el);
283 Roo.debug && Roo.log(echild);
284 Roo.debug && Roo.log(cn);
290 // if object has flexy:if - then it may or may not be rendered.
291 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
292 // skip a flexy if element.
293 Roo.debug && Roo.log('skipping render');
294 Roo.debug && Roo.log(tree);
296 Roo.debug && Roo.log('skipping all children');
297 skip_children = true;
302 // actually if flexy:foreach is found, we really want to create
303 // multiple copies here...
305 //Roo.log(this[cntr]());
306 cn.render(this[cntr](true));
308 // then add the element..
316 if (typeof (tree.menu) != 'undefined') {
317 tree.menu.parentType = cn.xtype;
318 tree.menu.triggerEl = cn.el;
319 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
323 if (!tree.items || !tree.items.length) {
327 var items = tree.items;
330 //Roo.log(items.length);
332 if (!skip_children) {
333 for(var i =0;i < items.length;i++) {
334 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
340 this.fireEvent('childrenrendered', this);
345 * Show a component - removes 'hidden' class
350 this.el.removeClass('hidden');
354 * Hide a component - adds 'hidden' class
358 if (this.el && !this.el.hasClass('hidden')) {
359 this.el.addClass('hidden');
373 * @class Roo.bootstrap.Body
374 * @extends Roo.bootstrap.Component
375 * Bootstrap Body class
379 * @param {Object} config The config object
382 Roo.bootstrap.Body = function(config){
383 Roo.bootstrap.Body.superclass.constructor.call(this, config);
384 this.el = Roo.get(document.body);
385 if (this.cls && this.cls.length) {
386 Roo.get(document.body).addClass(this.cls);
390 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
395 onRender : function(ct, position)
397 /* Roo.log("Roo.bootstrap.Body - onRender");
398 if (this.cls && this.cls.length) {
399 Roo.get(document.body).addClass(this.cls);
419 * @class Roo.bootstrap.ButtonGroup
420 * @extends Roo.bootstrap.Component
421 * Bootstrap ButtonGroup class
422 * @cfg {String} size lg | sm | xs (default empty normal)
423 * @cfg {String} align vertical | justified (default none)
424 * @cfg {String} direction up | down (default down)
425 * @cfg {Boolean} toolbar false | true
426 * @cfg {Boolean} btn true | false
431 * @param {Object} config The config object
434 Roo.bootstrap.ButtonGroup = function(config){
435 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
438 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
446 getAutoCreate : function(){
452 cfg.html = this.html || cfg.html;
463 if (['vertical','justified'].indexOf(this.align)!==-1) {
464 cfg.cls = 'btn-group-' + this.align;
466 if (this.align == 'justified') {
467 console.log(this.items);
471 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
472 cfg.cls += ' btn-group-' + this.size;
475 if (this.direction == 'up') {
476 cfg.cls += ' dropup' ;
492 * @class Roo.bootstrap.Button
493 * @extends Roo.bootstrap.Component
494 * Bootstrap Button class
495 * @cfg {String} html The button content
496 * @cfg {String} weight ( primary | success | info | warning | danger | link ) default
497 * @cfg {String} size ( lg | sm | xs)
498 * @cfg {String} tag ( a | input | submit)
499 * @cfg {String} href empty or href
500 * @cfg {Boolean} disabled default false;
501 * @cfg {Boolean} isClose default false;
502 * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
503 * @cfg {String} badge text for badge
504 * @cfg {String} theme default
505 * @cfg {Boolean} inverse
506 * @cfg {Boolean} toggle
507 * @cfg {String} ontext text for on toggle state
508 * @cfg {String} offtext text for off toggle state
509 * @cfg {Boolean} defaulton
510 * @cfg {Boolean} preventDefault default true
511 * @cfg {Boolean} removeClass remove the standard class..
512 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
515 * Create a new button
516 * @param {Object} config The config object
520 Roo.bootstrap.Button = function(config){
521 Roo.bootstrap.Button.superclass.constructor.call(this, config);
526 * When a butotn is pressed
527 * @param {Roo.bootstrap.Button} this
528 * @param {Roo.EventObject} e
533 * After the button has been toggles
534 * @param {Roo.EventObject} e
535 * @param {boolean} pressed (also available as button.pressed)
541 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
559 preventDefault: true,
568 getAutoCreate : function(){
576 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
577 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
582 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
584 if (this.toggle == true) {
587 cls: 'slider-frame roo-button',
592 'data-off-text':'OFF',
593 cls: 'slider-button',
599 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
600 cfg.cls += ' '+this.weight;
609 cfg["aria-hidden"] = true;
611 cfg.html = "×";
617 if (this.theme==='default') {
618 cfg.cls = 'btn roo-button';
620 //if (this.parentType != 'Navbar') {
621 this.weight = this.weight.length ? this.weight : 'default';
623 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
625 cfg.cls += ' btn-' + this.weight;
627 } else if (this.theme==='glow') {
630 cfg.cls = 'btn-glow roo-button';
632 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
634 cfg.cls += ' ' + this.weight;
640 this.cls += ' inverse';
645 cfg.cls += ' active';
649 cfg.disabled = 'disabled';
653 Roo.log('changing to ul' );
655 this.glyphicon = 'caret';
658 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
660 //gsRoo.log(this.parentType);
661 if (this.parentType === 'Navbar' && !this.parent().bar) {
662 Roo.log('changing to li?');
671 href : this.href || '#'
674 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
675 cfg.cls += ' dropdown';
682 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
684 if (this.glyphicon) {
685 cfg.html = ' ' + cfg.html;
690 cls: 'glyphicon glyphicon-' + this.glyphicon
700 // cfg.cls='btn roo-button';
704 var value = cfg.html;
709 cls: 'glyphicon glyphicon-' + this.glyphicon,
728 cfg.cls += ' dropdown';
729 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
732 if (cfg.tag !== 'a' && this.href !== '') {
733 throw "Tag must be a to set href.";
734 } else if (this.href.length > 0) {
735 cfg.href = this.href;
738 if(this.removeClass){
743 cfg.target = this.target;
748 initEvents: function() {
749 // Roo.log('init events?');
750 // Roo.log(this.el.dom);
753 if (typeof (this.menu) != 'undefined') {
754 this.menu.parentType = this.xtype;
755 this.menu.triggerEl = this.el;
756 this.addxtype(Roo.apply({}, this.menu));
760 if (this.el.hasClass('roo-button')) {
761 this.el.on('click', this.onClick, this);
763 this.el.select('.roo-button').on('click', this.onClick, this);
766 if(this.removeClass){
767 this.el.on('click', this.onClick, this);
770 this.el.enableDisplayMode();
773 onClick : function(e)
780 Roo.log('button on click ');
781 if(this.preventDefault){
784 if (this.pressed === true || this.pressed === false) {
785 this.pressed = !this.pressed;
786 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
787 this.fireEvent('toggle', this, e, this.pressed);
791 this.fireEvent('click', this, e);
795 * Enables this button
799 this.disabled = false;
800 this.el.removeClass('disabled');
804 * Disable this button
808 this.disabled = true;
809 this.el.addClass('disabled');
812 * sets the active state on/off,
813 * @param {Boolean} state (optional) Force a particular state
815 setActive : function(v) {
817 this.el[v ? 'addClass' : 'removeClass']('active');
820 * toggles the current active state
822 toggleActive : function()
824 var active = this.el.hasClass('active');
825 this.setActive(!active);
829 setText : function(str)
831 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
835 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
858 * @class Roo.bootstrap.Column
859 * @extends Roo.bootstrap.Component
860 * Bootstrap Column class
861 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
862 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
863 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
864 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
865 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
866 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
867 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
868 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
871 * @cfg {Boolean} hidden (true|false) hide the element
872 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
873 * @cfg {String} fa (ban|check|...) font awesome icon
874 * @cfg {Number} fasize (1|2|....) font awsome size
876 * @cfg {String} icon (info-sign|check|...) glyphicon name
878 * @cfg {String} html content of column.
881 * Create a new Column
882 * @param {Object} config The config object
885 Roo.bootstrap.Column = function(config){
886 Roo.bootstrap.Column.superclass.constructor.call(this, config);
889 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
907 getAutoCreate : function(){
908 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
916 ['xs','sm','md','lg'].map(function(size){
917 //Roo.log( size + ':' + settings[size]);
919 if (settings[size+'off'] !== false) {
920 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
923 if (settings[size] === false) {
926 Roo.log(settings[size]);
927 if (!settings[size]) { // 0 = hidden
928 cfg.cls += ' hidden-' + size;
931 cfg.cls += ' col-' + size + '-' + settings[size];
936 cfg.cls += ' hidden';
939 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
940 cfg.cls +=' alert alert-' + this.alert;
944 if (this.html.length) {
945 cfg.html = this.html;
949 if (this.fasize > 1) {
950 fasize = ' fa-' + this.fasize + 'x';
952 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
957 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
976 * @class Roo.bootstrap.Container
977 * @extends Roo.bootstrap.Component
978 * Bootstrap Container class
979 * @cfg {Boolean} jumbotron is it a jumbotron element
980 * @cfg {String} html content of element
981 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
982 * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
983 * @cfg {String} header content of header (for panel)
984 * @cfg {String} footer content of footer (for panel)
985 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
986 * @cfg {String} tag (header|aside|section) type of HTML tag.
987 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
988 * @cfg {String} fa (ban|check|...) 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
997 * Create a new Container
998 * @param {Object} config The config object
1001 Roo.bootstrap.Container = function(config){
1002 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1008 * After the panel has been expand
1010 * @param {Roo.bootstrap.Container} this
1015 * After the panel has been collapsed
1017 * @param {Roo.bootstrap.Container} this
1023 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1040 getChildContainer : function() {
1046 if (this.panel.length) {
1047 return this.el.select('.panel-body',true).first();
1054 getAutoCreate : function(){
1057 tag : this.tag || 'div',
1061 if (this.jumbotron) {
1062 cfg.cls = 'jumbotron';
1067 // - this is applied by the parent..
1069 // cfg.cls = this.cls + '';
1072 if (this.sticky.length) {
1074 var bd = Roo.get(document.body);
1075 if (!bd.hasClass('bootstrap-sticky')) {
1076 bd.addClass('bootstrap-sticky');
1077 Roo.select('html',true).setStyle('height', '100%');
1080 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1084 if (this.well.length) {
1085 switch (this.well) {
1088 cfg.cls +=' well well-' +this.well;
1097 cfg.cls += ' hidden';
1101 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1102 cfg.cls +=' alert alert-' + this.alert;
1107 if (this.panel.length) {
1108 cfg.cls += ' panel panel-' + this.panel;
1110 if (this.header.length) {
1114 if(this.expandable){
1116 cfg.cls = cfg.cls + ' expandable';
1120 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1128 cls : 'panel-title',
1129 html : (this.expandable ? ' ' : '') + this.header
1133 cls: 'panel-header-right',
1139 cls : 'panel-heading',
1140 style : this.expandable ? 'cursor: pointer' : '',
1148 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1153 if (this.footer.length) {
1155 cls : 'panel-footer',
1164 body.html = this.html || cfg.html;
1165 // prefix with the icons..
1167 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1170 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1175 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1176 cfg.cls = 'container';
1182 initEvents: function()
1184 if(!this.expandable){
1188 var headerEl = this.headerEl();
1194 headerEl.on('click', this.onToggleClick, this);
1198 onToggleClick : function()
1200 var headerEl = this.headerEl();
1216 if(this.fireEvent('expand', this)) {
1218 this.expanded = true;
1220 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1222 this.el.select('.panel-body',true).first().removeClass('hide');
1224 var toggleEl = this.toggleEl();
1230 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1235 collapse : function()
1237 if(this.fireEvent('collapse', this)) {
1239 this.expanded = false;
1241 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1242 this.el.select('.panel-body',true).first().addClass('hide');
1244 var toggleEl = this.toggleEl();
1250 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1254 toggleEl : function()
1256 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1260 return this.el.select('.panel-heading .fa',true).first();
1263 headerEl : function()
1265 if(!this.el || !this.panel.length || !this.header.length){
1269 return this.el.select('.panel-heading',true).first()
1272 titleEl : function()
1274 if(!this.el || !this.panel.length || !this.header.length){
1278 return this.el.select('.panel-title',true).first();
1281 setTitle : function(v)
1283 var titleEl = this.titleEl();
1289 titleEl.dom.innerHTML = v;
1292 getTitle : function()
1295 var titleEl = this.titleEl();
1301 return titleEl.dom.innerHTML;
1304 setRightTitle : function(v)
1306 var t = this.el.select('.panel-header-right',true).first();
1312 t.dom.innerHTML = v;
1326 * @class Roo.bootstrap.Img
1327 * @extends Roo.bootstrap.Component
1328 * Bootstrap Img class
1329 * @cfg {Boolean} imgResponsive false | true
1330 * @cfg {String} border rounded | circle | thumbnail
1331 * @cfg {String} src image source
1332 * @cfg {String} alt image alternative text
1333 * @cfg {String} href a tag href
1334 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1335 * @cfg {String} xsUrl xs image source
1336 * @cfg {String} smUrl sm image source
1337 * @cfg {String} mdUrl md image source
1338 * @cfg {String} lgUrl lg image source
1341 * Create a new Input
1342 * @param {Object} config The config object
1345 Roo.bootstrap.Img = function(config){
1346 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1352 * The img click event for the img.
1353 * @param {Roo.EventObject} e
1359 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1361 imgResponsive: true,
1371 getAutoCreate : function()
1373 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1374 return this.createSingleImg();
1379 cls: 'roo-image-responsive-group',
1384 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1386 if(!_this[size + 'Url']){
1392 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1393 html: _this.html || cfg.html,
1394 src: _this[size + 'Url']
1397 img.cls += ' roo-image-responsive-' + size;
1399 var s = ['xs', 'sm', 'md', 'lg'];
1401 s.splice(s.indexOf(size), 1);
1403 Roo.each(s, function(ss){
1404 img.cls += ' hidden-' + ss;
1407 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1408 cfg.cls += ' img-' + _this.border;
1412 cfg.alt = _this.alt;
1425 a.target = _this.target;
1429 cfg.cn.push((_this.href) ? a : img);
1436 createSingleImg : function()
1440 cls: (this.imgResponsive) ? 'img-responsive' : '',
1444 cfg.html = this.html || cfg.html;
1446 cfg.src = this.src || cfg.src;
1448 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1449 cfg.cls += ' img-' + this.border;
1466 a.target = this.target;
1471 return (this.href) ? a : cfg;
1474 initEvents: function()
1477 this.el.on('click', this.onClick, this);
1482 onClick : function(e)
1484 Roo.log('img onclick');
1485 this.fireEvent('click', this, e);
1499 * @class Roo.bootstrap.Link
1500 * @extends Roo.bootstrap.Component
1501 * Bootstrap Link Class
1502 * @cfg {String} alt image alternative text
1503 * @cfg {String} href a tag href
1504 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1505 * @cfg {String} html the content of the link.
1506 * @cfg {String} anchor name for the anchor link
1508 * @cfg {Boolean} preventDefault (true | false) default false
1512 * Create a new Input
1513 * @param {Object} config The config object
1516 Roo.bootstrap.Link = function(config){
1517 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1523 * The img click event for the img.
1524 * @param {Roo.EventObject} e
1530 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1534 preventDefault: false,
1538 getAutoCreate : function()
1544 // anchor's do not require html/href...
1545 if (this.anchor === false) {
1546 cfg.html = this.html || '';
1547 cfg.href = this.href || '#';
1549 cfg.name = this.anchor;
1550 if (this.html !== false) {
1551 cfg.html = this.html;
1553 if (this.href !== false) {
1554 cfg.href = this.href;
1558 if(this.alt !== false){
1563 if(this.target !== false) {
1564 cfg.target = this.target;
1570 initEvents: function() {
1572 if(!this.href || this.preventDefault){
1573 this.el.on('click', this.onClick, this);
1577 onClick : function(e)
1579 if(this.preventDefault){
1582 //Roo.log('img onclick');
1583 this.fireEvent('click', this, e);
1596 * @class Roo.bootstrap.Header
1597 * @extends Roo.bootstrap.Component
1598 * Bootstrap Header class
1599 * @cfg {String} html content of header
1600 * @cfg {Number} level (1|2|3|4|5|6) default 1
1603 * Create a new Header
1604 * @param {Object} config The config object
1608 Roo.bootstrap.Header = function(config){
1609 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1612 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1620 getAutoCreate : function(){
1625 tag: 'h' + (1 *this.level),
1626 html: this.html || ''
1638 * Ext JS Library 1.1.1
1639 * Copyright(c) 2006-2007, Ext JS, LLC.
1641 * Originally Released Under LGPL - original licence link has changed is not relivant.
1644 * <script type="text/javascript">
1648 * @class Roo.bootstrap.MenuMgr
1649 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1652 Roo.bootstrap.MenuMgr = function(){
1653 var menus, active, groups = {}, attached = false, lastShow = new Date();
1655 // private - called when first menu is created
1658 active = new Roo.util.MixedCollection();
1659 Roo.get(document).addKeyListener(27, function(){
1660 if(active.length > 0){
1668 if(active && active.length > 0){
1669 var c = active.clone();
1679 if(active.length < 1){
1680 Roo.get(document).un("mouseup", onMouseDown);
1688 var last = active.last();
1689 lastShow = new Date();
1692 Roo.get(document).on("mouseup", onMouseDown);
1697 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1698 m.parentMenu.activeChild = m;
1699 }else if(last && last.isVisible()){
1700 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1705 function onBeforeHide(m){
1707 m.activeChild.hide();
1709 if(m.autoHideTimer){
1710 clearTimeout(m.autoHideTimer);
1711 delete m.autoHideTimer;
1716 function onBeforeShow(m){
1717 var pm = m.parentMenu;
1718 if(!pm && !m.allowOtherMenus){
1720 }else if(pm && pm.activeChild && active != m){
1721 pm.activeChild.hide();
1725 // private this should really trigger on mouseup..
1726 function onMouseDown(e){
1727 Roo.log("on Mouse Up");
1728 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1738 function onBeforeCheck(mi, state){
1740 var g = groups[mi.group];
1741 for(var i = 0, l = g.length; i < l; i++){
1743 g[i].setChecked(false);
1752 * Hides all menus that are currently visible
1754 hideAll : function(){
1759 register : function(menu){
1763 menus[menu.id] = menu;
1764 menu.on("beforehide", onBeforeHide);
1765 menu.on("hide", onHide);
1766 menu.on("beforeshow", onBeforeShow);
1767 menu.on("show", onShow);
1769 if(g && menu.events["checkchange"]){
1773 groups[g].push(menu);
1774 menu.on("checkchange", onCheck);
1779 * Returns a {@link Roo.menu.Menu} object
1780 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1781 * be used to generate and return a new Menu instance.
1783 get : function(menu){
1784 if(typeof menu == "string"){ // menu id
1786 }else if(menu.events){ // menu instance
1789 /*else if(typeof menu.length == 'number'){ // array of menu items?
1790 return new Roo.bootstrap.Menu({items:menu});
1791 }else{ // otherwise, must be a config
1792 return new Roo.bootstrap.Menu(menu);
1799 unregister : function(menu){
1800 delete menus[menu.id];
1801 menu.un("beforehide", onBeforeHide);
1802 menu.un("hide", onHide);
1803 menu.un("beforeshow", onBeforeShow);
1804 menu.un("show", onShow);
1806 if(g && menu.events["checkchange"]){
1807 groups[g].remove(menu);
1808 menu.un("checkchange", onCheck);
1813 registerCheckable : function(menuItem){
1814 var g = menuItem.group;
1819 groups[g].push(menuItem);
1820 menuItem.on("beforecheckchange", onBeforeCheck);
1825 unregisterCheckable : function(menuItem){
1826 var g = menuItem.group;
1828 groups[g].remove(menuItem);
1829 menuItem.un("beforecheckchange", onBeforeCheck);
1841 * @class Roo.bootstrap.Menu
1842 * @extends Roo.bootstrap.Component
1843 * Bootstrap Menu class - container for MenuItems
1844 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1848 * @param {Object} config The config object
1852 Roo.bootstrap.Menu = function(config){
1853 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1854 if (this.registerMenu) {
1855 Roo.bootstrap.MenuMgr.register(this);
1860 * Fires before this menu is displayed
1861 * @param {Roo.menu.Menu} this
1866 * Fires before this menu is hidden
1867 * @param {Roo.menu.Menu} this
1872 * Fires after this menu is displayed
1873 * @param {Roo.menu.Menu} this
1878 * Fires after this menu is hidden
1879 * @param {Roo.menu.Menu} this
1884 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1885 * @param {Roo.menu.Menu} this
1886 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1887 * @param {Roo.EventObject} e
1892 * Fires when the mouse is hovering over this menu
1893 * @param {Roo.menu.Menu} this
1894 * @param {Roo.EventObject} e
1895 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1900 * Fires when the mouse exits this menu
1901 * @param {Roo.menu.Menu} this
1902 * @param {Roo.EventObject} e
1903 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1908 * Fires when a menu item contained in this menu is clicked
1909 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1910 * @param {Roo.EventObject} e
1914 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1917 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1921 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1924 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1926 registerMenu : true,
1928 menuItems :false, // stores the menu items..
1934 getChildContainer : function() {
1938 getAutoCreate : function(){
1940 //if (['right'].indexOf(this.align)!==-1) {
1941 // cfg.cn[1].cls += ' pull-right'
1947 cls : 'dropdown-menu' ,
1948 style : 'z-index:1000'
1952 if (this.type === 'submenu') {
1953 cfg.cls = 'submenu active';
1955 if (this.type === 'treeview') {
1956 cfg.cls = 'treeview-menu';
1961 initEvents : function() {
1963 // Roo.log("ADD event");
1964 // Roo.log(this.triggerEl.dom);
1965 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1967 this.triggerEl.addClass('dropdown-toggle');
1973 this.el.on('touchstart' , this.onTouch, this);
1975 this.el.on('click' , this.onClick, this);
1977 this.el.on("mouseover", this.onMouseOver, this);
1978 this.el.on("mouseout", this.onMouseOut, this);
1982 findTargetItem : function(e){
1983 var t = e.getTarget(".dropdown-menu-item", this.el, true);
1987 //Roo.log(t); Roo.log(t.id);
1989 //Roo.log(this.menuitems);
1990 return this.menuitems.get(t.id);
1992 //return this.items.get(t.menuItemId);
1998 onTouch : function(e) {
2003 onClick : function(e){
2004 Roo.log("menu.onClick");
2005 var t = this.findTargetItem(e);
2006 if(!t || t.isContainer){
2011 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2012 if(t == this.activeItem && t.shouldDeactivate(e)){
2013 this.activeItem.deactivate();
2014 delete this.activeItem;
2018 this.setActiveItem(t, true);
2026 Roo.log('pass click event');
2030 this.fireEvent("click", this, t, e);
2034 onMouseOver : function(e){
2035 var t = this.findTargetItem(e);
2038 // if(t.canActivate && !t.disabled){
2039 // this.setActiveItem(t, true);
2043 this.fireEvent("mouseover", this, e, t);
2045 isVisible : function(){
2046 return !this.hidden;
2048 onMouseOut : function(e){
2049 var t = this.findTargetItem(e);
2052 // if(t == this.activeItem && t.shouldDeactivate(e)){
2053 // this.activeItem.deactivate();
2054 // delete this.activeItem;
2057 this.fireEvent("mouseout", this, e, t);
2062 * Displays this menu relative to another element
2063 * @param {String/HTMLElement/Roo.Element} element The element to align to
2064 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2065 * the element (defaults to this.defaultAlign)
2066 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2068 show : function(el, pos, parentMenu){
2069 this.parentMenu = parentMenu;
2073 this.fireEvent("beforeshow", this);
2074 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2077 * Displays this menu at a specific xy position
2078 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2079 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2081 showAt : function(xy, parentMenu, /* private: */_e){
2082 this.parentMenu = parentMenu;
2087 this.fireEvent("beforeshow", this);
2088 //xy = this.el.adjustForConstraints(xy);
2092 this.hideMenuItems();
2093 this.hidden = false;
2094 this.triggerEl.addClass('open');
2096 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2097 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2102 this.fireEvent("show", this);
2108 this.doFocus.defer(50, this);
2112 doFocus : function(){
2114 this.focusEl.focus();
2119 * Hides this menu and optionally all parent menus
2120 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2122 hide : function(deep){
2124 this.hideMenuItems();
2125 if(this.el && this.isVisible()){
2126 this.fireEvent("beforehide", this);
2127 if(this.activeItem){
2128 this.activeItem.deactivate();
2129 this.activeItem = null;
2131 this.triggerEl.removeClass('open');;
2133 this.fireEvent("hide", this);
2135 if(deep === true && this.parentMenu){
2136 this.parentMenu.hide(true);
2140 onTriggerPress : function(e)
2143 Roo.log('trigger press');
2144 //Roo.log(e.getTarget());
2145 // Roo.log(this.triggerEl.dom);
2146 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2150 if (this.isVisible()) {
2155 this.show(this.triggerEl, false, false);
2164 hideMenuItems : function()
2166 //$(backdrop).remove()
2167 Roo.select('.open',true).each(function(aa) {
2169 aa.removeClass('open');
2170 //var parent = getParent($(this))
2171 //var relatedTarget = { relatedTarget: this }
2173 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2174 //if (e.isDefaultPrevented()) return
2175 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2178 addxtypeChild : function (tree, cntr) {
2179 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2181 this.menuitems.add(comp);
2202 * @class Roo.bootstrap.MenuItem
2203 * @extends Roo.bootstrap.Component
2204 * Bootstrap MenuItem class
2205 * @cfg {String} html the menu label
2206 * @cfg {String} href the link
2207 * @cfg {Boolean} preventDefault (true | false) default true
2208 * @cfg {Boolean} isContainer (true | false) default false
2212 * Create a new MenuItem
2213 * @param {Object} config The config object
2217 Roo.bootstrap.MenuItem = function(config){
2218 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2223 * The raw click event for the entire grid.
2224 * @param {Roo.bootstrap.MenuItem} this
2225 * @param {Roo.EventObject} e
2231 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2235 preventDefault: true,
2236 isContainer : false,
2238 getAutoCreate : function(){
2240 if(this.isContainer){
2243 cls: 'dropdown-menu-item'
2249 cls: 'dropdown-menu-item',
2258 if (this.parent().type == 'treeview') {
2259 cfg.cls = 'treeview-menu';
2262 cfg.cn[0].href = this.href || cfg.cn[0].href ;
2263 cfg.cn[0].html = this.html || cfg.cn[0].html ;
2267 initEvents: function() {
2269 //this.el.select('a').on('click', this.onClick, this);
2272 onClick : function(e)
2274 Roo.log('item on click ');
2275 //if(this.preventDefault){
2276 // e.preventDefault();
2278 //this.parent().hideMenuItems();
2280 this.fireEvent('click', this, e);
2299 * @class Roo.bootstrap.MenuSeparator
2300 * @extends Roo.bootstrap.Component
2301 * Bootstrap MenuSeparator class
2304 * Create a new MenuItem
2305 * @param {Object} config The config object
2309 Roo.bootstrap.MenuSeparator = function(config){
2310 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2313 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2315 getAutoCreate : function(){
2334 * @class Roo.bootstrap.Modal
2335 * @extends Roo.bootstrap.Component
2336 * Bootstrap Modal class
2337 * @cfg {String} title Title of dialog
2338 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2339 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2340 * @cfg {Boolean} specificTitle default false
2341 * @cfg {Array} buttons Array of buttons or standard button set..
2342 * @cfg {String} buttonPosition (left|right|center) default right
2343 * @cfg {Boolean} animate default true
2344 * @cfg {Boolean} allow_close default true
2347 * Create a new Modal Dialog
2348 * @param {Object} config The config object
2351 Roo.bootstrap.Modal = function(config){
2352 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2357 * The raw btnclick event for the button
2358 * @param {Roo.EventObject} e
2362 this.buttons = this.buttons || [];
2365 this.tmpl = Roo.factory(this.tmpl);
2370 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2372 title : 'test dialog',
2382 specificTitle: false,
2384 buttonPosition: 'right',
2398 onRender : function(ct, position)
2400 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2403 var cfg = Roo.apply({}, this.getAutoCreate());
2406 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2408 //if (!cfg.name.length) {
2412 cfg.cls += ' ' + this.cls;
2415 cfg.style = this.style;
2417 this.el = Roo.get(document.body).createChild(cfg, position);
2419 //var type = this.el.dom.type;
2424 if(this.tabIndex !== undefined){
2425 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2429 this.bodyEl = this.el.select('.modal-body',true).first();
2430 this.closeEl = this.el.select('.modal-header .close', true).first();
2431 this.footerEl = this.el.select('.modal-footer',true).first();
2432 this.titleEl = this.el.select('.modal-title',true).first();
2436 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2437 this.maskEl.enableDisplayMode("block");
2439 //this.el.addClass("x-dlg-modal");
2441 if (this.buttons.length) {
2442 Roo.each(this.buttons, function(bb) {
2443 var b = Roo.apply({}, bb);
2444 b.xns = b.xns || Roo.bootstrap;
2445 b.xtype = b.xtype || 'Button';
2446 if (typeof(b.listeners) == 'undefined') {
2447 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2450 var btn = Roo.factory(b);
2452 btn.onRender(this.el.select('.modal-footer div').first());
2456 // render the children.
2459 if(typeof(this.items) != 'undefined'){
2460 var items = this.items;
2463 for(var i =0;i < items.length;i++) {
2464 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2468 this.items = nitems;
2470 // where are these used - they used to be body/close/footer
2474 //this.el.addClass([this.fieldClass, this.cls]);
2478 getAutoCreate : function(){
2483 html : this.html || ''
2488 cls : 'modal-title',
2492 if(this.specificTitle){
2498 if (this.allow_close) {
2509 style : 'display: none',
2512 cls: "modal-dialog",
2515 cls : "modal-content",
2518 cls : 'modal-header',
2523 cls : 'modal-footer',
2527 cls: 'btn-' + this.buttonPosition
2544 modal.cls += ' fade';
2550 getChildContainer : function() {
2555 getButtonContainer : function() {
2556 return this.el.select('.modal-footer div',true).first();
2559 initEvents : function()
2561 if (this.allow_close) {
2562 this.closeEl.on('click', this.hide, this);
2567 window.addEventListener("resize", function() { _this.resize(); } );
2573 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2578 if (!this.rendered) {
2582 this.el.setStyle('display', 'block');
2586 (function(){ _this.el.addClass('in'); }).defer(50);
2588 this.el.addClass('in');
2591 // not sure how we can show data in here..
2593 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2596 Roo.get(document.body).addClass("x-body-masked");
2597 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2599 this.el.setStyle('zIndex', '10001');
2601 this.fireEvent('show', this);
2608 Roo.get(document.body).removeClass("x-body-masked");
2609 this.el.removeClass('in');
2613 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2615 this.el.setStyle('display', 'none');
2618 this.fireEvent('hide', this);
2621 addButton : function(str, cb)
2625 var b = Roo.apply({}, { html : str } );
2626 b.xns = b.xns || Roo.bootstrap;
2627 b.xtype = b.xtype || 'Button';
2628 if (typeof(b.listeners) == 'undefined') {
2629 b.listeners = { click : cb.createDelegate(this) };
2632 var btn = Roo.factory(b);
2634 btn.onRender(this.el.select('.modal-footer div').first());
2640 setDefaultButton : function(btn)
2642 //this.el.select('.modal-footer').()
2644 resizeTo: function(w,h)
2648 setContentSize : function(w, h)
2652 onButtonClick: function(btn,e)
2655 this.fireEvent('btnclick', btn.name, e);
2658 * Set the title of the Dialog
2659 * @param {String} str new Title
2661 setTitle: function(str) {
2662 this.titleEl.dom.innerHTML = str;
2665 * Set the body of the Dialog
2666 * @param {String} str new Title
2668 setBody: function(str) {
2669 this.bodyEl.dom.innerHTML = str;
2672 * Set the body of the Dialog using the template
2673 * @param {Obj} data - apply this data to the template and replace the body contents.
2675 applyBody: function(obj)
2678 Roo.log("Error - using apply Body without a template");
2681 this.tmpl.overwrite(this.bodyEl, obj);
2687 Roo.apply(Roo.bootstrap.Modal, {
2689 * Button config that displays a single OK button
2698 * Button config that displays Yes and No buttons
2714 * Button config that displays OK and Cancel buttons
2729 * Button config that displays Yes, No and Cancel buttons
2752 * messagebox - can be used as a replace
2756 * @class Roo.MessageBox
2757 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2761 Roo.Msg.alert('Status', 'Changes saved successfully.');
2763 // Prompt for user data:
2764 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2766 // process text value...
2770 // Show a dialog using config options:
2772 title:'Save Changes?',
2773 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2774 buttons: Roo.Msg.YESNOCANCEL,
2781 Roo.bootstrap.MessageBox = function(){
2782 var dlg, opt, mask, waitTimer;
2783 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2784 var buttons, activeTextEl, bwidth;
2788 var handleButton = function(button){
2790 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2794 var handleHide = function(){
2796 dlg.el.removeClass(opt.cls);
2799 // Roo.TaskMgr.stop(waitTimer);
2800 // waitTimer = null;
2805 var updateButtons = function(b){
2808 buttons["ok"].hide();
2809 buttons["cancel"].hide();
2810 buttons["yes"].hide();
2811 buttons["no"].hide();
2812 //dlg.footer.dom.style.display = 'none';
2815 dlg.footerEl.dom.style.display = '';
2816 for(var k in buttons){
2817 if(typeof buttons[k] != "function"){
2820 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2821 width += buttons[k].el.getWidth()+15;
2831 var handleEsc = function(d, k, e){
2832 if(opt && opt.closable !== false){
2842 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2843 * @return {Roo.BasicDialog} The BasicDialog element
2845 getDialog : function(){
2847 dlg = new Roo.bootstrap.Modal( {
2850 //constraintoviewport:false,
2852 //collapsible : false,
2857 //buttonAlign:"center",
2858 closeClick : function(){
2859 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2862 handleButton("cancel");
2867 dlg.on("hide", handleHide);
2869 //dlg.addKeyListener(27, handleEsc);
2871 this.buttons = buttons;
2872 var bt = this.buttonText;
2873 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2874 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2875 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2876 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2878 bodyEl = dlg.bodyEl.createChild({
2880 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2881 '<textarea class="roo-mb-textarea"></textarea>' +
2882 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2884 msgEl = bodyEl.dom.firstChild;
2885 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2886 textboxEl.enableDisplayMode();
2887 textboxEl.addKeyListener([10,13], function(){
2888 if(dlg.isVisible() && opt && opt.buttons){
2891 }else if(opt.buttons.yes){
2892 handleButton("yes");
2896 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2897 textareaEl.enableDisplayMode();
2898 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2899 progressEl.enableDisplayMode();
2900 var pf = progressEl.dom.firstChild;
2902 pp = Roo.get(pf.firstChild);
2903 pp.setHeight(pf.offsetHeight);
2911 * Updates the message box body text
2912 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2913 * the XHTML-compliant non-breaking space character '&#160;')
2914 * @return {Roo.MessageBox} This message box
2916 updateText : function(text){
2917 if(!dlg.isVisible() && !opt.width){
2918 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2920 msgEl.innerHTML = text || ' ';
2922 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2923 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2925 Math.min(opt.width || cw , this.maxWidth),
2926 Math.max(opt.minWidth || this.minWidth, bwidth)
2929 activeTextEl.setWidth(w);
2931 if(dlg.isVisible()){
2932 dlg.fixedcenter = false;
2934 // to big, make it scroll. = But as usual stupid IE does not support
2937 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2938 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2939 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2941 bodyEl.dom.style.height = '';
2942 bodyEl.dom.style.overflowY = '';
2945 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2947 bodyEl.dom.style.overflowX = '';
2950 dlg.setContentSize(w, bodyEl.getHeight());
2951 if(dlg.isVisible()){
2952 dlg.fixedcenter = true;
2958 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
2959 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2960 * @param {Number} value Any number between 0 and 1 (e.g., .5)
2961 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2962 * @return {Roo.MessageBox} This message box
2964 updateProgress : function(value, text){
2966 this.updateText(text);
2968 if (pp) { // weird bug on my firefox - for some reason this is not defined
2969 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2975 * Returns true if the message box is currently displayed
2976 * @return {Boolean} True if the message box is visible, else false
2978 isVisible : function(){
2979 return dlg && dlg.isVisible();
2983 * Hides the message box if it is displayed
2986 if(this.isVisible()){
2992 * Displays a new message box, or reinitializes an existing message box, based on the config options
2993 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2994 * The following config object properties are supported:
2996 Property Type Description
2997 ---------- --------------- ------------------------------------------------------------------------------------
2998 animEl String/Element An id or Element from which the message box should animate as it opens and
2999 closes (defaults to undefined)
3000 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3001 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3002 closable Boolean False to hide the top-right close button (defaults to true). Note that
3003 progress and wait dialogs will ignore this property and always hide the
3004 close button as they can only be closed programmatically.
3005 cls String A custom CSS class to apply to the message box element
3006 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3007 displayed (defaults to 75)
3008 fn Function A callback function to execute after closing the dialog. The arguments to the
3009 function will be btn (the name of the button that was clicked, if applicable,
3010 e.g. "ok"), and text (the value of the active text field, if applicable).
3011 Progress and wait dialogs will ignore this option since they do not respond to
3012 user actions and can only be closed programmatically, so any required function
3013 should be called by the same code after it closes the dialog.
3014 icon String A CSS class that provides a background image to be used as an icon for
3015 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3016 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3017 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3018 modal Boolean False to allow user interaction with the page while the message box is
3019 displayed (defaults to true)
3020 msg String A string that will replace the existing message box body text (defaults
3021 to the XHTML-compliant non-breaking space character ' ')
3022 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3023 progress Boolean True to display a progress bar (defaults to false)
3024 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3025 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3026 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3027 title String The title text
3028 value String The string value to set into the active textbox element if displayed
3029 wait Boolean True to display a progress bar (defaults to false)
3030 width Number The width of the dialog in pixels
3037 msg: 'Please enter your address:',
3039 buttons: Roo.MessageBox.OKCANCEL,
3042 animEl: 'addAddressBtn'
3045 * @param {Object} config Configuration options
3046 * @return {Roo.MessageBox} This message box
3048 show : function(options)
3051 // this causes nightmares if you show one dialog after another
3052 // especially on callbacks..
3054 if(this.isVisible()){
3057 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3058 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3059 Roo.log("New Dialog Message:" + options.msg )
3060 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3061 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3064 var d = this.getDialog();
3066 d.setTitle(opt.title || " ");
3067 d.closeEl.setDisplayed(opt.closable !== false);
3068 activeTextEl = textboxEl;
3069 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3074 textareaEl.setHeight(typeof opt.multiline == "number" ?
3075 opt.multiline : this.defaultTextHeight);
3076 activeTextEl = textareaEl;
3085 progressEl.setDisplayed(opt.progress === true);
3086 this.updateProgress(0);
3087 activeTextEl.dom.value = opt.value || "";
3089 dlg.setDefaultButton(activeTextEl);
3091 var bs = opt.buttons;
3095 }else if(bs && bs.yes){
3096 db = buttons["yes"];
3098 dlg.setDefaultButton(db);
3100 bwidth = updateButtons(opt.buttons);
3101 this.updateText(opt.msg);
3103 d.el.addClass(opt.cls);
3105 d.proxyDrag = opt.proxyDrag === true;
3106 d.modal = opt.modal !== false;
3107 d.mask = opt.modal !== false ? mask : false;
3109 // force it to the end of the z-index stack so it gets a cursor in FF
3110 document.body.appendChild(dlg.el.dom);
3111 d.animateTarget = null;
3112 d.show(options.animEl);
3118 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3119 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3120 * and closing the message box when the process is complete.
3121 * @param {String} title The title bar text
3122 * @param {String} msg The message box body text
3123 * @return {Roo.MessageBox} This message box
3125 progress : function(title, msg){
3132 minWidth: this.minProgressWidth,
3139 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3140 * If a callback function is passed it will be called after the user clicks the button, and the
3141 * id of the button that was clicked will be passed as the only parameter to the callback
3142 * (could also be the top-right close button).
3143 * @param {String} title The title bar text
3144 * @param {String} msg The message box body text
3145 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3146 * @param {Object} scope (optional) The scope of the callback function
3147 * @return {Roo.MessageBox} This message box
3149 alert : function(title, msg, fn, scope){
3162 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3163 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3164 * You are responsible for closing the message box when the process is complete.
3165 * @param {String} msg The message box body text
3166 * @param {String} title (optional) The title bar text
3167 * @return {Roo.MessageBox} This message box
3169 wait : function(msg, title){
3180 waitTimer = Roo.TaskMgr.start({
3182 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3190 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3191 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3192 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3193 * @param {String} title The title bar text
3194 * @param {String} msg The message box body text
3195 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3196 * @param {Object} scope (optional) The scope of the callback function
3197 * @return {Roo.MessageBox} This message box
3199 confirm : function(title, msg, fn, scope){
3203 buttons: this.YESNO,
3212 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3213 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3214 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3215 * (could also be the top-right close button) and the text that was entered will be passed as the two
3216 * parameters to the callback.
3217 * @param {String} title The title bar text
3218 * @param {String} msg The message box body text
3219 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3220 * @param {Object} scope (optional) The scope of the callback function
3221 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3222 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3223 * @return {Roo.MessageBox} This message box
3225 prompt : function(title, msg, fn, scope, multiline){
3229 buttons: this.OKCANCEL,
3234 multiline: multiline,
3241 * Button config that displays a single OK button
3246 * Button config that displays Yes and No buttons
3249 YESNO : {yes:true, no:true},
3251 * Button config that displays OK and Cancel buttons
3254 OKCANCEL : {ok:true, cancel:true},
3256 * Button config that displays Yes, No and Cancel buttons
3259 YESNOCANCEL : {yes:true, no:true, cancel:true},
3262 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3265 defaultTextHeight : 75,
3267 * The maximum width in pixels of the message box (defaults to 600)
3272 * The minimum width in pixels of the message box (defaults to 100)
3277 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3278 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3281 minProgressWidth : 250,
3283 * An object containing the default button text strings that can be overriden for localized language support.
3284 * Supported properties are: ok, cancel, yes and no.
3285 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3298 * Shorthand for {@link Roo.MessageBox}
3300 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3301 Roo.Msg = Roo.Msg || Roo.MessageBox;
3310 * @class Roo.bootstrap.Navbar
3311 * @extends Roo.bootstrap.Component
3312 * Bootstrap Navbar class
3315 * Create a new Navbar
3316 * @param {Object} config The config object
3320 Roo.bootstrap.Navbar = function(config){
3321 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3325 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3334 getAutoCreate : function(){
3337 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3341 initEvents :function ()
3343 //Roo.log(this.el.select('.navbar-toggle',true));
3344 this.el.select('.navbar-toggle',true).on('click', function() {
3345 // Roo.log('click');
3346 this.el.select('.navbar-collapse',true).toggleClass('in');
3354 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3356 var size = this.el.getSize();
3357 this.maskEl.setSize(size.width, size.height);
3358 this.maskEl.enableDisplayMode("block");
3367 getChildContainer : function()
3369 if (this.el.select('.collapse').getCount()) {
3370 return this.el.select('.collapse',true).first();
3403 * @class Roo.bootstrap.NavSimplebar
3404 * @extends Roo.bootstrap.Navbar
3405 * Bootstrap Sidebar class
3407 * @cfg {Boolean} inverse is inverted color
3409 * @cfg {String} type (nav | pills | tabs)
3410 * @cfg {Boolean} arrangement stacked | justified
3411 * @cfg {String} align (left | right) alignment
3413 * @cfg {Boolean} main (true|false) main nav bar? default false
3414 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3416 * @cfg {String} tag (header|footer|nav|div) default is nav
3422 * Create a new Sidebar
3423 * @param {Object} config The config object
3427 Roo.bootstrap.NavSimplebar = function(config){
3428 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3431 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3447 getAutoCreate : function(){
3451 tag : this.tag || 'div',
3464 this.type = this.type || 'nav';
3465 if (['tabs','pills'].indexOf(this.type)!==-1) {
3466 cfg.cn[0].cls += ' nav-' + this.type
3470 if (this.type!=='nav') {
3471 Roo.log('nav type must be nav/tabs/pills')
3473 cfg.cn[0].cls += ' navbar-nav'
3479 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3480 cfg.cn[0].cls += ' nav-' + this.arrangement;
3484 if (this.align === 'right') {
3485 cfg.cn[0].cls += ' navbar-right';
3489 cfg.cls += ' navbar-inverse';
3516 * @class Roo.bootstrap.NavHeaderbar
3517 * @extends Roo.bootstrap.NavSimplebar
3518 * Bootstrap Sidebar class
3520 * @cfg {String} brand what is brand
3521 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3522 * @cfg {String} brand_href href of the brand
3523 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3524 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3525 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3526 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3529 * Create a new Sidebar
3530 * @param {Object} config The config object
3534 Roo.bootstrap.NavHeaderbar = function(config){
3535 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3539 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3546 desktopCenter : false,
3549 getAutoCreate : function(){
3552 tag: this.nav || 'nav',
3559 if (this.desktopCenter) {
3560 cn.push({cls : 'container', cn : []});
3567 cls: 'navbar-header',
3572 cls: 'navbar-toggle',
3573 'data-toggle': 'collapse',
3578 html: 'Toggle navigation'
3600 cls: 'collapse navbar-collapse',
3604 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3606 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3607 cfg.cls += ' navbar-' + this.position;
3609 // tag can override this..
3611 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3614 if (this.brand !== '') {
3617 href: this.brand_href ? this.brand_href : '#',
3618 cls: 'navbar-brand',
3626 cfg.cls += ' main-nav';
3634 getHeaderChildContainer : function()
3636 if (this.el.select('.navbar-header').getCount()) {
3637 return this.el.select('.navbar-header',true).first();
3640 return this.getChildContainer();
3644 initEvents : function()
3646 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3648 if (this.autohide) {
3653 Roo.get(document).on('scroll',function(e) {
3654 var ns = Roo.get(document).getScroll().top;
3655 var os = prevScroll;
3659 ft.removeClass('slideDown');
3660 ft.addClass('slideUp');
3663 ft.removeClass('slideUp');
3664 ft.addClass('slideDown');
3685 * @class Roo.bootstrap.NavSidebar
3686 * @extends Roo.bootstrap.Navbar
3687 * Bootstrap Sidebar class
3690 * Create a new Sidebar
3691 * @param {Object} config The config object
3695 Roo.bootstrap.NavSidebar = function(config){
3696 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3699 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3701 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3703 getAutoCreate : function(){
3708 cls: 'sidebar sidebar-nav'
3730 * @class Roo.bootstrap.NavGroup
3731 * @extends Roo.bootstrap.Component
3732 * Bootstrap NavGroup class
3733 * @cfg {String} align (left|right)
3734 * @cfg {Boolean} inverse
3735 * @cfg {String} type (nav|pills|tab) default nav
3736 * @cfg {String} navId - reference Id for navbar.
3740 * Create a new nav group
3741 * @param {Object} config The config object
3744 Roo.bootstrap.NavGroup = function(config){
3745 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3748 Roo.bootstrap.NavGroup.register(this);
3752 * Fires when the active item changes
3753 * @param {Roo.bootstrap.NavGroup} this
3754 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3755 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3762 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3773 getAutoCreate : function()
3775 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3782 if (['tabs','pills'].indexOf(this.type)!==-1) {
3783 cfg.cls += ' nav-' + this.type
3785 if (this.type!=='nav') {
3786 Roo.log('nav type must be nav/tabs/pills')
3788 cfg.cls += ' navbar-nav'
3791 if (this.parent().sidebar) {
3794 cls: 'dashboard-menu sidebar-menu'
3800 if (this.form === true) {
3806 if (this.align === 'right') {
3807 cfg.cls += ' navbar-right';
3809 cfg.cls += ' navbar-left';
3813 if (this.align === 'right') {
3814 cfg.cls += ' navbar-right';
3818 cfg.cls += ' navbar-inverse';
3826 * sets the active Navigation item
3827 * @param {Roo.bootstrap.NavItem} the new current navitem
3829 setActiveItem : function(item)
3832 Roo.each(this.navItems, function(v){
3837 v.setActive(false, true);
3844 item.setActive(true, true);
3845 this.fireEvent('changed', this, item, prev);
3850 * gets the active Navigation item
3851 * @return {Roo.bootstrap.NavItem} the current navitem
3853 getActive : function()
3857 Roo.each(this.navItems, function(v){
3868 indexOfNav : function()
3872 Roo.each(this.navItems, function(v,i){
3883 * adds a Navigation item
3884 * @param {Roo.bootstrap.NavItem} the navitem to add
3886 addItem : function(cfg)
3888 var cn = new Roo.bootstrap.NavItem(cfg);
3890 cn.parentId = this.id;
3891 cn.onRender(this.el, null);
3895 * register a Navigation item
3896 * @param {Roo.bootstrap.NavItem} the navitem to add
3898 register : function(item)
3900 this.navItems.push( item);
3901 item.navId = this.navId;
3906 * clear all the Navigation item
3909 clearAll : function()
3912 this.el.dom.innerHTML = '';
3915 getNavItem: function(tabId)
3918 Roo.each(this.navItems, function(e) {
3919 if (e.tabId == tabId) {
3929 setActiveNext : function()
3931 var i = this.indexOfNav(this.getActive());
3932 if (i > this.navItems.length) {
3935 this.setActiveItem(this.navItems[i+1]);
3937 setActivePrev : function()
3939 var i = this.indexOfNav(this.getActive());
3943 this.setActiveItem(this.navItems[i-1]);
3945 clearWasActive : function(except) {
3946 Roo.each(this.navItems, function(e) {
3947 if (e.tabId != except.tabId && e.was_active) {
3948 e.was_active = false;
3955 getWasActive : function ()
3958 Roo.each(this.navItems, function(e) {
3973 Roo.apply(Roo.bootstrap.NavGroup, {
3977 * register a Navigation Group
3978 * @param {Roo.bootstrap.NavGroup} the navgroup to add
3980 register : function(navgrp)
3982 this.groups[navgrp.navId] = navgrp;
3986 * fetch a Navigation Group based on the navigation ID
3987 * @param {string} the navgroup to add
3988 * @returns {Roo.bootstrap.NavGroup} the navgroup
3990 get: function(navId) {
3991 if (typeof(this.groups[navId]) == 'undefined') {
3993 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3995 return this.groups[navId] ;
4010 * @class Roo.bootstrap.NavItem
4011 * @extends Roo.bootstrap.Component
4012 * Bootstrap Navbar.NavItem class
4013 * @cfg {String} href link to
4014 * @cfg {String} html content of button
4015 * @cfg {String} badge text inside badge
4016 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4017 * @cfg {String} glyphicon name of glyphicon
4018 * @cfg {String} icon name of font awesome icon
4019 * @cfg {Boolean} active Is item active
4020 * @cfg {Boolean} disabled Is item disabled
4022 * @cfg {Boolean} preventDefault (true | false) default false
4023 * @cfg {String} tabId the tab that this item activates.
4024 * @cfg {String} tagtype (a|span) render as a href or span?
4025 * @cfg {Boolean} animateRef (true|false) link to element default false
4028 * Create a new Navbar Item
4029 * @param {Object} config The config object
4031 Roo.bootstrap.NavItem = function(config){
4032 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4037 * The raw click event for the entire grid.
4038 * @param {Roo.EventObject} e
4043 * Fires when the active item active state changes
4044 * @param {Roo.bootstrap.NavItem} this
4045 * @param {boolean} state the new state
4051 * Fires when scroll to element
4052 * @param {Roo.bootstrap.NavItem} this
4053 * @param {Object} options
4054 * @param {Roo.EventObject} e
4062 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4070 preventDefault : false,
4077 getAutoCreate : function(){
4086 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4088 if (this.disabled) {
4089 cfg.cls += ' disabled';
4092 if (this.href || this.html || this.glyphicon || this.icon) {
4096 href : this.href || "#",
4097 html: this.html || ''
4102 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4105 if(this.glyphicon) {
4106 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4111 cfg.cn[0].html += " <span class='caret'></span>";
4115 if (this.badge !== '') {
4117 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4125 initEvents: function()
4127 if (typeof (this.menu) != 'undefined') {
4128 this.menu.parentType = this.xtype;
4129 this.menu.triggerEl = this.el;
4130 this.menu = this.addxtype(Roo.apply({}, this.menu));
4133 this.el.select('a',true).on('click', this.onClick, this);
4135 if(this.tagtype == 'span'){
4136 this.el.select('span',true).on('click', this.onClick, this);
4139 // at this point parent should be available..
4140 this.parent().register(this);
4143 onClick : function(e)
4146 this.preventDefault ||
4153 if (this.disabled) {
4157 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4158 if (tg && tg.transition) {
4159 Roo.log("waiting for the transitionend");
4165 //Roo.log("fire event clicked");
4166 if(this.fireEvent('click', this, e) === false){
4170 if(this.tagtype == 'span'){
4174 //Roo.log(this.href);
4175 var ael = this.el.select('a',true).first();
4178 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4179 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4180 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4181 return; // ignore... - it's a 'hash' to another page.
4185 this.scrollToElement(e);
4189 var p = this.parent();
4191 if (['tabs','pills'].indexOf(p.type)!==-1) {
4192 if (typeof(p.setActiveItem) !== 'undefined') {
4193 p.setActiveItem(this);
4197 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4198 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4199 // remove the collapsed menu expand...
4200 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4204 isActive: function () {
4207 setActive : function(state, fire, is_was_active)
4209 if (this.active && !state && this.navId) {
4210 this.was_active = true;
4211 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4213 nv.clearWasActive(this);
4217 this.active = state;
4220 this.el.removeClass('active');
4221 } else if (!this.el.hasClass('active')) {
4222 this.el.addClass('active');
4225 this.fireEvent('changed', this, state);
4228 // show a panel if it's registered and related..
4230 if (!this.navId || !this.tabId || !state || is_was_active) {
4234 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4238 var pan = tg.getPanelByName(this.tabId);
4242 // if we can not flip to new panel - go back to old nav highlight..
4243 if (false == tg.showPanel(pan)) {
4244 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4246 var onav = nv.getWasActive();
4248 onav.setActive(true, false, true);
4257 // this should not be here...
4258 setDisabled : function(state)
4260 this.disabled = state;
4262 this.el.removeClass('disabled');
4263 } else if (!this.el.hasClass('disabled')) {
4264 this.el.addClass('disabled');
4270 * Fetch the element to display the tooltip on.
4271 * @return {Roo.Element} defaults to this.el
4273 tooltipEl : function()
4275 return this.el.select('' + this.tagtype + '', true).first();
4278 scrollToElement : function(e)
4280 var c = document.body;
4283 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4285 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4286 c = document.documentElement;
4289 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4295 var o = target.calcOffsetsTo(c);
4302 this.fireEvent('scrollto', this, options, e);
4304 Roo.get(c).scrollTo('top', options.value, true);
4317 * <span> icon </span>
4318 * <span> text </span>
4319 * <span>badge </span>
4323 * @class Roo.bootstrap.NavSidebarItem
4324 * @extends Roo.bootstrap.NavItem
4325 * Bootstrap Navbar.NavSidebarItem class
4326 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4328 * Create a new Navbar Button
4329 * @param {Object} config The config object
4331 Roo.bootstrap.NavSidebarItem = function(config){
4332 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4337 * The raw click event for the entire grid.
4338 * @param {Roo.EventObject} e
4343 * Fires when the active item active state changes
4344 * @param {Roo.bootstrap.NavSidebarItem} this
4345 * @param {boolean} state the new state
4353 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4355 badgeWeight : 'default',
4357 getAutoCreate : function(){
4362 href : this.href || '#',
4374 html : this.html || ''
4379 cfg.cls += ' active';
4382 if (this.disabled) {
4383 cfg.cls += ' disabled';
4387 if (this.glyphicon || this.icon) {
4388 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4389 a.cn.push({ tag : 'i', cls : c }) ;
4394 if (this.badge !== '') {
4396 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4400 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4401 a.cls += 'dropdown-toggle treeview' ;
4412 initEvents : function()
4414 this.el.on('click', this.onClick, this);
4417 if(this.badge !== ''){
4419 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4424 onClick : function(e)
4431 if(this.preventDefault){
4435 this.fireEvent('click', this);
4438 disable : function()
4440 this.setDisabled(true);
4445 this.setDisabled(false);
4448 setDisabled : function(state)
4450 if(this.disabled == state){
4454 this.disabled = state;
4457 this.el.addClass('disabled');
4461 this.el.removeClass('disabled');
4466 setActive : function(state)
4468 if(this.active == state){
4472 this.active = state;
4475 this.el.addClass('active');
4479 this.el.removeClass('active');
4484 isActive: function ()
4489 setBadge : function(str)
4495 this.badgeEl.dom.innerHTML = str;
4512 * @class Roo.bootstrap.Row
4513 * @extends Roo.bootstrap.Component
4514 * Bootstrap Row class (contains columns...)
4518 * @param {Object} config The config object
4521 Roo.bootstrap.Row = function(config){
4522 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4525 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4527 getAutoCreate : function(){
4546 * @class Roo.bootstrap.Element
4547 * @extends Roo.bootstrap.Component
4548 * Bootstrap Element class
4549 * @cfg {String} html contents of the element
4550 * @cfg {String} tag tag of the element
4551 * @cfg {String} cls class of the element
4552 * @cfg {Boolean} preventDefault (true|false) default false
4553 * @cfg {Boolean} clickable (true|false) default false
4556 * Create a new Element
4557 * @param {Object} config The config object
4560 Roo.bootstrap.Element = function(config){
4561 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4567 * When a element is chick
4568 * @param {Roo.bootstrap.Element} this
4569 * @param {Roo.EventObject} e
4575 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4580 preventDefault: false,
4583 getAutoCreate : function(){
4594 initEvents: function()
4596 Roo.bootstrap.Element.superclass.initEvents.call(this);
4599 this.el.on('click', this.onClick, this);
4604 onClick : function(e)
4606 if(this.preventDefault){
4610 this.fireEvent('click', this, e);
4613 getValue : function()
4615 return this.el.dom.innerHTML;
4618 setValue : function(value)
4620 this.el.dom.innerHTML = value;
4635 * @class Roo.bootstrap.Pagination
4636 * @extends Roo.bootstrap.Component
4637 * Bootstrap Pagination class
4638 * @cfg {String} size xs | sm | md | lg
4639 * @cfg {Boolean} inverse false | true
4642 * Create a new Pagination
4643 * @param {Object} config The config object
4646 Roo.bootstrap.Pagination = function(config){
4647 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4650 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4656 getAutoCreate : function(){
4662 cfg.cls += ' inverse';
4668 cfg.cls += " " + this.cls;
4686 * @class Roo.bootstrap.PaginationItem
4687 * @extends Roo.bootstrap.Component
4688 * Bootstrap PaginationItem class
4689 * @cfg {String} html text
4690 * @cfg {String} href the link
4691 * @cfg {Boolean} preventDefault (true | false) default true
4692 * @cfg {Boolean} active (true | false) default false
4693 * @cfg {Boolean} disabled default false
4697 * Create a new PaginationItem
4698 * @param {Object} config The config object
4702 Roo.bootstrap.PaginationItem = function(config){
4703 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4708 * The raw click event for the entire grid.
4709 * @param {Roo.EventObject} e
4715 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4719 preventDefault: true,
4724 getAutoCreate : function(){
4730 href : this.href ? this.href : '#',
4731 html : this.html ? this.html : ''
4741 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4745 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4751 initEvents: function() {
4753 this.el.on('click', this.onClick, this);
4756 onClick : function(e)
4758 Roo.log('PaginationItem on click ');
4759 if(this.preventDefault){
4767 this.fireEvent('click', this, e);
4783 * @class Roo.bootstrap.Slider
4784 * @extends Roo.bootstrap.Component
4785 * Bootstrap Slider class
4788 * Create a new Slider
4789 * @param {Object} config The config object
4792 Roo.bootstrap.Slider = function(config){
4793 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4796 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4798 getAutoCreate : function(){
4802 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4806 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4818 * Ext JS Library 1.1.1
4819 * Copyright(c) 2006-2007, Ext JS, LLC.
4821 * Originally Released Under LGPL - original licence link has changed is not relivant.
4824 * <script type="text/javascript">
4829 * @class Roo.grid.ColumnModel
4830 * @extends Roo.util.Observable
4831 * This is the default implementation of a ColumnModel used by the Grid. It defines
4832 * the columns in the grid.
4835 var colModel = new Roo.grid.ColumnModel([
4836 {header: "Ticker", width: 60, sortable: true, locked: true},
4837 {header: "Company Name", width: 150, sortable: true},
4838 {header: "Market Cap.", width: 100, sortable: true},
4839 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4840 {header: "Employees", width: 100, sortable: true, resizable: false}
4845 * The config options listed for this class are options which may appear in each
4846 * individual column definition.
4847 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4849 * @param {Object} config An Array of column config objects. See this class's
4850 * config objects for details.
4852 Roo.grid.ColumnModel = function(config){
4854 * The config passed into the constructor
4856 this.config = config;
4859 // if no id, create one
4860 // if the column does not have a dataIndex mapping,
4861 // map it to the order it is in the config
4862 for(var i = 0, len = config.length; i < len; i++){
4864 if(typeof c.dataIndex == "undefined"){
4867 if(typeof c.renderer == "string"){
4868 c.renderer = Roo.util.Format[c.renderer];
4870 if(typeof c.id == "undefined"){
4873 if(c.editor && c.editor.xtype){
4874 c.editor = Roo.factory(c.editor, Roo.grid);
4876 if(c.editor && c.editor.isFormField){
4877 c.editor = new Roo.grid.GridEditor(c.editor);
4879 this.lookup[c.id] = c;
4883 * The width of columns which have no width specified (defaults to 100)
4886 this.defaultWidth = 100;
4889 * Default sortable of columns which have no sortable specified (defaults to false)
4892 this.defaultSortable = false;
4896 * @event widthchange
4897 * Fires when the width of a column changes.
4898 * @param {ColumnModel} this
4899 * @param {Number} columnIndex The column index
4900 * @param {Number} newWidth The new width
4902 "widthchange": true,
4904 * @event headerchange
4905 * Fires when the text of a header changes.
4906 * @param {ColumnModel} this
4907 * @param {Number} columnIndex The column index
4908 * @param {Number} newText The new header text
4910 "headerchange": true,
4912 * @event hiddenchange
4913 * Fires when a column is hidden or "unhidden".
4914 * @param {ColumnModel} this
4915 * @param {Number} columnIndex The column index
4916 * @param {Boolean} hidden true if hidden, false otherwise
4918 "hiddenchange": true,
4920 * @event columnmoved
4921 * Fires when a column is moved.
4922 * @param {ColumnModel} this
4923 * @param {Number} oldIndex
4924 * @param {Number} newIndex
4926 "columnmoved" : true,
4928 * @event columlockchange
4929 * Fires when a column's locked state is changed
4930 * @param {ColumnModel} this
4931 * @param {Number} colIndex
4932 * @param {Boolean} locked true if locked
4934 "columnlockchange" : true
4936 Roo.grid.ColumnModel.superclass.constructor.call(this);
4938 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4940 * @cfg {String} header The header text to display in the Grid view.
4943 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4944 * {@link Roo.data.Record} definition from which to draw the column's value. If not
4945 * specified, the column's index is used as an index into the Record's data Array.
4948 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4949 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4952 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4953 * Defaults to the value of the {@link #defaultSortable} property.
4954 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4957 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
4960 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
4963 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4966 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4969 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4970 * given the cell's data value. See {@link #setRenderer}. If not specified, the
4971 * default renderer uses the raw data value. If an object is returned (bootstrap only)
4972 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4975 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
4978 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
4981 * @cfg {String} cursor (Optional)
4984 * @cfg {String} tooltip (Optional)
4987 * @cfg {Number} xs (Optional)
4990 * @cfg {Number} sm (Optional)
4993 * @cfg {Number} md (Optional)
4996 * @cfg {Number} lg (Optional)
4999 * Returns the id of the column at the specified index.
5000 * @param {Number} index The column index
5001 * @return {String} the id
5003 getColumnId : function(index){
5004 return this.config[index].id;
5008 * Returns the column for a specified id.
5009 * @param {String} id The column id
5010 * @return {Object} the column
5012 getColumnById : function(id){
5013 return this.lookup[id];
5018 * Returns the column for a specified dataIndex.
5019 * @param {String} dataIndex The column dataIndex
5020 * @return {Object|Boolean} the column or false if not found
5022 getColumnByDataIndex: function(dataIndex){
5023 var index = this.findColumnIndex(dataIndex);
5024 return index > -1 ? this.config[index] : false;
5028 * Returns the index for a specified column id.
5029 * @param {String} id The column id
5030 * @return {Number} the index, or -1 if not found
5032 getIndexById : function(id){
5033 for(var i = 0, len = this.config.length; i < len; i++){
5034 if(this.config[i].id == id){
5042 * Returns the index for a specified column dataIndex.
5043 * @param {String} dataIndex The column dataIndex
5044 * @return {Number} the index, or -1 if not found
5047 findColumnIndex : function(dataIndex){
5048 for(var i = 0, len = this.config.length; i < len; i++){
5049 if(this.config[i].dataIndex == dataIndex){
5057 moveColumn : function(oldIndex, newIndex){
5058 var c = this.config[oldIndex];
5059 this.config.splice(oldIndex, 1);
5060 this.config.splice(newIndex, 0, c);
5061 this.dataMap = null;
5062 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5065 isLocked : function(colIndex){
5066 return this.config[colIndex].locked === true;
5069 setLocked : function(colIndex, value, suppressEvent){
5070 if(this.isLocked(colIndex) == value){
5073 this.config[colIndex].locked = value;
5075 this.fireEvent("columnlockchange", this, colIndex, value);
5079 getTotalLockedWidth : function(){
5081 for(var i = 0; i < this.config.length; i++){
5082 if(this.isLocked(i) && !this.isHidden(i)){
5083 this.totalWidth += this.getColumnWidth(i);
5089 getLockedCount : function(){
5090 for(var i = 0, len = this.config.length; i < len; i++){
5091 if(!this.isLocked(i)){
5098 * Returns the number of columns.
5101 getColumnCount : function(visibleOnly){
5102 if(visibleOnly === true){
5104 for(var i = 0, len = this.config.length; i < len; i++){
5105 if(!this.isHidden(i)){
5111 return this.config.length;
5115 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5116 * @param {Function} fn
5117 * @param {Object} scope (optional)
5118 * @return {Array} result
5120 getColumnsBy : function(fn, scope){
5122 for(var i = 0, len = this.config.length; i < len; i++){
5123 var c = this.config[i];
5124 if(fn.call(scope||this, c, i) === true){
5132 * Returns true if the specified column is sortable.
5133 * @param {Number} col The column index
5136 isSortable : function(col){
5137 if(typeof this.config[col].sortable == "undefined"){
5138 return this.defaultSortable;
5140 return this.config[col].sortable;
5144 * Returns the rendering (formatting) function defined for the column.
5145 * @param {Number} col The column index.
5146 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5148 getRenderer : function(col){
5149 if(!this.config[col].renderer){
5150 return Roo.grid.ColumnModel.defaultRenderer;
5152 return this.config[col].renderer;
5156 * Sets the rendering (formatting) function for a column.
5157 * @param {Number} col The column index
5158 * @param {Function} fn The function to use to process the cell's raw data
5159 * to return HTML markup for the grid view. The render function is called with
5160 * the following parameters:<ul>
5161 * <li>Data value.</li>
5162 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5163 * <li>css A CSS style string to apply to the table cell.</li>
5164 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5165 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5166 * <li>Row index</li>
5167 * <li>Column index</li>
5168 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5170 setRenderer : function(col, fn){
5171 this.config[col].renderer = fn;
5175 * Returns the width for the specified column.
5176 * @param {Number} col The column index
5179 getColumnWidth : function(col){
5180 return this.config[col].width * 1 || this.defaultWidth;
5184 * Sets the width for a column.
5185 * @param {Number} col The column index
5186 * @param {Number} width The new width
5188 setColumnWidth : function(col, width, suppressEvent){
5189 this.config[col].width = width;
5190 this.totalWidth = null;
5192 this.fireEvent("widthchange", this, col, width);
5197 * Returns the total width of all columns.
5198 * @param {Boolean} includeHidden True to include hidden column widths
5201 getTotalWidth : function(includeHidden){
5202 if(!this.totalWidth){
5203 this.totalWidth = 0;
5204 for(var i = 0, len = this.config.length; i < len; i++){
5205 if(includeHidden || !this.isHidden(i)){
5206 this.totalWidth += this.getColumnWidth(i);
5210 return this.totalWidth;
5214 * Returns the header for the specified column.
5215 * @param {Number} col The column index
5218 getColumnHeader : function(col){
5219 return this.config[col].header;
5223 * Sets the header for a column.
5224 * @param {Number} col The column index
5225 * @param {String} header The new header
5227 setColumnHeader : function(col, header){
5228 this.config[col].header = header;
5229 this.fireEvent("headerchange", this, col, header);
5233 * Returns the tooltip for the specified column.
5234 * @param {Number} col The column index
5237 getColumnTooltip : function(col){
5238 return this.config[col].tooltip;
5241 * Sets the tooltip for a column.
5242 * @param {Number} col The column index
5243 * @param {String} tooltip The new tooltip
5245 setColumnTooltip : function(col, tooltip){
5246 this.config[col].tooltip = tooltip;
5250 * Returns the dataIndex for the specified column.
5251 * @param {Number} col The column index
5254 getDataIndex : function(col){
5255 return this.config[col].dataIndex;
5259 * Sets the dataIndex for a column.
5260 * @param {Number} col The column index
5261 * @param {Number} dataIndex The new dataIndex
5263 setDataIndex : function(col, dataIndex){
5264 this.config[col].dataIndex = dataIndex;
5270 * Returns true if the cell is editable.
5271 * @param {Number} colIndex The column index
5272 * @param {Number} rowIndex The row index
5275 isCellEditable : function(colIndex, rowIndex){
5276 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5280 * Returns the editor defined for the cell/column.
5281 * return false or null to disable editing.
5282 * @param {Number} colIndex The column index
5283 * @param {Number} rowIndex The row index
5286 getCellEditor : function(colIndex, rowIndex){
5287 return this.config[colIndex].editor;
5291 * Sets if a column is editable.
5292 * @param {Number} col The column index
5293 * @param {Boolean} editable True if the column is editable
5295 setEditable : function(col, editable){
5296 this.config[col].editable = editable;
5301 * Returns true if the column is hidden.
5302 * @param {Number} colIndex The column index
5305 isHidden : function(colIndex){
5306 return this.config[colIndex].hidden;
5311 * Returns true if the column width cannot be changed
5313 isFixed : function(colIndex){
5314 return this.config[colIndex].fixed;
5318 * Returns true if the column can be resized
5321 isResizable : function(colIndex){
5322 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5325 * Sets if a column is hidden.
5326 * @param {Number} colIndex The column index
5327 * @param {Boolean} hidden True if the column is hidden
5329 setHidden : function(colIndex, hidden){
5330 this.config[colIndex].hidden = hidden;
5331 this.totalWidth = null;
5332 this.fireEvent("hiddenchange", this, colIndex, hidden);
5336 * Sets the editor for a column.
5337 * @param {Number} col The column index
5338 * @param {Object} editor The editor object
5340 setEditor : function(col, editor){
5341 this.config[col].editor = editor;
5345 Roo.grid.ColumnModel.defaultRenderer = function(value){
5346 if(typeof value == "string" && value.length < 1){
5352 // Alias for backwards compatibility
5353 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5356 * Ext JS Library 1.1.1
5357 * Copyright(c) 2006-2007, Ext JS, LLC.
5359 * Originally Released Under LGPL - original licence link has changed is not relivant.
5362 * <script type="text/javascript">
5366 * @class Roo.LoadMask
5367 * A simple utility class for generically masking elements while loading data. If the element being masked has
5368 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5369 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5370 * element's UpdateManager load indicator and will be destroyed after the initial load.
5372 * Create a new LoadMask
5373 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5374 * @param {Object} config The config object
5376 Roo.LoadMask = function(el, config){
5377 this.el = Roo.get(el);
5378 Roo.apply(this, config);
5380 this.store.on('beforeload', this.onBeforeLoad, this);
5381 this.store.on('load', this.onLoad, this);
5382 this.store.on('loadexception', this.onLoadException, this);
5383 this.removeMask = false;
5385 var um = this.el.getUpdateManager();
5386 um.showLoadIndicator = false; // disable the default indicator
5387 um.on('beforeupdate', this.onBeforeLoad, this);
5388 um.on('update', this.onLoad, this);
5389 um.on('failure', this.onLoad, this);
5390 this.removeMask = true;
5394 Roo.LoadMask.prototype = {
5396 * @cfg {Boolean} removeMask
5397 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5398 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5402 * The text to display in a centered loading message box (defaults to 'Loading...')
5406 * @cfg {String} msgCls
5407 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5409 msgCls : 'x-mask-loading',
5412 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5418 * Disables the mask to prevent it from being displayed
5420 disable : function(){
5421 this.disabled = true;
5425 * Enables the mask so that it can be displayed
5427 enable : function(){
5428 this.disabled = false;
5431 onLoadException : function()
5435 if (typeof(arguments[3]) != 'undefined') {
5436 Roo.MessageBox.alert("Error loading",arguments[3]);
5440 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5441 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5450 this.el.unmask(this.removeMask);
5455 this.el.unmask(this.removeMask);
5459 onBeforeLoad : function(){
5461 this.el.mask(this.msg, this.msgCls);
5466 destroy : function(){
5468 this.store.un('beforeload', this.onBeforeLoad, this);
5469 this.store.un('load', this.onLoad, this);
5470 this.store.un('loadexception', this.onLoadException, this);
5472 var um = this.el.getUpdateManager();
5473 um.un('beforeupdate', this.onBeforeLoad, this);
5474 um.un('update', this.onLoad, this);
5475 um.un('failure', this.onLoad, this);
5486 * @class Roo.bootstrap.Table
5487 * @extends Roo.bootstrap.Component
5488 * Bootstrap Table class
5489 * @cfg {String} cls table class
5490 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5491 * @cfg {String} bgcolor Specifies the background color for a table
5492 * @cfg {Number} border Specifies whether the table cells should have borders or not
5493 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5494 * @cfg {Number} cellspacing Specifies the space between cells
5495 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5496 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5497 * @cfg {String} sortable Specifies that the table should be sortable
5498 * @cfg {String} summary Specifies a summary of the content of a table
5499 * @cfg {Number} width Specifies the width of a table
5500 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5502 * @cfg {boolean} striped Should the rows be alternative striped
5503 * @cfg {boolean} bordered Add borders to the table
5504 * @cfg {boolean} hover Add hover highlighting
5505 * @cfg {boolean} condensed Format condensed
5506 * @cfg {boolean} responsive Format condensed
5507 * @cfg {Boolean} loadMask (true|false) default false
5508 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5509 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5510 * @cfg {Boolean} rowSelection (true|false) default false
5511 * @cfg {Boolean} cellSelection (true|false) default false
5512 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5516 * Create a new Table
5517 * @param {Object} config The config object
5520 Roo.bootstrap.Table = function(config){
5521 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5524 this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5525 this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5526 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5527 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5531 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5532 this.sm = this.selModel;
5533 this.sm.xmodule = this.xmodule || false;
5535 if (this.cm && typeof(this.cm.config) == 'undefined') {
5536 this.colModel = new Roo.grid.ColumnModel(this.cm);
5537 this.cm = this.colModel;
5538 this.cm.xmodule = this.xmodule || false;
5541 this.store= Roo.factory(this.store, Roo.data);
5542 this.ds = this.store;
5543 this.ds.xmodule = this.xmodule || false;
5546 if (this.footer && this.store) {
5547 this.footer.dataSource = this.ds;
5548 this.footer = Roo.factory(this.footer);
5555 * Fires when a cell is clicked
5556 * @param {Roo.bootstrap.Table} this
5557 * @param {Roo.Element} el
5558 * @param {Number} rowIndex
5559 * @param {Number} columnIndex
5560 * @param {Roo.EventObject} e
5564 * @event celldblclick
5565 * Fires when a cell is double clicked
5566 * @param {Roo.bootstrap.Table} this
5567 * @param {Roo.Element} el
5568 * @param {Number} rowIndex
5569 * @param {Number} columnIndex
5570 * @param {Roo.EventObject} e
5572 "celldblclick" : true,
5575 * Fires when a row is clicked
5576 * @param {Roo.bootstrap.Table} this
5577 * @param {Roo.Element} el
5578 * @param {Number} rowIndex
5579 * @param {Roo.EventObject} e
5583 * @event rowdblclick
5584 * Fires when a row is double clicked
5585 * @param {Roo.bootstrap.Table} this
5586 * @param {Roo.Element} el
5587 * @param {Number} rowIndex
5588 * @param {Roo.EventObject} e
5590 "rowdblclick" : true,
5593 * Fires when a mouseover occur
5594 * @param {Roo.bootstrap.Table} this
5595 * @param {Roo.Element} el
5596 * @param {Number} rowIndex
5597 * @param {Number} columnIndex
5598 * @param {Roo.EventObject} e
5603 * Fires when a mouseout occur
5604 * @param {Roo.bootstrap.Table} this
5605 * @param {Roo.Element} el
5606 * @param {Number} rowIndex
5607 * @param {Number} columnIndex
5608 * @param {Roo.EventObject} e
5613 * Fires when a row is rendered, so you can change add a style to it.
5614 * @param {Roo.bootstrap.Table} this
5615 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5619 * @event rowsrendered
5620 * Fires when all the rows have been rendered
5621 * @param {Roo.bootstrap.Table} this
5623 'rowsrendered' : true
5628 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5653 rowSelection : false,
5654 cellSelection : false,
5657 // Roo.Element - the tbody
5660 getAutoCreate : function(){
5661 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5670 cfg.cls += ' table-striped';
5674 cfg.cls += ' table-hover';
5676 if (this.bordered) {
5677 cfg.cls += ' table-bordered';
5679 if (this.condensed) {
5680 cfg.cls += ' table-condensed';
5682 if (this.responsive) {
5683 cfg.cls += ' table-responsive';
5687 cfg.cls+= ' ' +this.cls;
5690 // this lot should be simplifed...
5693 cfg.align=this.align;
5696 cfg.bgcolor=this.bgcolor;
5699 cfg.border=this.border;
5701 if (this.cellpadding) {
5702 cfg.cellpadding=this.cellpadding;
5704 if (this.cellspacing) {
5705 cfg.cellspacing=this.cellspacing;
5708 cfg.frame=this.frame;
5711 cfg.rules=this.rules;
5713 if (this.sortable) {
5714 cfg.sortable=this.sortable;
5717 cfg.summary=this.summary;
5720 cfg.width=this.width;
5723 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5726 if(this.store || this.cm){
5727 if(this.headerShow){
5728 cfg.cn.push(this.renderHeader());
5731 cfg.cn.push(this.renderBody());
5733 if(this.footerShow){
5734 cfg.cn.push(this.renderFooter());
5737 cfg.cls+= ' TableGrid';
5740 return { cn : [ cfg ] };
5743 initEvents : function()
5745 if(!this.store || !this.cm){
5749 //Roo.log('initEvents with ds!!!!');
5751 this.mainBody = this.el.select('tbody', true).first();
5756 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5757 e.on('click', _this.sort, _this);
5760 this.el.on("click", this.onClick, this);
5761 this.el.on("dblclick", this.onDblClick, this);
5763 // why is this done????? = it breaks dialogs??
5764 //this.parent().el.setStyle('position', 'relative');
5768 this.footer.parentId = this.id;
5769 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5772 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5774 this.store.on('load', this.onLoad, this);
5775 this.store.on('beforeload', this.onBeforeLoad, this);
5776 this.store.on('update', this.onUpdate, this);
5777 this.store.on('add', this.onAdd, this);
5781 onMouseover : function(e, el)
5783 var cell = Roo.get(el);
5789 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5790 cell = cell.findParent('td', false, true);
5793 var row = cell.findParent('tr', false, true);
5794 var cellIndex = cell.dom.cellIndex;
5795 var rowIndex = row.dom.rowIndex - 1; // start from 0
5797 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5801 onMouseout : 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('mouseout', this, cell, rowIndex, cellIndex, e);
5821 onClick : function(e, el)
5823 var cell = Roo.get(el);
5825 if(!cell || (!this.cellSelection && !this.rowSelection)){
5829 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5830 cell = cell.findParent('td', false, true);
5833 if(!cell || typeof(cell) == 'undefined'){
5837 var row = cell.findParent('tr', false, true);
5839 if(!row || typeof(row) == 'undefined'){
5843 var cellIndex = cell.dom.cellIndex;
5844 var rowIndex = this.getRowIndex(row);
5846 // why??? - should these not be based on SelectionModel?
5847 if(this.cellSelection){
5848 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5851 if(this.rowSelection){
5852 this.fireEvent('rowclick', this, row, rowIndex, e);
5858 onDblClick : function(e,el)
5860 var cell = Roo.get(el);
5862 if(!cell || (!this.CellSelection && !this.RowSelection)){
5866 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5867 cell = cell.findParent('td', false, true);
5870 if(!cell || typeof(cell) == 'undefined'){
5874 var row = cell.findParent('tr', false, true);
5876 if(!row || typeof(row) == 'undefined'){
5880 var cellIndex = cell.dom.cellIndex;
5881 var rowIndex = this.getRowIndex(row);
5883 if(this.CellSelection){
5884 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5887 if(this.RowSelection){
5888 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5892 sort : function(e,el)
5894 var col = Roo.get(el);
5896 if(!col.hasClass('sortable')){
5900 var sort = col.attr('sort');
5903 if(col.hasClass('glyphicon-arrow-up')){
5907 this.store.sortInfo = {field : sort, direction : dir};
5910 Roo.log("calling footer first");
5911 this.footer.onClick('first');
5914 this.store.load({ params : { start : 0 } });
5918 renderHeader : function()
5927 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5929 var config = cm.config[i];
5934 html: cm.getColumnHeader(i)
5939 if(typeof(config.lgHeader) != 'undefined'){
5940 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5943 if(typeof(config.mdHeader) != 'undefined'){
5944 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5947 if(typeof(config.smHeader) != 'undefined'){
5948 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5951 if(typeof(config.xsHeader) != 'undefined'){
5952 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5959 if(typeof(config.tooltip) != 'undefined'){
5960 c.tooltip = config.tooltip;
5963 if(typeof(config.colspan) != 'undefined'){
5964 c.colspan = config.colspan;
5967 if(typeof(config.hidden) != 'undefined' && config.hidden){
5968 c.style += ' display:none;';
5971 if(typeof(config.dataIndex) != 'undefined'){
5972 c.sort = config.dataIndex;
5975 if(typeof(config.sortable) != 'undefined' && config.sortable){
5979 if(typeof(config.align) != 'undefined' && config.align.length){
5980 c.style += ' text-align:' + config.align + ';';
5983 if(typeof(config.width) != 'undefined'){
5984 c.style += ' width:' + config.width + 'px;';
5987 if(typeof(config.cls) != 'undefined'){
5988 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5991 ['xs','sm','md','lg'].map(function(size){
5993 if(typeof(config[size]) == 'undefined'){
5997 if (!config[size]) { // 0 = hidden
5998 cfg.cls += ' hidden-' + size;
6002 cfg.cls += ' col-' + size + '-' + config[size];
6012 renderBody : function()
6022 colspan : this.cm.getColumnCount()
6032 renderFooter : function()
6042 colspan : this.cm.getColumnCount()
6056 Roo.log('ds onload');
6061 var ds = this.store;
6063 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6064 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6066 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6067 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6070 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6071 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6075 var tbody = this.mainBody;
6077 if(ds.getCount() > 0){
6078 ds.data.each(function(d,rowIndex){
6079 var row = this.renderRow(cm, ds, rowIndex);
6081 tbody.createChild(row);
6085 if(row.cellObjects.length){
6086 Roo.each(row.cellObjects, function(r){
6087 _this.renderCellObject(r);
6094 Roo.each(this.el.select('tbody td', true).elements, function(e){
6095 e.on('mouseover', _this.onMouseover, _this);
6098 Roo.each(this.el.select('tbody td', true).elements, function(e){
6099 e.on('mouseout', _this.onMouseout, _this);
6101 this.fireEvent('rowsrendered', this);
6102 //if(this.loadMask){
6103 // this.maskEl.hide();
6108 onUpdate : function(ds,record)
6110 this.refreshRow(record);
6113 onRemove : function(ds, record, index, isUpdate){
6114 if(isUpdate !== true){
6115 this.fireEvent("beforerowremoved", this, index, record);
6117 var bt = this.mainBody.dom;
6119 var rows = this.el.select('tbody > tr', true).elements;
6121 if(typeof(rows[index]) != 'undefined'){
6122 bt.removeChild(rows[index].dom);
6125 // if(bt.rows[index]){
6126 // bt.removeChild(bt.rows[index]);
6129 if(isUpdate !== true){
6130 //this.stripeRows(index);
6131 //this.syncRowHeights(index, index);
6133 this.fireEvent("rowremoved", this, index, record);
6137 onAdd : function(ds, records, rowIndex)
6139 //Roo.log('on Add called');
6140 // - note this does not handle multiple adding very well..
6141 var bt = this.mainBody.dom;
6142 for (var i =0 ; i < records.length;i++) {
6143 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6144 //Roo.log(records[i]);
6145 //Roo.log(this.store.getAt(rowIndex+i));
6146 this.insertRow(this.store, rowIndex + i, false);
6153 refreshRow : function(record){
6154 var ds = this.store, index;
6155 if(typeof record == 'number'){
6157 record = ds.getAt(index);
6159 index = ds.indexOf(record);
6161 this.insertRow(ds, index, true);
6162 this.onRemove(ds, record, index+1, true);
6163 //this.syncRowHeights(index, index);
6165 this.fireEvent("rowupdated", this, index, record);
6168 insertRow : function(dm, rowIndex, isUpdate){
6171 this.fireEvent("beforerowsinserted", this, rowIndex);
6173 //var s = this.getScrollState();
6174 var row = this.renderRow(this.cm, this.store, rowIndex);
6175 // insert before rowIndex..
6176 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6180 if(row.cellObjects.length){
6181 Roo.each(row.cellObjects, function(r){
6182 _this.renderCellObject(r);
6187 this.fireEvent("rowsinserted", this, rowIndex);
6188 //this.syncRowHeights(firstRow, lastRow);
6189 //this.stripeRows(firstRow);
6196 getRowDom : function(rowIndex)
6198 var rows = this.el.select('tbody > tr', true).elements;
6200 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6203 // returns the object tree for a tr..
6206 renderRow : function(cm, ds, rowIndex)
6209 var d = ds.getAt(rowIndex);
6216 var cellObjects = [];
6218 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6219 var config = cm.config[i];
6221 var renderer = cm.getRenderer(i);
6225 if(typeof(renderer) !== 'undefined'){
6226 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6228 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6229 // and are rendered into the cells after the row is rendered - using the id for the element.
6231 if(typeof(value) === 'object'){
6241 rowIndex : rowIndex,
6246 this.fireEvent('rowclass', this, rowcfg);
6250 cls : rowcfg.rowClass,
6252 html: (typeof(value) === 'object') ? '' : value
6259 if(typeof(config.colspan) != 'undefined'){
6260 td.colspan = config.colspan;
6263 if(typeof(config.hidden) != 'undefined' && config.hidden){
6264 td.style += ' display:none;';
6267 if(typeof(config.align) != 'undefined' && config.align.length){
6268 td.style += ' text-align:' + config.align + ';';
6271 if(typeof(config.width) != 'undefined'){
6272 td.style += ' width:' + config.width + 'px;';
6275 if(typeof(config.cursor) != 'undefined'){
6276 td.style += ' cursor:' + config.cursor + ';';
6279 if(typeof(config.cls) != 'undefined'){
6280 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6283 ['xs','sm','md','lg'].map(function(size){
6285 if(typeof(config[size]) == 'undefined'){
6289 if (!config[size]) { // 0 = hidden
6290 td.cls += ' hidden-' + size;
6294 td.cls += ' col-' + size + '-' + config[size];
6302 row.cellObjects = cellObjects;
6310 onBeforeLoad : function()
6312 //Roo.log('ds onBeforeLoad');
6316 //if(this.loadMask){
6317 // this.maskEl.show();
6325 this.el.select('tbody', true).first().dom.innerHTML = '';
6328 * Show or hide a row.
6329 * @param {Number} rowIndex to show or hide
6330 * @param {Boolean} state hide
6332 setRowVisibility : function(rowIndex, state)
6334 var bt = this.mainBody.dom;
6336 var rows = this.el.select('tbody > tr', true).elements;
6338 if(typeof(rows[rowIndex]) == 'undefined'){
6341 rows[rowIndex].dom.style.display = state ? '' : 'none';
6345 getSelectionModel : function(){
6347 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6349 return this.selModel;
6352 * Render the Roo.bootstrap object from renderder
6354 renderCellObject : function(r)
6358 var t = r.cfg.render(r.container);
6361 Roo.each(r.cfg.cn, function(c){
6363 container: t.getChildContainer(),
6366 _this.renderCellObject(child);
6371 getRowIndex : function(row)
6375 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6398 * @class Roo.bootstrap.TableCell
6399 * @extends Roo.bootstrap.Component
6400 * Bootstrap TableCell class
6401 * @cfg {String} html cell contain text
6402 * @cfg {String} cls cell class
6403 * @cfg {String} tag cell tag (td|th) default td
6404 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6405 * @cfg {String} align Aligns the content in a cell
6406 * @cfg {String} axis Categorizes cells
6407 * @cfg {String} bgcolor Specifies the background color of a cell
6408 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6409 * @cfg {Number} colspan Specifies the number of columns a cell should span
6410 * @cfg {String} headers Specifies one or more header cells a cell is related to
6411 * @cfg {Number} height Sets the height of a cell
6412 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6413 * @cfg {Number} rowspan Sets the number of rows a cell should span
6414 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6415 * @cfg {String} valign Vertical aligns the content in a cell
6416 * @cfg {Number} width Specifies the width of a cell
6419 * Create a new TableCell
6420 * @param {Object} config The config object
6423 Roo.bootstrap.TableCell = function(config){
6424 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6427 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6447 getAutoCreate : function(){
6448 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6468 cfg.align=this.align
6474 cfg.bgcolor=this.bgcolor
6477 cfg.charoff=this.charoff
6480 cfg.colspan=this.colspan
6483 cfg.headers=this.headers
6486 cfg.height=this.height
6489 cfg.nowrap=this.nowrap
6492 cfg.rowspan=this.rowspan
6495 cfg.scope=this.scope
6498 cfg.valign=this.valign
6501 cfg.width=this.width
6520 * @class Roo.bootstrap.TableRow
6521 * @extends Roo.bootstrap.Component
6522 * Bootstrap TableRow class
6523 * @cfg {String} cls row class
6524 * @cfg {String} align Aligns the content in a table row
6525 * @cfg {String} bgcolor Specifies a background color for a table row
6526 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6527 * @cfg {String} valign Vertical aligns the content in a table row
6530 * Create a new TableRow
6531 * @param {Object} config The config object
6534 Roo.bootstrap.TableRow = function(config){
6535 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6538 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6546 getAutoCreate : function(){
6547 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6557 cfg.align = this.align;
6560 cfg.bgcolor = this.bgcolor;
6563 cfg.charoff = this.charoff;
6566 cfg.valign = this.valign;
6584 * @class Roo.bootstrap.TableBody
6585 * @extends Roo.bootstrap.Component
6586 * Bootstrap TableBody class
6587 * @cfg {String} cls element class
6588 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6589 * @cfg {String} align Aligns the content inside the element
6590 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6591 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6594 * Create a new TableBody
6595 * @param {Object} config The config object
6598 Roo.bootstrap.TableBody = function(config){
6599 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6602 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6610 getAutoCreate : function(){
6611 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6625 cfg.align = this.align;
6628 cfg.charoff = this.charoff;
6631 cfg.valign = this.valign;
6638 // initEvents : function()
6645 // this.store = Roo.factory(this.store, Roo.data);
6646 // this.store.on('load', this.onLoad, this);
6648 // this.store.load();
6652 // onLoad: function ()
6654 // this.fireEvent('load', this);
6664 * Ext JS Library 1.1.1
6665 * Copyright(c) 2006-2007, Ext JS, LLC.
6667 * Originally Released Under LGPL - original licence link has changed is not relivant.
6670 * <script type="text/javascript">
6673 // as we use this in bootstrap.
6674 Roo.namespace('Roo.form');
6676 * @class Roo.form.Action
6677 * Internal Class used to handle form actions
6679 * @param {Roo.form.BasicForm} el The form element or its id
6680 * @param {Object} config Configuration options
6685 // define the action interface
6686 Roo.form.Action = function(form, options){
6688 this.options = options || {};
6691 * Client Validation Failed
6694 Roo.form.Action.CLIENT_INVALID = 'client';
6696 * Server Validation Failed
6699 Roo.form.Action.SERVER_INVALID = 'server';
6701 * Connect to Server Failed
6704 Roo.form.Action.CONNECT_FAILURE = 'connect';
6706 * Reading Data from Server Failed
6709 Roo.form.Action.LOAD_FAILURE = 'load';
6711 Roo.form.Action.prototype = {
6713 failureType : undefined,
6714 response : undefined,
6718 run : function(options){
6723 success : function(response){
6728 handleResponse : function(response){
6732 // default connection failure
6733 failure : function(response){
6735 this.response = response;
6736 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6737 this.form.afterAction(this, false);
6740 processResponse : function(response){
6741 this.response = response;
6742 if(!response.responseText){
6745 this.result = this.handleResponse(response);
6749 // utility functions used internally
6750 getUrl : function(appendParams){
6751 var url = this.options.url || this.form.url || this.form.el.dom.action;
6753 var p = this.getParams();
6755 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6761 getMethod : function(){
6762 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6765 getParams : function(){
6766 var bp = this.form.baseParams;
6767 var p = this.options.params;
6769 if(typeof p == "object"){
6770 p = Roo.urlEncode(Roo.applyIf(p, bp));
6771 }else if(typeof p == 'string' && bp){
6772 p += '&' + Roo.urlEncode(bp);
6775 p = Roo.urlEncode(bp);
6780 createCallback : function(){
6782 success: this.success,
6783 failure: this.failure,
6785 timeout: (this.form.timeout*1000),
6786 upload: this.form.fileUpload ? this.success : undefined
6791 Roo.form.Action.Submit = function(form, options){
6792 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6795 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6798 haveProgress : false,
6799 uploadComplete : false,
6801 // uploadProgress indicator.
6802 uploadProgress : function()
6804 if (!this.form.progressUrl) {
6808 if (!this.haveProgress) {
6809 Roo.MessageBox.progress("Uploading", "Uploading");
6811 if (this.uploadComplete) {
6812 Roo.MessageBox.hide();
6816 this.haveProgress = true;
6818 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6820 var c = new Roo.data.Connection();
6822 url : this.form.progressUrl,
6827 success : function(req){
6828 //console.log(data);
6832 rdata = Roo.decode(req.responseText)
6834 Roo.log("Invalid data from server..");
6838 if (!rdata || !rdata.success) {
6840 Roo.MessageBox.alert(Roo.encode(rdata));
6843 var data = rdata.data;
6845 if (this.uploadComplete) {
6846 Roo.MessageBox.hide();
6851 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6852 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6855 this.uploadProgress.defer(2000,this);
6858 failure: function(data) {
6859 Roo.log('progress url failed ');
6870 // run get Values on the form, so it syncs any secondary forms.
6871 this.form.getValues();
6873 var o = this.options;
6874 var method = this.getMethod();
6875 var isPost = method == 'POST';
6876 if(o.clientValidation === false || this.form.isValid()){
6878 if (this.form.progressUrl) {
6879 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6880 (new Date() * 1) + '' + Math.random());
6885 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6886 form:this.form.el.dom,
6887 url:this.getUrl(!isPost),
6889 params:isPost ? this.getParams() : null,
6890 isUpload: this.form.fileUpload
6893 this.uploadProgress();
6895 }else if (o.clientValidation !== false){ // client validation failed
6896 this.failureType = Roo.form.Action.CLIENT_INVALID;
6897 this.form.afterAction(this, false);
6901 success : function(response)
6903 this.uploadComplete= true;
6904 if (this.haveProgress) {
6905 Roo.MessageBox.hide();
6909 var result = this.processResponse(response);
6910 if(result === true || result.success){
6911 this.form.afterAction(this, true);
6915 this.form.markInvalid(result.errors);
6916 this.failureType = Roo.form.Action.SERVER_INVALID;
6918 this.form.afterAction(this, false);
6920 failure : function(response)
6922 this.uploadComplete= true;
6923 if (this.haveProgress) {
6924 Roo.MessageBox.hide();
6927 this.response = response;
6928 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6929 this.form.afterAction(this, false);
6932 handleResponse : function(response){
6933 if(this.form.errorReader){
6934 var rs = this.form.errorReader.read(response);
6937 for(var i = 0, len = rs.records.length; i < len; i++) {
6938 var r = rs.records[i];
6942 if(errors.length < 1){
6946 success : rs.success,
6952 ret = Roo.decode(response.responseText);
6956 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6966 Roo.form.Action.Load = function(form, options){
6967 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6968 this.reader = this.form.reader;
6971 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6976 Roo.Ajax.request(Roo.apply(
6977 this.createCallback(), {
6978 method:this.getMethod(),
6979 url:this.getUrl(false),
6980 params:this.getParams()
6984 success : function(response){
6986 var result = this.processResponse(response);
6987 if(result === true || !result.success || !result.data){
6988 this.failureType = Roo.form.Action.LOAD_FAILURE;
6989 this.form.afterAction(this, false);
6992 this.form.clearInvalid();
6993 this.form.setValues(result.data);
6994 this.form.afterAction(this, true);
6997 handleResponse : function(response){
6998 if(this.form.reader){
6999 var rs = this.form.reader.read(response);
7000 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7002 success : rs.success,
7006 return Roo.decode(response.responseText);
7010 Roo.form.Action.ACTION_TYPES = {
7011 'load' : Roo.form.Action.Load,
7012 'submit' : Roo.form.Action.Submit
7021 * @class Roo.bootstrap.Form
7022 * @extends Roo.bootstrap.Component
7023 * Bootstrap Form class
7024 * @cfg {String} method GET | POST (default POST)
7025 * @cfg {String} labelAlign top | left (default top)
7026 * @cfg {String} align left | right - for navbars
7027 * @cfg {Boolean} loadMask load mask when submit (default true)
7032 * @param {Object} config The config object
7036 Roo.bootstrap.Form = function(config){
7037 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7040 * @event clientvalidation
7041 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7042 * @param {Form} this
7043 * @param {Boolean} valid true if the form has passed client-side validation
7045 clientvalidation: true,
7047 * @event beforeaction
7048 * Fires before any action is performed. Return false to cancel the action.
7049 * @param {Form} this
7050 * @param {Action} action The action to be performed
7054 * @event actionfailed
7055 * Fires when an action fails.
7056 * @param {Form} this
7057 * @param {Action} action The action that failed
7059 actionfailed : true,
7061 * @event actioncomplete
7062 * Fires when an action is completed.
7063 * @param {Form} this
7064 * @param {Action} action The action that completed
7066 actioncomplete : true
7071 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7074 * @cfg {String} method
7075 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7080 * The URL to use for form actions if one isn't supplied in the action options.
7083 * @cfg {Boolean} fileUpload
7084 * Set to true if this form is a file upload.
7088 * @cfg {Object} baseParams
7089 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7093 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7097 * @cfg {Sting} align (left|right) for navbar forms
7102 activeAction : null,
7105 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7106 * element by passing it or its id or mask the form itself by passing in true.
7109 waitMsgTarget : false,
7113 getAutoCreate : function(){
7117 method : this.method || 'POST',
7118 id : this.id || Roo.id(),
7121 if (this.parent().xtype.match(/^Nav/)) {
7122 cfg.cls = 'navbar-form navbar-' + this.align;
7126 if (this.labelAlign == 'left' ) {
7127 cfg.cls += ' form-horizontal';
7133 initEvents : function()
7135 this.el.on('submit', this.onSubmit, this);
7136 // this was added as random key presses on the form where triggering form submit.
7137 this.el.on('keypress', function(e) {
7138 if (e.getCharCode() != 13) {
7141 // we might need to allow it for textareas.. and some other items.
7142 // check e.getTarget().
7144 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7148 Roo.log("keypress blocked");
7156 onSubmit : function(e){
7161 * Returns true if client-side validation on the form is successful.
7164 isValid : function(){
7165 var items = this.getItems();
7167 items.each(function(f){
7176 * Returns true if any fields in this form have changed since their original load.
7179 isDirty : function(){
7181 var items = this.getItems();
7182 items.each(function(f){
7192 * Performs a predefined action (submit or load) or custom actions you define on this form.
7193 * @param {String} actionName The name of the action type
7194 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7195 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7196 * accept other config options):
7198 Property Type Description
7199 ---------------- --------------- ----------------------------------------------------------------------------------
7200 url String The url for the action (defaults to the form's url)
7201 method String The form method to use (defaults to the form's method, or POST if not defined)
7202 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7203 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7204 validate the form on the client (defaults to false)
7206 * @return {BasicForm} this
7208 doAction : function(action, options){
7209 if(typeof action == 'string'){
7210 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7212 if(this.fireEvent('beforeaction', this, action) !== false){
7213 this.beforeAction(action);
7214 action.run.defer(100, action);
7220 beforeAction : function(action){
7221 var o = action.options;
7224 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7226 // not really supported yet.. ??
7228 //if(this.waitMsgTarget === true){
7229 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7230 //}else if(this.waitMsgTarget){
7231 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7232 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7234 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7240 afterAction : function(action, success){
7241 this.activeAction = null;
7242 var o = action.options;
7244 //if(this.waitMsgTarget === true){
7246 //}else if(this.waitMsgTarget){
7247 // this.waitMsgTarget.unmask();
7249 // Roo.MessageBox.updateProgress(1);
7250 // Roo.MessageBox.hide();
7257 Roo.callback(o.success, o.scope, [this, action]);
7258 this.fireEvent('actioncomplete', this, action);
7262 // failure condition..
7263 // we have a scenario where updates need confirming.
7264 // eg. if a locking scenario exists..
7265 // we look for { errors : { needs_confirm : true }} in the response.
7267 (typeof(action.result) != 'undefined') &&
7268 (typeof(action.result.errors) != 'undefined') &&
7269 (typeof(action.result.errors.needs_confirm) != 'undefined')
7272 Roo.log("not supported yet");
7275 Roo.MessageBox.confirm(
7276 "Change requires confirmation",
7277 action.result.errorMsg,
7282 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7292 Roo.callback(o.failure, o.scope, [this, action]);
7293 // show an error message if no failed handler is set..
7294 if (!this.hasListener('actionfailed')) {
7295 Roo.log("need to add dialog support");
7297 Roo.MessageBox.alert("Error",
7298 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7299 action.result.errorMsg :
7300 "Saving Failed, please check your entries or try again"
7305 this.fireEvent('actionfailed', this, action);
7310 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7311 * @param {String} id The value to search for
7314 findField : function(id){
7315 var items = this.getItems();
7316 var field = items.get(id);
7318 items.each(function(f){
7319 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7326 return field || null;
7329 * Mark fields in this form invalid in bulk.
7330 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7331 * @return {BasicForm} this
7333 markInvalid : function(errors){
7334 if(errors instanceof Array){
7335 for(var i = 0, len = errors.length; i < len; i++){
7336 var fieldError = errors[i];
7337 var f = this.findField(fieldError.id);
7339 f.markInvalid(fieldError.msg);
7345 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7346 field.markInvalid(errors[id]);
7350 //Roo.each(this.childForms || [], function (f) {
7351 // f.markInvalid(errors);
7358 * Set values for fields in this form in bulk.
7359 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7360 * @return {BasicForm} this
7362 setValues : function(values){
7363 if(values instanceof Array){ // array of objects
7364 for(var i = 0, len = values.length; i < len; i++){
7366 var f = this.findField(v.id);
7368 f.setValue(v.value);
7369 if(this.trackResetOnLoad){
7370 f.originalValue = f.getValue();
7374 }else{ // object hash
7377 if(typeof values[id] != 'function' && (field = this.findField(id))){
7379 if (field.setFromData &&
7381 field.displayField &&
7382 // combos' with local stores can
7383 // be queried via setValue()
7384 // to set their value..
7385 (field.store && !field.store.isLocal)
7389 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7390 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7391 field.setFromData(sd);
7394 field.setValue(values[id]);
7398 if(this.trackResetOnLoad){
7399 field.originalValue = field.getValue();
7405 //Roo.each(this.childForms || [], function (f) {
7406 // f.setValues(values);
7413 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7414 * they are returned as an array.
7415 * @param {Boolean} asString
7418 getValues : function(asString){
7419 //if (this.childForms) {
7420 // copy values from the child forms
7421 // Roo.each(this.childForms, function (f) {
7422 // this.setValues(f.getValues());
7428 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7429 if(asString === true){
7432 return Roo.urlDecode(fs);
7436 * Returns the fields in this form as an object with key/value pairs.
7437 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7440 getFieldValues : function(with_hidden)
7442 var items = this.getItems();
7444 items.each(function(f){
7448 var v = f.getValue();
7449 if (f.inputType =='radio') {
7450 if (typeof(ret[f.getName()]) == 'undefined') {
7451 ret[f.getName()] = ''; // empty..
7454 if (!f.el.dom.checked) {
7462 // not sure if this supported any more..
7463 if ((typeof(v) == 'object') && f.getRawValue) {
7464 v = f.getRawValue() ; // dates..
7466 // combo boxes where name != hiddenName...
7467 if (f.name != f.getName()) {
7468 ret[f.name] = f.getRawValue();
7470 ret[f.getName()] = v;
7477 * Clears all invalid messages in this form.
7478 * @return {BasicForm} this
7480 clearInvalid : function(){
7481 var items = this.getItems();
7483 items.each(function(f){
7494 * @return {BasicForm} this
7497 var items = this.getItems();
7498 items.each(function(f){
7502 Roo.each(this.childForms || [], function (f) {
7509 getItems : function()
7511 var r=new Roo.util.MixedCollection(false, function(o){
7512 return o.id || (o.id = Roo.id());
7514 var iter = function(el) {
7521 Roo.each(el.items,function(e) {
7541 * Ext JS Library 1.1.1
7542 * Copyright(c) 2006-2007, Ext JS, LLC.
7544 * Originally Released Under LGPL - original licence link has changed is not relivant.
7547 * <script type="text/javascript">
7550 * @class Roo.form.VTypes
7551 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7554 Roo.form.VTypes = function(){
7555 // closure these in so they are only created once.
7556 var alpha = /^[a-zA-Z_]+$/;
7557 var alphanum = /^[a-zA-Z0-9_]+$/;
7558 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7559 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7561 // All these messages and functions are configurable
7564 * The function used to validate email addresses
7565 * @param {String} value The email address
7567 'email' : function(v){
7568 return email.test(v);
7571 * The error text to display when the email validation function returns false
7574 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7576 * The keystroke filter mask to be applied on email input
7579 'emailMask' : /[a-z0-9_\.\-@]/i,
7582 * The function used to validate URLs
7583 * @param {String} value The URL
7585 'url' : function(v){
7589 * The error text to display when the url validation function returns false
7592 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7595 * The function used to validate alpha values
7596 * @param {String} value The value
7598 'alpha' : function(v){
7599 return alpha.test(v);
7602 * The error text to display when the alpha validation function returns false
7605 'alphaText' : 'This field should only contain letters and _',
7607 * The keystroke filter mask to be applied on alpha input
7610 'alphaMask' : /[a-z_]/i,
7613 * The function used to validate alphanumeric values
7614 * @param {String} value The value
7616 'alphanum' : function(v){
7617 return alphanum.test(v);
7620 * The error text to display when the alphanumeric validation function returns false
7623 'alphanumText' : 'This field should only contain letters, numbers and _',
7625 * The keystroke filter mask to be applied on alphanumeric input
7628 'alphanumMask' : /[a-z0-9_]/i
7638 * @class Roo.bootstrap.Input
7639 * @extends Roo.bootstrap.Component
7640 * Bootstrap Input class
7641 * @cfg {Boolean} disabled is it disabled
7642 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7643 * @cfg {String} name name of the input
7644 * @cfg {string} fieldLabel - the label associated
7645 * @cfg {string} placeholder - placeholder to put in text.
7646 * @cfg {string} before - input group add on before
7647 * @cfg {string} after - input group add on after
7648 * @cfg {string} size - (lg|sm) or leave empty..
7649 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7650 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7651 * @cfg {Number} md colspan out of 12 for computer-sized screens
7652 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7653 * @cfg {string} value default value of the input
7654 * @cfg {Number} labelWidth set the width of label (0-12)
7655 * @cfg {String} labelAlign (top|left)
7656 * @cfg {Boolean} readOnly Specifies that the field should be read-only
7657 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7659 * @cfg {String} align (left|center|right) Default left
7660 * @cfg {Boolean} forceFeedback (true|false) Default false
7666 * Create a new Input
7667 * @param {Object} config The config object
7670 Roo.bootstrap.Input = function(config){
7671 Roo.bootstrap.Input.superclass.constructor.call(this, config);
7676 * Fires when this field receives input focus.
7677 * @param {Roo.form.Field} this
7682 * Fires when this field loses input focus.
7683 * @param {Roo.form.Field} this
7688 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
7689 * {@link Roo.EventObject#getKey} to determine which key was pressed.
7690 * @param {Roo.form.Field} this
7691 * @param {Roo.EventObject} e The event object
7696 * Fires just before the field blurs if the field value has changed.
7697 * @param {Roo.form.Field} this
7698 * @param {Mixed} newValue The new value
7699 * @param {Mixed} oldValue The original value
7704 * Fires after the field has been marked as invalid.
7705 * @param {Roo.form.Field} this
7706 * @param {String} msg The validation message
7711 * Fires after the field has been validated with no errors.
7712 * @param {Roo.form.Field} this
7717 * Fires after the key up
7718 * @param {Roo.form.Field} this
7719 * @param {Roo.EventObject} e The event Object
7725 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
7727 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7728 automatic validation (defaults to "keyup").
7730 validationEvent : "keyup",
7732 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7734 validateOnBlur : true,
7736 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7738 validationDelay : 250,
7740 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7742 focusClass : "x-form-focus", // not needed???
7746 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7748 invalidClass : "has-warning",
7751 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7753 validClass : "has-success",
7756 * @cfg {Boolean} hasFeedback (true|false) default true
7761 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7763 invalidFeedbackClass : "glyphicon-warning-sign",
7766 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7768 validFeedbackClass : "glyphicon-ok",
7771 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7773 selectOnFocus : false,
7776 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7780 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7785 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7787 disableKeyFilter : false,
7790 * @cfg {Boolean} disabled True to disable the field (defaults to false).
7794 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7798 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7800 blankText : "This field is required",
7803 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7807 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7809 maxLength : Number.MAX_VALUE,
7811 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7813 minLengthText : "The minimum length for this field is {0}",
7815 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7817 maxLengthText : "The maximum length for this field is {0}",
7821 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7822 * If available, this function will be called only after the basic validators all return true, and will be passed the
7823 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7827 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7828 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7829 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
7833 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7837 autocomplete: false,
7856 formatedValue : false,
7857 forceFeedback : false,
7859 parentLabelAlign : function()
7862 while (parent.parent()) {
7863 parent = parent.parent();
7864 if (typeof(parent.labelAlign) !='undefined') {
7865 return parent.labelAlign;
7872 getAutoCreate : function(){
7874 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7880 if(this.inputType != 'hidden'){
7881 cfg.cls = 'form-group' //input-group
7887 type : this.inputType,
7889 cls : 'form-control',
7890 placeholder : this.placeholder || '',
7891 autocomplete : this.autocomplete || 'new-password'
7896 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7899 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7900 input.maxLength = this.maxLength;
7903 if (this.disabled) {
7904 input.disabled=true;
7907 if (this.readOnly) {
7908 input.readonly=true;
7912 input.name = this.name;
7915 input.cls += ' input-' + this.size;
7918 ['xs','sm','md','lg'].map(function(size){
7919 if (settings[size]) {
7920 cfg.cls += ' col-' + size + '-' + settings[size];
7924 var inputblock = input;
7928 cls: 'glyphicon form-control-feedback'
7931 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7934 cls : 'has-feedback',
7942 if (this.before || this.after) {
7945 cls : 'input-group',
7949 if (this.before && typeof(this.before) == 'string') {
7951 inputblock.cn.push({
7953 cls : 'roo-input-before input-group-addon',
7957 if (this.before && typeof(this.before) == 'object') {
7958 this.before = Roo.factory(this.before);
7959 Roo.log(this.before);
7960 inputblock.cn.push({
7962 cls : 'roo-input-before input-group-' +
7963 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7967 inputblock.cn.push(input);
7969 if (this.after && typeof(this.after) == 'string') {
7970 inputblock.cn.push({
7972 cls : 'roo-input-after input-group-addon',
7976 if (this.after && typeof(this.after) == 'object') {
7977 this.after = Roo.factory(this.after);
7978 Roo.log(this.after);
7979 inputblock.cn.push({
7981 cls : 'roo-input-after input-group-' +
7982 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7986 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7987 inputblock.cls += ' has-feedback';
7988 inputblock.cn.push(feedback);
7992 if (align ==='left' && this.fieldLabel.length) {
7993 Roo.log("left and has label");
7999 cls : 'control-label col-sm-' + this.labelWidth,
8000 html : this.fieldLabel
8004 cls : "col-sm-" + (12 - this.labelWidth),
8011 } else if ( this.fieldLabel.length) {
8017 //cls : 'input-group-addon',
8018 html : this.fieldLabel
8028 Roo.log(" no label && no align");
8037 Roo.log('input-parentType: ' + this.parentType);
8039 if (this.parentType === 'Navbar' && this.parent().bar) {
8040 cfg.cls += ' navbar-form';
8048 * return the real input element.
8050 inputEl: function ()
8052 return this.el.select('input.form-control',true).first();
8055 tooltipEl : function()
8057 return this.inputEl();
8060 setDisabled : function(v)
8062 var i = this.inputEl().dom;
8064 i.removeAttribute('disabled');
8068 i.setAttribute('disabled','true');
8070 initEvents : function()
8073 this.inputEl().on("keydown" , this.fireKey, this);
8074 this.inputEl().on("focus", this.onFocus, this);
8075 this.inputEl().on("blur", this.onBlur, this);
8077 this.inputEl().relayEvent('keyup', this);
8079 // reference to original value for reset
8080 this.originalValue = this.getValue();
8081 //Roo.form.TextField.superclass.initEvents.call(this);
8082 if(this.validationEvent == 'keyup'){
8083 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8084 this.inputEl().on('keyup', this.filterValidation, this);
8086 else if(this.validationEvent !== false){
8087 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8090 if(this.selectOnFocus){
8091 this.on("focus", this.preFocus, this);
8094 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8095 this.inputEl().on("keypress", this.filterKeys, this);
8098 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8099 this.el.on("click", this.autoSize, this);
8102 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8103 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8106 if (typeof(this.before) == 'object') {
8107 this.before.render(this.el.select('.roo-input-before',true).first());
8109 if (typeof(this.after) == 'object') {
8110 this.after.render(this.el.select('.roo-input-after',true).first());
8115 filterValidation : function(e){
8116 if(!e.isNavKeyPress()){
8117 this.validationTask.delay(this.validationDelay);
8121 * Validates the field value
8122 * @return {Boolean} True if the value is valid, else false
8124 validate : function(){
8125 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8126 if(this.disabled || this.validateValue(this.getRawValue())){
8137 * Validates a value according to the field's validation rules and marks the field as invalid
8138 * if the validation fails
8139 * @param {Mixed} value The value to validate
8140 * @return {Boolean} True if the value is valid, else false
8142 validateValue : function(value){
8143 if(value.length < 1) { // if it's blank
8144 if(this.allowBlank){
8150 if(value.length < this.minLength){
8153 if(value.length > this.maxLength){
8157 var vt = Roo.form.VTypes;
8158 if(!vt[this.vtype](value, this)){
8162 if(typeof this.validator == "function"){
8163 var msg = this.validator(value);
8169 if(this.regex && !this.regex.test(value)){
8179 fireKey : function(e){
8180 //Roo.log('field ' + e.getKey());
8181 if(e.isNavKeyPress()){
8182 this.fireEvent("specialkey", this, e);
8185 focus : function (selectText){
8187 this.inputEl().focus();
8188 if(selectText === true){
8189 this.inputEl().dom.select();
8195 onFocus : function(){
8196 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8197 // this.el.addClass(this.focusClass);
8200 this.hasFocus = true;
8201 this.startValue = this.getValue();
8202 this.fireEvent("focus", this);
8206 beforeBlur : Roo.emptyFn,
8210 onBlur : function(){
8212 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8213 //this.el.removeClass(this.focusClass);
8215 this.hasFocus = false;
8216 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8219 var v = this.getValue();
8220 if(String(v) !== String(this.startValue)){
8221 this.fireEvent('change', this, v, this.startValue);
8223 this.fireEvent("blur", this);
8227 * Resets the current field value to the originally loaded value and clears any validation messages
8230 this.setValue(this.originalValue);
8234 * Returns the name of the field
8235 * @return {Mixed} name The name field
8237 getName: function(){
8241 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8242 * @return {Mixed} value The field value
8244 getValue : function(){
8246 var v = this.inputEl().getValue();
8251 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8252 * @return {Mixed} value The field value
8254 getRawValue : function(){
8255 var v = this.inputEl().getValue();
8261 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8262 * @param {Mixed} value The value to set
8264 setRawValue : function(v){
8265 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8268 selectText : function(start, end){
8269 var v = this.getRawValue();
8271 start = start === undefined ? 0 : start;
8272 end = end === undefined ? v.length : end;
8273 var d = this.inputEl().dom;
8274 if(d.setSelectionRange){
8275 d.setSelectionRange(start, end);
8276 }else if(d.createTextRange){
8277 var range = d.createTextRange();
8278 range.moveStart("character", start);
8279 range.moveEnd("character", v.length-end);
8286 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8287 * @param {Mixed} value The value to set
8289 setValue : function(v){
8292 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8298 processValue : function(value){
8299 if(this.stripCharsRe){
8300 var newValue = value.replace(this.stripCharsRe, '');
8301 if(newValue !== value){
8302 this.setRawValue(newValue);
8309 preFocus : function(){
8311 if(this.selectOnFocus){
8312 this.inputEl().dom.select();
8315 filterKeys : function(e){
8317 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8320 var c = e.getCharCode(), cc = String.fromCharCode(c);
8321 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8324 if(!this.maskRe.test(cc)){
8329 * Clear any invalid styles/messages for this field
8331 clearInvalid : function(){
8333 if(!this.el || this.preventMark){ // not rendered
8336 this.el.removeClass(this.invalidClass);
8338 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8340 var feedback = this.el.select('.form-control-feedback', true).first();
8343 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8348 this.fireEvent('valid', this);
8352 * Mark this field as valid
8354 markValid : function()
8356 if(!this.el || this.preventMark){ // not rendered
8360 this.el.removeClass([this.invalidClass, this.validClass]);
8362 var feedback = this.el.select('.form-control-feedback', true).first();
8365 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8368 if(this.disabled || this.allowBlank){
8372 var formGroup = this.el.findParent('.form-group', false, true);
8376 var label = formGroup.select('label', true).first();
8377 var icon = formGroup.select('i.fa-star', true).first();
8384 this.el.addClass(this.validClass);
8386 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8388 var feedback = this.el.select('.form-control-feedback', true).first();
8391 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8392 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8397 this.fireEvent('valid', this);
8401 * Mark this field as invalid
8402 * @param {String} msg The validation message
8404 markInvalid : function(msg)
8406 if(!this.el || this.preventMark){ // not rendered
8410 this.el.removeClass([this.invalidClass, this.validClass]);
8412 var feedback = this.el.select('.form-control-feedback', true).first();
8415 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8418 if(this.disabled || this.allowBlank){
8422 var formGroup = this.el.findParent('.form-group', false, true);
8425 var label = formGroup.select('label', true).first();
8426 var icon = formGroup.select('i.fa-star', true).first();
8428 if(!this.getValue().length && label && !icon){
8429 this.el.findParent('.form-group', false, true).createChild({
8431 cls : 'text-danger fa fa-lg fa-star',
8432 tooltip : 'This field is required',
8433 style : 'margin-right:5px;'
8439 this.el.addClass(this.invalidClass);
8441 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8443 var feedback = this.el.select('.form-control-feedback', true).first();
8446 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8448 if(this.getValue().length || this.forceFeedback){
8449 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8456 this.fireEvent('invalid', this, msg);
8459 SafariOnKeyDown : function(event)
8461 // this is a workaround for a password hang bug on chrome/ webkit.
8463 var isSelectAll = false;
8465 if(this.inputEl().dom.selectionEnd > 0){
8466 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8468 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8469 event.preventDefault();
8474 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8476 event.preventDefault();
8477 // this is very hacky as keydown always get's upper case.
8479 var cc = String.fromCharCode(event.getCharCode());
8480 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8484 adjustWidth : function(tag, w){
8485 tag = tag.toLowerCase();
8486 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8487 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8491 if(tag == 'textarea'){
8494 }else if(Roo.isOpera){
8498 if(tag == 'textarea'){
8517 * @class Roo.bootstrap.TextArea
8518 * @extends Roo.bootstrap.Input
8519 * Bootstrap TextArea class
8520 * @cfg {Number} cols Specifies the visible width of a text area
8521 * @cfg {Number} rows Specifies the visible number of lines in a text area
8522 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8523 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8524 * @cfg {string} html text
8527 * Create a new TextArea
8528 * @param {Object} config The config object
8531 Roo.bootstrap.TextArea = function(config){
8532 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8536 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8546 getAutoCreate : function(){
8548 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8559 value : this.value || '',
8560 html: this.html || '',
8561 cls : 'form-control',
8562 placeholder : this.placeholder || ''
8566 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8567 input.maxLength = this.maxLength;
8571 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8575 input.cols = this.cols;
8578 if (this.readOnly) {
8579 input.readonly = true;
8583 input.name = this.name;
8587 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8591 ['xs','sm','md','lg'].map(function(size){
8592 if (settings[size]) {
8593 cfg.cls += ' col-' + size + '-' + settings[size];
8597 var inputblock = input;
8599 if(this.hasFeedback && !this.allowBlank){
8603 cls: 'glyphicon form-control-feedback'
8607 cls : 'has-feedback',
8616 if (this.before || this.after) {
8619 cls : 'input-group',
8623 inputblock.cn.push({
8625 cls : 'input-group-addon',
8630 inputblock.cn.push(input);
8632 if(this.hasFeedback && !this.allowBlank){
8633 inputblock.cls += ' has-feedback';
8634 inputblock.cn.push(feedback);
8638 inputblock.cn.push({
8640 cls : 'input-group-addon',
8647 if (align ==='left' && this.fieldLabel.length) {
8648 Roo.log("left and has label");
8654 cls : 'control-label col-sm-' + this.labelWidth,
8655 html : this.fieldLabel
8659 cls : "col-sm-" + (12 - this.labelWidth),
8666 } else if ( this.fieldLabel.length) {
8672 //cls : 'input-group-addon',
8673 html : this.fieldLabel
8683 Roo.log(" no label && no align");
8693 if (this.disabled) {
8694 input.disabled=true;
8701 * return the real textarea element.
8703 inputEl: function ()
8705 return this.el.select('textarea.form-control',true).first();
8713 * trigger field - base class for combo..
8718 * @class Roo.bootstrap.TriggerField
8719 * @extends Roo.bootstrap.Input
8720 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8721 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8722 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8723 * for which you can provide a custom implementation. For example:
8725 var trigger = new Roo.bootstrap.TriggerField();
8726 trigger.onTriggerClick = myTriggerFn;
8727 trigger.applyTo('my-field');
8730 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8731 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8732 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
8733 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8734 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8737 * Create a new TriggerField.
8738 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8739 * to the base TextField)
8741 Roo.bootstrap.TriggerField = function(config){
8742 this.mimicing = false;
8743 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8746 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
8748 * @cfg {String} triggerClass A CSS class to apply to the trigger
8751 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8756 * @cfg {Boolean} removable (true|false) special filter default false
8760 /** @cfg {Boolean} grow @hide */
8761 /** @cfg {Number} growMin @hide */
8762 /** @cfg {Number} growMax @hide */
8768 autoSize: Roo.emptyFn,
8775 actionMode : 'wrap',
8780 getAutoCreate : function(){
8782 var align = this.labelAlign || this.parentLabelAlign();
8787 cls: 'form-group' //input-group
8794 type : this.inputType,
8795 cls : 'form-control',
8796 autocomplete: 'new-password',
8797 placeholder : this.placeholder || ''
8801 input.name = this.name;
8804 input.cls += ' input-' + this.size;
8807 if (this.disabled) {
8808 input.disabled=true;
8811 var inputblock = input;
8813 if(this.hasFeedback && !this.allowBlank){
8817 cls: 'glyphicon form-control-feedback'
8820 if(this.removable && !this.editable && !this.tickable){
8822 cls : 'has-feedback',
8828 cls : 'roo-combo-removable-btn close'
8835 cls : 'has-feedback',
8844 if(this.removable && !this.editable && !this.tickable){
8846 cls : 'roo-removable',
8852 cls : 'roo-combo-removable-btn close'
8859 if (this.before || this.after) {
8862 cls : 'input-group',
8866 inputblock.cn.push({
8868 cls : 'input-group-addon',
8873 inputblock.cn.push(input);
8875 if(this.hasFeedback && !this.allowBlank){
8876 inputblock.cls += ' has-feedback';
8877 inputblock.cn.push(feedback);
8881 inputblock.cn.push({
8883 cls : 'input-group-addon',
8896 cls: 'form-hidden-field'
8904 Roo.log('multiple');
8912 cls: 'form-hidden-field'
8916 cls: 'select2-choices',
8920 cls: 'select2-search-field',
8933 cls: 'select2-container input-group',
8938 // cls: 'typeahead typeahead-long dropdown-menu',
8939 // style: 'display:none'
8944 if(!this.multiple && this.showToggleBtn){
8950 if (this.caret != false) {
8953 cls: 'fa fa-' + this.caret
8960 cls : 'input-group-addon btn dropdown-toggle',
8965 cls: 'combobox-clear',
8979 combobox.cls += ' select2-container-multi';
8982 if (align ==='left' && this.fieldLabel.length) {
8984 Roo.log("left and has label");
8990 cls : 'control-label col-sm-' + this.labelWidth,
8991 html : this.fieldLabel
8995 cls : "col-sm-" + (12 - this.labelWidth),
9002 } else if ( this.fieldLabel.length) {
9008 //cls : 'input-group-addon',
9009 html : this.fieldLabel
9019 Roo.log(" no label && no align");
9026 ['xs','sm','md','lg'].map(function(size){
9027 if (settings[size]) {
9028 cfg.cls += ' col-' + size + '-' + settings[size];
9039 onResize : function(w, h){
9040 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9041 // if(typeof w == 'number'){
9042 // var x = w - this.trigger.getWidth();
9043 // this.inputEl().setWidth(this.adjustWidth('input', x));
9044 // this.trigger.setStyle('left', x+'px');
9049 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9052 getResizeEl : function(){
9053 return this.inputEl();
9057 getPositionEl : function(){
9058 return this.inputEl();
9062 alignErrorIcon : function(){
9063 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9067 initEvents : function(){
9071 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9072 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9073 if(!this.multiple && this.showToggleBtn){
9074 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9075 if(this.hideTrigger){
9076 this.trigger.setDisplayed(false);
9078 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9082 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9085 if(this.removable && !this.editable && !this.tickable){
9086 var close = this.closeTriggerEl();
9089 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9090 close.on('click', this.removeBtnClick, this, close);
9094 //this.trigger.addClassOnOver('x-form-trigger-over');
9095 //this.trigger.addClassOnClick('x-form-trigger-click');
9098 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9102 closeTriggerEl : function()
9104 var close = this.el.select('.roo-combo-removable-btn', true).first();
9105 return close ? close : false;
9108 removeBtnClick : function(e, h, el)
9112 if(this.fireEvent("remove", this) !== false){
9117 createList : function()
9119 this.list = Roo.get(document.body).createChild({
9121 cls: 'typeahead typeahead-long dropdown-menu',
9122 style: 'display:none'
9125 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9130 initTrigger : function(){
9135 onDestroy : function(){
9137 this.trigger.removeAllListeners();
9138 // this.trigger.remove();
9141 // this.wrap.remove();
9143 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9147 onFocus : function(){
9148 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9151 this.wrap.addClass('x-trigger-wrap-focus');
9152 this.mimicing = true;
9153 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9154 if(this.monitorTab){
9155 this.el.on("keydown", this.checkTab, this);
9162 checkTab : function(e){
9163 if(e.getKey() == e.TAB){
9169 onBlur : function(){
9174 mimicBlur : function(e, t){
9176 if(!this.wrap.contains(t) && this.validateBlur()){
9183 triggerBlur : function(){
9184 this.mimicing = false;
9185 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9186 if(this.monitorTab){
9187 this.el.un("keydown", this.checkTab, this);
9189 //this.wrap.removeClass('x-trigger-wrap-focus');
9190 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9194 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9195 validateBlur : function(e, t){
9200 onDisable : function(){
9201 this.inputEl().dom.disabled = true;
9202 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9204 // this.wrap.addClass('x-item-disabled');
9209 onEnable : function(){
9210 this.inputEl().dom.disabled = false;
9211 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9213 // this.el.removeClass('x-item-disabled');
9218 onShow : function(){
9219 var ae = this.getActionEl();
9222 ae.dom.style.display = '';
9223 ae.dom.style.visibility = 'visible';
9229 onHide : function(){
9230 var ae = this.getActionEl();
9231 ae.dom.style.display = 'none';
9235 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9236 * by an implementing function.
9238 * @param {EventObject} e
9240 onTriggerClick : Roo.emptyFn
9244 * Ext JS Library 1.1.1
9245 * Copyright(c) 2006-2007, Ext JS, LLC.
9247 * Originally Released Under LGPL - original licence link has changed is not relivant.
9250 * <script type="text/javascript">
9255 * @class Roo.data.SortTypes
9257 * Defines the default sorting (casting?) comparison functions used when sorting data.
9259 Roo.data.SortTypes = {
9261 * Default sort that does nothing
9262 * @param {Mixed} s The value being converted
9263 * @return {Mixed} The comparison value
9270 * The regular expression used to strip tags
9274 stripTagsRE : /<\/?[^>]+>/gi,
9277 * Strips all HTML tags to sort on text only
9278 * @param {Mixed} s The value being converted
9279 * @return {String} The comparison value
9281 asText : function(s){
9282 return String(s).replace(this.stripTagsRE, "");
9286 * Strips all HTML tags to sort on text only - Case insensitive
9287 * @param {Mixed} s The value being converted
9288 * @return {String} The comparison value
9290 asUCText : function(s){
9291 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9295 * Case insensitive string
9296 * @param {Mixed} s The value being converted
9297 * @return {String} The comparison value
9299 asUCString : function(s) {
9300 return String(s).toUpperCase();
9305 * @param {Mixed} s The value being converted
9306 * @return {Number} The comparison value
9308 asDate : function(s) {
9312 if(s instanceof Date){
9315 return Date.parse(String(s));
9320 * @param {Mixed} s The value being converted
9321 * @return {Float} The comparison value
9323 asFloat : function(s) {
9324 var val = parseFloat(String(s).replace(/,/g, ""));
9325 if(isNaN(val)) val = 0;
9331 * @param {Mixed} s The value being converted
9332 * @return {Number} The comparison value
9334 asInt : function(s) {
9335 var val = parseInt(String(s).replace(/,/g, ""));
9336 if(isNaN(val)) val = 0;
9341 * Ext JS Library 1.1.1
9342 * Copyright(c) 2006-2007, Ext JS, LLC.
9344 * Originally Released Under LGPL - original licence link has changed is not relivant.
9347 * <script type="text/javascript">
9351 * @class Roo.data.Record
9352 * Instances of this class encapsulate both record <em>definition</em> information, and record
9353 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9354 * to access Records cached in an {@link Roo.data.Store} object.<br>
9356 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9357 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9360 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9362 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9363 * {@link #create}. The parameters are the same.
9364 * @param {Array} data An associative Array of data values keyed by the field name.
9365 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9366 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9367 * not specified an integer id is generated.
9369 Roo.data.Record = function(data, id){
9370 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9375 * Generate a constructor for a specific record layout.
9376 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9377 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9378 * Each field definition object may contain the following properties: <ul>
9379 * <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,
9380 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9381 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9382 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9383 * is being used, then this is a string containing the javascript expression to reference the data relative to
9384 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9385 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9386 * this may be omitted.</p></li>
9387 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9388 * <ul><li>auto (Default, implies no conversion)</li>
9393 * <li>date</li></ul></p></li>
9394 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9395 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9396 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9397 * by the Reader into an object that will be stored in the Record. It is passed the
9398 * following parameters:<ul>
9399 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9401 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9403 * <br>usage:<br><pre><code>
9404 var TopicRecord = Roo.data.Record.create(
9405 {name: 'title', mapping: 'topic_title'},
9406 {name: 'author', mapping: 'username'},
9407 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9408 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9409 {name: 'lastPoster', mapping: 'user2'},
9410 {name: 'excerpt', mapping: 'post_text'}
9413 var myNewRecord = new TopicRecord({
9414 title: 'Do my job please',
9417 lastPost: new Date(),
9418 lastPoster: 'Animal',
9419 excerpt: 'No way dude!'
9421 myStore.add(myNewRecord);
9426 Roo.data.Record.create = function(o){
9428 f.superclass.constructor.apply(this, arguments);
9430 Roo.extend(f, Roo.data.Record);
9431 var p = f.prototype;
9432 p.fields = new Roo.util.MixedCollection(false, function(field){
9435 for(var i = 0, len = o.length; i < len; i++){
9436 p.fields.add(new Roo.data.Field(o[i]));
9438 f.getField = function(name){
9439 return p.fields.get(name);
9444 Roo.data.Record.AUTO_ID = 1000;
9445 Roo.data.Record.EDIT = 'edit';
9446 Roo.data.Record.REJECT = 'reject';
9447 Roo.data.Record.COMMIT = 'commit';
9449 Roo.data.Record.prototype = {
9451 * Readonly flag - true if this record has been modified.
9460 join : function(store){
9465 * Set the named field to the specified value.
9466 * @param {String} name The name of the field to set.
9467 * @param {Object} value The value to set the field to.
9469 set : function(name, value){
9470 if(this.data[name] == value){
9477 if(typeof this.modified[name] == 'undefined'){
9478 this.modified[name] = this.data[name];
9480 this.data[name] = value;
9481 if(!this.editing && this.store){
9482 this.store.afterEdit(this);
9487 * Get the value of the named field.
9488 * @param {String} name The name of the field to get the value of.
9489 * @return {Object} The value of the field.
9491 get : function(name){
9492 return this.data[name];
9496 beginEdit : function(){
9497 this.editing = true;
9502 cancelEdit : function(){
9503 this.editing = false;
9504 delete this.modified;
9508 endEdit : function(){
9509 this.editing = false;
9510 if(this.dirty && this.store){
9511 this.store.afterEdit(this);
9516 * Usually called by the {@link Roo.data.Store} which owns the Record.
9517 * Rejects all changes made to the Record since either creation, or the last commit operation.
9518 * Modified fields are reverted to their original values.
9520 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9521 * of reject operations.
9523 reject : function(){
9524 var m = this.modified;
9526 if(typeof m[n] != "function"){
9527 this.data[n] = m[n];
9531 delete this.modified;
9532 this.editing = false;
9534 this.store.afterReject(this);
9539 * Usually called by the {@link Roo.data.Store} which owns the Record.
9540 * Commits all changes made to the Record since either creation, or the last commit operation.
9542 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9543 * of commit operations.
9545 commit : function(){
9547 delete this.modified;
9548 this.editing = false;
9550 this.store.afterCommit(this);
9555 hasError : function(){
9556 return this.error != null;
9560 clearError : function(){
9565 * Creates a copy of this record.
9566 * @param {String} id (optional) A new record id if you don't want to use this record's id
9569 copy : function(newId) {
9570 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9574 * Ext JS Library 1.1.1
9575 * Copyright(c) 2006-2007, Ext JS, LLC.
9577 * Originally Released Under LGPL - original licence link has changed is not relivant.
9580 * <script type="text/javascript">
9586 * @class Roo.data.Store
9587 * @extends Roo.util.Observable
9588 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9589 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9591 * 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
9592 * has no knowledge of the format of the data returned by the Proxy.<br>
9594 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9595 * instances from the data object. These records are cached and made available through accessor functions.
9597 * Creates a new Store.
9598 * @param {Object} config A config object containing the objects needed for the Store to access data,
9599 * and read the data into Records.
9601 Roo.data.Store = function(config){
9602 this.data = new Roo.util.MixedCollection(false);
9603 this.data.getKey = function(o){
9606 this.baseParams = {};
9613 "multisort" : "_multisort"
9616 if(config && config.data){
9617 this.inlineData = config.data;
9621 Roo.apply(this, config);
9623 if(this.reader){ // reader passed
9624 this.reader = Roo.factory(this.reader, Roo.data);
9625 this.reader.xmodule = this.xmodule || false;
9626 if(!this.recordType){
9627 this.recordType = this.reader.recordType;
9629 if(this.reader.onMetaChange){
9630 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9634 if(this.recordType){
9635 this.fields = this.recordType.prototype.fields;
9641 * @event datachanged
9642 * Fires when the data cache has changed, and a widget which is using this Store
9643 * as a Record cache should refresh its view.
9644 * @param {Store} this
9649 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9650 * @param {Store} this
9651 * @param {Object} meta The JSON metadata
9656 * Fires when Records have been added to the Store
9657 * @param {Store} this
9658 * @param {Roo.data.Record[]} records The array of Records added
9659 * @param {Number} index The index at which the record(s) were added
9664 * Fires when a Record has been removed from the Store
9665 * @param {Store} this
9666 * @param {Roo.data.Record} record The Record that was removed
9667 * @param {Number} index The index at which the record was removed
9672 * Fires when a Record has been updated
9673 * @param {Store} this
9674 * @param {Roo.data.Record} record The Record that was updated
9675 * @param {String} operation The update operation being performed. Value may be one of:
9677 Roo.data.Record.EDIT
9678 Roo.data.Record.REJECT
9679 Roo.data.Record.COMMIT
9685 * Fires when the data cache has been cleared.
9686 * @param {Store} this
9691 * Fires before a request is made for a new data object. If the beforeload handler returns false
9692 * the load action will be canceled.
9693 * @param {Store} this
9694 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9698 * @event beforeloadadd
9699 * Fires after a new set of Records has been loaded.
9700 * @param {Store} this
9701 * @param {Roo.data.Record[]} records The Records that were loaded
9702 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9704 beforeloadadd : true,
9707 * Fires after a new set of Records has been loaded, before they are added to the store.
9708 * @param {Store} this
9709 * @param {Roo.data.Record[]} records The Records that were loaded
9710 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9711 * @params {Object} return from reader
9715 * @event loadexception
9716 * Fires if an exception occurs in the Proxy during loading.
9717 * Called with the signature of the Proxy's "loadexception" event.
9718 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9721 * @param {Object} return from JsonData.reader() - success, totalRecords, records
9722 * @param {Object} load options
9723 * @param {Object} jsonData from your request (normally this contains the Exception)
9725 loadexception : true
9729 this.proxy = Roo.factory(this.proxy, Roo.data);
9730 this.proxy.xmodule = this.xmodule || false;
9731 this.relayEvents(this.proxy, ["loadexception"]);
9733 this.sortToggle = {};
9734 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9736 Roo.data.Store.superclass.constructor.call(this);
9738 if(this.inlineData){
9739 this.loadData(this.inlineData);
9740 delete this.inlineData;
9744 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9746 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
9747 * without a remote query - used by combo/forms at present.
9751 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9754 * @cfg {Array} data Inline data to be loaded when the store is initialized.
9757 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9758 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9761 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9762 * on any HTTP request
9765 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9768 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9772 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9773 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9778 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9779 * loaded or when a record is removed. (defaults to false).
9781 pruneModifiedRecords : false,
9787 * Add Records to the Store and fires the add event.
9788 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9790 add : function(records){
9791 records = [].concat(records);
9792 for(var i = 0, len = records.length; i < len; i++){
9793 records[i].join(this);
9795 var index = this.data.length;
9796 this.data.addAll(records);
9797 this.fireEvent("add", this, records, index);
9801 * Remove a Record from the Store and fires the remove event.
9802 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9804 remove : function(record){
9805 var index = this.data.indexOf(record);
9806 this.data.removeAt(index);
9807 if(this.pruneModifiedRecords){
9808 this.modified.remove(record);
9810 this.fireEvent("remove", this, record, index);
9814 * Remove all Records from the Store and fires the clear event.
9816 removeAll : function(){
9818 if(this.pruneModifiedRecords){
9821 this.fireEvent("clear", this);
9825 * Inserts Records to the Store at the given index and fires the add event.
9826 * @param {Number} index The start index at which to insert the passed Records.
9827 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9829 insert : function(index, records){
9830 records = [].concat(records);
9831 for(var i = 0, len = records.length; i < len; i++){
9832 this.data.insert(index, records[i]);
9833 records[i].join(this);
9835 this.fireEvent("add", this, records, index);
9839 * Get the index within the cache of the passed Record.
9840 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9841 * @return {Number} The index of the passed Record. Returns -1 if not found.
9843 indexOf : function(record){
9844 return this.data.indexOf(record);
9848 * Get the index within the cache of the Record with the passed id.
9849 * @param {String} id The id of the Record to find.
9850 * @return {Number} The index of the Record. Returns -1 if not found.
9852 indexOfId : function(id){
9853 return this.data.indexOfKey(id);
9857 * Get the Record with the specified id.
9858 * @param {String} id The id of the Record to find.
9859 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9861 getById : function(id){
9862 return this.data.key(id);
9866 * Get the Record at the specified index.
9867 * @param {Number} index The index of the Record to find.
9868 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9870 getAt : function(index){
9871 return this.data.itemAt(index);
9875 * Returns a range of Records between specified indices.
9876 * @param {Number} startIndex (optional) The starting index (defaults to 0)
9877 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9878 * @return {Roo.data.Record[]} An array of Records
9880 getRange : function(start, end){
9881 return this.data.getRange(start, end);
9885 storeOptions : function(o){
9886 o = Roo.apply({}, o);
9889 this.lastOptions = o;
9893 * Loads the Record cache from the configured Proxy using the configured Reader.
9895 * If using remote paging, then the first load call must specify the <em>start</em>
9896 * and <em>limit</em> properties in the options.params property to establish the initial
9897 * position within the dataset, and the number of Records to cache on each read from the Proxy.
9899 * <strong>It is important to note that for remote data sources, loading is asynchronous,
9900 * and this call will return before the new data has been loaded. Perform any post-processing
9901 * in a callback function, or in a "load" event handler.</strong>
9903 * @param {Object} options An object containing properties which control loading options:<ul>
9904 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9905 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9906 * passed the following arguments:<ul>
9907 * <li>r : Roo.data.Record[]</li>
9908 * <li>options: Options object from the load call</li>
9909 * <li>success: Boolean success indicator</li></ul></li>
9910 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9911 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9914 load : function(options){
9915 options = options || {};
9916 if(this.fireEvent("beforeload", this, options) !== false){
9917 this.storeOptions(options);
9918 var p = Roo.apply(options.params || {}, this.baseParams);
9919 // if meta was not loaded from remote source.. try requesting it.
9920 if (!this.reader.metaFromRemote) {
9923 if(this.sortInfo && this.remoteSort){
9924 var pn = this.paramNames;
9925 p[pn["sort"]] = this.sortInfo.field;
9926 p[pn["dir"]] = this.sortInfo.direction;
9928 if (this.multiSort) {
9929 var pn = this.paramNames;
9930 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9933 this.proxy.load(p, this.reader, this.loadRecords, this, options);
9938 * Reloads the Record cache from the configured Proxy using the configured Reader and
9939 * the options from the last load operation performed.
9940 * @param {Object} options (optional) An object containing properties which may override the options
9941 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9942 * the most recently used options are reused).
9944 reload : function(options){
9945 this.load(Roo.applyIf(options||{}, this.lastOptions));
9949 // Called as a callback by the Reader during a load operation.
9950 loadRecords : function(o, options, success){
9951 if(!o || success === false){
9952 if(success !== false){
9953 this.fireEvent("load", this, [], options, o);
9955 if(options.callback){
9956 options.callback.call(options.scope || this, [], options, false);
9960 // if data returned failure - throw an exception.
9961 if (o.success === false) {
9962 // show a message if no listener is registered.
9963 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9964 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9966 // loadmask wil be hooked into this..
9967 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9970 var r = o.records, t = o.totalRecords || r.length;
9972 this.fireEvent("beforeloadadd", this, r, options, o);
9974 if(!options || options.add !== true){
9975 if(this.pruneModifiedRecords){
9978 for(var i = 0, len = r.length; i < len; i++){
9982 this.data = this.snapshot;
9983 delete this.snapshot;
9986 this.data.addAll(r);
9987 this.totalLength = t;
9989 this.fireEvent("datachanged", this);
9991 this.totalLength = Math.max(t, this.data.length+r.length);
9994 this.fireEvent("load", this, r, options, o);
9995 if(options.callback){
9996 options.callback.call(options.scope || this, r, options, true);
10002 * Loads data from a passed data block. A Reader which understands the format of the data
10003 * must have been configured in the constructor.
10004 * @param {Object} data The data block from which to read the Records. The format of the data expected
10005 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10006 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10008 loadData : function(o, append){
10009 var r = this.reader.readRecords(o);
10010 this.loadRecords(r, {add: append}, true);
10014 * Gets the number of cached records.
10016 * <em>If using paging, this may not be the total size of the dataset. If the data object
10017 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10018 * the data set size</em>
10020 getCount : function(){
10021 return this.data.length || 0;
10025 * Gets the total number of records in the dataset as returned by the server.
10027 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10028 * the dataset size</em>
10030 getTotalCount : function(){
10031 return this.totalLength || 0;
10035 * Returns the sort state of the Store as an object with two properties:
10037 field {String} The name of the field by which the Records are sorted
10038 direction {String} The sort order, "ASC" or "DESC"
10041 getSortState : function(){
10042 return this.sortInfo;
10046 applySort : function(){
10047 if(this.sortInfo && !this.remoteSort){
10048 var s = this.sortInfo, f = s.field;
10049 var st = this.fields.get(f).sortType;
10050 var fn = function(r1, r2){
10051 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10052 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10054 this.data.sort(s.direction, fn);
10055 if(this.snapshot && this.snapshot != this.data){
10056 this.snapshot.sort(s.direction, fn);
10062 * Sets the default sort column and order to be used by the next load operation.
10063 * @param {String} fieldName The name of the field to sort by.
10064 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10066 setDefaultSort : function(field, dir){
10067 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10071 * Sort the Records.
10072 * If remote sorting is used, the sort is performed on the server, and the cache is
10073 * reloaded. If local sorting is used, the cache is sorted internally.
10074 * @param {String} fieldName The name of the field to sort by.
10075 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10077 sort : function(fieldName, dir){
10078 var f = this.fields.get(fieldName);
10080 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10082 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10083 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10088 this.sortToggle[f.name] = dir;
10089 this.sortInfo = {field: f.name, direction: dir};
10090 if(!this.remoteSort){
10092 this.fireEvent("datachanged", this);
10094 this.load(this.lastOptions);
10099 * Calls the specified function for each of the Records in the cache.
10100 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10101 * Returning <em>false</em> aborts and exits the iteration.
10102 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10104 each : function(fn, scope){
10105 this.data.each(fn, scope);
10109 * Gets all records modified since the last commit. Modified records are persisted across load operations
10110 * (e.g., during paging).
10111 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10113 getModifiedRecords : function(){
10114 return this.modified;
10118 createFilterFn : function(property, value, anyMatch){
10119 if(!value.exec){ // not a regex
10120 value = String(value);
10121 if(value.length == 0){
10124 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10126 return function(r){
10127 return value.test(r.data[property]);
10132 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10133 * @param {String} property A field on your records
10134 * @param {Number} start The record index to start at (defaults to 0)
10135 * @param {Number} end The last record index to include (defaults to length - 1)
10136 * @return {Number} The sum
10138 sum : function(property, start, end){
10139 var rs = this.data.items, v = 0;
10140 start = start || 0;
10141 end = (end || end === 0) ? end : rs.length-1;
10143 for(var i = start; i <= end; i++){
10144 v += (rs[i].data[property] || 0);
10150 * Filter the records by a specified property.
10151 * @param {String} field A field on your records
10152 * @param {String/RegExp} value Either a string that the field
10153 * should start with or a RegExp to test against the field
10154 * @param {Boolean} anyMatch True to match any part not just the beginning
10156 filter : function(property, value, anyMatch){
10157 var fn = this.createFilterFn(property, value, anyMatch);
10158 return fn ? this.filterBy(fn) : this.clearFilter();
10162 * Filter by a function. The specified function will be called with each
10163 * record in this data source. If the function returns true the record is included,
10164 * otherwise it is filtered.
10165 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10166 * @param {Object} scope (optional) The scope of the function (defaults to this)
10168 filterBy : function(fn, scope){
10169 this.snapshot = this.snapshot || this.data;
10170 this.data = this.queryBy(fn, scope||this);
10171 this.fireEvent("datachanged", this);
10175 * Query the records by a specified property.
10176 * @param {String} field A field on your records
10177 * @param {String/RegExp} value Either a string that the field
10178 * should start with or a RegExp to test against the field
10179 * @param {Boolean} anyMatch True to match any part not just the beginning
10180 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10182 query : function(property, value, anyMatch){
10183 var fn = this.createFilterFn(property, value, anyMatch);
10184 return fn ? this.queryBy(fn) : this.data.clone();
10188 * Query by a function. The specified function will be called with each
10189 * record in this data source. If the function returns true the record is included
10191 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10192 * @param {Object} scope (optional) The scope of the function (defaults to this)
10193 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10195 queryBy : function(fn, scope){
10196 var data = this.snapshot || this.data;
10197 return data.filterBy(fn, scope||this);
10201 * Collects unique values for a particular dataIndex from this store.
10202 * @param {String} dataIndex The property to collect
10203 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10204 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10205 * @return {Array} An array of the unique values
10207 collect : function(dataIndex, allowNull, bypassFilter){
10208 var d = (bypassFilter === true && this.snapshot) ?
10209 this.snapshot.items : this.data.items;
10210 var v, sv, r = [], l = {};
10211 for(var i = 0, len = d.length; i < len; i++){
10212 v = d[i].data[dataIndex];
10214 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10223 * Revert to a view of the Record cache with no filtering applied.
10224 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10226 clearFilter : function(suppressEvent){
10227 if(this.snapshot && this.snapshot != this.data){
10228 this.data = this.snapshot;
10229 delete this.snapshot;
10230 if(suppressEvent !== true){
10231 this.fireEvent("datachanged", this);
10237 afterEdit : function(record){
10238 if(this.modified.indexOf(record) == -1){
10239 this.modified.push(record);
10241 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10245 afterReject : function(record){
10246 this.modified.remove(record);
10247 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10251 afterCommit : function(record){
10252 this.modified.remove(record);
10253 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10257 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10258 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10260 commitChanges : function(){
10261 var m = this.modified.slice(0);
10262 this.modified = [];
10263 for(var i = 0, len = m.length; i < len; i++){
10269 * Cancel outstanding changes on all changed records.
10271 rejectChanges : function(){
10272 var m = this.modified.slice(0);
10273 this.modified = [];
10274 for(var i = 0, len = m.length; i < len; i++){
10279 onMetaChange : function(meta, rtype, o){
10280 this.recordType = rtype;
10281 this.fields = rtype.prototype.fields;
10282 delete this.snapshot;
10283 this.sortInfo = meta.sortInfo || this.sortInfo;
10284 this.modified = [];
10285 this.fireEvent('metachange', this, this.reader.meta);
10288 moveIndex : function(data, type)
10290 var index = this.indexOf(data);
10292 var newIndex = index + type;
10296 this.insert(newIndex, data);
10301 * Ext JS Library 1.1.1
10302 * Copyright(c) 2006-2007, Ext JS, LLC.
10304 * Originally Released Under LGPL - original licence link has changed is not relivant.
10307 * <script type="text/javascript">
10311 * @class Roo.data.SimpleStore
10312 * @extends Roo.data.Store
10313 * Small helper class to make creating Stores from Array data easier.
10314 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10315 * @cfg {Array} fields An array of field definition objects, or field name strings.
10316 * @cfg {Array} data The multi-dimensional array of data
10318 * @param {Object} config
10320 Roo.data.SimpleStore = function(config){
10321 Roo.data.SimpleStore.superclass.constructor.call(this, {
10323 reader: new Roo.data.ArrayReader({
10326 Roo.data.Record.create(config.fields)
10328 proxy : new Roo.data.MemoryProxy(config.data)
10332 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10334 * Ext JS Library 1.1.1
10335 * Copyright(c) 2006-2007, Ext JS, LLC.
10337 * Originally Released Under LGPL - original licence link has changed is not relivant.
10340 * <script type="text/javascript">
10345 * @extends Roo.data.Store
10346 * @class Roo.data.JsonStore
10347 * Small helper class to make creating Stores for JSON data easier. <br/>
10349 var store = new Roo.data.JsonStore({
10350 url: 'get-images.php',
10352 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10355 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10356 * JsonReader and HttpProxy (unless inline data is provided).</b>
10357 * @cfg {Array} fields An array of field definition objects, or field name strings.
10359 * @param {Object} config
10361 Roo.data.JsonStore = function(c){
10362 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10363 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10364 reader: new Roo.data.JsonReader(c, c.fields)
10367 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10369 * Ext JS Library 1.1.1
10370 * Copyright(c) 2006-2007, Ext JS, LLC.
10372 * Originally Released Under LGPL - original licence link has changed is not relivant.
10375 * <script type="text/javascript">
10379 Roo.data.Field = function(config){
10380 if(typeof config == "string"){
10381 config = {name: config};
10383 Roo.apply(this, config);
10386 this.type = "auto";
10389 var st = Roo.data.SortTypes;
10390 // named sortTypes are supported, here we look them up
10391 if(typeof this.sortType == "string"){
10392 this.sortType = st[this.sortType];
10395 // set default sortType for strings and dates
10396 if(!this.sortType){
10399 this.sortType = st.asUCString;
10402 this.sortType = st.asDate;
10405 this.sortType = st.none;
10410 var stripRe = /[\$,%]/g;
10412 // prebuilt conversion function for this field, instead of
10413 // switching every time we're reading a value
10415 var cv, dateFormat = this.dateFormat;
10420 cv = function(v){ return v; };
10423 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10427 return v !== undefined && v !== null && v !== '' ?
10428 parseInt(String(v).replace(stripRe, ""), 10) : '';
10433 return v !== undefined && v !== null && v !== '' ?
10434 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10439 cv = function(v){ return v === true || v === "true" || v == 1; };
10446 if(v instanceof Date){
10450 if(dateFormat == "timestamp"){
10451 return new Date(v*1000);
10453 return Date.parseDate(v, dateFormat);
10455 var parsed = Date.parse(v);
10456 return parsed ? new Date(parsed) : null;
10465 Roo.data.Field.prototype = {
10473 * Ext JS Library 1.1.1
10474 * Copyright(c) 2006-2007, Ext JS, LLC.
10476 * Originally Released Under LGPL - original licence link has changed is not relivant.
10479 * <script type="text/javascript">
10482 // Base class for reading structured data from a data source. This class is intended to be
10483 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10486 * @class Roo.data.DataReader
10487 * Base class for reading structured data from a data source. This class is intended to be
10488 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10491 Roo.data.DataReader = function(meta, recordType){
10495 this.recordType = recordType instanceof Array ?
10496 Roo.data.Record.create(recordType) : recordType;
10499 Roo.data.DataReader.prototype = {
10501 * Create an empty record
10502 * @param {Object} data (optional) - overlay some values
10503 * @return {Roo.data.Record} record created.
10505 newRow : function(d) {
10507 this.recordType.prototype.fields.each(function(c) {
10509 case 'int' : da[c.name] = 0; break;
10510 case 'date' : da[c.name] = new Date(); break;
10511 case 'float' : da[c.name] = 0.0; break;
10512 case 'boolean' : da[c.name] = false; break;
10513 default : da[c.name] = ""; break;
10517 return new this.recordType(Roo.apply(da, d));
10522 * Ext JS Library 1.1.1
10523 * Copyright(c) 2006-2007, Ext JS, LLC.
10525 * Originally Released Under LGPL - original licence link has changed is not relivant.
10528 * <script type="text/javascript">
10532 * @class Roo.data.DataProxy
10533 * @extends Roo.data.Observable
10534 * This class is an abstract base class for implementations which provide retrieval of
10535 * unformatted data objects.<br>
10537 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10538 * (of the appropriate type which knows how to parse the data object) to provide a block of
10539 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10541 * Custom implementations must implement the load method as described in
10542 * {@link Roo.data.HttpProxy#load}.
10544 Roo.data.DataProxy = function(){
10547 * @event beforeload
10548 * Fires before a network request is made to retrieve a data object.
10549 * @param {Object} This DataProxy object.
10550 * @param {Object} params The params parameter to the load function.
10555 * Fires before the load method's callback is called.
10556 * @param {Object} This DataProxy object.
10557 * @param {Object} o The data object.
10558 * @param {Object} arg The callback argument object passed to the load function.
10562 * @event loadexception
10563 * Fires if an Exception occurs during data retrieval.
10564 * @param {Object} This DataProxy object.
10565 * @param {Object} o The data object.
10566 * @param {Object} arg The callback argument object passed to the load function.
10567 * @param {Object} e The Exception.
10569 loadexception : true
10571 Roo.data.DataProxy.superclass.constructor.call(this);
10574 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10577 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10581 * Ext JS Library 1.1.1
10582 * Copyright(c) 2006-2007, Ext JS, LLC.
10584 * Originally Released Under LGPL - original licence link has changed is not relivant.
10587 * <script type="text/javascript">
10590 * @class Roo.data.MemoryProxy
10591 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10592 * to the Reader when its load method is called.
10594 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10596 Roo.data.MemoryProxy = function(data){
10600 Roo.data.MemoryProxy.superclass.constructor.call(this);
10604 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10606 * Load data from the requested source (in this case an in-memory
10607 * data object passed to the constructor), read the data object into
10608 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10609 * process that block using the passed callback.
10610 * @param {Object} params This parameter is not used by the MemoryProxy class.
10611 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10612 * object into a block of Roo.data.Records.
10613 * @param {Function} callback The function into which to pass the block of Roo.data.records.
10614 * The function must be passed <ul>
10615 * <li>The Record block object</li>
10616 * <li>The "arg" argument from the load function</li>
10617 * <li>A boolean success indicator</li>
10619 * @param {Object} scope The scope in which to call the callback
10620 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10622 load : function(params, reader, callback, scope, arg){
10623 params = params || {};
10626 result = reader.readRecords(this.data);
10628 this.fireEvent("loadexception", this, arg, null, e);
10629 callback.call(scope, null, arg, false);
10632 callback.call(scope, result, arg, true);
10636 update : function(params, records){
10641 * Ext JS Library 1.1.1
10642 * Copyright(c) 2006-2007, Ext JS, LLC.
10644 * Originally Released Under LGPL - original licence link has changed is not relivant.
10647 * <script type="text/javascript">
10650 * @class Roo.data.HttpProxy
10651 * @extends Roo.data.DataProxy
10652 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10653 * configured to reference a certain URL.<br><br>
10655 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10656 * from which the running page was served.<br><br>
10658 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10660 * Be aware that to enable the browser to parse an XML document, the server must set
10661 * the Content-Type header in the HTTP response to "text/xml".
10663 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10664 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
10665 * will be used to make the request.
10667 Roo.data.HttpProxy = function(conn){
10668 Roo.data.HttpProxy.superclass.constructor.call(this);
10669 // is conn a conn config or a real conn?
10671 this.useAjax = !conn || !conn.events;
10675 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10676 // thse are take from connection...
10679 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10682 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10683 * extra parameters to each request made by this object. (defaults to undefined)
10686 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10687 * to each request made by this object. (defaults to undefined)
10690 * @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)
10693 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10696 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10702 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10706 * Return the {@link Roo.data.Connection} object being used by this Proxy.
10707 * @return {Connection} The Connection object. This object may be used to subscribe to events on
10708 * a finer-grained basis than the DataProxy events.
10710 getConnection : function(){
10711 return this.useAjax ? Roo.Ajax : this.conn;
10715 * Load data from the configured {@link Roo.data.Connection}, read the data object into
10716 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10717 * process that block using the passed callback.
10718 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10719 * for the request to the remote server.
10720 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10721 * object into a block of Roo.data.Records.
10722 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10723 * The function must be passed <ul>
10724 * <li>The Record block object</li>
10725 * <li>The "arg" argument from the load function</li>
10726 * <li>A boolean success indicator</li>
10728 * @param {Object} scope The scope in which to call the callback
10729 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10731 load : function(params, reader, callback, scope, arg){
10732 if(this.fireEvent("beforeload", this, params) !== false){
10734 params : params || {},
10736 callback : callback,
10741 callback : this.loadResponse,
10745 Roo.applyIf(o, this.conn);
10746 if(this.activeRequest){
10747 Roo.Ajax.abort(this.activeRequest);
10749 this.activeRequest = Roo.Ajax.request(o);
10751 this.conn.request(o);
10754 callback.call(scope||this, null, arg, false);
10759 loadResponse : function(o, success, response){
10760 delete this.activeRequest;
10762 this.fireEvent("loadexception", this, o, response);
10763 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10768 result = o.reader.read(response);
10770 this.fireEvent("loadexception", this, o, response, e);
10771 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10775 this.fireEvent("load", this, o, o.request.arg);
10776 o.request.callback.call(o.request.scope, result, o.request.arg, true);
10780 update : function(dataSet){
10785 updateResponse : function(dataSet){
10790 * Ext JS Library 1.1.1
10791 * Copyright(c) 2006-2007, Ext JS, LLC.
10793 * Originally Released Under LGPL - original licence link has changed is not relivant.
10796 * <script type="text/javascript">
10800 * @class Roo.data.ScriptTagProxy
10801 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10802 * other than the originating domain of the running page.<br><br>
10804 * <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
10805 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10807 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10808 * source code that is used as the source inside a <script> tag.<br><br>
10810 * In order for the browser to process the returned data, the server must wrap the data object
10811 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10812 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10813 * depending on whether the callback name was passed:
10816 boolean scriptTag = false;
10817 String cb = request.getParameter("callback");
10820 response.setContentType("text/javascript");
10822 response.setContentType("application/x-json");
10824 Writer out = response.getWriter();
10826 out.write(cb + "(");
10828 out.print(dataBlock.toJsonString());
10835 * @param {Object} config A configuration object.
10837 Roo.data.ScriptTagProxy = function(config){
10838 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10839 Roo.apply(this, config);
10840 this.head = document.getElementsByTagName("head")[0];
10843 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10845 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10847 * @cfg {String} url The URL from which to request the data object.
10850 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10854 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10855 * the server the name of the callback function set up by the load call to process the returned data object.
10856 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10857 * javascript output which calls this named function passing the data object as its only parameter.
10859 callbackParam : "callback",
10861 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10862 * name to the request.
10867 * Load data from the configured URL, read the data object into
10868 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10869 * process that block using the passed callback.
10870 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10871 * for the request to the remote server.
10872 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10873 * object into a block of Roo.data.Records.
10874 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10875 * The function must be passed <ul>
10876 * <li>The Record block object</li>
10877 * <li>The "arg" argument from the load function</li>
10878 * <li>A boolean success indicator</li>
10880 * @param {Object} scope The scope in which to call the callback
10881 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10883 load : function(params, reader, callback, scope, arg){
10884 if(this.fireEvent("beforeload", this, params) !== false){
10886 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10888 var url = this.url;
10889 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10891 url += "&_dc=" + (new Date().getTime());
10893 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10896 cb : "stcCallback"+transId,
10897 scriptId : "stcScript"+transId,
10901 callback : callback,
10907 window[trans.cb] = function(o){
10908 conn.handleResponse(o, trans);
10911 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10913 if(this.autoAbort !== false){
10917 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10919 var script = document.createElement("script");
10920 script.setAttribute("src", url);
10921 script.setAttribute("type", "text/javascript");
10922 script.setAttribute("id", trans.scriptId);
10923 this.head.appendChild(script);
10925 this.trans = trans;
10927 callback.call(scope||this, null, arg, false);
10932 isLoading : function(){
10933 return this.trans ? true : false;
10937 * Abort the current server request.
10939 abort : function(){
10940 if(this.isLoading()){
10941 this.destroyTrans(this.trans);
10946 destroyTrans : function(trans, isLoaded){
10947 this.head.removeChild(document.getElementById(trans.scriptId));
10948 clearTimeout(trans.timeoutId);
10950 window[trans.cb] = undefined;
10952 delete window[trans.cb];
10955 // if hasn't been loaded, wait for load to remove it to prevent script error
10956 window[trans.cb] = function(){
10957 window[trans.cb] = undefined;
10959 delete window[trans.cb];
10966 handleResponse : function(o, trans){
10967 this.trans = false;
10968 this.destroyTrans(trans, true);
10971 result = trans.reader.readRecords(o);
10973 this.fireEvent("loadexception", this, o, trans.arg, e);
10974 trans.callback.call(trans.scope||window, null, trans.arg, false);
10977 this.fireEvent("load", this, o, trans.arg);
10978 trans.callback.call(trans.scope||window, result, trans.arg, true);
10982 handleFailure : function(trans){
10983 this.trans = false;
10984 this.destroyTrans(trans, false);
10985 this.fireEvent("loadexception", this, null, trans.arg);
10986 trans.callback.call(trans.scope||window, null, trans.arg, false);
10990 * Ext JS Library 1.1.1
10991 * Copyright(c) 2006-2007, Ext JS, LLC.
10993 * Originally Released Under LGPL - original licence link has changed is not relivant.
10996 * <script type="text/javascript">
11000 * @class Roo.data.JsonReader
11001 * @extends Roo.data.DataReader
11002 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11003 * based on mappings in a provided Roo.data.Record constructor.
11005 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11006 * in the reply previously.
11011 var RecordDef = Roo.data.Record.create([
11012 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11013 {name: 'occupation'} // This field will use "occupation" as the mapping.
11015 var myReader = new Roo.data.JsonReader({
11016 totalProperty: "results", // The property which contains the total dataset size (optional)
11017 root: "rows", // The property which contains an Array of row objects
11018 id: "id" // The property within each row object that provides an ID for the record (optional)
11022 * This would consume a JSON file like this:
11024 { 'results': 2, 'rows': [
11025 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11026 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11029 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11030 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11031 * paged from the remote server.
11032 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11033 * @cfg {String} root name of the property which contains the Array of row objects.
11034 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11035 * @cfg {Array} fields Array of field definition objects
11037 * Create a new JsonReader
11038 * @param {Object} meta Metadata configuration options
11039 * @param {Object} recordType Either an Array of field definition objects,
11040 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11042 Roo.data.JsonReader = function(meta, recordType){
11045 // set some defaults:
11046 Roo.applyIf(meta, {
11047 totalProperty: 'total',
11048 successProperty : 'success',
11053 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11055 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11058 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11059 * Used by Store query builder to append _requestMeta to params.
11062 metaFromRemote : false,
11064 * This method is only used by a DataProxy which has retrieved data from a remote server.
11065 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11066 * @return {Object} data A data block which is used by an Roo.data.Store object as
11067 * a cache of Roo.data.Records.
11069 read : function(response){
11070 var json = response.responseText;
11072 var o = /* eval:var:o */ eval("("+json+")");
11074 throw {message: "JsonReader.read: Json object not found"};
11080 this.metaFromRemote = true;
11081 this.meta = o.metaData;
11082 this.recordType = Roo.data.Record.create(o.metaData.fields);
11083 this.onMetaChange(this.meta, this.recordType, o);
11085 return this.readRecords(o);
11088 // private function a store will implement
11089 onMetaChange : function(meta, recordType, o){
11096 simpleAccess: function(obj, subsc) {
11103 getJsonAccessor: function(){
11105 return function(expr) {
11107 return(re.test(expr))
11108 ? new Function("obj", "return obj." + expr)
11113 return Roo.emptyFn;
11118 * Create a data block containing Roo.data.Records from an XML document.
11119 * @param {Object} o An object which contains an Array of row objects in the property specified
11120 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11121 * which contains the total size of the dataset.
11122 * @return {Object} data A data block which is used by an Roo.data.Store object as
11123 * a cache of Roo.data.Records.
11125 readRecords : function(o){
11127 * After any data loads, the raw JSON data is available for further custom processing.
11131 var s = this.meta, Record = this.recordType,
11132 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11134 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11136 if(s.totalProperty) {
11137 this.getTotal = this.getJsonAccessor(s.totalProperty);
11139 if(s.successProperty) {
11140 this.getSuccess = this.getJsonAccessor(s.successProperty);
11142 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11144 var g = this.getJsonAccessor(s.id);
11145 this.getId = function(rec) {
11147 return (r === undefined || r === "") ? null : r;
11150 this.getId = function(){return null;};
11153 for(var jj = 0; jj < fl; jj++){
11155 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11156 this.ef[jj] = this.getJsonAccessor(map);
11160 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11161 if(s.totalProperty){
11162 var vt = parseInt(this.getTotal(o), 10);
11167 if(s.successProperty){
11168 var vs = this.getSuccess(o);
11169 if(vs === false || vs === 'false'){
11174 for(var i = 0; i < c; i++){
11177 var id = this.getId(n);
11178 for(var j = 0; j < fl; j++){
11180 var v = this.ef[j](n);
11182 Roo.log('missing convert for ' + f.name);
11186 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11188 var record = new Record(values, id);
11190 records[i] = record;
11196 totalRecords : totalRecords
11201 * Ext JS Library 1.1.1
11202 * Copyright(c) 2006-2007, Ext JS, LLC.
11204 * Originally Released Under LGPL - original licence link has changed is not relivant.
11207 * <script type="text/javascript">
11211 * @class Roo.data.ArrayReader
11212 * @extends Roo.data.DataReader
11213 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11214 * Each element of that Array represents a row of data fields. The
11215 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11216 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11220 var RecordDef = Roo.data.Record.create([
11221 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11222 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11224 var myReader = new Roo.data.ArrayReader({
11225 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11229 * This would consume an Array like this:
11231 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11233 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11235 * Create a new JsonReader
11236 * @param {Object} meta Metadata configuration options.
11237 * @param {Object} recordType Either an Array of field definition objects
11238 * as specified to {@link Roo.data.Record#create},
11239 * or an {@link Roo.data.Record} object
11240 * created using {@link Roo.data.Record#create}.
11242 Roo.data.ArrayReader = function(meta, recordType){
11243 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11246 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11248 * Create a data block containing Roo.data.Records from an XML document.
11249 * @param {Object} o An Array of row objects which represents the dataset.
11250 * @return {Object} data A data block which is used by an Roo.data.Store object as
11251 * a cache of Roo.data.Records.
11253 readRecords : function(o){
11254 var sid = this.meta ? this.meta.id : null;
11255 var recordType = this.recordType, fields = recordType.prototype.fields;
11258 for(var i = 0; i < root.length; i++){
11261 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11262 for(var j = 0, jlen = fields.length; j < jlen; j++){
11263 var f = fields.items[j];
11264 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11265 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11267 values[f.name] = v;
11269 var record = new recordType(values, id);
11271 records[records.length] = record;
11275 totalRecords : records.length
11284 * @class Roo.bootstrap.ComboBox
11285 * @extends Roo.bootstrap.TriggerField
11286 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11287 * @cfg {Boolean} append (true|false) default false
11288 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11289 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11290 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11291 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11292 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11293 * @cfg {Boolean} animate default true
11294 * @cfg {Boolean} emptyResultText only for touch device
11295 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11297 * Create a new ComboBox.
11298 * @param {Object} config Configuration options
11300 Roo.bootstrap.ComboBox = function(config){
11301 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11305 * Fires when the dropdown list is expanded
11306 * @param {Roo.bootstrap.ComboBox} combo This combo box
11311 * Fires when the dropdown list is collapsed
11312 * @param {Roo.bootstrap.ComboBox} combo This combo box
11316 * @event beforeselect
11317 * Fires before a list item is selected. Return false to cancel the selection.
11318 * @param {Roo.bootstrap.ComboBox} combo This combo box
11319 * @param {Roo.data.Record} record The data record returned from the underlying store
11320 * @param {Number} index The index of the selected item in the dropdown list
11322 'beforeselect' : true,
11325 * Fires when a list item is selected
11326 * @param {Roo.bootstrap.ComboBox} combo This combo box
11327 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11328 * @param {Number} index The index of the selected item in the dropdown list
11332 * @event beforequery
11333 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11334 * The event object passed has these properties:
11335 * @param {Roo.bootstrap.ComboBox} combo This combo box
11336 * @param {String} query The query
11337 * @param {Boolean} forceAll true to force "all" query
11338 * @param {Boolean} cancel true to cancel the query
11339 * @param {Object} e The query event object
11341 'beforequery': true,
11344 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11345 * @param {Roo.bootstrap.ComboBox} combo This combo box
11350 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11351 * @param {Roo.bootstrap.ComboBox} combo This combo box
11352 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11357 * Fires when the remove value from the combobox array
11358 * @param {Roo.bootstrap.ComboBox} combo This combo box
11362 * @event specialfilter
11363 * Fires when specialfilter
11364 * @param {Roo.bootstrap.ComboBox} combo This combo box
11366 'specialfilter' : true
11371 this.tickItems = [];
11373 this.selectedIndex = -1;
11374 if(this.mode == 'local'){
11375 if(config.queryDelay === undefined){
11376 this.queryDelay = 10;
11378 if(config.minChars === undefined){
11384 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11387 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11388 * rendering into an Roo.Editor, defaults to false)
11391 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11392 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11395 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11398 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11399 * the dropdown list (defaults to undefined, with no header element)
11403 * @cfg {String/Roo.Template} tpl The template to use to render the output
11407 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11409 listWidth: undefined,
11411 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11412 * mode = 'remote' or 'text' if mode = 'local')
11414 displayField: undefined,
11417 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11418 * mode = 'remote' or 'value' if mode = 'local').
11419 * Note: use of a valueField requires the user make a selection
11420 * in order for a value to be mapped.
11422 valueField: undefined,
11426 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11427 * field's data value (defaults to the underlying DOM element's name)
11429 hiddenName: undefined,
11431 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11435 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11437 selectedClass: 'active',
11440 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11444 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11445 * anchor positions (defaults to 'tl-bl')
11447 listAlign: 'tl-bl?',
11449 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11453 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11454 * query specified by the allQuery config option (defaults to 'query')
11456 triggerAction: 'query',
11458 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11459 * (defaults to 4, does not apply if editable = false)
11463 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11464 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11468 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11469 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11473 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11474 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11478 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11479 * when editable = true (defaults to false)
11481 selectOnFocus:false,
11483 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11485 queryParam: 'query',
11487 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
11488 * when mode = 'remote' (defaults to 'Loading...')
11490 loadingText: 'Loading...',
11492 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11496 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11500 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11501 * traditional select (defaults to true)
11505 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11509 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11513 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11514 * listWidth has a higher value)
11518 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11519 * allow the user to set arbitrary text into the field (defaults to false)
11521 forceSelection:false,
11523 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11524 * if typeAhead = true (defaults to 250)
11526 typeAheadDelay : 250,
11528 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11529 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11531 valueNotFoundText : undefined,
11533 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11535 blockFocus : false,
11538 * @cfg {Boolean} disableClear Disable showing of clear button.
11540 disableClear : false,
11542 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
11544 alwaysQuery : false,
11547 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
11552 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11554 invalidClass : "has-warning",
11557 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11559 validClass : "has-success",
11562 * @cfg {Boolean} specialFilter (true|false) special filter default false
11564 specialFilter : false,
11567 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11569 mobileTouchView : true,
11581 btnPosition : 'right',
11582 triggerList : true,
11583 showToggleBtn : true,
11585 emptyResultText: 'Empty',
11586 triggerText : 'Select',
11588 // element that contains real text value.. (when hidden is used..)
11590 getAutoCreate : function()
11598 if(Roo.isTouch && this.mobileTouchView){
11599 cfg = this.getAutoCreateTouchView();
11606 if(!this.tickable){
11607 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11612 * ComboBox with tickable selections
11615 var align = this.labelAlign || this.parentLabelAlign();
11618 cls : 'form-group roo-combobox-tickable' //input-group
11623 cls : 'tickable-buttons',
11628 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11629 html : this.triggerText
11635 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11642 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11649 buttons.cn.unshift({
11651 cls: 'select2-search-field-input'
11657 Roo.each(buttons.cn, function(c){
11659 c.cls += ' btn-' + _this.size;
11662 if (_this.disabled) {
11673 cls: 'form-hidden-field'
11677 cls: 'select2-choices',
11681 cls: 'select2-search-field',
11693 cls: 'select2-container input-group select2-container-multi',
11698 // cls: 'typeahead typeahead-long dropdown-menu',
11699 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
11704 if(this.hasFeedback && !this.allowBlank){
11708 cls: 'glyphicon form-control-feedback'
11711 combobox.cn.push(feedback);
11714 if (align ==='left' && this.fieldLabel.length) {
11716 Roo.log("left and has label");
11722 cls : 'control-label col-sm-' + this.labelWidth,
11723 html : this.fieldLabel
11727 cls : "col-sm-" + (12 - this.labelWidth),
11734 } else if ( this.fieldLabel.length) {
11740 //cls : 'input-group-addon',
11741 html : this.fieldLabel
11751 Roo.log(" no label && no align");
11758 ['xs','sm','md','lg'].map(function(size){
11759 if (settings[size]) {
11760 cfg.cls += ' col-' + size + '-' + settings[size];
11768 _initEventsCalled : false,
11771 initEvents: function()
11774 if (this._initEventsCalled) { // as we call render... prevent looping...
11777 this._initEventsCalled = true;
11780 throw "can not find store for combo";
11783 this.store = Roo.factory(this.store, Roo.data);
11785 // if we are building from html. then this element is so complex, that we can not really
11786 // use the rendered HTML.
11787 // so we have to trash and replace the previous code.
11788 if (Roo.XComponent.build_from_html) {
11790 // remove this element....
11791 var e = this.el.dom, k=0;
11792 while (e ) { e = e.previousSibling; ++k;}
11797 this.rendered = false;
11799 this.render(this.parent().getChildContainer(true), k);
11810 if(Roo.isTouch && this.mobileTouchView){
11811 this.initTouchView();
11816 this.initTickableEvents();
11820 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11822 if(this.hiddenName){
11824 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11826 this.hiddenField.dom.value =
11827 this.hiddenValue !== undefined ? this.hiddenValue :
11828 this.value !== undefined ? this.value : '';
11830 // prevent input submission
11831 this.el.dom.removeAttribute('name');
11832 this.hiddenField.dom.setAttribute('name', this.hiddenName);
11837 // this.el.dom.setAttribute('autocomplete', 'off');
11840 var cls = 'x-combo-list';
11842 //this.list = new Roo.Layer({
11843 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11849 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11850 _this.list.setWidth(lw);
11853 this.list.on('mouseover', this.onViewOver, this);
11854 this.list.on('mousemove', this.onViewMove, this);
11856 this.list.on('scroll', this.onViewScroll, this);
11859 this.list.swallowEvent('mousewheel');
11860 this.assetHeight = 0;
11863 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11864 this.assetHeight += this.header.getHeight();
11867 this.innerList = this.list.createChild({cls:cls+'-inner'});
11868 this.innerList.on('mouseover', this.onViewOver, this);
11869 this.innerList.on('mousemove', this.onViewMove, this);
11870 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11872 if(this.allowBlank && !this.pageSize && !this.disableClear){
11873 this.footer = this.list.createChild({cls:cls+'-ft'});
11874 this.pageTb = new Roo.Toolbar(this.footer);
11878 this.footer = this.list.createChild({cls:cls+'-ft'});
11879 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11880 {pageSize: this.pageSize});
11884 if (this.pageTb && this.allowBlank && !this.disableClear) {
11886 this.pageTb.add(new Roo.Toolbar.Fill(), {
11887 cls: 'x-btn-icon x-btn-clear',
11889 handler: function()
11892 _this.clearValue();
11893 _this.onSelect(false, -1);
11898 this.assetHeight += this.footer.getHeight();
11903 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11906 this.view = new Roo.View(this.list, this.tpl, {
11907 singleSelect:true, store: this.store, selectedClass: this.selectedClass
11909 //this.view.wrapEl.setDisplayed(false);
11910 this.view.on('click', this.onViewClick, this);
11914 this.store.on('beforeload', this.onBeforeLoad, this);
11915 this.store.on('load', this.onLoad, this);
11916 this.store.on('loadexception', this.onLoadException, this);
11918 if(this.resizable){
11919 this.resizer = new Roo.Resizable(this.list, {
11920 pinned:true, handles:'se'
11922 this.resizer.on('resize', function(r, w, h){
11923 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11924 this.listWidth = w;
11925 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11926 this.restrictHeight();
11928 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11931 if(!this.editable){
11932 this.editable = true;
11933 this.setEditable(false);
11938 if (typeof(this.events.add.listeners) != 'undefined') {
11940 this.addicon = this.wrap.createChild(
11941 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
11943 this.addicon.on('click', function(e) {
11944 this.fireEvent('add', this);
11947 if (typeof(this.events.edit.listeners) != 'undefined') {
11949 this.editicon = this.wrap.createChild(
11950 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
11951 if (this.addicon) {
11952 this.editicon.setStyle('margin-left', '40px');
11954 this.editicon.on('click', function(e) {
11956 // we fire even if inothing is selected..
11957 this.fireEvent('edit', this, this.lastData );
11963 this.keyNav = new Roo.KeyNav(this.inputEl(), {
11964 "up" : function(e){
11965 this.inKeyMode = true;
11969 "down" : function(e){
11970 if(!this.isExpanded()){
11971 this.onTriggerClick();
11973 this.inKeyMode = true;
11978 "enter" : function(e){
11979 // this.onViewClick();
11983 if(this.fireEvent("specialkey", this, e)){
11984 this.onViewClick(false);
11990 "esc" : function(e){
11994 "tab" : function(e){
11997 if(this.fireEvent("specialkey", this, e)){
11998 this.onViewClick(false);
12006 doRelay : function(foo, bar, hname){
12007 if(hname == 'down' || this.scope.isExpanded()){
12008 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12017 this.queryDelay = Math.max(this.queryDelay || 10,
12018 this.mode == 'local' ? 10 : 250);
12021 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12023 if(this.typeAhead){
12024 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12026 if(this.editable !== false){
12027 this.inputEl().on("keyup", this.onKeyUp, this);
12029 if(this.forceSelection){
12030 this.inputEl().on('blur', this.doForce, this);
12034 this.choices = this.el.select('ul.select2-choices', true).first();
12035 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12039 initTickableEvents: function()
12043 if(this.hiddenName){
12045 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12047 this.hiddenField.dom.value =
12048 this.hiddenValue !== undefined ? this.hiddenValue :
12049 this.value !== undefined ? this.value : '';
12051 // prevent input submission
12052 this.el.dom.removeAttribute('name');
12053 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12058 // this.list = this.el.select('ul.dropdown-menu',true).first();
12060 this.choices = this.el.select('ul.select2-choices', true).first();
12061 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12062 if(this.triggerList){
12063 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12066 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12067 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12069 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12070 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12072 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12073 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12075 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12076 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12077 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12080 this.cancelBtn.hide();
12085 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12086 _this.list.setWidth(lw);
12089 this.list.on('mouseover', this.onViewOver, this);
12090 this.list.on('mousemove', this.onViewMove, this);
12092 this.list.on('scroll', this.onViewScroll, this);
12095 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>';
12098 this.view = new Roo.View(this.list, this.tpl, {
12099 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12102 //this.view.wrapEl.setDisplayed(false);
12103 this.view.on('click', this.onViewClick, this);
12107 this.store.on('beforeload', this.onBeforeLoad, this);
12108 this.store.on('load', this.onLoad, this);
12109 this.store.on('loadexception', this.onLoadException, this);
12112 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12113 "up" : function(e){
12114 this.inKeyMode = true;
12118 "down" : function(e){
12119 this.inKeyMode = true;
12123 "enter" : function(e){
12124 if(this.fireEvent("specialkey", this, e)){
12125 this.onViewClick(false);
12131 "esc" : function(e){
12132 this.onTickableFooterButtonClick(e, false, false);
12135 "tab" : function(e){
12136 this.fireEvent("specialkey", this, e);
12138 this.onTickableFooterButtonClick(e, false, false);
12145 doRelay : function(e, fn, key){
12146 if(this.scope.isExpanded()){
12147 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12156 this.queryDelay = Math.max(this.queryDelay || 10,
12157 this.mode == 'local' ? 10 : 250);
12160 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12162 if(this.typeAhead){
12163 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12166 if(this.editable !== false){
12167 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12172 onDestroy : function(){
12174 this.view.setStore(null);
12175 this.view.el.removeAllListeners();
12176 this.view.el.remove();
12177 this.view.purgeListeners();
12180 this.list.dom.innerHTML = '';
12184 this.store.un('beforeload', this.onBeforeLoad, this);
12185 this.store.un('load', this.onLoad, this);
12186 this.store.un('loadexception', this.onLoadException, this);
12188 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12192 fireKey : function(e){
12193 if(e.isNavKeyPress() && !this.list.isVisible()){
12194 this.fireEvent("specialkey", this, e);
12199 onResize: function(w, h){
12200 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12202 // if(typeof w != 'number'){
12203 // // we do not handle it!?!?
12206 // var tw = this.trigger.getWidth();
12207 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12208 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12210 // this.inputEl().setWidth( this.adjustWidth('input', x));
12212 // //this.trigger.setStyle('left', x+'px');
12214 // if(this.list && this.listWidth === undefined){
12215 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12216 // this.list.setWidth(lw);
12217 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12225 * Allow or prevent the user from directly editing the field text. If false is passed,
12226 * the user will only be able to select from the items defined in the dropdown list. This method
12227 * is the runtime equivalent of setting the 'editable' config option at config time.
12228 * @param {Boolean} value True to allow the user to directly edit the field text
12230 setEditable : function(value){
12231 if(value == this.editable){
12234 this.editable = value;
12236 this.inputEl().dom.setAttribute('readOnly', true);
12237 this.inputEl().on('mousedown', this.onTriggerClick, this);
12238 this.inputEl().addClass('x-combo-noedit');
12240 this.inputEl().dom.setAttribute('readOnly', false);
12241 this.inputEl().un('mousedown', this.onTriggerClick, this);
12242 this.inputEl().removeClass('x-combo-noedit');
12248 onBeforeLoad : function(combo,opts){
12249 if(!this.hasFocus){
12253 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12255 this.restrictHeight();
12256 this.selectedIndex = -1;
12260 onLoad : function(){
12262 this.hasQuery = false;
12264 if(!this.hasFocus){
12268 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12269 this.loading.hide();
12272 if(this.store.getCount() > 0){
12274 this.restrictHeight();
12275 if(this.lastQuery == this.allQuery){
12276 if(this.editable && !this.tickable){
12277 this.inputEl().dom.select();
12281 !this.selectByValue(this.value, true) &&
12284 !this.store.lastOptions ||
12285 typeof(this.store.lastOptions.add) == 'undefined' ||
12286 this.store.lastOptions.add != true
12289 this.select(0, true);
12292 if(this.autoFocus){
12295 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12296 this.taTask.delay(this.typeAheadDelay);
12300 this.onEmptyResults();
12306 onLoadException : function()
12308 this.hasQuery = false;
12310 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12311 this.loading.hide();
12314 if(this.tickable && this.editable){
12320 Roo.log(this.store.reader.jsonData);
12321 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12323 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12329 onTypeAhead : function(){
12330 if(this.store.getCount() > 0){
12331 var r = this.store.getAt(0);
12332 var newValue = r.data[this.displayField];
12333 var len = newValue.length;
12334 var selStart = this.getRawValue().length;
12336 if(selStart != len){
12337 this.setRawValue(newValue);
12338 this.selectText(selStart, newValue.length);
12344 onSelect : function(record, index){
12346 if(this.fireEvent('beforeselect', this, record, index) !== false){
12348 this.setFromData(index > -1 ? record.data : false);
12351 this.fireEvent('select', this, record, index);
12356 * Returns the currently selected field value or empty string if no value is set.
12357 * @return {String} value The selected value
12359 getValue : function(){
12362 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12365 if(this.valueField){
12366 return typeof this.value != 'undefined' ? this.value : '';
12368 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12373 * Clears any text/value currently set in the field
12375 clearValue : function(){
12376 if(this.hiddenField){
12377 this.hiddenField.dom.value = '';
12380 this.setRawValue('');
12381 this.lastSelectionText = '';
12382 this.lastData = false;
12384 var close = this.closeTriggerEl();
12393 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12394 * will be displayed in the field. If the value does not match the data value of an existing item,
12395 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12396 * Otherwise the field will be blank (although the value will still be set).
12397 * @param {String} value The value to match
12399 setValue : function(v){
12406 if(this.valueField){
12407 var r = this.findRecord(this.valueField, v);
12409 text = r.data[this.displayField];
12410 }else if(this.valueNotFoundText !== undefined){
12411 text = this.valueNotFoundText;
12414 this.lastSelectionText = text;
12415 if(this.hiddenField){
12416 this.hiddenField.dom.value = v;
12418 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12421 var close = this.closeTriggerEl();
12424 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12428 * @property {Object} the last set data for the element
12433 * Sets the value of the field based on a object which is related to the record format for the store.
12434 * @param {Object} value the value to set as. or false on reset?
12436 setFromData : function(o){
12443 var dv = ''; // display value
12444 var vv = ''; // value value..
12446 if (this.displayField) {
12447 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12449 // this is an error condition!!!
12450 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12453 if(this.valueField){
12454 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12457 var close = this.closeTriggerEl();
12460 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12463 if(this.hiddenField){
12464 this.hiddenField.dom.value = vv;
12466 this.lastSelectionText = dv;
12467 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12471 // no hidden field.. - we store the value in 'value', but still display
12472 // display field!!!!
12473 this.lastSelectionText = dv;
12474 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12481 reset : function(){
12482 // overridden so that last data is reset..
12489 this.setValue(this.originalValue);
12490 this.clearInvalid();
12491 this.lastData = false;
12493 this.view.clearSelections();
12497 findRecord : function(prop, value){
12499 if(this.store.getCount() > 0){
12500 this.store.each(function(r){
12501 if(r.data[prop] == value){
12511 getName: function()
12513 // returns hidden if it's set..
12514 if (!this.rendered) {return ''};
12515 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
12519 onViewMove : function(e, t){
12520 this.inKeyMode = false;
12524 onViewOver : function(e, t){
12525 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12528 var item = this.view.findItemFromChild(t);
12531 var index = this.view.indexOf(item);
12532 this.select(index, false);
12537 onViewClick : function(view, doFocus, el, e)
12539 var index = this.view.getSelectedIndexes()[0];
12541 var r = this.store.getAt(index);
12545 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12552 Roo.each(this.tickItems, function(v,k){
12554 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12555 _this.tickItems.splice(k, 1);
12557 if(typeof(e) == 'undefined' && view == false){
12558 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12570 this.tickItems.push(r.data);
12572 if(typeof(e) == 'undefined' && view == false){
12573 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12580 this.onSelect(r, index);
12582 if(doFocus !== false && !this.blockFocus){
12583 this.inputEl().focus();
12588 restrictHeight : function(){
12589 //this.innerList.dom.style.height = '';
12590 //var inner = this.innerList.dom;
12591 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12592 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12593 //this.list.beginUpdate();
12594 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12595 this.list.alignTo(this.inputEl(), this.listAlign);
12596 this.list.alignTo(this.inputEl(), this.listAlign);
12597 //this.list.endUpdate();
12601 onEmptyResults : function(){
12603 if(this.tickable && this.editable){
12604 this.restrictHeight();
12612 * Returns true if the dropdown list is expanded, else false.
12614 isExpanded : function(){
12615 return this.list.isVisible();
12619 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12620 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12621 * @param {String} value The data value of the item to select
12622 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12623 * selected item if it is not currently in view (defaults to true)
12624 * @return {Boolean} True if the value matched an item in the list, else false
12626 selectByValue : function(v, scrollIntoView){
12627 if(v !== undefined && v !== null){
12628 var r = this.findRecord(this.valueField || this.displayField, v);
12630 this.select(this.store.indexOf(r), scrollIntoView);
12638 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12639 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12640 * @param {Number} index The zero-based index of the list item to select
12641 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12642 * selected item if it is not currently in view (defaults to true)
12644 select : function(index, scrollIntoView){
12645 this.selectedIndex = index;
12646 this.view.select(index);
12647 if(scrollIntoView !== false){
12648 var el = this.view.getNode(index);
12650 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12653 this.list.scrollChildIntoView(el, false);
12659 selectNext : function(){
12660 var ct = this.store.getCount();
12662 if(this.selectedIndex == -1){
12664 }else if(this.selectedIndex < ct-1){
12665 this.select(this.selectedIndex+1);
12671 selectPrev : function(){
12672 var ct = this.store.getCount();
12674 if(this.selectedIndex == -1){
12676 }else if(this.selectedIndex != 0){
12677 this.select(this.selectedIndex-1);
12683 onKeyUp : function(e){
12684 if(this.editable !== false && !e.isSpecialKey()){
12685 this.lastKey = e.getKey();
12686 this.dqTask.delay(this.queryDelay);
12691 validateBlur : function(){
12692 return !this.list || !this.list.isVisible();
12696 initQuery : function(){
12698 var v = this.getRawValue();
12700 if(this.tickable && this.editable){
12701 v = this.tickableInputEl().getValue();
12708 doForce : function(){
12709 if(this.inputEl().dom.value.length > 0){
12710 this.inputEl().dom.value =
12711 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12717 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
12718 * query allowing the query action to be canceled if needed.
12719 * @param {String} query The SQL query to execute
12720 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12721 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
12722 * saved in the current store (defaults to false)
12724 doQuery : function(q, forceAll){
12726 if(q === undefined || q === null){
12731 forceAll: forceAll,
12735 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12740 forceAll = qe.forceAll;
12741 if(forceAll === true || (q.length >= this.minChars)){
12743 this.hasQuery = true;
12745 if(this.lastQuery != q || this.alwaysQuery){
12746 this.lastQuery = q;
12747 if(this.mode == 'local'){
12748 this.selectedIndex = -1;
12750 this.store.clearFilter();
12753 if(this.specialFilter){
12754 this.fireEvent('specialfilter', this);
12759 this.store.filter(this.displayField, q);
12762 this.store.fireEvent("datachanged", this.store);
12769 this.store.baseParams[this.queryParam] = q;
12771 var options = {params : this.getParams(q)};
12774 options.add = true;
12775 options.params.start = this.page * this.pageSize;
12778 this.store.load(options);
12781 * this code will make the page width larger, at the beginning, the list not align correctly,
12782 * we should expand the list on onLoad
12783 * so command out it
12788 this.selectedIndex = -1;
12793 this.loadNext = false;
12797 getParams : function(q){
12799 //p[this.queryParam] = q;
12803 p.limit = this.pageSize;
12809 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12811 collapse : function(){
12812 if(!this.isExpanded()){
12819 this.hasFocus = false;
12821 this.cancelBtn.hide();
12822 this.trigger.show();
12825 this.tickableInputEl().dom.value = '';
12826 this.tickableInputEl().blur();
12831 Roo.get(document).un('mousedown', this.collapseIf, this);
12832 Roo.get(document).un('mousewheel', this.collapseIf, this);
12833 if (!this.editable) {
12834 Roo.get(document).un('keydown', this.listKeyPress, this);
12836 this.fireEvent('collapse', this);
12840 collapseIf : function(e){
12841 var in_combo = e.within(this.el);
12842 var in_list = e.within(this.list);
12843 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12845 if (in_combo || in_list || is_list) {
12846 //e.stopPropagation();
12851 this.onTickableFooterButtonClick(e, false, false);
12859 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12861 expand : function(){
12863 if(this.isExpanded() || !this.hasFocus){
12867 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12868 this.list.setWidth(lw);
12875 this.restrictHeight();
12879 this.tickItems = Roo.apply([], this.item);
12882 this.cancelBtn.show();
12883 this.trigger.hide();
12886 this.tickableInputEl().focus();
12891 Roo.get(document).on('mousedown', this.collapseIf, this);
12892 Roo.get(document).on('mousewheel', this.collapseIf, this);
12893 if (!this.editable) {
12894 Roo.get(document).on('keydown', this.listKeyPress, this);
12897 this.fireEvent('expand', this);
12901 // Implements the default empty TriggerField.onTriggerClick function
12902 onTriggerClick : function(e)
12904 Roo.log('trigger click');
12906 if(this.disabled || !this.triggerList){
12911 this.loadNext = false;
12913 if(this.isExpanded()){
12915 if (!this.blockFocus) {
12916 this.inputEl().focus();
12920 this.hasFocus = true;
12921 if(this.triggerAction == 'all') {
12922 this.doQuery(this.allQuery, true);
12924 this.doQuery(this.getRawValue());
12926 if (!this.blockFocus) {
12927 this.inputEl().focus();
12932 onTickableTriggerClick : function(e)
12939 this.loadNext = false;
12940 this.hasFocus = true;
12942 if(this.triggerAction == 'all') {
12943 this.doQuery(this.allQuery, true);
12945 this.doQuery(this.getRawValue());
12949 onSearchFieldClick : function(e)
12951 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12952 this.onTickableFooterButtonClick(e, false, false);
12956 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12961 this.loadNext = false;
12962 this.hasFocus = true;
12964 if(this.triggerAction == 'all') {
12965 this.doQuery(this.allQuery, true);
12967 this.doQuery(this.getRawValue());
12971 listKeyPress : function(e)
12973 //Roo.log('listkeypress');
12974 // scroll to first matching element based on key pres..
12975 if (e.isSpecialKey()) {
12978 var k = String.fromCharCode(e.getKey()).toUpperCase();
12981 var csel = this.view.getSelectedNodes();
12982 var cselitem = false;
12984 var ix = this.view.indexOf(csel[0]);
12985 cselitem = this.store.getAt(ix);
12986 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12992 this.store.each(function(v) {
12994 // start at existing selection.
12995 if (cselitem.id == v.id) {
13001 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13002 match = this.store.indexOf(v);
13008 if (match === false) {
13009 return true; // no more action?
13012 this.view.select(match);
13013 var sn = Roo.get(this.view.getSelectedNodes()[0])
13014 sn.scrollIntoView(sn.dom.parentNode, false);
13017 onViewScroll : function(e, t){
13019 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){
13023 this.hasQuery = true;
13025 this.loading = this.list.select('.loading', true).first();
13027 if(this.loading === null){
13028 this.list.createChild({
13030 cls: 'loading select2-more-results select2-active',
13031 html: 'Loading more results...'
13034 this.loading = this.list.select('.loading', true).first();
13036 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13038 this.loading.hide();
13041 this.loading.show();
13046 this.loadNext = true;
13048 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13053 addItem : function(o)
13055 var dv = ''; // display value
13057 if (this.displayField) {
13058 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13060 // this is an error condition!!!
13061 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13068 var choice = this.choices.createChild({
13070 cls: 'select2-search-choice',
13079 cls: 'select2-search-choice-close',
13084 }, this.searchField);
13086 var close = choice.select('a.select2-search-choice-close', true).first()
13088 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13096 this.inputEl().dom.value = '';
13101 onRemoveItem : function(e, _self, o)
13103 e.preventDefault();
13105 this.lastItem = Roo.apply([], this.item);
13107 var index = this.item.indexOf(o.data) * 1;
13110 Roo.log('not this item?!');
13114 this.item.splice(index, 1);
13119 this.fireEvent('remove', this, e);
13125 syncValue : function()
13127 if(!this.item.length){
13134 Roo.each(this.item, function(i){
13135 if(_this.valueField){
13136 value.push(i[_this.valueField]);
13143 this.value = value.join(',');
13145 if(this.hiddenField){
13146 this.hiddenField.dom.value = this.value;
13149 this.store.fireEvent("datachanged", this.store);
13152 clearItem : function()
13154 if(!this.multiple){
13160 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13168 if(this.tickable && !Roo.isTouch){
13169 this.view.refresh();
13173 inputEl: function ()
13175 if(Roo.isTouch && this.mobileTouchView){
13176 return this.el.select('input.form-control',true).first();
13180 return this.searchField;
13183 return this.el.select('input.form-control',true).first();
13187 onTickableFooterButtonClick : function(e, btn, el)
13189 e.preventDefault();
13191 this.lastItem = Roo.apply([], this.item);
13193 if(btn && btn.name == 'cancel'){
13194 this.tickItems = Roo.apply([], this.item);
13203 Roo.each(this.tickItems, function(o){
13211 validate : function()
13213 var v = this.getRawValue();
13216 v = this.getValue();
13219 if(this.disabled || this.allowBlank || v.length){
13224 this.markInvalid();
13228 tickableInputEl : function()
13230 if(!this.tickable || !this.editable){
13231 return this.inputEl();
13234 return this.inputEl().select('.select2-search-field-input', true).first();
13238 getAutoCreateTouchView : function()
13243 cls: 'form-group' //input-group
13249 type : this.inputType,
13250 cls : 'form-control x-combo-noedit',
13251 autocomplete: 'new-password',
13252 placeholder : this.placeholder || '',
13257 input.name = this.name;
13261 input.cls += ' input-' + this.size;
13264 if (this.disabled) {
13265 input.disabled = true;
13276 inputblock.cls += ' input-group';
13278 inputblock.cn.unshift({
13280 cls : 'input-group-addon',
13285 if(this.removable && !this.multiple){
13286 inputblock.cls += ' roo-removable';
13288 inputblock.cn.push({
13291 cls : 'roo-combo-removable-btn close'
13295 if(this.hasFeedback && !this.allowBlank){
13297 inputblock.cls += ' has-feedback';
13299 inputblock.cn.push({
13301 cls: 'glyphicon form-control-feedback'
13308 inputblock.cls += (this.before) ? '' : ' input-group';
13310 inputblock.cn.push({
13312 cls : 'input-group-addon',
13323 cls: 'form-hidden-field'
13337 cls: 'form-hidden-field'
13341 cls: 'select2-choices',
13345 cls: 'select2-search-field',
13358 cls: 'select2-container input-group',
13365 combobox.cls += ' select2-container-multi';
13368 var align = this.labelAlign || this.parentLabelAlign();
13372 if(this.fieldLabel.length){
13374 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13375 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13380 cls : 'control-label ' + lw,
13381 html : this.fieldLabel
13393 var settings = this;
13395 ['xs','sm','md','lg'].map(function(size){
13396 if (settings[size]) {
13397 cfg.cls += ' col-' + size + '-' + settings[size];
13404 initTouchView : function()
13406 this.renderTouchView();
13408 this.touchViewEl.on('scroll', function(){
13409 this.el.dom.scrollTop = 0;
13412 this.inputEl().on("click", this.showTouchView, this);
13413 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13414 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13416 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13418 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13419 this.store.on('load', this.onTouchViewLoad, this);
13420 this.store.on('loadexception', this.onTouchViewLoadException, this);
13422 if(this.hiddenName){
13424 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13426 this.hiddenField.dom.value =
13427 this.hiddenValue !== undefined ? this.hiddenValue :
13428 this.value !== undefined ? this.value : '';
13430 this.el.dom.removeAttribute('name');
13431 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13435 this.choices = this.el.select('ul.select2-choices', true).first();
13436 this.searchField = this.el.select('ul li.select2-search-field', true).first();
13439 if(this.removable && !this.multiple){
13440 var close = this.closeTriggerEl();
13442 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13443 close.on('click', this.removeBtnClick, this, close);
13452 renderTouchView : function()
13454 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13455 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13457 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13458 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13460 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13461 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13462 this.touchViewBodyEl.setStyle('overflow', 'auto');
13464 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13465 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13467 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13468 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13472 showTouchView : function()
13474 this.touchViewHeaderEl.hide();
13476 if(this.fieldLabel.length){
13477 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13478 this.touchViewHeaderEl.show();
13481 this.touchViewEl.show();
13483 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13484 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13486 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13488 if(this.fieldLabel.length){
13489 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13492 this.touchViewBodyEl.setHeight(bodyHeight);
13496 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13498 this.touchViewEl.addClass('in');
13501 this.doTouchViewQuery();
13505 hideTouchView : function()
13507 this.touchViewEl.removeClass('in');
13511 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13513 this.touchViewEl.setStyle('display', 'none');
13518 setTouchViewValue : function()
13525 Roo.each(this.tickItems, function(o){
13530 this.hideTouchView();
13533 doTouchViewQuery : function()
13542 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13546 if(!this.alwaysQuery || this.mode == 'local'){
13547 this.onTouchViewLoad();
13554 onTouchViewBeforeLoad : function(combo,opts)
13560 onTouchViewLoad : function()
13562 if(this.store.getCount() < 1){
13563 this.onTouchViewEmptyResults();
13567 this.clearTouchView();
13569 var rawValue = this.getRawValue();
13571 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13573 this.tickItems = [];
13575 this.store.data.each(function(d, rowIndex){
13576 var row = this.touchViewListGroup.createChild(template);
13578 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13579 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13582 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13583 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13586 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13587 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13588 this.tickItems.push(d.data);
13591 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13595 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13597 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13599 if(this.fieldLabel.length){
13600 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13603 var listHeight = this.touchViewListGroup.getHeight();
13607 if(firstChecked && listHeight > bodyHeight){
13608 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13613 onTouchViewLoadException : function()
13615 this.hideTouchView();
13618 onTouchViewEmptyResults : function()
13620 this.clearTouchView();
13622 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13624 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13628 clearTouchView : function()
13630 this.touchViewListGroup.dom.innerHTML = '';
13633 onTouchViewClick : function(e, el, o)
13635 e.preventDefault();
13638 var rowIndex = o.rowIndex;
13640 var r = this.store.getAt(rowIndex);
13642 if(!this.multiple){
13643 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13644 c.dom.removeAttribute('checked');
13647 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13649 this.setFromData(r.data);
13651 var close = this.closeTriggerEl();
13657 this.hideTouchView();
13659 this.fireEvent('select', this, r, rowIndex);
13664 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13665 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13666 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13670 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13671 this.addItem(r.data);
13672 this.tickItems.push(r.data);
13678 * @cfg {Boolean} grow
13682 * @cfg {Number} growMin
13686 * @cfg {Number} growMax
13695 Roo.apply(Roo.bootstrap.ComboBox, {
13699 cls: 'modal-header',
13721 cls: 'list-group-item',
13725 cls: 'roo-combobox-list-group-item-value'
13729 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13743 listItemCheckbox : {
13745 cls: 'list-group-item',
13749 cls: 'roo-combobox-list-group-item-value'
13753 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13769 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13774 cls: 'modal-footer',
13782 cls: 'col-xs-6 text-left',
13785 cls: 'btn btn-danger roo-touch-view-cancel',
13791 cls: 'col-xs-6 text-right',
13794 cls: 'btn btn-success roo-touch-view-ok',
13805 Roo.apply(Roo.bootstrap.ComboBox, {
13807 touchViewTemplate : {
13809 cls: 'modal fade roo-combobox-touch-view',
13813 cls: 'modal-dialog',
13817 cls: 'modal-content',
13819 Roo.bootstrap.ComboBox.header,
13820 Roo.bootstrap.ComboBox.body,
13821 Roo.bootstrap.ComboBox.footer
13830 * Ext JS Library 1.1.1
13831 * Copyright(c) 2006-2007, Ext JS, LLC.
13833 * Originally Released Under LGPL - original licence link has changed is not relivant.
13836 * <script type="text/javascript">
13841 * @extends Roo.util.Observable
13842 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
13843 * This class also supports single and multi selection modes. <br>
13844 * Create a data model bound view:
13846 var store = new Roo.data.Store(...);
13848 var view = new Roo.View({
13850 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
13852 singleSelect: true,
13853 selectedClass: "ydataview-selected",
13857 // listen for node click?
13858 view.on("click", function(vw, index, node, e){
13859 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13863 dataModel.load("foobar.xml");
13865 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13867 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13868 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13870 * Note: old style constructor is still suported (container, template, config)
13873 * Create a new View
13874 * @param {Object} config The config object
13877 Roo.View = function(config, depreciated_tpl, depreciated_config){
13879 this.parent = false;
13881 if (typeof(depreciated_tpl) == 'undefined') {
13882 // new way.. - universal constructor.
13883 Roo.apply(this, config);
13884 this.el = Roo.get(this.el);
13887 this.el = Roo.get(config);
13888 this.tpl = depreciated_tpl;
13889 Roo.apply(this, depreciated_config);
13891 this.wrapEl = this.el.wrap().wrap();
13892 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13895 if(typeof(this.tpl) == "string"){
13896 this.tpl = new Roo.Template(this.tpl);
13898 // support xtype ctors..
13899 this.tpl = new Roo.factory(this.tpl, Roo);
13903 this.tpl.compile();
13908 * @event beforeclick
13909 * Fires before a click is processed. Returns false to cancel the default action.
13910 * @param {Roo.View} this
13911 * @param {Number} index The index of the target node
13912 * @param {HTMLElement} node The target node
13913 * @param {Roo.EventObject} e The raw event object
13915 "beforeclick" : true,
13918 * Fires when a template node is clicked.
13919 * @param {Roo.View} this
13920 * @param {Number} index The index of the target node
13921 * @param {HTMLElement} node The target node
13922 * @param {Roo.EventObject} e The raw event object
13927 * Fires when a template node is double clicked.
13928 * @param {Roo.View} this
13929 * @param {Number} index The index of the target node
13930 * @param {HTMLElement} node The target node
13931 * @param {Roo.EventObject} e The raw event object
13935 * @event contextmenu
13936 * Fires when a template node is right clicked.
13937 * @param {Roo.View} this
13938 * @param {Number} index The index of the target node
13939 * @param {HTMLElement} node The target node
13940 * @param {Roo.EventObject} e The raw event object
13942 "contextmenu" : true,
13944 * @event selectionchange
13945 * Fires when the selected nodes change.
13946 * @param {Roo.View} this
13947 * @param {Array} selections Array of the selected nodes
13949 "selectionchange" : true,
13952 * @event beforeselect
13953 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13954 * @param {Roo.View} this
13955 * @param {HTMLElement} node The node to be selected
13956 * @param {Array} selections Array of currently selected nodes
13958 "beforeselect" : true,
13960 * @event preparedata
13961 * Fires on every row to render, to allow you to change the data.
13962 * @param {Roo.View} this
13963 * @param {Object} data to be rendered (change this)
13965 "preparedata" : true
13973 "click": this.onClick,
13974 "dblclick": this.onDblClick,
13975 "contextmenu": this.onContextMenu,
13979 this.selections = [];
13981 this.cmp = new Roo.CompositeElementLite([]);
13983 this.store = Roo.factory(this.store, Roo.data);
13984 this.setStore(this.store, true);
13987 if ( this.footer && this.footer.xtype) {
13989 var fctr = this.wrapEl.appendChild(document.createElement("div"));
13991 this.footer.dataSource = this.store;
13992 this.footer.container = fctr;
13993 this.footer = Roo.factory(this.footer, Roo);
13994 fctr.insertFirst(this.el);
13996 // this is a bit insane - as the paging toolbar seems to detach the el..
13997 // dom.parentNode.parentNode.parentNode
13998 // they get detached?
14002 Roo.View.superclass.constructor.call(this);
14007 Roo.extend(Roo.View, Roo.util.Observable, {
14010 * @cfg {Roo.data.Store} store Data store to load data from.
14015 * @cfg {String|Roo.Element} el The container element.
14020 * @cfg {String|Roo.Template} tpl The template used by this View
14024 * @cfg {String} dataName the named area of the template to use as the data area
14025 * Works with domtemplates roo-name="name"
14029 * @cfg {String} selectedClass The css class to add to selected nodes
14031 selectedClass : "x-view-selected",
14033 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14038 * @cfg {String} text to display on mask (default Loading)
14042 * @cfg {Boolean} multiSelect Allow multiple selection
14044 multiSelect : false,
14046 * @cfg {Boolean} singleSelect Allow single selection
14048 singleSelect: false,
14051 * @cfg {Boolean} toggleSelect - selecting
14053 toggleSelect : false,
14056 * @cfg {Boolean} tickable - selecting
14061 * Returns the element this view is bound to.
14062 * @return {Roo.Element}
14064 getEl : function(){
14065 return this.wrapEl;
14071 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14073 refresh : function(){
14074 //Roo.log('refresh');
14077 // if we are using something like 'domtemplate', then
14078 // the what gets used is:
14079 // t.applySubtemplate(NAME, data, wrapping data..)
14080 // the outer template then get' applied with
14081 // the store 'extra data'
14082 // and the body get's added to the
14083 // roo-name="data" node?
14084 // <span class='roo-tpl-{name}'></span> ?????
14088 this.clearSelections();
14089 this.el.update("");
14091 var records = this.store.getRange();
14092 if(records.length < 1) {
14094 // is this valid?? = should it render a template??
14096 this.el.update(this.emptyText);
14100 if (this.dataName) {
14101 this.el.update(t.apply(this.store.meta)); //????
14102 el = this.el.child('.roo-tpl-' + this.dataName);
14105 for(var i = 0, len = records.length; i < len; i++){
14106 var data = this.prepareData(records[i].data, i, records[i]);
14107 this.fireEvent("preparedata", this, data, i, records[i]);
14109 var d = Roo.apply({}, data);
14112 Roo.apply(d, {'roo-id' : Roo.id()});
14116 Roo.each(this.parent.item, function(item){
14117 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14120 Roo.apply(d, {'roo-data-checked' : 'checked'});
14124 html[html.length] = Roo.util.Format.trim(
14126 t.applySubtemplate(this.dataName, d, this.store.meta) :
14133 el.update(html.join(""));
14134 this.nodes = el.dom.childNodes;
14135 this.updateIndexes(0);
14140 * Function to override to reformat the data that is sent to
14141 * the template for each node.
14142 * DEPRICATED - use the preparedata event handler.
14143 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14144 * a JSON object for an UpdateManager bound view).
14146 prepareData : function(data, index, record)
14148 this.fireEvent("preparedata", this, data, index, record);
14152 onUpdate : function(ds, record){
14153 // Roo.log('on update');
14154 this.clearSelections();
14155 var index = this.store.indexOf(record);
14156 var n = this.nodes[index];
14157 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14158 n.parentNode.removeChild(n);
14159 this.updateIndexes(index, index);
14165 onAdd : function(ds, records, index)
14167 //Roo.log(['on Add', ds, records, index] );
14168 this.clearSelections();
14169 if(this.nodes.length == 0){
14173 var n = this.nodes[index];
14174 for(var i = 0, len = records.length; i < len; i++){
14175 var d = this.prepareData(records[i].data, i, records[i]);
14177 this.tpl.insertBefore(n, d);
14180 this.tpl.append(this.el, d);
14183 this.updateIndexes(index);
14186 onRemove : function(ds, record, index){
14187 // Roo.log('onRemove');
14188 this.clearSelections();
14189 var el = this.dataName ?
14190 this.el.child('.roo-tpl-' + this.dataName) :
14193 el.dom.removeChild(this.nodes[index]);
14194 this.updateIndexes(index);
14198 * Refresh an individual node.
14199 * @param {Number} index
14201 refreshNode : function(index){
14202 this.onUpdate(this.store, this.store.getAt(index));
14205 updateIndexes : function(startIndex, endIndex){
14206 var ns = this.nodes;
14207 startIndex = startIndex || 0;
14208 endIndex = endIndex || ns.length - 1;
14209 for(var i = startIndex; i <= endIndex; i++){
14210 ns[i].nodeIndex = i;
14215 * Changes the data store this view uses and refresh the view.
14216 * @param {Store} store
14218 setStore : function(store, initial){
14219 if(!initial && this.store){
14220 this.store.un("datachanged", this.refresh);
14221 this.store.un("add", this.onAdd);
14222 this.store.un("remove", this.onRemove);
14223 this.store.un("update", this.onUpdate);
14224 this.store.un("clear", this.refresh);
14225 this.store.un("beforeload", this.onBeforeLoad);
14226 this.store.un("load", this.onLoad);
14227 this.store.un("loadexception", this.onLoad);
14231 store.on("datachanged", this.refresh, this);
14232 store.on("add", this.onAdd, this);
14233 store.on("remove", this.onRemove, this);
14234 store.on("update", this.onUpdate, this);
14235 store.on("clear", this.refresh, this);
14236 store.on("beforeload", this.onBeforeLoad, this);
14237 store.on("load", this.onLoad, this);
14238 store.on("loadexception", this.onLoad, this);
14246 * onbeforeLoad - masks the loading area.
14249 onBeforeLoad : function(store,opts)
14251 //Roo.log('onBeforeLoad');
14253 this.el.update("");
14255 this.el.mask(this.mask ? this.mask : "Loading" );
14257 onLoad : function ()
14264 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14265 * @param {HTMLElement} node
14266 * @return {HTMLElement} The template node
14268 findItemFromChild : function(node){
14269 var el = this.dataName ?
14270 this.el.child('.roo-tpl-' + this.dataName,true) :
14273 if(!node || node.parentNode == el){
14276 var p = node.parentNode;
14277 while(p && p != el){
14278 if(p.parentNode == el){
14287 onClick : function(e){
14288 var item = this.findItemFromChild(e.getTarget());
14290 var index = this.indexOf(item);
14291 if(this.onItemClick(item, index, e) !== false){
14292 this.fireEvent("click", this, index, item, e);
14295 this.clearSelections();
14300 onContextMenu : function(e){
14301 var item = this.findItemFromChild(e.getTarget());
14303 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14308 onDblClick : function(e){
14309 var item = this.findItemFromChild(e.getTarget());
14311 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14315 onItemClick : function(item, index, e)
14317 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14320 if (this.toggleSelect) {
14321 var m = this.isSelected(item) ? 'unselect' : 'select';
14324 _t[m](item, true, false);
14327 if(this.multiSelect || this.singleSelect){
14328 if(this.multiSelect && e.shiftKey && this.lastSelection){
14329 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14331 this.select(item, this.multiSelect && e.ctrlKey);
14332 this.lastSelection = item;
14335 if(!this.tickable){
14336 e.preventDefault();
14344 * Get the number of selected nodes.
14347 getSelectionCount : function(){
14348 return this.selections.length;
14352 * Get the currently selected nodes.
14353 * @return {Array} An array of HTMLElements
14355 getSelectedNodes : function(){
14356 return this.selections;
14360 * Get the indexes of the selected nodes.
14363 getSelectedIndexes : function(){
14364 var indexes = [], s = this.selections;
14365 for(var i = 0, len = s.length; i < len; i++){
14366 indexes.push(s[i].nodeIndex);
14372 * Clear all selections
14373 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14375 clearSelections : function(suppressEvent){
14376 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14377 this.cmp.elements = this.selections;
14378 this.cmp.removeClass(this.selectedClass);
14379 this.selections = [];
14380 if(!suppressEvent){
14381 this.fireEvent("selectionchange", this, this.selections);
14387 * Returns true if the passed node is selected
14388 * @param {HTMLElement/Number} node The node or node index
14389 * @return {Boolean}
14391 isSelected : function(node){
14392 var s = this.selections;
14396 node = this.getNode(node);
14397 return s.indexOf(node) !== -1;
14402 * @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
14403 * @param {Boolean} keepExisting (optional) true to keep existing selections
14404 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14406 select : function(nodeInfo, keepExisting, suppressEvent){
14407 if(nodeInfo instanceof Array){
14409 this.clearSelections(true);
14411 for(var i = 0, len = nodeInfo.length; i < len; i++){
14412 this.select(nodeInfo[i], true, true);
14416 var node = this.getNode(nodeInfo);
14417 if(!node || this.isSelected(node)){
14418 return; // already selected.
14421 this.clearSelections(true);
14424 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14425 Roo.fly(node).addClass(this.selectedClass);
14426 this.selections.push(node);
14427 if(!suppressEvent){
14428 this.fireEvent("selectionchange", this, this.selections);
14436 * @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
14437 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14438 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14440 unselect : function(nodeInfo, keepExisting, suppressEvent)
14442 if(nodeInfo instanceof Array){
14443 Roo.each(this.selections, function(s) {
14444 this.unselect(s, nodeInfo);
14448 var node = this.getNode(nodeInfo);
14449 if(!node || !this.isSelected(node)){
14450 //Roo.log("not selected");
14451 return; // not selected.
14455 Roo.each(this.selections, function(s) {
14457 Roo.fly(node).removeClass(this.selectedClass);
14464 this.selections= ns;
14465 this.fireEvent("selectionchange", this, this.selections);
14469 * Gets a template node.
14470 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14471 * @return {HTMLElement} The node or null if it wasn't found
14473 getNode : function(nodeInfo){
14474 if(typeof nodeInfo == "string"){
14475 return document.getElementById(nodeInfo);
14476 }else if(typeof nodeInfo == "number"){
14477 return this.nodes[nodeInfo];
14483 * Gets a range template nodes.
14484 * @param {Number} startIndex
14485 * @param {Number} endIndex
14486 * @return {Array} An array of nodes
14488 getNodes : function(start, end){
14489 var ns = this.nodes;
14490 start = start || 0;
14491 end = typeof end == "undefined" ? ns.length - 1 : end;
14494 for(var i = start; i <= end; i++){
14498 for(var i = start; i >= end; i--){
14506 * Finds the index of the passed node
14507 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14508 * @return {Number} The index of the node or -1
14510 indexOf : function(node){
14511 node = this.getNode(node);
14512 if(typeof node.nodeIndex == "number"){
14513 return node.nodeIndex;
14515 var ns = this.nodes;
14516 for(var i = 0, len = ns.length; i < len; i++){
14527 * based on jquery fullcalendar
14531 Roo.bootstrap = Roo.bootstrap || {};
14533 * @class Roo.bootstrap.Calendar
14534 * @extends Roo.bootstrap.Component
14535 * Bootstrap Calendar class
14536 * @cfg {Boolean} loadMask (true|false) default false
14537 * @cfg {Object} header generate the user specific header of the calendar, default false
14540 * Create a new Container
14541 * @param {Object} config The config object
14546 Roo.bootstrap.Calendar = function(config){
14547 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14551 * Fires when a date is selected
14552 * @param {DatePicker} this
14553 * @param {Date} date The selected date
14557 * @event monthchange
14558 * Fires when the displayed month changes
14559 * @param {DatePicker} this
14560 * @param {Date} date The selected month
14562 'monthchange': true,
14564 * @event evententer
14565 * Fires when mouse over an event
14566 * @param {Calendar} this
14567 * @param {event} Event
14569 'evententer': true,
14571 * @event eventleave
14572 * Fires when the mouse leaves an
14573 * @param {Calendar} this
14576 'eventleave': true,
14578 * @event eventclick
14579 * Fires when the mouse click an
14580 * @param {Calendar} this
14589 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
14592 * @cfg {Number} startDay
14593 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14601 getAutoCreate : function(){
14604 var fc_button = function(name, corner, style, content ) {
14605 return Roo.apply({},{
14607 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
14609 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14612 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14623 style : 'width:100%',
14630 cls : 'fc-header-left',
14632 fc_button('prev', 'left', 'arrow', '‹' ),
14633 fc_button('next', 'right', 'arrow', '›' ),
14634 { tag: 'span', cls: 'fc-header-space' },
14635 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
14643 cls : 'fc-header-center',
14647 cls: 'fc-header-title',
14650 html : 'month / year'
14658 cls : 'fc-header-right',
14660 /* fc_button('month', 'left', '', 'month' ),
14661 fc_button('week', '', '', 'week' ),
14662 fc_button('day', 'right', '', 'day' )
14674 header = this.header;
14677 var cal_heads = function() {
14679 // fixme - handle this.
14681 for (var i =0; i < Date.dayNames.length; i++) {
14682 var d = Date.dayNames[i];
14685 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14686 html : d.substring(0,3)
14690 ret[0].cls += ' fc-first';
14691 ret[6].cls += ' fc-last';
14694 var cal_cell = function(n) {
14697 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14702 cls: 'fc-day-number',
14706 cls: 'fc-day-content',
14710 style: 'position: relative;' // height: 17px;
14722 var cal_rows = function() {
14725 for (var r = 0; r < 6; r++) {
14732 for (var i =0; i < Date.dayNames.length; i++) {
14733 var d = Date.dayNames[i];
14734 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14737 row.cn[0].cls+=' fc-first';
14738 row.cn[0].cn[0].style = 'min-height:90px';
14739 row.cn[6].cls+=' fc-last';
14743 ret[0].cls += ' fc-first';
14744 ret[4].cls += ' fc-prev-last';
14745 ret[5].cls += ' fc-last';
14752 cls: 'fc-border-separate',
14753 style : 'width:100%',
14761 cls : 'fc-first fc-last',
14779 cls : 'fc-content',
14780 style : "position: relative;",
14783 cls : 'fc-view fc-view-month fc-grid',
14784 style : 'position: relative',
14785 unselectable : 'on',
14788 cls : 'fc-event-container',
14789 style : 'position:absolute;z-index:8;top:0;left:0;'
14807 initEvents : function()
14810 throw "can not find store for calendar";
14816 style: "text-align:center",
14820 style: "background-color:white;width:50%;margin:250 auto",
14824 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
14835 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14837 var size = this.el.select('.fc-content', true).first().getSize();
14838 this.maskEl.setSize(size.width, size.height);
14839 this.maskEl.enableDisplayMode("block");
14840 if(!this.loadMask){
14841 this.maskEl.hide();
14844 this.store = Roo.factory(this.store, Roo.data);
14845 this.store.on('load', this.onLoad, this);
14846 this.store.on('beforeload', this.onBeforeLoad, this);
14850 this.cells = this.el.select('.fc-day',true);
14851 //Roo.log(this.cells);
14852 this.textNodes = this.el.query('.fc-day-number');
14853 this.cells.addClassOnOver('fc-state-hover');
14855 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14856 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14857 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14858 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14860 this.on('monthchange', this.onMonthChange, this);
14862 this.update(new Date().clearTime());
14865 resize : function() {
14866 var sz = this.el.getSize();
14868 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14869 this.el.select('.fc-day-content div',true).setHeight(34);
14874 showPrevMonth : function(e){
14875 this.update(this.activeDate.add("mo", -1));
14877 showToday : function(e){
14878 this.update(new Date().clearTime());
14881 showNextMonth : function(e){
14882 this.update(this.activeDate.add("mo", 1));
14886 showPrevYear : function(){
14887 this.update(this.activeDate.add("y", -1));
14891 showNextYear : function(){
14892 this.update(this.activeDate.add("y", 1));
14897 update : function(date)
14899 var vd = this.activeDate;
14900 this.activeDate = date;
14901 // if(vd && this.el){
14902 // var t = date.getTime();
14903 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14904 // Roo.log('using add remove');
14906 // this.fireEvent('monthchange', this, date);
14908 // this.cells.removeClass("fc-state-highlight");
14909 // this.cells.each(function(c){
14910 // if(c.dateValue == t){
14911 // c.addClass("fc-state-highlight");
14912 // setTimeout(function(){
14913 // try{c.dom.firstChild.focus();}catch(e){}
14923 var days = date.getDaysInMonth();
14925 var firstOfMonth = date.getFirstDateOfMonth();
14926 var startingPos = firstOfMonth.getDay()-this.startDay;
14928 if(startingPos < this.startDay){
14932 var pm = date.add(Date.MONTH, -1);
14933 var prevStart = pm.getDaysInMonth()-startingPos;
14935 this.cells = this.el.select('.fc-day',true);
14936 this.textNodes = this.el.query('.fc-day-number');
14937 this.cells.addClassOnOver('fc-state-hover');
14939 var cells = this.cells.elements;
14940 var textEls = this.textNodes;
14942 Roo.each(cells, function(cell){
14943 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14946 days += startingPos;
14948 // convert everything to numbers so it's fast
14949 var day = 86400000;
14950 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14953 //Roo.log(prevStart);
14955 var today = new Date().clearTime().getTime();
14956 var sel = date.clearTime().getTime();
14957 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14958 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14959 var ddMatch = this.disabledDatesRE;
14960 var ddText = this.disabledDatesText;
14961 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14962 var ddaysText = this.disabledDaysText;
14963 var format = this.format;
14965 var setCellClass = function(cal, cell){
14969 //Roo.log('set Cell Class');
14971 var t = d.getTime();
14975 cell.dateValue = t;
14977 cell.className += " fc-today";
14978 cell.className += " fc-state-highlight";
14979 cell.title = cal.todayText;
14982 // disable highlight in other month..
14983 //cell.className += " fc-state-highlight";
14988 cell.className = " fc-state-disabled";
14989 cell.title = cal.minText;
14993 cell.className = " fc-state-disabled";
14994 cell.title = cal.maxText;
14998 if(ddays.indexOf(d.getDay()) != -1){
14999 cell.title = ddaysText;
15000 cell.className = " fc-state-disabled";
15003 if(ddMatch && format){
15004 var fvalue = d.dateFormat(format);
15005 if(ddMatch.test(fvalue)){
15006 cell.title = ddText.replace("%0", fvalue);
15007 cell.className = " fc-state-disabled";
15011 if (!cell.initialClassName) {
15012 cell.initialClassName = cell.dom.className;
15015 cell.dom.className = cell.initialClassName + ' ' + cell.className;
15020 for(; i < startingPos; i++) {
15021 textEls[i].innerHTML = (++prevStart);
15022 d.setDate(d.getDate()+1);
15024 cells[i].className = "fc-past fc-other-month";
15025 setCellClass(this, cells[i]);
15030 for(; i < days; i++){
15031 intDay = i - startingPos + 1;
15032 textEls[i].innerHTML = (intDay);
15033 d.setDate(d.getDate()+1);
15035 cells[i].className = ''; // "x-date-active";
15036 setCellClass(this, cells[i]);
15040 for(; i < 42; i++) {
15041 textEls[i].innerHTML = (++extraDays);
15042 d.setDate(d.getDate()+1);
15044 cells[i].className = "fc-future fc-other-month";
15045 setCellClass(this, cells[i]);
15048 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15050 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15052 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15053 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15055 if(totalRows != 6){
15056 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15057 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15060 this.fireEvent('monthchange', this, date);
15064 if(!this.internalRender){
15065 var main = this.el.dom.firstChild;
15066 var w = main.offsetWidth;
15067 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15068 Roo.fly(main).setWidth(w);
15069 this.internalRender = true;
15070 // opera does not respect the auto grow header center column
15071 // then, after it gets a width opera refuses to recalculate
15072 // without a second pass
15073 if(Roo.isOpera && !this.secondPass){
15074 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15075 this.secondPass = true;
15076 this.update.defer(10, this, [date]);
15083 findCell : function(dt) {
15084 dt = dt.clearTime().getTime();
15086 this.cells.each(function(c){
15087 //Roo.log("check " +c.dateValue + '?=' + dt);
15088 if(c.dateValue == dt){
15098 findCells : function(ev) {
15099 var s = ev.start.clone().clearTime().getTime();
15101 var e= ev.end.clone().clearTime().getTime();
15104 this.cells.each(function(c){
15105 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15107 if(c.dateValue > e){
15110 if(c.dateValue < s){
15119 // findBestRow: function(cells)
15123 // for (var i =0 ; i < cells.length;i++) {
15124 // ret = Math.max(cells[i].rows || 0,ret);
15131 addItem : function(ev)
15133 // look for vertical location slot in
15134 var cells = this.findCells(ev);
15136 // ev.row = this.findBestRow(cells);
15138 // work out the location.
15142 for(var i =0; i < cells.length; i++) {
15144 cells[i].row = cells[0].row;
15147 cells[i].row = cells[i].row + 1;
15157 if (crow.start.getY() == cells[i].getY()) {
15159 crow.end = cells[i];
15176 cells[0].events.push(ev);
15178 this.calevents.push(ev);
15181 clearEvents: function() {
15183 if(!this.calevents){
15187 Roo.each(this.cells.elements, function(c){
15193 Roo.each(this.calevents, function(e) {
15194 Roo.each(e.els, function(el) {
15195 el.un('mouseenter' ,this.onEventEnter, this);
15196 el.un('mouseleave' ,this.onEventLeave, this);
15201 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15207 renderEvents: function()
15211 this.cells.each(function(c) {
15220 if(c.row != c.events.length){
15221 r = 4 - (4 - (c.row - c.events.length));
15224 c.events = ev.slice(0, r);
15225 c.more = ev.slice(r);
15227 if(c.more.length && c.more.length == 1){
15228 c.events.push(c.more.pop());
15231 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15235 this.cells.each(function(c) {
15237 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15240 for (var e = 0; e < c.events.length; e++){
15241 var ev = c.events[e];
15242 var rows = ev.rows;
15244 for(var i = 0; i < rows.length; i++) {
15246 // how many rows should it span..
15249 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15250 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15252 unselectable : "on",
15255 cls: 'fc-event-inner',
15259 // cls: 'fc-event-time',
15260 // html : cells.length > 1 ? '' : ev.time
15264 cls: 'fc-event-title',
15265 html : String.format('{0}', ev.title)
15272 cls: 'ui-resizable-handle ui-resizable-e',
15273 html : '  '
15280 cfg.cls += ' fc-event-start';
15282 if ((i+1) == rows.length) {
15283 cfg.cls += ' fc-event-end';
15286 var ctr = _this.el.select('.fc-event-container',true).first();
15287 var cg = ctr.createChild(cfg);
15289 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15290 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15292 var r = (c.more.length) ? 1 : 0;
15293 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15294 cg.setWidth(ebox.right - sbox.x -2);
15296 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15297 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15298 cg.on('click', _this.onEventClick, _this, ev);
15309 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15310 style : 'position: absolute',
15311 unselectable : "on",
15314 cls: 'fc-event-inner',
15318 cls: 'fc-event-title',
15326 cls: 'ui-resizable-handle ui-resizable-e',
15327 html : '  '
15333 var ctr = _this.el.select('.fc-event-container',true).first();
15334 var cg = ctr.createChild(cfg);
15336 var sbox = c.select('.fc-day-content',true).first().getBox();
15337 var ebox = c.select('.fc-day-content',true).first().getBox();
15339 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15340 cg.setWidth(ebox.right - sbox.x -2);
15342 cg.on('click', _this.onMoreEventClick, _this, c.more);
15352 onEventEnter: function (e, el,event,d) {
15353 this.fireEvent('evententer', this, el, event);
15356 onEventLeave: function (e, el,event,d) {
15357 this.fireEvent('eventleave', this, el, event);
15360 onEventClick: function (e, el,event,d) {
15361 this.fireEvent('eventclick', this, el, event);
15364 onMonthChange: function () {
15368 onMoreEventClick: function(e, el, more)
15372 this.calpopover.placement = 'right';
15373 this.calpopover.setTitle('More');
15375 this.calpopover.setContent('');
15377 var ctr = this.calpopover.el.select('.popover-content', true).first();
15379 Roo.each(more, function(m){
15381 cls : 'fc-event-hori fc-event-draggable',
15384 var cg = ctr.createChild(cfg);
15386 cg.on('click', _this.onEventClick, _this, m);
15389 this.calpopover.show(el);
15394 onLoad: function ()
15396 this.calevents = [];
15399 if(this.store.getCount() > 0){
15400 this.store.data.each(function(d){
15403 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15404 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15405 time : d.data.start_time,
15406 title : d.data.title,
15407 description : d.data.description,
15408 venue : d.data.venue
15413 this.renderEvents();
15415 if(this.calevents.length && this.loadMask){
15416 this.maskEl.hide();
15420 onBeforeLoad: function()
15422 this.clearEvents();
15424 this.maskEl.show();
15438 * @class Roo.bootstrap.Popover
15439 * @extends Roo.bootstrap.Component
15440 * Bootstrap Popover class
15441 * @cfg {String} html contents of the popover (or false to use children..)
15442 * @cfg {String} title of popover (or false to hide)
15443 * @cfg {String} placement how it is placed
15444 * @cfg {String} trigger click || hover (or false to trigger manually)
15445 * @cfg {String} over what (parent or false to trigger manually.)
15446 * @cfg {Number} delay - delay before showing
15449 * Create a new Popover
15450 * @param {Object} config The config object
15453 Roo.bootstrap.Popover = function(config){
15454 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15457 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
15459 title: 'Fill in a title',
15462 placement : 'right',
15463 trigger : 'hover', // hover
15469 can_build_overlaid : false,
15471 getChildContainer : function()
15473 return this.el.select('.popover-content',true).first();
15476 getAutoCreate : function(){
15477 Roo.log('make popover?');
15479 cls : 'popover roo-dynamic',
15480 style: 'display:block',
15486 cls : 'popover-inner',
15490 cls: 'popover-title',
15494 cls : 'popover-content',
15505 setTitle: function(str)
15508 this.el.select('.popover-title',true).first().dom.innerHTML = str;
15510 setContent: function(str)
15513 this.el.select('.popover-content',true).first().dom.innerHTML = str;
15515 // as it get's added to the bottom of the page.
15516 onRender : function(ct, position)
15518 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15520 var cfg = Roo.apply({}, this.getAutoCreate());
15524 cfg.cls += ' ' + this.cls;
15527 cfg.style = this.style;
15529 Roo.log("adding to ")
15530 this.el = Roo.get(document.body).createChild(cfg, position);
15536 initEvents : function()
15538 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15539 this.el.enableDisplayMode('block');
15541 if (this.over === false) {
15544 if (this.triggers === false) {
15547 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15548 var triggers = this.trigger ? this.trigger.split(' ') : [];
15549 Roo.each(triggers, function(trigger) {
15551 if (trigger == 'click') {
15552 on_el.on('click', this.toggle, this);
15553 } else if (trigger != 'manual') {
15554 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
15555 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15557 on_el.on(eventIn ,this.enter, this);
15558 on_el.on(eventOut, this.leave, this);
15569 toggle : function () {
15570 this.hoverState == 'in' ? this.leave() : this.enter();
15573 enter : function () {
15576 clearTimeout(this.timeout);
15578 this.hoverState = 'in';
15580 if (!this.delay || !this.delay.show) {
15585 this.timeout = setTimeout(function () {
15586 if (_t.hoverState == 'in') {
15589 }, this.delay.show)
15591 leave : function() {
15592 clearTimeout(this.timeout);
15594 this.hoverState = 'out';
15596 if (!this.delay || !this.delay.hide) {
15601 this.timeout = setTimeout(function () {
15602 if (_t.hoverState == 'out') {
15605 }, this.delay.hide)
15608 show : function (on_el)
15611 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15614 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15615 if (this.html !== false) {
15616 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15618 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15619 if (!this.title.length) {
15620 this.el.select('.popover-title',true).hide();
15623 var placement = typeof this.placement == 'function' ?
15624 this.placement.call(this, this.el, on_el) :
15627 var autoToken = /\s?auto?\s?/i;
15628 var autoPlace = autoToken.test(placement);
15630 placement = placement.replace(autoToken, '') || 'top';
15634 //this.el.setXY([0,0]);
15636 this.el.dom.style.display='block';
15637 this.el.addClass(placement);
15639 //this.el.appendTo(on_el);
15641 var p = this.getPosition();
15642 var box = this.el.getBox();
15647 var align = Roo.bootstrap.Popover.alignment[placement];
15648 this.el.alignTo(on_el, align[0],align[1]);
15649 //var arrow = this.el.select('.arrow',true).first();
15650 //arrow.set(align[2],
15652 this.el.addClass('in');
15655 if (this.el.hasClass('fade')) {
15662 this.el.setXY([0,0]);
15663 this.el.removeClass('in');
15665 this.hoverState = null;
15671 Roo.bootstrap.Popover.alignment = {
15672 'left' : ['r-l', [-10,0], 'right'],
15673 'right' : ['l-r', [10,0], 'left'],
15674 'bottom' : ['t-b', [0,10], 'top'],
15675 'top' : [ 'b-t', [0,-10], 'bottom']
15686 * @class Roo.bootstrap.Progress
15687 * @extends Roo.bootstrap.Component
15688 * Bootstrap Progress class
15689 * @cfg {Boolean} striped striped of the progress bar
15690 * @cfg {Boolean} active animated of the progress bar
15694 * Create a new Progress
15695 * @param {Object} config The config object
15698 Roo.bootstrap.Progress = function(config){
15699 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15702 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
15707 getAutoCreate : function(){
15715 cfg.cls += ' progress-striped';
15719 cfg.cls += ' active';
15738 * @class Roo.bootstrap.ProgressBar
15739 * @extends Roo.bootstrap.Component
15740 * Bootstrap ProgressBar class
15741 * @cfg {Number} aria_valuenow aria-value now
15742 * @cfg {Number} aria_valuemin aria-value min
15743 * @cfg {Number} aria_valuemax aria-value max
15744 * @cfg {String} label label for the progress bar
15745 * @cfg {String} panel (success | info | warning | danger )
15746 * @cfg {String} role role of the progress bar
15747 * @cfg {String} sr_only text
15751 * Create a new ProgressBar
15752 * @param {Object} config The config object
15755 Roo.bootstrap.ProgressBar = function(config){
15756 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15759 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
15763 aria_valuemax : 100,
15769 getAutoCreate : function()
15774 cls: 'progress-bar',
15775 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15787 cfg.role = this.role;
15790 if(this.aria_valuenow){
15791 cfg['aria-valuenow'] = this.aria_valuenow;
15794 if(this.aria_valuemin){
15795 cfg['aria-valuemin'] = this.aria_valuemin;
15798 if(this.aria_valuemax){
15799 cfg['aria-valuemax'] = this.aria_valuemax;
15802 if(this.label && !this.sr_only){
15803 cfg.html = this.label;
15807 cfg.cls += ' progress-bar-' + this.panel;
15813 update : function(aria_valuenow)
15815 this.aria_valuenow = aria_valuenow;
15817 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15832 * @class Roo.bootstrap.TabGroup
15833 * @extends Roo.bootstrap.Column
15834 * Bootstrap Column class
15835 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15836 * @cfg {Boolean} carousel true to make the group behave like a carousel
15837 * @cfg {Number} bullets show the panel pointer.. default 0
15838 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15839 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15840 * @cfg {Number} timer auto slide timer .. default 0 millisecond
15843 * Create a new TabGroup
15844 * @param {Object} config The config object
15847 Roo.bootstrap.TabGroup = function(config){
15848 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15850 this.navId = Roo.id();
15853 Roo.bootstrap.TabGroup.register(this);
15857 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
15860 transition : false,
15865 slideOnTouch : false,
15867 getAutoCreate : function()
15869 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15871 cfg.cls += ' tab-content';
15873 Roo.log('get auto create...............');
15875 if (this.carousel) {
15876 cfg.cls += ' carousel slide';
15879 cls : 'carousel-inner'
15882 if(this.bullets > 0 && !Roo.isTouch){
15885 cls : 'carousel-bullets',
15889 if(this.bullets_cls){
15890 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15893 for (var i = 0; i < this.bullets; i++){
15895 cls : 'bullet bullet-' + i
15903 cfg.cn[0].cn = bullets;
15910 initEvents: function()
15912 Roo.log('-------- init events on tab group ---------');
15914 if(this.bullets > 0 && !Roo.isTouch){
15920 if(Roo.isTouch && this.slideOnTouch){
15921 this.el.on("touchstart", this.onTouchStart, this);
15924 if(this.autoslide){
15927 this.slideFn = window.setInterval(function() {
15928 _this.showPanelNext();
15934 onTouchStart : function(e, el, o)
15936 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15940 this.showPanelNext();
15943 getChildContainer : function()
15945 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15949 * register a Navigation item
15950 * @param {Roo.bootstrap.NavItem} the navitem to add
15952 register : function(item)
15954 this.tabs.push( item);
15955 item.navId = this.navId; // not really needed..
15959 getActivePanel : function()
15962 Roo.each(this.tabs, function(t) {
15972 getPanelByName : function(n)
15975 Roo.each(this.tabs, function(t) {
15976 if (t.tabId == n) {
15984 indexOfPanel : function(p)
15987 Roo.each(this.tabs, function(t,i) {
15988 if (t.tabId == p.tabId) {
15997 * show a specific panel
15998 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15999 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16001 showPanel : function (pan)
16003 if(this.transition){
16004 Roo.log("waiting for the transitionend");
16008 if (typeof(pan) == 'number') {
16009 pan = this.tabs[pan];
16011 if (typeof(pan) == 'string') {
16012 pan = this.getPanelByName(pan);
16014 if (pan.tabId == this.getActivePanel().tabId) {
16017 var cur = this.getActivePanel();
16019 if (false === cur.fireEvent('beforedeactivate')) {
16023 if(this.bullets > 0 && !Roo.isTouch){
16024 this.setActiveBullet(this.indexOfPanel(pan));
16027 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16029 this.transition = true;
16030 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16031 var lr = dir == 'next' ? 'left' : 'right';
16032 pan.el.addClass(dir); // or prev
16033 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16034 cur.el.addClass(lr); // or right
16035 pan.el.addClass(lr);
16038 cur.el.on('transitionend', function() {
16039 Roo.log("trans end?");
16041 pan.el.removeClass([lr,dir]);
16042 pan.setActive(true);
16044 cur.el.removeClass([lr]);
16045 cur.setActive(false);
16047 _this.transition = false;
16049 }, this, { single: true } );
16054 cur.setActive(false);
16055 pan.setActive(true);
16060 showPanelNext : function()
16062 var i = this.indexOfPanel(this.getActivePanel());
16064 if (i >= this.tabs.length - 1 && !this.autoslide) {
16068 if (i >= this.tabs.length - 1 && this.autoslide) {
16072 this.showPanel(this.tabs[i+1]);
16075 showPanelPrev : function()
16077 var i = this.indexOfPanel(this.getActivePanel());
16079 if (i < 1 && !this.autoslide) {
16083 if (i < 1 && this.autoslide) {
16084 i = this.tabs.length;
16087 this.showPanel(this.tabs[i-1]);
16090 initBullet : function()
16098 for (var i = 0; i < this.bullets; i++){
16099 var bullet = this.el.select('.bullet-' + i, true).first();
16105 bullet.on('click', (function(e, el, o, ii, t){
16107 e.preventDefault();
16109 _this.showPanel(ii);
16111 if(_this.autoslide && _this.slideFn){
16112 clearInterval(_this.slideFn);
16113 _this.slideFn = window.setInterval(function() {
16114 _this.showPanelNext();
16118 }).createDelegate(this, [i, bullet], true));
16122 setActiveBullet : function(i)
16128 Roo.each(this.el.select('.bullet', true).elements, function(el){
16129 el.removeClass('selected');
16132 var bullet = this.el.select('.bullet-' + i, true).first();
16138 bullet.addClass('selected');
16149 Roo.apply(Roo.bootstrap.TabGroup, {
16153 * register a Navigation Group
16154 * @param {Roo.bootstrap.NavGroup} the navgroup to add
16156 register : function(navgrp)
16158 this.groups[navgrp.navId] = navgrp;
16162 * fetch a Navigation Group based on the navigation ID
16163 * if one does not exist , it will get created.
16164 * @param {string} the navgroup to add
16165 * @returns {Roo.bootstrap.NavGroup} the navgroup
16167 get: function(navId) {
16168 if (typeof(this.groups[navId]) == 'undefined') {
16169 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16171 return this.groups[navId] ;
16186 * @class Roo.bootstrap.TabPanel
16187 * @extends Roo.bootstrap.Component
16188 * Bootstrap TabPanel class
16189 * @cfg {Boolean} active panel active
16190 * @cfg {String} html panel content
16191 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16192 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16196 * Create a new TabPanel
16197 * @param {Object} config The config object
16200 Roo.bootstrap.TabPanel = function(config){
16201 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16205 * Fires when the active status changes
16206 * @param {Roo.bootstrap.TabPanel} this
16207 * @param {Boolean} state the new state
16212 * @event beforedeactivate
16213 * Fires before a tab is de-activated - can be used to do validation on a form.
16214 * @param {Roo.bootstrap.TabPanel} this
16215 * @return {Boolean} false if there is an error
16218 'beforedeactivate': true
16221 this.tabId = this.tabId || Roo.id();
16225 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16232 getAutoCreate : function(){
16235 // item is needed for carousel - not sure if it has any effect otherwise
16236 cls: 'tab-pane item',
16237 html: this.html || ''
16241 cfg.cls += ' active';
16245 cfg.tabId = this.tabId;
16252 initEvents: function()
16254 Roo.log('-------- init events on tab panel ---------');
16256 var p = this.parent();
16257 this.navId = this.navId || p.navId;
16259 if (typeof(this.navId) != 'undefined') {
16260 // not really needed.. but just in case.. parent should be a NavGroup.
16261 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16262 Roo.log(['register', tg, this]);
16265 var i = tg.tabs.length - 1;
16267 if(this.active && tg.bullets > 0 && i < tg.bullets){
16268 tg.setActiveBullet(i);
16275 onRender : function(ct, position)
16277 // Roo.log("Call onRender: " + this.xtype);
16279 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16287 setActive: function(state)
16289 Roo.log("panel - set active " + this.tabId + "=" + state);
16291 this.active = state;
16293 this.el.removeClass('active');
16295 } else if (!this.el.hasClass('active')) {
16296 this.el.addClass('active');
16299 this.fireEvent('changed', this, state);
16316 * @class Roo.bootstrap.DateField
16317 * @extends Roo.bootstrap.Input
16318 * Bootstrap DateField class
16319 * @cfg {Number} weekStart default 0
16320 * @cfg {String} viewMode default empty, (months|years)
16321 * @cfg {String} minViewMode default empty, (months|years)
16322 * @cfg {Number} startDate default -Infinity
16323 * @cfg {Number} endDate default Infinity
16324 * @cfg {Boolean} todayHighlight default false
16325 * @cfg {Boolean} todayBtn default false
16326 * @cfg {Boolean} calendarWeeks default false
16327 * @cfg {Object} daysOfWeekDisabled default empty
16328 * @cfg {Boolean} singleMode default false (true | false)
16330 * @cfg {Boolean} keyboardNavigation default true
16331 * @cfg {String} language default en
16334 * Create a new DateField
16335 * @param {Object} config The config object
16338 Roo.bootstrap.DateField = function(config){
16339 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16343 * Fires when this field show.
16344 * @param {Roo.bootstrap.DateField} this
16345 * @param {Mixed} date The date value
16350 * Fires when this field hide.
16351 * @param {Roo.bootstrap.DateField} this
16352 * @param {Mixed} date The date value
16357 * Fires when select a date.
16358 * @param {Roo.bootstrap.DateField} this
16359 * @param {Mixed} date The date value
16363 * @event beforeselect
16364 * Fires when before select a date.
16365 * @param {Roo.bootstrap.DateField} this
16366 * @param {Mixed} date The date value
16368 beforeselect : true
16372 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16375 * @cfg {String} format
16376 * The default date format string which can be overriden for localization support. The format must be
16377 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16381 * @cfg {String} altFormats
16382 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16383 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16385 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16393 todayHighlight : false,
16399 keyboardNavigation: true,
16401 calendarWeeks: false,
16403 startDate: -Infinity,
16407 daysOfWeekDisabled: [],
16411 singleMode : false,
16413 UTCDate: function()
16415 return new Date(Date.UTC.apply(Date, arguments));
16418 UTCToday: function()
16420 var today = new Date();
16421 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16424 getDate: function() {
16425 var d = this.getUTCDate();
16426 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16429 getUTCDate: function() {
16433 setDate: function(d) {
16434 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16437 setUTCDate: function(d) {
16439 this.setValue(this.formatDate(this.date));
16442 onRender: function(ct, position)
16445 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16447 this.language = this.language || 'en';
16448 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16449 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16451 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16452 this.format = this.format || 'm/d/y';
16453 this.isInline = false;
16454 this.isInput = true;
16455 this.component = this.el.select('.add-on', true).first() || false;
16456 this.component = (this.component && this.component.length === 0) ? false : this.component;
16457 this.hasInput = this.component && this.inputEL().length;
16459 if (typeof(this.minViewMode === 'string')) {
16460 switch (this.minViewMode) {
16462 this.minViewMode = 1;
16465 this.minViewMode = 2;
16468 this.minViewMode = 0;
16473 if (typeof(this.viewMode === 'string')) {
16474 switch (this.viewMode) {
16487 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16489 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16491 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16493 this.picker().on('mousedown', this.onMousedown, this);
16494 this.picker().on('click', this.onClick, this);
16496 this.picker().addClass('datepicker-dropdown');
16498 this.startViewMode = this.viewMode;
16500 if(this.singleMode){
16501 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16502 v.setVisibilityMode(Roo.Element.DISPLAY)
16506 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16507 v.setStyle('width', '189px');
16511 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16512 if(!this.calendarWeeks){
16517 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16518 v.attr('colspan', function(i, val){
16519 return parseInt(val) + 1;
16524 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16526 this.setStartDate(this.startDate);
16527 this.setEndDate(this.endDate);
16529 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16536 if(this.isInline) {
16541 picker : function()
16543 return this.pickerEl;
16544 // return this.el.select('.datepicker', true).first();
16547 fillDow: function()
16549 var dowCnt = this.weekStart;
16558 if(this.calendarWeeks){
16566 while (dowCnt < this.weekStart + 7) {
16570 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16574 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16577 fillMonths: function()
16580 var months = this.picker().select('>.datepicker-months td', true).first();
16582 months.dom.innerHTML = '';
16588 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16591 months.createChild(month);
16598 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;
16600 if (this.date < this.startDate) {
16601 this.viewDate = new Date(this.startDate);
16602 } else if (this.date > this.endDate) {
16603 this.viewDate = new Date(this.endDate);
16605 this.viewDate = new Date(this.date);
16613 var d = new Date(this.viewDate),
16614 year = d.getUTCFullYear(),
16615 month = d.getUTCMonth(),
16616 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16617 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16618 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16619 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16620 currentDate = this.date && this.date.valueOf(),
16621 today = this.UTCToday();
16623 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16625 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16627 // this.picker.select('>tfoot th.today').
16628 // .text(dates[this.language].today)
16629 // .toggle(this.todayBtn !== false);
16631 this.updateNavArrows();
16634 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16636 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16638 prevMonth.setUTCDate(day);
16640 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16642 var nextMonth = new Date(prevMonth);
16644 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16646 nextMonth = nextMonth.valueOf();
16648 var fillMonths = false;
16650 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16652 while(prevMonth.valueOf() < nextMonth) {
16655 if (prevMonth.getUTCDay() === this.weekStart) {
16657 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16665 if(this.calendarWeeks){
16666 // ISO 8601: First week contains first thursday.
16667 // ISO also states week starts on Monday, but we can be more abstract here.
16669 // Start of current week: based on weekstart/current date
16670 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16671 // Thursday of this week
16672 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16673 // First Thursday of year, year from thursday
16674 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16675 // Calendar week: ms between thursdays, div ms per day, div 7 days
16676 calWeek = (th - yth) / 864e5 / 7 + 1;
16678 fillMonths.cn.push({
16686 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16688 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16691 if (this.todayHighlight &&
16692 prevMonth.getUTCFullYear() == today.getFullYear() &&
16693 prevMonth.getUTCMonth() == today.getMonth() &&
16694 prevMonth.getUTCDate() == today.getDate()) {
16695 clsName += ' today';
16698 if (currentDate && prevMonth.valueOf() === currentDate) {
16699 clsName += ' active';
16702 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16703 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16704 clsName += ' disabled';
16707 fillMonths.cn.push({
16709 cls: 'day ' + clsName,
16710 html: prevMonth.getDate()
16713 prevMonth.setDate(prevMonth.getDate()+1);
16716 var currentYear = this.date && this.date.getUTCFullYear();
16717 var currentMonth = this.date && this.date.getUTCMonth();
16719 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16721 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16722 v.removeClass('active');
16724 if(currentYear === year && k === currentMonth){
16725 v.addClass('active');
16728 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16729 v.addClass('disabled');
16735 year = parseInt(year/10, 10) * 10;
16737 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16739 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16742 for (var i = -1; i < 11; i++) {
16743 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16745 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16753 showMode: function(dir)
16756 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16759 Roo.each(this.picker().select('>div',true).elements, function(v){
16760 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16763 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16768 if(this.isInline) return;
16770 this.picker().removeClass(['bottom', 'top']);
16772 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16774 * place to the top of element!
16778 this.picker().addClass('top');
16779 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16784 this.picker().addClass('bottom');
16786 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16789 parseDate : function(value)
16791 if(!value || value instanceof Date){
16794 var v = Date.parseDate(value, this.format);
16795 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16796 v = Date.parseDate(value, 'Y-m-d');
16798 if(!v && this.altFormats){
16799 if(!this.altFormatsArray){
16800 this.altFormatsArray = this.altFormats.split("|");
16802 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16803 v = Date.parseDate(value, this.altFormatsArray[i]);
16809 formatDate : function(date, fmt)
16811 return (!date || !(date instanceof Date)) ?
16812 date : date.dateFormat(fmt || this.format);
16815 onFocus : function()
16817 Roo.bootstrap.DateField.superclass.onFocus.call(this);
16821 onBlur : function()
16823 Roo.bootstrap.DateField.superclass.onBlur.call(this);
16825 var d = this.inputEl().getValue();
16834 this.picker().show();
16838 this.fireEvent('show', this, this.date);
16843 if(this.isInline) return;
16844 this.picker().hide();
16845 this.viewMode = this.startViewMode;
16848 this.fireEvent('hide', this, this.date);
16852 onMousedown: function(e)
16854 e.stopPropagation();
16855 e.preventDefault();
16860 Roo.bootstrap.DateField.superclass.keyup.call(this);
16864 setValue: function(v)
16866 if(this.fireEvent('beforeselect', this, v) !== false){
16867 var d = new Date(this.parseDate(v) ).clearTime();
16869 if(isNaN(d.getTime())){
16870 this.date = this.viewDate = '';
16871 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16875 v = this.formatDate(d);
16877 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16879 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16883 this.fireEvent('select', this, this.date);
16887 getValue: function()
16889 return this.formatDate(this.date);
16892 fireKey: function(e)
16894 if (!this.picker().isVisible()){
16895 if (e.keyCode == 27) // allow escape to hide and re-show picker
16900 var dateChanged = false,
16902 newDate, newViewDate;
16907 e.preventDefault();
16911 if (!this.keyboardNavigation) break;
16912 dir = e.keyCode == 37 ? -1 : 1;
16915 newDate = this.moveYear(this.date, dir);
16916 newViewDate = this.moveYear(this.viewDate, dir);
16917 } else if (e.shiftKey){
16918 newDate = this.moveMonth(this.date, dir);
16919 newViewDate = this.moveMonth(this.viewDate, dir);
16921 newDate = new Date(this.date);
16922 newDate.setUTCDate(this.date.getUTCDate() + dir);
16923 newViewDate = new Date(this.viewDate);
16924 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16926 if (this.dateWithinRange(newDate)){
16927 this.date = newDate;
16928 this.viewDate = newViewDate;
16929 this.setValue(this.formatDate(this.date));
16931 e.preventDefault();
16932 dateChanged = true;
16937 if (!this.keyboardNavigation) break;
16938 dir = e.keyCode == 38 ? -1 : 1;
16940 newDate = this.moveYear(this.date, dir);
16941 newViewDate = this.moveYear(this.viewDate, dir);
16942 } else if (e.shiftKey){
16943 newDate = this.moveMonth(this.date, dir);
16944 newViewDate = this.moveMonth(this.viewDate, dir);
16946 newDate = new Date(this.date);
16947 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16948 newViewDate = new Date(this.viewDate);
16949 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16951 if (this.dateWithinRange(newDate)){
16952 this.date = newDate;
16953 this.viewDate = newViewDate;
16954 this.setValue(this.formatDate(this.date));
16956 e.preventDefault();
16957 dateChanged = true;
16961 this.setValue(this.formatDate(this.date));
16963 e.preventDefault();
16966 this.setValue(this.formatDate(this.date));
16980 onClick: function(e)
16982 e.stopPropagation();
16983 e.preventDefault();
16985 var target = e.getTarget();
16987 if(target.nodeName.toLowerCase() === 'i'){
16988 target = Roo.get(target).dom.parentNode;
16991 var nodeName = target.nodeName;
16992 var className = target.className;
16993 var html = target.innerHTML;
16994 //Roo.log(nodeName);
16996 switch(nodeName.toLowerCase()) {
16998 switch(className) {
17004 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17005 switch(this.viewMode){
17007 this.viewDate = this.moveMonth(this.viewDate, dir);
17011 this.viewDate = this.moveYear(this.viewDate, dir);
17017 var date = new Date();
17018 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17020 this.setValue(this.formatDate(this.date));
17027 if (className.indexOf('disabled') < 0) {
17028 this.viewDate.setUTCDate(1);
17029 if (className.indexOf('month') > -1) {
17030 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17032 var year = parseInt(html, 10) || 0;
17033 this.viewDate.setUTCFullYear(year);
17037 if(this.singleMode){
17038 this.setValue(this.formatDate(this.viewDate));
17049 //Roo.log(className);
17050 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17051 var day = parseInt(html, 10) || 1;
17052 var year = this.viewDate.getUTCFullYear(),
17053 month = this.viewDate.getUTCMonth();
17055 if (className.indexOf('old') > -1) {
17062 } else if (className.indexOf('new') > -1) {
17070 //Roo.log([year,month,day]);
17071 this.date = this.UTCDate(year, month, day,0,0,0,0);
17072 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17074 //Roo.log(this.formatDate(this.date));
17075 this.setValue(this.formatDate(this.date));
17082 setStartDate: function(startDate)
17084 this.startDate = startDate || -Infinity;
17085 if (this.startDate !== -Infinity) {
17086 this.startDate = this.parseDate(this.startDate);
17089 this.updateNavArrows();
17092 setEndDate: function(endDate)
17094 this.endDate = endDate || Infinity;
17095 if (this.endDate !== Infinity) {
17096 this.endDate = this.parseDate(this.endDate);
17099 this.updateNavArrows();
17102 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17104 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17105 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17106 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17108 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17109 return parseInt(d, 10);
17112 this.updateNavArrows();
17115 updateNavArrows: function()
17117 if(this.singleMode){
17121 var d = new Date(this.viewDate),
17122 year = d.getUTCFullYear(),
17123 month = d.getUTCMonth();
17125 Roo.each(this.picker().select('.prev', true).elements, function(v){
17127 switch (this.viewMode) {
17130 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17136 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17143 Roo.each(this.picker().select('.next', true).elements, function(v){
17145 switch (this.viewMode) {
17148 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17154 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17162 moveMonth: function(date, dir)
17164 if (!dir) return date;
17165 var new_date = new Date(date.valueOf()),
17166 day = new_date.getUTCDate(),
17167 month = new_date.getUTCMonth(),
17168 mag = Math.abs(dir),
17170 dir = dir > 0 ? 1 : -1;
17173 // If going back one month, make sure month is not current month
17174 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17176 return new_date.getUTCMonth() == month;
17178 // If going forward one month, make sure month is as expected
17179 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17181 return new_date.getUTCMonth() != new_month;
17183 new_month = month + dir;
17184 new_date.setUTCMonth(new_month);
17185 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17186 if (new_month < 0 || new_month > 11)
17187 new_month = (new_month + 12) % 12;
17189 // For magnitudes >1, move one month at a time...
17190 for (var i=0; i<mag; i++)
17191 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17192 new_date = this.moveMonth(new_date, dir);
17193 // ...then reset the day, keeping it in the new month
17194 new_month = new_date.getUTCMonth();
17195 new_date.setUTCDate(day);
17197 return new_month != new_date.getUTCMonth();
17200 // Common date-resetting loop -- if date is beyond end of month, make it
17203 new_date.setUTCDate(--day);
17204 new_date.setUTCMonth(new_month);
17209 moveYear: function(date, dir)
17211 return this.moveMonth(date, dir*12);
17214 dateWithinRange: function(date)
17216 return date >= this.startDate && date <= this.endDate;
17222 this.picker().remove();
17227 Roo.apply(Roo.bootstrap.DateField, {
17238 html: '<i class="fa fa-arrow-left"/>'
17248 html: '<i class="fa fa-arrow-right"/>'
17290 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17291 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17292 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17293 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17294 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17307 navFnc: 'FullYear',
17312 navFnc: 'FullYear',
17317 Roo.apply(Roo.bootstrap.DateField, {
17321 cls: 'datepicker dropdown-menu roo-dynamic',
17325 cls: 'datepicker-days',
17329 cls: 'table-condensed',
17331 Roo.bootstrap.DateField.head,
17335 Roo.bootstrap.DateField.footer
17342 cls: 'datepicker-months',
17346 cls: 'table-condensed',
17348 Roo.bootstrap.DateField.head,
17349 Roo.bootstrap.DateField.content,
17350 Roo.bootstrap.DateField.footer
17357 cls: 'datepicker-years',
17361 cls: 'table-condensed',
17363 Roo.bootstrap.DateField.head,
17364 Roo.bootstrap.DateField.content,
17365 Roo.bootstrap.DateField.footer
17384 * @class Roo.bootstrap.TimeField
17385 * @extends Roo.bootstrap.Input
17386 * Bootstrap DateField class
17390 * Create a new TimeField
17391 * @param {Object} config The config object
17394 Roo.bootstrap.TimeField = function(config){
17395 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17399 * Fires when this field show.
17400 * @param {Roo.bootstrap.DateField} thisthis
17401 * @param {Mixed} date The date value
17406 * Fires when this field hide.
17407 * @param {Roo.bootstrap.DateField} this
17408 * @param {Mixed} date The date value
17413 * Fires when select a date.
17414 * @param {Roo.bootstrap.DateField} this
17415 * @param {Mixed} date The date value
17421 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17424 * @cfg {String} format
17425 * The default time format string which can be overriden for localization support. The format must be
17426 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17430 onRender: function(ct, position)
17433 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17435 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17437 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17439 this.pop = this.picker().select('>.datepicker-time',true).first();
17440 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17442 this.picker().on('mousedown', this.onMousedown, this);
17443 this.picker().on('click', this.onClick, this);
17445 this.picker().addClass('datepicker-dropdown');
17450 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17451 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17452 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17453 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17454 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17455 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17459 fireKey: function(e){
17460 if (!this.picker().isVisible()){
17461 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17467 e.preventDefault();
17475 this.onTogglePeriod();
17478 this.onIncrementMinutes();
17481 this.onDecrementMinutes();
17490 onClick: function(e) {
17491 e.stopPropagation();
17492 e.preventDefault();
17495 picker : function()
17497 return this.el.select('.datepicker', true).first();
17500 fillTime: function()
17502 var time = this.pop.select('tbody', true).first();
17504 time.dom.innerHTML = '';
17519 cls: 'hours-up glyphicon glyphicon-chevron-up'
17539 cls: 'minutes-up glyphicon glyphicon-chevron-up'
17560 cls: 'timepicker-hour',
17575 cls: 'timepicker-minute',
17590 cls: 'btn btn-primary period',
17612 cls: 'hours-down glyphicon glyphicon-chevron-down'
17632 cls: 'minutes-down glyphicon glyphicon-chevron-down'
17650 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17657 var hours = this.time.getHours();
17658 var minutes = this.time.getMinutes();
17671 hours = hours - 12;
17675 hours = '0' + hours;
17679 minutes = '0' + minutes;
17682 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17683 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17684 this.pop.select('button', true).first().dom.innerHTML = period;
17690 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17692 var cls = ['bottom'];
17694 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17701 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17706 this.picker().addClass(cls.join('-'));
17710 Roo.each(cls, function(c){
17712 _this.picker().setTop(_this.inputEl().getHeight());
17716 _this.picker().setTop(0 - _this.picker().getHeight());
17721 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17725 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17732 onFocus : function()
17734 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17738 onBlur : function()
17740 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17746 this.picker().show();
17751 this.fireEvent('show', this, this.date);
17756 this.picker().hide();
17759 this.fireEvent('hide', this, this.date);
17762 setTime : function()
17765 this.setValue(this.time.format(this.format));
17767 this.fireEvent('select', this, this.date);
17772 onMousedown: function(e){
17773 e.stopPropagation();
17774 e.preventDefault();
17777 onIncrementHours: function()
17779 Roo.log('onIncrementHours');
17780 this.time = this.time.add(Date.HOUR, 1);
17785 onDecrementHours: function()
17787 Roo.log('onDecrementHours');
17788 this.time = this.time.add(Date.HOUR, -1);
17792 onIncrementMinutes: function()
17794 Roo.log('onIncrementMinutes');
17795 this.time = this.time.add(Date.MINUTE, 1);
17799 onDecrementMinutes: function()
17801 Roo.log('onDecrementMinutes');
17802 this.time = this.time.add(Date.MINUTE, -1);
17806 onTogglePeriod: function()
17808 Roo.log('onTogglePeriod');
17809 this.time = this.time.add(Date.HOUR, 12);
17816 Roo.apply(Roo.bootstrap.TimeField, {
17846 cls: 'btn btn-info ok',
17858 Roo.apply(Roo.bootstrap.TimeField, {
17862 cls: 'datepicker dropdown-menu',
17866 cls: 'datepicker-time',
17870 cls: 'table-condensed',
17872 Roo.bootstrap.TimeField.content,
17873 Roo.bootstrap.TimeField.footer
17892 * @class Roo.bootstrap.MonthField
17893 * @extends Roo.bootstrap.Input
17894 * Bootstrap MonthField class
17896 * @cfg {String} language default en
17899 * Create a new MonthField
17900 * @param {Object} config The config object
17903 Roo.bootstrap.MonthField = function(config){
17904 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17909 * Fires when this field show.
17910 * @param {Roo.bootstrap.MonthField} this
17911 * @param {Mixed} date The date value
17916 * Fires when this field hide.
17917 * @param {Roo.bootstrap.MonthField} this
17918 * @param {Mixed} date The date value
17923 * Fires when select a date.
17924 * @param {Roo.bootstrap.MonthField} this
17925 * @param {String} oldvalue The old value
17926 * @param {String} newvalue The new value
17932 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
17934 onRender: function(ct, position)
17937 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17939 this.language = this.language || 'en';
17940 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17941 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17943 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17944 this.isInline = false;
17945 this.isInput = true;
17946 this.component = this.el.select('.add-on', true).first() || false;
17947 this.component = (this.component && this.component.length === 0) ? false : this.component;
17948 this.hasInput = this.component && this.inputEL().length;
17950 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17952 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17954 this.picker().on('mousedown', this.onMousedown, this);
17955 this.picker().on('click', this.onClick, this);
17957 this.picker().addClass('datepicker-dropdown');
17959 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17960 v.setStyle('width', '189px');
17967 if(this.isInline) {
17973 setValue: function(v, suppressEvent)
17975 var o = this.getValue();
17977 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17981 if(suppressEvent !== true){
17982 this.fireEvent('select', this, o, v);
17987 getValue: function()
17992 onClick: function(e)
17994 e.stopPropagation();
17995 e.preventDefault();
17997 var target = e.getTarget();
17999 if(target.nodeName.toLowerCase() === 'i'){
18000 target = Roo.get(target).dom.parentNode;
18003 var nodeName = target.nodeName;
18004 var className = target.className;
18005 var html = target.innerHTML;
18007 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18011 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18013 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18019 picker : function()
18021 return this.pickerEl;
18024 fillMonths: function()
18027 var months = this.picker().select('>.datepicker-months td', true).first();
18029 months.dom.innerHTML = '';
18035 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18038 months.createChild(month);
18047 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18048 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18051 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18052 e.removeClass('active');
18054 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18055 e.addClass('active');
18062 if(this.isInline) return;
18064 this.picker().removeClass(['bottom', 'top']);
18066 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18068 * place to the top of element!
18072 this.picker().addClass('top');
18073 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18078 this.picker().addClass('bottom');
18080 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18083 onFocus : function()
18085 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18089 onBlur : function()
18091 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18093 var d = this.inputEl().getValue();
18102 this.picker().show();
18103 this.picker().select('>.datepicker-months', true).first().show();
18107 this.fireEvent('show', this, this.date);
18112 if(this.isInline) return;
18113 this.picker().hide();
18114 this.fireEvent('hide', this, this.date);
18118 onMousedown: function(e)
18120 e.stopPropagation();
18121 e.preventDefault();
18126 Roo.bootstrap.MonthField.superclass.keyup.call(this);
18130 fireKey: function(e)
18132 if (!this.picker().isVisible()){
18133 if (e.keyCode == 27) // allow escape to hide and re-show picker
18143 e.preventDefault();
18147 dir = e.keyCode == 37 ? -1 : 1;
18149 this.vIndex = this.vIndex + dir;
18151 if(this.vIndex < 0){
18155 if(this.vIndex > 11){
18159 if(isNaN(this.vIndex)){
18163 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18169 dir = e.keyCode == 38 ? -1 : 1;
18171 this.vIndex = this.vIndex + dir * 4;
18173 if(this.vIndex < 0){
18177 if(this.vIndex > 11){
18181 if(isNaN(this.vIndex)){
18185 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18190 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18191 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18195 e.preventDefault();
18198 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18199 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18215 this.picker().remove();
18220 Roo.apply(Roo.bootstrap.MonthField, {
18239 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18240 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18245 Roo.apply(Roo.bootstrap.MonthField, {
18249 cls: 'datepicker dropdown-menu roo-dynamic',
18253 cls: 'datepicker-months',
18257 cls: 'table-condensed',
18259 Roo.bootstrap.DateField.content
18279 * @class Roo.bootstrap.CheckBox
18280 * @extends Roo.bootstrap.Input
18281 * Bootstrap CheckBox class
18283 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18284 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18285 * @cfg {String} boxLabel The text that appears beside the checkbox
18286 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18287 * @cfg {Boolean} checked initnal the element
18288 * @cfg {Boolean} inline inline the element (default false)
18289 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18292 * Create a new CheckBox
18293 * @param {Object} config The config object
18296 Roo.bootstrap.CheckBox = function(config){
18297 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18302 * Fires when the element is checked or unchecked.
18303 * @param {Roo.bootstrap.CheckBox} this This input
18304 * @param {Boolean} checked The new checked value
18311 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18313 inputType: 'checkbox',
18321 getAutoCreate : function()
18323 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18329 cfg.cls = 'form-group ' + this.inputType; //input-group
18332 cfg.cls += ' ' + this.inputType + '-inline';
18338 type : this.inputType,
18339 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18340 cls : 'roo-' + this.inputType, //'form-box',
18341 placeholder : this.placeholder || ''
18345 if (this.weight) { // Validity check?
18346 cfg.cls += " " + this.inputType + "-" + this.weight;
18349 if (this.disabled) {
18350 input.disabled=true;
18354 input.checked = this.checked;
18358 input.name = this.name;
18362 input.cls += ' input-' + this.size;
18367 ['xs','sm','md','lg'].map(function(size){
18368 if (settings[size]) {
18369 cfg.cls += ' col-' + size + '-' + settings[size];
18373 var inputblock = input;
18375 if (this.before || this.after) {
18378 cls : 'input-group',
18383 inputblock.cn.push({
18385 cls : 'input-group-addon',
18390 inputblock.cn.push(input);
18393 inputblock.cn.push({
18395 cls : 'input-group-addon',
18402 if (align ==='left' && this.fieldLabel.length) {
18403 Roo.log("left and has label");
18409 cls : 'control-label col-md-' + this.labelWidth,
18410 html : this.fieldLabel
18414 cls : "col-md-" + (12 - this.labelWidth),
18421 } else if ( this.fieldLabel.length) {
18426 tag: this.boxLabel ? 'span' : 'label',
18428 cls: 'control-label box-input-label',
18429 //cls : 'input-group-addon',
18430 html : this.fieldLabel
18440 Roo.log(" no label && no align");
18441 cfg.cn = [ inputblock ] ;
18446 var boxLabelCfg = {
18448 //'for': id, // box label is handled by onclick - so no for...
18450 html: this.boxLabel
18454 boxLabelCfg.tooltip = this.tooltip;
18457 cfg.cn.push(boxLabelCfg);
18467 * return the real input element.
18469 inputEl: function ()
18471 return this.el.select('input.roo-' + this.inputType,true).first();
18474 labelEl: function()
18476 return this.el.select('label.control-label',true).first();
18478 /* depricated... */
18482 return this.labelEl();
18485 initEvents : function()
18487 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18489 this.inputEl().on('click', this.onClick, this);
18491 if (this.boxLabel) {
18492 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18495 this.startValue = this.getValue();
18498 Roo.bootstrap.CheckBox.register(this);
18502 onClick : function()
18504 this.setChecked(!this.checked);
18507 setChecked : function(state,suppressEvent)
18509 this.startValue = this.getValue();
18511 if(this.inputType == 'radio'){
18513 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18514 e.dom.checked = false;
18517 this.inputEl().dom.checked = true;
18519 this.inputEl().dom.value = this.inputValue;
18521 if(suppressEvent !== true){
18522 this.fireEvent('check', this, true);
18530 this.checked = state;
18532 this.inputEl().dom.checked = state;
18534 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18536 if(suppressEvent !== true){
18537 this.fireEvent('check', this, state);
18543 getValue : function()
18545 if(this.inputType == 'radio'){
18546 return this.getGroupValue();
18549 return this.inputEl().getValue();
18553 getGroupValue : function()
18555 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18559 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18562 setValue : function(v,suppressEvent)
18564 if(this.inputType == 'radio'){
18565 this.setGroupValue(v, suppressEvent);
18569 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18574 setGroupValue : function(v, suppressEvent)
18576 this.startValue = this.getValue();
18578 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18579 e.dom.checked = false;
18581 if(e.dom.value == v){
18582 e.dom.checked = true;
18586 if(suppressEvent !== true){
18587 this.fireEvent('check', this, true);
18595 validate : function()
18599 (this.inputType == 'radio' && this.validateRadio()) ||
18600 (this.inputType == 'checkbox' && this.validateCheckbox())
18606 this.markInvalid();
18610 validateRadio : function()
18614 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18615 if(!e.dom.checked){
18627 validateCheckbox : function()
18630 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18633 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18641 for(var i in group){
18646 r = (group[i].getValue() == group[i].inputValue) ? true : false;
18653 * Mark this field as valid
18655 markValid : function()
18657 if(this.allowBlank){
18663 this.fireEvent('valid', this);
18665 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18668 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18675 if(this.inputType == 'radio'){
18676 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18677 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18678 e.findParent('.form-group', false, true).addClass(_this.validClass);
18685 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18686 this.el.findParent('.form-group', false, true).addClass(this.validClass);
18690 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18696 for(var i in group){
18697 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18698 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18703 * Mark this field as invalid
18704 * @param {String} msg The validation message
18706 markInvalid : function(msg)
18708 if(this.allowBlank){
18714 this.fireEvent('invalid', this, msg);
18716 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18719 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18723 label.markInvalid();
18726 if(this.inputType == 'radio'){
18727 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18728 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18729 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18736 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18737 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18741 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18747 for(var i in group){
18748 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18749 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18756 Roo.apply(Roo.bootstrap.CheckBox, {
18761 * register a CheckBox Group
18762 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18764 register : function(checkbox)
18766 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18767 this.groups[checkbox.groupId] = {};
18770 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18774 this.groups[checkbox.groupId][checkbox.name] = checkbox;
18778 * fetch a CheckBox Group based on the group ID
18779 * @param {string} the group ID
18780 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18782 get: function(groupId) {
18783 if (typeof(this.groups[groupId]) == 'undefined') {
18787 return this.groups[groupId] ;
18799 *<div class="radio">
18801 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18802 Option one is this and that—be sure to include why it's great
18809 *<label class="radio-inline">fieldLabel</label>
18810 *<label class="radio-inline">
18811 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18819 * @class Roo.bootstrap.Radio
18820 * @extends Roo.bootstrap.CheckBox
18821 * Bootstrap Radio class
18824 * Create a new Radio
18825 * @param {Object} config The config object
18828 Roo.bootstrap.Radio = function(config){
18829 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18833 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
18835 inputType: 'radio',
18839 getAutoCreate : function()
18841 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18842 align = align || 'left'; // default...
18849 tag : this.inline ? 'span' : 'div',
18854 var inline = this.inline ? ' radio-inline' : '';
18858 // does not need for, as we wrap the input with it..
18860 cls : 'control-label box-label' + inline,
18863 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18867 //cls : 'control-label' + inline,
18868 html : this.fieldLabel,
18869 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18878 type : this.inputType,
18879 //value : (!this.checked) ? this.valueOff : this.inputValue,
18880 value : this.inputValue,
18882 placeholder : this.placeholder || '' // ?? needed????
18885 if (this.weight) { // Validity check?
18886 input.cls += " radio-" + this.weight;
18888 if (this.disabled) {
18889 input.disabled=true;
18893 input.checked = this.checked;
18897 input.name = this.name;
18901 input.cls += ' input-' + this.size;
18904 //?? can span's inline have a width??
18907 ['xs','sm','md','lg'].map(function(size){
18908 if (settings[size]) {
18909 cfg.cls += ' col-' + size + '-' + settings[size];
18913 var inputblock = input;
18915 if (this.before || this.after) {
18918 cls : 'input-group',
18923 inputblock.cn.push({
18925 cls : 'input-group-addon',
18929 inputblock.cn.push(input);
18931 inputblock.cn.push({
18933 cls : 'input-group-addon',
18941 if (this.fieldLabel && this.fieldLabel.length) {
18942 cfg.cn.push(fieldLabel);
18945 // normal bootstrap puts the input inside the label.
18946 // however with our styled version - it has to go after the input.
18948 //lbl.cn.push(inputblock);
18952 cls: 'radio' + inline,
18959 cfg.cn.push( lblwrap);
18964 html: this.boxLabel
18973 initEvents : function()
18975 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18977 this.inputEl().on('click', this.onClick, this);
18978 if (this.boxLabel) {
18979 Roo.log('find label')
18980 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
18985 inputEl: function ()
18987 return this.el.select('input.roo-radio',true).first();
18989 onClick : function()
18992 this.setChecked(true);
18995 setChecked : function(state,suppressEvent)
18998 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18999 v.dom.checked = false;
19002 Roo.log(this.inputEl().dom);
19003 this.checked = state;
19004 this.inputEl().dom.checked = state;
19006 if(suppressEvent !== true){
19007 this.fireEvent('check', this, state);
19010 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19014 getGroupValue : function()
19017 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19018 if(v.dom.checked == true){
19019 value = v.dom.value;
19027 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
19028 * @return {Mixed} value The field value
19030 getValue : function(){
19031 return this.getGroupValue();
19037 //<script type="text/javascript">
19040 * Based Ext JS Library 1.1.1
19041 * Copyright(c) 2006-2007, Ext JS, LLC.
19047 * @class Roo.HtmlEditorCore
19048 * @extends Roo.Component
19049 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19051 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19054 Roo.HtmlEditorCore = function(config){
19057 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19062 * @event initialize
19063 * Fires when the editor is fully initialized (including the iframe)
19064 * @param {Roo.HtmlEditorCore} this
19069 * Fires when the editor is first receives the focus. Any insertion must wait
19070 * until after this event.
19071 * @param {Roo.HtmlEditorCore} this
19075 * @event beforesync
19076 * Fires before the textarea is updated with content from the editor iframe. Return false
19077 * to cancel the sync.
19078 * @param {Roo.HtmlEditorCore} this
19079 * @param {String} html
19083 * @event beforepush
19084 * Fires before the iframe editor is updated with content from the textarea. Return false
19085 * to cancel the push.
19086 * @param {Roo.HtmlEditorCore} this
19087 * @param {String} html
19092 * Fires when the textarea is updated with content from the editor iframe.
19093 * @param {Roo.HtmlEditorCore} this
19094 * @param {String} html
19099 * Fires when the iframe editor is updated with content from the textarea.
19100 * @param {Roo.HtmlEditorCore} this
19101 * @param {String} html
19106 * @event editorevent
19107 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19108 * @param {Roo.HtmlEditorCore} this
19114 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19116 // defaults : white / black...
19117 this.applyBlacklists();
19124 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
19128 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
19134 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
19139 * @cfg {Number} height (in pixels)
19143 * @cfg {Number} width (in pixels)
19148 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19151 stylesheets: false,
19156 // private properties
19157 validationEvent : false,
19159 initialized : false,
19161 sourceEditMode : false,
19162 onFocus : Roo.emptyFn,
19164 hideMode:'offsets',
19168 // blacklist + whitelisted elements..
19175 * Protected method that will not generally be called directly. It
19176 * is called when the editor initializes the iframe with HTML contents. Override this method if you
19177 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19179 getDocMarkup : function(){
19183 // inherit styels from page...??
19184 if (this.stylesheets === false) {
19186 Roo.get(document.head).select('style').each(function(node) {
19187 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19190 Roo.get(document.head).select('link').each(function(node) {
19191 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19194 } else if (!this.stylesheets.length) {
19196 st = '<style type="text/css">' +
19197 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19203 st += '<style type="text/css">' +
19204 'IMG { cursor: pointer } ' +
19208 return '<html><head>' + st +
19209 //<style type="text/css">' +
19210 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19212 ' </head><body class="roo-htmleditor-body"></body></html>';
19216 onRender : function(ct, position)
19219 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19220 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19223 this.el.dom.style.border = '0 none';
19224 this.el.dom.setAttribute('tabIndex', -1);
19225 this.el.addClass('x-hidden hide');
19229 if(Roo.isIE){ // fix IE 1px bogus margin
19230 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19234 this.frameId = Roo.id();
19238 var iframe = this.owner.wrap.createChild({
19240 cls: 'form-control', // bootstrap..
19242 name: this.frameId,
19243 frameBorder : 'no',
19244 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19249 this.iframe = iframe.dom;
19251 this.assignDocWin();
19253 this.doc.designMode = 'on';
19256 this.doc.write(this.getDocMarkup());
19260 var task = { // must defer to wait for browser to be ready
19262 //console.log("run task?" + this.doc.readyState);
19263 this.assignDocWin();
19264 if(this.doc.body || this.doc.readyState == 'complete'){
19266 this.doc.designMode="on";
19270 Roo.TaskMgr.stop(task);
19271 this.initEditor.defer(10, this);
19278 Roo.TaskMgr.start(task);
19283 onResize : function(w, h)
19285 Roo.log('resize: ' +w + ',' + h );
19286 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19290 if(typeof w == 'number'){
19292 this.iframe.style.width = w + 'px';
19294 if(typeof h == 'number'){
19296 this.iframe.style.height = h + 'px';
19298 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19305 * Toggles the editor between standard and source edit mode.
19306 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19308 toggleSourceEdit : function(sourceEditMode){
19310 this.sourceEditMode = sourceEditMode === true;
19312 if(this.sourceEditMode){
19314 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19317 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19318 //this.iframe.className = '';
19321 //this.setSize(this.owner.wrap.getSize());
19322 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19329 * Protected method that will not generally be called directly. If you need/want
19330 * custom HTML cleanup, this is the method you should override.
19331 * @param {String} html The HTML to be cleaned
19332 * return {String} The cleaned HTML
19334 cleanHtml : function(html){
19335 html = String(html);
19336 if(html.length > 5){
19337 if(Roo.isSafari){ // strip safari nonsense
19338 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19341 if(html == ' '){
19348 * HTML Editor -> Textarea
19349 * Protected method that will not generally be called directly. Syncs the contents
19350 * of the editor iframe with the textarea.
19352 syncValue : function(){
19353 if(this.initialized){
19354 var bd = (this.doc.body || this.doc.documentElement);
19355 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19356 var html = bd.innerHTML;
19358 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19359 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19361 html = '<div style="'+m[0]+'">' + html + '</div>';
19364 html = this.cleanHtml(html);
19365 // fix up the special chars.. normaly like back quotes in word...
19366 // however we do not want to do this with chinese..
19367 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19368 var cc = b.charCodeAt();
19370 (cc >= 0x4E00 && cc < 0xA000 ) ||
19371 (cc >= 0x3400 && cc < 0x4E00 ) ||
19372 (cc >= 0xf900 && cc < 0xfb00 )
19378 if(this.owner.fireEvent('beforesync', this, html) !== false){
19379 this.el.dom.value = html;
19380 this.owner.fireEvent('sync', this, html);
19386 * Protected method that will not generally be called directly. Pushes the value of the textarea
19387 * into the iframe editor.
19389 pushValue : function(){
19390 if(this.initialized){
19391 var v = this.el.dom.value.trim();
19393 // if(v.length < 1){
19397 if(this.owner.fireEvent('beforepush', this, v) !== false){
19398 var d = (this.doc.body || this.doc.documentElement);
19400 this.cleanUpPaste();
19401 this.el.dom.value = d.innerHTML;
19402 this.owner.fireEvent('push', this, v);
19408 deferFocus : function(){
19409 this.focus.defer(10, this);
19413 focus : function(){
19414 if(this.win && !this.sourceEditMode){
19421 assignDocWin: function()
19423 var iframe = this.iframe;
19426 this.doc = iframe.contentWindow.document;
19427 this.win = iframe.contentWindow;
19429 // if (!Roo.get(this.frameId)) {
19432 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19433 // this.win = Roo.get(this.frameId).dom.contentWindow;
19435 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19439 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19440 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19445 initEditor : function(){
19446 //console.log("INIT EDITOR");
19447 this.assignDocWin();
19451 this.doc.designMode="on";
19453 this.doc.write(this.getDocMarkup());
19456 var dbody = (this.doc.body || this.doc.documentElement);
19457 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19458 // this copies styles from the containing element into thsi one..
19459 // not sure why we need all of this..
19460 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19462 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19463 //ss['background-attachment'] = 'fixed'; // w3c
19464 dbody.bgProperties = 'fixed'; // ie
19465 //Roo.DomHelper.applyStyles(dbody, ss);
19466 Roo.EventManager.on(this.doc, {
19467 //'mousedown': this.onEditorEvent,
19468 'mouseup': this.onEditorEvent,
19469 'dblclick': this.onEditorEvent,
19470 'click': this.onEditorEvent,
19471 'keyup': this.onEditorEvent,
19476 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19478 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19479 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19481 this.initialized = true;
19483 this.owner.fireEvent('initialize', this);
19488 onDestroy : function(){
19494 //for (var i =0; i < this.toolbars.length;i++) {
19495 // // fixme - ask toolbars for heights?
19496 // this.toolbars[i].onDestroy();
19499 //this.wrap.dom.innerHTML = '';
19500 //this.wrap.remove();
19505 onFirstFocus : function(){
19507 this.assignDocWin();
19510 this.activated = true;
19513 if(Roo.isGecko){ // prevent silly gecko errors
19515 var s = this.win.getSelection();
19516 if(!s.focusNode || s.focusNode.nodeType != 3){
19517 var r = s.getRangeAt(0);
19518 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19523 this.execCmd('useCSS', true);
19524 this.execCmd('styleWithCSS', false);
19527 this.owner.fireEvent('activate', this);
19531 adjustFont: function(btn){
19532 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19533 //if(Roo.isSafari){ // safari
19536 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19537 if(Roo.isSafari){ // safari
19538 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19539 v = (v < 10) ? 10 : v;
19540 v = (v > 48) ? 48 : v;
19541 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19546 v = Math.max(1, v+adjust);
19548 this.execCmd('FontSize', v );
19551 onEditorEvent : function(e)
19553 this.owner.fireEvent('editorevent', this, e);
19554 // this.updateToolbar();
19555 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19558 insertTag : function(tg)
19560 // could be a bit smarter... -> wrap the current selected tRoo..
19561 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19563 range = this.createRange(this.getSelection());
19564 var wrappingNode = this.doc.createElement(tg.toLowerCase());
19565 wrappingNode.appendChild(range.extractContents());
19566 range.insertNode(wrappingNode);
19573 this.execCmd("formatblock", tg);
19577 insertText : function(txt)
19581 var range = this.createRange();
19582 range.deleteContents();
19583 //alert(Sender.getAttribute('label'));
19585 range.insertNode(this.doc.createTextNode(txt));
19591 * Executes a Midas editor command on the editor document and performs necessary focus and
19592 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19593 * @param {String} cmd The Midas command
19594 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19596 relayCmd : function(cmd, value){
19598 this.execCmd(cmd, value);
19599 this.owner.fireEvent('editorevent', this);
19600 //this.updateToolbar();
19601 this.owner.deferFocus();
19605 * Executes a Midas editor command directly on the editor document.
19606 * For visual commands, you should use {@link #relayCmd} instead.
19607 * <b>This should only be called after the editor is initialized.</b>
19608 * @param {String} cmd The Midas command
19609 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19611 execCmd : function(cmd, value){
19612 this.doc.execCommand(cmd, false, value === undefined ? null : value);
19619 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19621 * @param {String} text | dom node..
19623 insertAtCursor : function(text)
19628 if(!this.activated){
19634 var r = this.doc.selection.createRange();
19645 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19649 // from jquery ui (MIT licenced)
19651 var win = this.win;
19653 if (win.getSelection && win.getSelection().getRangeAt) {
19654 range = win.getSelection().getRangeAt(0);
19655 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19656 range.insertNode(node);
19657 } else if (win.document.selection && win.document.selection.createRange) {
19658 // no firefox support
19659 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19660 win.document.selection.createRange().pasteHTML(txt);
19662 // no firefox support
19663 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19664 this.execCmd('InsertHTML', txt);
19673 mozKeyPress : function(e){
19675 var c = e.getCharCode(), cmd;
19678 c = String.fromCharCode(c).toLowerCase();
19692 this.cleanUpPaste.defer(100, this);
19700 e.preventDefault();
19708 fixKeys : function(){ // load time branching for fastest keydown performance
19710 return function(e){
19711 var k = e.getKey(), r;
19714 r = this.doc.selection.createRange();
19717 r.pasteHTML('    ');
19724 r = this.doc.selection.createRange();
19726 var target = r.parentElement();
19727 if(!target || target.tagName.toLowerCase() != 'li'){
19729 r.pasteHTML('<br />');
19735 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19736 this.cleanUpPaste.defer(100, this);
19742 }else if(Roo.isOpera){
19743 return function(e){
19744 var k = e.getKey();
19748 this.execCmd('InsertHTML','    ');
19751 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19752 this.cleanUpPaste.defer(100, this);
19757 }else if(Roo.isSafari){
19758 return function(e){
19759 var k = e.getKey();
19763 this.execCmd('InsertText','\t');
19767 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19768 this.cleanUpPaste.defer(100, this);
19776 getAllAncestors: function()
19778 var p = this.getSelectedNode();
19781 a.push(p); // push blank onto stack..
19782 p = this.getParentElement();
19786 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19790 a.push(this.doc.body);
19794 lastSelNode : false,
19797 getSelection : function()
19799 this.assignDocWin();
19800 return Roo.isIE ? this.doc.selection : this.win.getSelection();
19803 getSelectedNode: function()
19805 // this may only work on Gecko!!!
19807 // should we cache this!!!!
19812 var range = this.createRange(this.getSelection()).cloneRange();
19815 var parent = range.parentElement();
19817 var testRange = range.duplicate();
19818 testRange.moveToElementText(parent);
19819 if (testRange.inRange(range)) {
19822 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19825 parent = parent.parentElement;
19830 // is ancestor a text element.
19831 var ac = range.commonAncestorContainer;
19832 if (ac.nodeType == 3) {
19833 ac = ac.parentNode;
19836 var ar = ac.childNodes;
19839 var other_nodes = [];
19840 var has_other_nodes = false;
19841 for (var i=0;i<ar.length;i++) {
19842 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
19845 // fullly contained node.
19847 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19852 // probably selected..
19853 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19854 other_nodes.push(ar[i]);
19858 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
19863 has_other_nodes = true;
19865 if (!nodes.length && other_nodes.length) {
19866 nodes= other_nodes;
19868 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19874 createRange: function(sel)
19876 // this has strange effects when using with
19877 // top toolbar - not sure if it's a great idea.
19878 //this.editor.contentWindow.focus();
19879 if (typeof sel != "undefined") {
19881 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19883 return this.doc.createRange();
19886 return this.doc.createRange();
19889 getParentElement: function()
19892 this.assignDocWin();
19893 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19895 var range = this.createRange(sel);
19898 var p = range.commonAncestorContainer;
19899 while (p.nodeType == 3) { // text node
19910 * Range intersection.. the hard stuff...
19914 * [ -- selected range --- ]
19918 * if end is before start or hits it. fail.
19919 * if start is after end or hits it fail.
19921 * if either hits (but other is outside. - then it's not
19927 // @see http://www.thismuchiknow.co.uk/?p=64.
19928 rangeIntersectsNode : function(range, node)
19930 var nodeRange = node.ownerDocument.createRange();
19932 nodeRange.selectNode(node);
19934 nodeRange.selectNodeContents(node);
19937 var rangeStartRange = range.cloneRange();
19938 rangeStartRange.collapse(true);
19940 var rangeEndRange = range.cloneRange();
19941 rangeEndRange.collapse(false);
19943 var nodeStartRange = nodeRange.cloneRange();
19944 nodeStartRange.collapse(true);
19946 var nodeEndRange = nodeRange.cloneRange();
19947 nodeEndRange.collapse(false);
19949 return rangeStartRange.compareBoundaryPoints(
19950 Range.START_TO_START, nodeEndRange) == -1 &&
19951 rangeEndRange.compareBoundaryPoints(
19952 Range.START_TO_START, nodeStartRange) == 1;
19956 rangeCompareNode : function(range, node)
19958 var nodeRange = node.ownerDocument.createRange();
19960 nodeRange.selectNode(node);
19962 nodeRange.selectNodeContents(node);
19966 range.collapse(true);
19968 nodeRange.collapse(true);
19970 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19971 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
19973 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19975 var nodeIsBefore = ss == 1;
19976 var nodeIsAfter = ee == -1;
19978 if (nodeIsBefore && nodeIsAfter)
19980 if (!nodeIsBefore && nodeIsAfter)
19981 return 1; //right trailed.
19983 if (nodeIsBefore && !nodeIsAfter)
19984 return 2; // left trailed.
19989 // private? - in a new class?
19990 cleanUpPaste : function()
19992 // cleans up the whole document..
19993 Roo.log('cleanuppaste');
19995 this.cleanUpChildren(this.doc.body);
19996 var clean = this.cleanWordChars(this.doc.body.innerHTML);
19997 if (clean != this.doc.body.innerHTML) {
19998 this.doc.body.innerHTML = clean;
20003 cleanWordChars : function(input) {// change the chars to hex code
20004 var he = Roo.HtmlEditorCore;
20006 var output = input;
20007 Roo.each(he.swapCodes, function(sw) {
20008 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20010 output = output.replace(swapper, sw[1]);
20017 cleanUpChildren : function (n)
20019 if (!n.childNodes.length) {
20022 for (var i = n.childNodes.length-1; i > -1 ; i--) {
20023 this.cleanUpChild(n.childNodes[i]);
20030 cleanUpChild : function (node)
20033 //console.log(node);
20034 if (node.nodeName == "#text") {
20035 // clean up silly Windows -- stuff?
20038 if (node.nodeName == "#comment") {
20039 node.parentNode.removeChild(node);
20040 // clean up silly Windows -- stuff?
20043 var lcname = node.tagName.toLowerCase();
20044 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20045 // whitelist of tags..
20047 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20049 node.parentNode.removeChild(node);
20054 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20056 // remove <a name=....> as rendering on yahoo mailer is borked with this.
20057 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20059 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20060 // remove_keep_children = true;
20063 if (remove_keep_children) {
20064 this.cleanUpChildren(node);
20065 // inserts everything just before this node...
20066 while (node.childNodes.length) {
20067 var cn = node.childNodes[0];
20068 node.removeChild(cn);
20069 node.parentNode.insertBefore(cn, node);
20071 node.parentNode.removeChild(node);
20075 if (!node.attributes || !node.attributes.length) {
20076 this.cleanUpChildren(node);
20080 function cleanAttr(n,v)
20083 if (v.match(/^\./) || v.match(/^\//)) {
20086 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20089 if (v.match(/^#/)) {
20092 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20093 node.removeAttribute(n);
20097 var cwhite = this.cwhite;
20098 var cblack = this.cblack;
20100 function cleanStyle(n,v)
20102 if (v.match(/expression/)) { //XSS?? should we even bother..
20103 node.removeAttribute(n);
20107 var parts = v.split(/;/);
20110 Roo.each(parts, function(p) {
20111 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20115 var l = p.split(':').shift().replace(/\s+/g,'');
20116 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20118 if ( cwhite.length && cblack.indexOf(l) > -1) {
20119 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20120 //node.removeAttribute(n);
20124 // only allow 'c whitelisted system attributes'
20125 if ( cwhite.length && cwhite.indexOf(l) < 0) {
20126 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20127 //node.removeAttribute(n);
20137 if (clean.length) {
20138 node.setAttribute(n, clean.join(';'));
20140 node.removeAttribute(n);
20146 for (var i = node.attributes.length-1; i > -1 ; i--) {
20147 var a = node.attributes[i];
20150 if (a.name.toLowerCase().substr(0,2)=='on') {
20151 node.removeAttribute(a.name);
20154 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20155 node.removeAttribute(a.name);
20158 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20159 cleanAttr(a.name,a.value); // fixme..
20162 if (a.name == 'style') {
20163 cleanStyle(a.name,a.value);
20166 /// clean up MS crap..
20167 // tecnically this should be a list of valid class'es..
20170 if (a.name == 'class') {
20171 if (a.value.match(/^Mso/)) {
20172 node.className = '';
20175 if (a.value.match(/body/)) {
20176 node.className = '';
20187 this.cleanUpChildren(node);
20193 * Clean up MS wordisms...
20195 cleanWord : function(node)
20200 this.cleanWord(this.doc.body);
20203 if (node.nodeName == "#text") {
20204 // clean up silly Windows -- stuff?
20207 if (node.nodeName == "#comment") {
20208 node.parentNode.removeChild(node);
20209 // clean up silly Windows -- stuff?
20213 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20214 node.parentNode.removeChild(node);
20218 // remove - but keep children..
20219 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20220 while (node.childNodes.length) {
20221 var cn = node.childNodes[0];
20222 node.removeChild(cn);
20223 node.parentNode.insertBefore(cn, node);
20225 node.parentNode.removeChild(node);
20226 this.iterateChildren(node, this.cleanWord);
20230 if (node.className.length) {
20232 var cn = node.className.split(/\W+/);
20234 Roo.each(cn, function(cls) {
20235 if (cls.match(/Mso[a-zA-Z]+/)) {
20240 node.className = cna.length ? cna.join(' ') : '';
20242 node.removeAttribute("class");
20246 if (node.hasAttribute("lang")) {
20247 node.removeAttribute("lang");
20250 if (node.hasAttribute("style")) {
20252 var styles = node.getAttribute("style").split(";");
20254 Roo.each(styles, function(s) {
20255 if (!s.match(/:/)) {
20258 var kv = s.split(":");
20259 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20262 // what ever is left... we allow.
20265 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20266 if (!nstyle.length) {
20267 node.removeAttribute('style');
20270 this.iterateChildren(node, this.cleanWord);
20276 * iterateChildren of a Node, calling fn each time, using this as the scole..
20277 * @param {DomNode} node node to iterate children of.
20278 * @param {Function} fn method of this class to call on each item.
20280 iterateChildren : function(node, fn)
20282 if (!node.childNodes.length) {
20285 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20286 fn.call(this, node.childNodes[i])
20292 * cleanTableWidths.
20294 * Quite often pasting from word etc.. results in tables with column and widths.
20295 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20298 cleanTableWidths : function(node)
20303 this.cleanTableWidths(this.doc.body);
20308 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20311 Roo.log(node.tagName);
20312 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20313 this.iterateChildren(node, this.cleanTableWidths);
20316 if (node.hasAttribute('width')) {
20317 node.removeAttribute('width');
20321 if (node.hasAttribute("style")) {
20324 var styles = node.getAttribute("style").split(";");
20326 Roo.each(styles, function(s) {
20327 if (!s.match(/:/)) {
20330 var kv = s.split(":");
20331 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20334 // what ever is left... we allow.
20337 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20338 if (!nstyle.length) {
20339 node.removeAttribute('style');
20343 this.iterateChildren(node, this.cleanTableWidths);
20351 domToHTML : function(currentElement, depth, nopadtext) {
20353 depth = depth || 0;
20354 nopadtext = nopadtext || false;
20356 if (!currentElement) {
20357 return this.domToHTML(this.doc.body);
20360 //Roo.log(currentElement);
20362 var allText = false;
20363 var nodeName = currentElement.nodeName;
20364 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20366 if (nodeName == '#text') {
20368 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20373 if (nodeName != 'BODY') {
20376 // Prints the node tagName, such as <A>, <IMG>, etc
20379 for(i = 0; i < currentElement.attributes.length;i++) {
20381 var aname = currentElement.attributes.item(i).name;
20382 if (!currentElement.attributes.item(i).value.length) {
20385 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20388 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20397 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20400 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20405 // Traverse the tree
20407 var currentElementChild = currentElement.childNodes.item(i);
20408 var allText = true;
20409 var innerHTML = '';
20411 while (currentElementChild) {
20412 // Formatting code (indent the tree so it looks nice on the screen)
20413 var nopad = nopadtext;
20414 if (lastnode == 'SPAN') {
20418 if (currentElementChild.nodeName == '#text') {
20419 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20420 toadd = nopadtext ? toadd : toadd.trim();
20421 if (!nopad && toadd.length > 80) {
20422 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20424 innerHTML += toadd;
20427 currentElementChild = currentElement.childNodes.item(i);
20433 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20435 // Recursively traverse the tree structure of the child node
20436 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20437 lastnode = currentElementChild.nodeName;
20439 currentElementChild=currentElement.childNodes.item(i);
20445 // The remaining code is mostly for formatting the tree
20446 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20451 ret+= "</"+tagName+">";
20457 applyBlacklists : function()
20459 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20460 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20464 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20465 if (b.indexOf(tag) > -1) {
20468 this.white.push(tag);
20472 Roo.each(w, function(tag) {
20473 if (b.indexOf(tag) > -1) {
20476 if (this.white.indexOf(tag) > -1) {
20479 this.white.push(tag);
20484 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20485 if (w.indexOf(tag) > -1) {
20488 this.black.push(tag);
20492 Roo.each(b, function(tag) {
20493 if (w.indexOf(tag) > -1) {
20496 if (this.black.indexOf(tag) > -1) {
20499 this.black.push(tag);
20504 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20505 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20509 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20510 if (b.indexOf(tag) > -1) {
20513 this.cwhite.push(tag);
20517 Roo.each(w, function(tag) {
20518 if (b.indexOf(tag) > -1) {
20521 if (this.cwhite.indexOf(tag) > -1) {
20524 this.cwhite.push(tag);
20529 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20530 if (w.indexOf(tag) > -1) {
20533 this.cblack.push(tag);
20537 Roo.each(b, function(tag) {
20538 if (w.indexOf(tag) > -1) {
20541 if (this.cblack.indexOf(tag) > -1) {
20544 this.cblack.push(tag);
20549 setStylesheets : function(stylesheets)
20551 if(typeof(stylesheets) == 'string'){
20552 Roo.get(this.iframe.contentDocument.head).createChild({
20554 rel : 'stylesheet',
20563 Roo.each(stylesheets, function(s) {
20568 Roo.get(_this.iframe.contentDocument.head).createChild({
20570 rel : 'stylesheet',
20579 removeStylesheets : function()
20583 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20588 // hide stuff that is not compatible
20602 * @event specialkey
20606 * @cfg {String} fieldClass @hide
20609 * @cfg {String} focusClass @hide
20612 * @cfg {String} autoCreate @hide
20615 * @cfg {String} inputType @hide
20618 * @cfg {String} invalidClass @hide
20621 * @cfg {String} invalidText @hide
20624 * @cfg {String} msgFx @hide
20627 * @cfg {String} validateOnBlur @hide
20631 Roo.HtmlEditorCore.white = [
20632 'area', 'br', 'img', 'input', 'hr', 'wbr',
20634 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
20635 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
20636 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
20637 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
20638 'table', 'ul', 'xmp',
20640 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
20643 'dir', 'menu', 'ol', 'ul', 'dl',
20649 Roo.HtmlEditorCore.black = [
20650 // 'embed', 'object', // enable - backend responsiblity to clean thiese
20652 'base', 'basefont', 'bgsound', 'blink', 'body',
20653 'frame', 'frameset', 'head', 'html', 'ilayer',
20654 'iframe', 'layer', 'link', 'meta', 'object',
20655 'script', 'style' ,'title', 'xml' // clean later..
20657 Roo.HtmlEditorCore.clean = [
20658 'script', 'style', 'title', 'xml'
20660 Roo.HtmlEditorCore.remove = [
20665 Roo.HtmlEditorCore.ablack = [
20669 Roo.HtmlEditorCore.aclean = [
20670 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
20674 Roo.HtmlEditorCore.pwhite= [
20675 'http', 'https', 'mailto'
20678 // white listed style attributes.
20679 Roo.HtmlEditorCore.cwhite= [
20680 // 'text-align', /// default is to allow most things..
20686 // black listed style attributes.
20687 Roo.HtmlEditorCore.cblack= [
20688 // 'font-size' -- this can be set by the project
20692 Roo.HtmlEditorCore.swapCodes =[
20711 * @class Roo.bootstrap.HtmlEditor
20712 * @extends Roo.bootstrap.TextArea
20713 * Bootstrap HtmlEditor class
20716 * Create a new HtmlEditor
20717 * @param {Object} config The config object
20720 Roo.bootstrap.HtmlEditor = function(config){
20721 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20722 if (!this.toolbars) {
20723 this.toolbars = [];
20725 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20728 * @event initialize
20729 * Fires when the editor is fully initialized (including the iframe)
20730 * @param {HtmlEditor} this
20735 * Fires when the editor is first receives the focus. Any insertion must wait
20736 * until after this event.
20737 * @param {HtmlEditor} this
20741 * @event beforesync
20742 * Fires before the textarea is updated with content from the editor iframe. Return false
20743 * to cancel the sync.
20744 * @param {HtmlEditor} this
20745 * @param {String} html
20749 * @event beforepush
20750 * Fires before the iframe editor is updated with content from the textarea. Return false
20751 * to cancel the push.
20752 * @param {HtmlEditor} this
20753 * @param {String} html
20758 * Fires when the textarea is updated with content from the editor iframe.
20759 * @param {HtmlEditor} this
20760 * @param {String} html
20765 * Fires when the iframe editor is updated with content from the textarea.
20766 * @param {HtmlEditor} this
20767 * @param {String} html
20771 * @event editmodechange
20772 * Fires when the editor switches edit modes
20773 * @param {HtmlEditor} this
20774 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20776 editmodechange: true,
20778 * @event editorevent
20779 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20780 * @param {HtmlEditor} this
20784 * @event firstfocus
20785 * Fires when on first focus - needed by toolbars..
20786 * @param {HtmlEditor} this
20791 * Auto save the htmlEditor value as a file into Events
20792 * @param {HtmlEditor} this
20796 * @event savedpreview
20797 * preview the saved version of htmlEditor
20798 * @param {HtmlEditor} this
20805 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
20809 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20814 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20819 * @cfg {Number} height (in pixels)
20823 * @cfg {Number} width (in pixels)
20828 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20831 stylesheets: false,
20836 // private properties
20837 validationEvent : false,
20839 initialized : false,
20842 onFocus : Roo.emptyFn,
20844 hideMode:'offsets',
20847 tbContainer : false,
20849 toolbarContainer :function() {
20850 return this.wrap.select('.x-html-editor-tb',true).first();
20854 * Protected method that will not generally be called directly. It
20855 * is called when the editor creates its toolbar. Override this method if you need to
20856 * add custom toolbar buttons.
20857 * @param {HtmlEditor} editor
20859 createToolbar : function(){
20861 Roo.log("create toolbars");
20863 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20864 this.toolbars[0].render(this.toolbarContainer());
20868 // if (!editor.toolbars || !editor.toolbars.length) {
20869 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20872 // for (var i =0 ; i < editor.toolbars.length;i++) {
20873 // editor.toolbars[i] = Roo.factory(
20874 // typeof(editor.toolbars[i]) == 'string' ?
20875 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
20876 // Roo.bootstrap.HtmlEditor);
20877 // editor.toolbars[i].init(editor);
20883 onRender : function(ct, position)
20885 // Roo.log("Call onRender: " + this.xtype);
20887 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20889 this.wrap = this.inputEl().wrap({
20890 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20893 this.editorcore.onRender(ct, position);
20895 if (this.resizable) {
20896 this.resizeEl = new Roo.Resizable(this.wrap, {
20900 minHeight : this.height,
20901 height: this.height,
20902 handles : this.resizable,
20905 resize : function(r, w, h) {
20906 _t.onResize(w,h); // -something
20912 this.createToolbar(this);
20915 if(!this.width && this.resizable){
20916 this.setSize(this.wrap.getSize());
20918 if (this.resizeEl) {
20919 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20920 // should trigger onReize..
20926 onResize : function(w, h)
20928 Roo.log('resize: ' +w + ',' + h );
20929 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20933 if(this.inputEl() ){
20934 if(typeof w == 'number'){
20935 var aw = w - this.wrap.getFrameWidth('lr');
20936 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20939 if(typeof h == 'number'){
20940 var tbh = -11; // fixme it needs to tool bar size!
20941 for (var i =0; i < this.toolbars.length;i++) {
20942 // fixme - ask toolbars for heights?
20943 tbh += this.toolbars[i].el.getHeight();
20944 //if (this.toolbars[i].footer) {
20945 // tbh += this.toolbars[i].footer.el.getHeight();
20953 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20954 ah -= 5; // knock a few pixes off for look..
20955 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20959 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20960 this.editorcore.onResize(ew,eh);
20965 * Toggles the editor between standard and source edit mode.
20966 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20968 toggleSourceEdit : function(sourceEditMode)
20970 this.editorcore.toggleSourceEdit(sourceEditMode);
20972 if(this.editorcore.sourceEditMode){
20973 Roo.log('editor - showing textarea');
20976 // Roo.log(this.syncValue());
20978 this.inputEl().removeClass(['hide', 'x-hidden']);
20979 this.inputEl().dom.removeAttribute('tabIndex');
20980 this.inputEl().focus();
20982 Roo.log('editor - hiding textarea');
20984 // Roo.log(this.pushValue());
20987 this.inputEl().addClass(['hide', 'x-hidden']);
20988 this.inputEl().dom.setAttribute('tabIndex', -1);
20989 //this.deferFocus();
20992 if(this.resizable){
20993 this.setSize(this.wrap.getSize());
20996 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20999 // private (for BoxComponent)
21000 adjustSize : Roo.BoxComponent.prototype.adjustSize,
21002 // private (for BoxComponent)
21003 getResizeEl : function(){
21007 // private (for BoxComponent)
21008 getPositionEl : function(){
21013 initEvents : function(){
21014 this.originalValue = this.getValue();
21018 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21021 // markInvalid : Roo.emptyFn,
21023 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21026 // clearInvalid : Roo.emptyFn,
21028 setValue : function(v){
21029 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21030 this.editorcore.pushValue();
21035 deferFocus : function(){
21036 this.focus.defer(10, this);
21040 focus : function(){
21041 this.editorcore.focus();
21047 onDestroy : function(){
21053 for (var i =0; i < this.toolbars.length;i++) {
21054 // fixme - ask toolbars for heights?
21055 this.toolbars[i].onDestroy();
21058 this.wrap.dom.innerHTML = '';
21059 this.wrap.remove();
21064 onFirstFocus : function(){
21065 //Roo.log("onFirstFocus");
21066 this.editorcore.onFirstFocus();
21067 for (var i =0; i < this.toolbars.length;i++) {
21068 this.toolbars[i].onFirstFocus();
21074 syncValue : function()
21076 this.editorcore.syncValue();
21079 pushValue : function()
21081 this.editorcore.pushValue();
21085 // hide stuff that is not compatible
21099 * @event specialkey
21103 * @cfg {String} fieldClass @hide
21106 * @cfg {String} focusClass @hide
21109 * @cfg {String} autoCreate @hide
21112 * @cfg {String} inputType @hide
21115 * @cfg {String} invalidClass @hide
21118 * @cfg {String} invalidText @hide
21121 * @cfg {String} msgFx @hide
21124 * @cfg {String} validateOnBlur @hide
21133 Roo.namespace('Roo.bootstrap.htmleditor');
21135 * @class Roo.bootstrap.HtmlEditorToolbar1
21140 new Roo.bootstrap.HtmlEditor({
21143 new Roo.bootstrap.HtmlEditorToolbar1({
21144 disable : { fonts: 1 , format: 1, ..., ... , ...],
21150 * @cfg {Object} disable List of elements to disable..
21151 * @cfg {Array} btns List of additional buttons.
21155 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21158 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21161 Roo.apply(this, config);
21163 // default disabled, based on 'good practice'..
21164 this.disable = this.disable || {};
21165 Roo.applyIf(this.disable, {
21168 specialElements : true
21170 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21172 this.editor = config.editor;
21173 this.editorcore = config.editor.editorcore;
21175 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21177 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21178 // dont call parent... till later.
21180 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
21185 editorcore : false,
21190 "h1","h2","h3","h4","h5","h6",
21192 "abbr", "acronym", "address", "cite", "samp", "var",
21196 onRender : function(ct, position)
21198 // Roo.log("Call onRender: " + this.xtype);
21200 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21202 this.el.dom.style.marginBottom = '0';
21204 var editorcore = this.editorcore;
21205 var editor= this.editor;
21208 var btn = function(id,cmd , toggle, handler){
21210 var event = toggle ? 'toggle' : 'click';
21215 xns: Roo.bootstrap,
21218 enableToggle:toggle !== false,
21220 pressed : toggle ? false : null,
21223 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21224 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21233 xns: Roo.bootstrap,
21234 glyphicon : 'font',
21238 xns: Roo.bootstrap,
21242 Roo.each(this.formats, function(f) {
21243 style.menu.items.push({
21245 xns: Roo.bootstrap,
21246 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21251 editorcore.insertTag(this.tagname);
21258 children.push(style);
21261 btn('bold',false,true);
21262 btn('italic',false,true);
21263 btn('align-left', 'justifyleft',true);
21264 btn('align-center', 'justifycenter',true);
21265 btn('align-right' , 'justifyright',true);
21266 btn('link', false, false, function(btn) {
21267 //Roo.log("create link?");
21268 var url = prompt(this.createLinkText, this.defaultLinkValue);
21269 if(url && url != 'http:/'+'/'){
21270 this.editorcore.relayCmd('createlink', url);
21273 btn('list','insertunorderedlist',true);
21274 btn('pencil', false,true, function(btn){
21277 this.toggleSourceEdit(btn.pressed);
21283 xns: Roo.bootstrap,
21288 xns: Roo.bootstrap,
21293 cog.menu.items.push({
21295 xns: Roo.bootstrap,
21296 html : Clean styles,
21301 editorcore.insertTag(this.tagname);
21310 this.xtype = 'NavSimplebar';
21312 for(var i=0;i< children.length;i++) {
21314 this.buttons.add(this.addxtypeChild(children[i]));
21318 editor.on('editorevent', this.updateToolbar, this);
21320 onBtnClick : function(id)
21322 this.editorcore.relayCmd(id);
21323 this.editorcore.focus();
21327 * Protected method that will not generally be called directly. It triggers
21328 * a toolbar update by reading the markup state of the current selection in the editor.
21330 updateToolbar: function(){
21332 if(!this.editorcore.activated){
21333 this.editor.onFirstFocus(); // is this neeed?
21337 var btns = this.buttons;
21338 var doc = this.editorcore.doc;
21339 btns.get('bold').setActive(doc.queryCommandState('bold'));
21340 btns.get('italic').setActive(doc.queryCommandState('italic'));
21341 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21343 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21344 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21345 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21347 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21348 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21351 var ans = this.editorcore.getAllAncestors();
21352 if (this.formatCombo) {
21355 var store = this.formatCombo.store;
21356 this.formatCombo.setValue("");
21357 for (var i =0; i < ans.length;i++) {
21358 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21360 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21368 // hides menus... - so this cant be on a menu...
21369 Roo.bootstrap.MenuMgr.hideAll();
21371 Roo.bootstrap.MenuMgr.hideAll();
21372 //this.editorsyncValue();
21374 onFirstFocus: function() {
21375 this.buttons.each(function(item){
21379 toggleSourceEdit : function(sourceEditMode){
21382 if(sourceEditMode){
21383 Roo.log("disabling buttons");
21384 this.buttons.each( function(item){
21385 if(item.cmd != 'pencil'){
21391 Roo.log("enabling buttons");
21392 if(this.editorcore.initialized){
21393 this.buttons.each( function(item){
21399 Roo.log("calling toggole on editor");
21400 // tell the editor that it's been pressed..
21401 this.editor.toggleSourceEdit(sourceEditMode);
21411 * @class Roo.bootstrap.Table.AbstractSelectionModel
21412 * @extends Roo.util.Observable
21413 * Abstract base class for grid SelectionModels. It provides the interface that should be
21414 * implemented by descendant classes. This class should not be directly instantiated.
21417 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21418 this.locked = false;
21419 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21423 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21424 /** @ignore Called by the grid automatically. Do not call directly. */
21425 init : function(grid){
21431 * Locks the selections.
21434 this.locked = true;
21438 * Unlocks the selections.
21440 unlock : function(){
21441 this.locked = false;
21445 * Returns true if the selections are locked.
21446 * @return {Boolean}
21448 isLocked : function(){
21449 return this.locked;
21453 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21454 * @class Roo.bootstrap.Table.RowSelectionModel
21455 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21456 * It supports multiple selections and keyboard selection/navigation.
21458 * @param {Object} config
21461 Roo.bootstrap.Table.RowSelectionModel = function(config){
21462 Roo.apply(this, config);
21463 this.selections = new Roo.util.MixedCollection(false, function(o){
21468 this.lastActive = false;
21472 * @event selectionchange
21473 * Fires when the selection changes
21474 * @param {SelectionModel} this
21476 "selectionchange" : true,
21478 * @event afterselectionchange
21479 * Fires after the selection changes (eg. by key press or clicking)
21480 * @param {SelectionModel} this
21482 "afterselectionchange" : true,
21484 * @event beforerowselect
21485 * Fires when a row is selected being selected, return false to cancel.
21486 * @param {SelectionModel} this
21487 * @param {Number} rowIndex The selected index
21488 * @param {Boolean} keepExisting False if other selections will be cleared
21490 "beforerowselect" : true,
21493 * Fires when a row is selected.
21494 * @param {SelectionModel} this
21495 * @param {Number} rowIndex The selected index
21496 * @param {Roo.data.Record} r The record
21498 "rowselect" : true,
21500 * @event rowdeselect
21501 * Fires when a row is deselected.
21502 * @param {SelectionModel} this
21503 * @param {Number} rowIndex The selected index
21505 "rowdeselect" : true
21507 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21508 this.locked = false;
21511 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21513 * @cfg {Boolean} singleSelect
21514 * True to allow selection of only one row at a time (defaults to false)
21516 singleSelect : false,
21519 initEvents : function(){
21521 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21522 this.grid.on("mousedown", this.handleMouseDown, this);
21523 }else{ // allow click to work like normal
21524 this.grid.on("rowclick", this.handleDragableRowClick, this);
21527 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21528 "up" : function(e){
21530 this.selectPrevious(e.shiftKey);
21531 }else if(this.last !== false && this.lastActive !== false){
21532 var last = this.last;
21533 this.selectRange(this.last, this.lastActive-1);
21534 this.grid.getView().focusRow(this.lastActive);
21535 if(last !== false){
21539 this.selectFirstRow();
21541 this.fireEvent("afterselectionchange", this);
21543 "down" : function(e){
21545 this.selectNext(e.shiftKey);
21546 }else if(this.last !== false && this.lastActive !== false){
21547 var last = this.last;
21548 this.selectRange(this.last, this.lastActive+1);
21549 this.grid.getView().focusRow(this.lastActive);
21550 if(last !== false){
21554 this.selectFirstRow();
21556 this.fireEvent("afterselectionchange", this);
21561 var view = this.grid.view;
21562 view.on("refresh", this.onRefresh, this);
21563 view.on("rowupdated", this.onRowUpdated, this);
21564 view.on("rowremoved", this.onRemove, this);
21568 onRefresh : function(){
21569 var ds = this.grid.dataSource, i, v = this.grid.view;
21570 var s = this.selections;
21571 s.each(function(r){
21572 if((i = ds.indexOfId(r.id)) != -1){
21581 onRemove : function(v, index, r){
21582 this.selections.remove(r);
21586 onRowUpdated : function(v, index, r){
21587 if(this.isSelected(r)){
21588 v.onRowSelect(index);
21594 * @param {Array} records The records to select
21595 * @param {Boolean} keepExisting (optional) True to keep existing selections
21597 selectRecords : function(records, keepExisting){
21599 this.clearSelections();
21601 var ds = this.grid.dataSource;
21602 for(var i = 0, len = records.length; i < len; i++){
21603 this.selectRow(ds.indexOf(records[i]), true);
21608 * Gets the number of selected rows.
21611 getCount : function(){
21612 return this.selections.length;
21616 * Selects the first row in the grid.
21618 selectFirstRow : function(){
21623 * Select the last row.
21624 * @param {Boolean} keepExisting (optional) True to keep existing selections
21626 selectLastRow : function(keepExisting){
21627 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21631 * Selects the row immediately following the last selected row.
21632 * @param {Boolean} keepExisting (optional) True to keep existing selections
21634 selectNext : function(keepExisting){
21635 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21636 this.selectRow(this.last+1, keepExisting);
21637 this.grid.getView().focusRow(this.last);
21642 * Selects the row that precedes the last selected row.
21643 * @param {Boolean} keepExisting (optional) True to keep existing selections
21645 selectPrevious : function(keepExisting){
21647 this.selectRow(this.last-1, keepExisting);
21648 this.grid.getView().focusRow(this.last);
21653 * Returns the selected records
21654 * @return {Array} Array of selected records
21656 getSelections : function(){
21657 return [].concat(this.selections.items);
21661 * Returns the first selected record.
21664 getSelected : function(){
21665 return this.selections.itemAt(0);
21670 * Clears all selections.
21672 clearSelections : function(fast){
21673 if(this.locked) return;
21675 var ds = this.grid.dataSource;
21676 var s = this.selections;
21677 s.each(function(r){
21678 this.deselectRow(ds.indexOfId(r.id));
21682 this.selections.clear();
21689 * Selects all rows.
21691 selectAll : function(){
21692 if(this.locked) return;
21693 this.selections.clear();
21694 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21695 this.selectRow(i, true);
21700 * Returns True if there is a selection.
21701 * @return {Boolean}
21703 hasSelection : function(){
21704 return this.selections.length > 0;
21708 * Returns True if the specified row is selected.
21709 * @param {Number/Record} record The record or index of the record to check
21710 * @return {Boolean}
21712 isSelected : function(index){
21713 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21714 return (r && this.selections.key(r.id) ? true : false);
21718 * Returns True if the specified record id is selected.
21719 * @param {String} id The id of record to check
21720 * @return {Boolean}
21722 isIdSelected : function(id){
21723 return (this.selections.key(id) ? true : false);
21727 handleMouseDown : function(e, t){
21728 var view = this.grid.getView(), rowIndex;
21729 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21732 if(e.shiftKey && this.last !== false){
21733 var last = this.last;
21734 this.selectRange(last, rowIndex, e.ctrlKey);
21735 this.last = last; // reset the last
21736 view.focusRow(rowIndex);
21738 var isSelected = this.isSelected(rowIndex);
21739 if(e.button !== 0 && isSelected){
21740 view.focusRow(rowIndex);
21741 }else if(e.ctrlKey && isSelected){
21742 this.deselectRow(rowIndex);
21743 }else if(!isSelected){
21744 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21745 view.focusRow(rowIndex);
21748 this.fireEvent("afterselectionchange", this);
21751 handleDragableRowClick : function(grid, rowIndex, e)
21753 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21754 this.selectRow(rowIndex, false);
21755 grid.view.focusRow(rowIndex);
21756 this.fireEvent("afterselectionchange", this);
21761 * Selects multiple rows.
21762 * @param {Array} rows Array of the indexes of the row to select
21763 * @param {Boolean} keepExisting (optional) True to keep existing selections
21765 selectRows : function(rows, keepExisting){
21767 this.clearSelections();
21769 for(var i = 0, len = rows.length; i < len; i++){
21770 this.selectRow(rows[i], true);
21775 * Selects a range of rows. All rows in between startRow and endRow are also selected.
21776 * @param {Number} startRow The index of the first row in the range
21777 * @param {Number} endRow The index of the last row in the range
21778 * @param {Boolean} keepExisting (optional) True to retain existing selections
21780 selectRange : function(startRow, endRow, keepExisting){
21781 if(this.locked) return;
21783 this.clearSelections();
21785 if(startRow <= endRow){
21786 for(var i = startRow; i <= endRow; i++){
21787 this.selectRow(i, true);
21790 for(var i = startRow; i >= endRow; i--){
21791 this.selectRow(i, true);
21797 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21798 * @param {Number} startRow The index of the first row in the range
21799 * @param {Number} endRow The index of the last row in the range
21801 deselectRange : function(startRow, endRow, preventViewNotify){
21802 if(this.locked) return;
21803 for(var i = startRow; i <= endRow; i++){
21804 this.deselectRow(i, preventViewNotify);
21810 * @param {Number} row The index of the row to select
21811 * @param {Boolean} keepExisting (optional) True to keep existing selections
21813 selectRow : function(index, keepExisting, preventViewNotify){
21814 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21815 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21816 if(!keepExisting || this.singleSelect){
21817 this.clearSelections();
21819 var r = this.grid.dataSource.getAt(index);
21820 this.selections.add(r);
21821 this.last = this.lastActive = index;
21822 if(!preventViewNotify){
21823 this.grid.getView().onRowSelect(index);
21825 this.fireEvent("rowselect", this, index, r);
21826 this.fireEvent("selectionchange", this);
21832 * @param {Number} row The index of the row to deselect
21834 deselectRow : function(index, preventViewNotify){
21835 if(this.locked) return;
21836 if(this.last == index){
21839 if(this.lastActive == index){
21840 this.lastActive = false;
21842 var r = this.grid.dataSource.getAt(index);
21843 this.selections.remove(r);
21844 if(!preventViewNotify){
21845 this.grid.getView().onRowDeselect(index);
21847 this.fireEvent("rowdeselect", this, index);
21848 this.fireEvent("selectionchange", this);
21852 restoreLast : function(){
21854 this.last = this._last;
21859 acceptsNav : function(row, col, cm){
21860 return !cm.isHidden(col) && cm.isCellEditable(col, row);
21864 onEditorKey : function(field, e){
21865 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21870 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21872 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21874 }else if(k == e.ENTER && !e.ctrlKey){
21878 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21880 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21882 }else if(k == e.ESC){
21886 g.startEditing(newCell[0], newCell[1]);
21891 * Ext JS Library 1.1.1
21892 * Copyright(c) 2006-2007, Ext JS, LLC.
21894 * Originally Released Under LGPL - original licence link has changed is not relivant.
21897 * <script type="text/javascript">
21901 * @class Roo.bootstrap.PagingToolbar
21902 * @extends Roo.bootstrap.NavSimplebar
21903 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21905 * Create a new PagingToolbar
21906 * @param {Object} config The config object
21907 * @param {Roo.data.Store} store
21909 Roo.bootstrap.PagingToolbar = function(config)
21911 // old args format still supported... - xtype is prefered..
21912 // created from xtype...
21914 this.ds = config.dataSource;
21916 if (config.store && !this.ds) {
21917 this.store= Roo.factory(config.store, Roo.data);
21918 this.ds = this.store;
21919 this.ds.xmodule = this.xmodule || false;
21922 this.toolbarItems = [];
21923 if (config.items) {
21924 this.toolbarItems = config.items;
21927 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21932 this.bind(this.ds);
21935 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21939 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21941 * @cfg {Roo.data.Store} dataSource
21942 * The underlying data store providing the paged data
21945 * @cfg {String/HTMLElement/Element} container
21946 * container The id or element that will contain the toolbar
21949 * @cfg {Boolean} displayInfo
21950 * True to display the displayMsg (defaults to false)
21953 * @cfg {Number} pageSize
21954 * The number of records to display per page (defaults to 20)
21958 * @cfg {String} displayMsg
21959 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21961 displayMsg : 'Displaying {0} - {1} of {2}',
21963 * @cfg {String} emptyMsg
21964 * The message to display when no records are found (defaults to "No data to display")
21966 emptyMsg : 'No data to display',
21968 * Customizable piece of the default paging text (defaults to "Page")
21971 beforePageText : "Page",
21973 * Customizable piece of the default paging text (defaults to "of %0")
21976 afterPageText : "of {0}",
21978 * Customizable piece of the default paging text (defaults to "First Page")
21981 firstText : "First Page",
21983 * Customizable piece of the default paging text (defaults to "Previous Page")
21986 prevText : "Previous Page",
21988 * Customizable piece of the default paging text (defaults to "Next Page")
21991 nextText : "Next Page",
21993 * Customizable piece of the default paging text (defaults to "Last Page")
21996 lastText : "Last Page",
21998 * Customizable piece of the default paging text (defaults to "Refresh")
22001 refreshText : "Refresh",
22005 onRender : function(ct, position)
22007 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22008 this.navgroup.parentId = this.id;
22009 this.navgroup.onRender(this.el, null);
22010 // add the buttons to the navgroup
22012 if(this.displayInfo){
22013 Roo.log(this.el.select('ul.navbar-nav',true).first());
22014 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22015 this.displayEl = this.el.select('.x-paging-info', true).first();
22016 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22017 // this.displayEl = navel.el.select('span',true).first();
22023 Roo.each(_this.buttons, function(e){ // this might need to use render????
22024 Roo.factory(e).onRender(_this.el, null);
22028 Roo.each(_this.toolbarItems, function(e) {
22029 _this.navgroup.addItem(e);
22033 this.first = this.navgroup.addItem({
22034 tooltip: this.firstText,
22036 icon : 'fa fa-backward',
22038 preventDefault: true,
22039 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22042 this.prev = this.navgroup.addItem({
22043 tooltip: this.prevText,
22045 icon : 'fa fa-step-backward',
22047 preventDefault: true,
22048 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
22050 //this.addSeparator();
22053 var field = this.navgroup.addItem( {
22055 cls : 'x-paging-position',
22057 html : this.beforePageText +
22058 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22059 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
22062 this.field = field.el.select('input', true).first();
22063 this.field.on("keydown", this.onPagingKeydown, this);
22064 this.field.on("focus", function(){this.dom.select();});
22067 this.afterTextEl = field.el.select('.x-paging-after',true).first();
22068 //this.field.setHeight(18);
22069 //this.addSeparator();
22070 this.next = this.navgroup.addItem({
22071 tooltip: this.nextText,
22073 html : ' <i class="fa fa-step-forward">',
22075 preventDefault: true,
22076 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
22078 this.last = this.navgroup.addItem({
22079 tooltip: this.lastText,
22080 icon : 'fa fa-forward',
22083 preventDefault: true,
22084 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
22086 //this.addSeparator();
22087 this.loading = this.navgroup.addItem({
22088 tooltip: this.refreshText,
22089 icon: 'fa fa-refresh',
22090 preventDefault: true,
22091 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22097 updateInfo : function(){
22098 if(this.displayEl){
22099 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22100 var msg = count == 0 ?
22104 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
22106 this.displayEl.update(msg);
22111 onLoad : function(ds, r, o){
22112 this.cursor = o.params ? o.params.start : 0;
22113 var d = this.getPageData(),
22117 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22118 this.field.dom.value = ap;
22119 this.first.setDisabled(ap == 1);
22120 this.prev.setDisabled(ap == 1);
22121 this.next.setDisabled(ap == ps);
22122 this.last.setDisabled(ap == ps);
22123 this.loading.enable();
22128 getPageData : function(){
22129 var total = this.ds.getTotalCount();
22132 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22133 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22138 onLoadError : function(){
22139 this.loading.enable();
22143 onPagingKeydown : function(e){
22144 var k = e.getKey();
22145 var d = this.getPageData();
22147 var v = this.field.dom.value, pageNum;
22148 if(!v || isNaN(pageNum = parseInt(v, 10))){
22149 this.field.dom.value = d.activePage;
22152 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22153 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22156 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))
22158 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22159 this.field.dom.value = pageNum;
22160 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22163 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22165 var v = this.field.dom.value, pageNum;
22166 var increment = (e.shiftKey) ? 10 : 1;
22167 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22169 if(!v || isNaN(pageNum = parseInt(v, 10))) {
22170 this.field.dom.value = d.activePage;
22173 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22175 this.field.dom.value = parseInt(v, 10) + increment;
22176 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22177 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22184 beforeLoad : function(){
22186 this.loading.disable();
22191 onClick : function(which){
22200 ds.load({params:{start: 0, limit: this.pageSize}});
22203 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22206 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22209 var total = ds.getTotalCount();
22210 var extra = total % this.pageSize;
22211 var lastStart = extra ? (total - extra) : total-this.pageSize;
22212 ds.load({params:{start: lastStart, limit: this.pageSize}});
22215 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22221 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22222 * @param {Roo.data.Store} store The data store to unbind
22224 unbind : function(ds){
22225 ds.un("beforeload", this.beforeLoad, this);
22226 ds.un("load", this.onLoad, this);
22227 ds.un("loadexception", this.onLoadError, this);
22228 ds.un("remove", this.updateInfo, this);
22229 ds.un("add", this.updateInfo, this);
22230 this.ds = undefined;
22234 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22235 * @param {Roo.data.Store} store The data store to bind
22237 bind : function(ds){
22238 ds.on("beforeload", this.beforeLoad, this);
22239 ds.on("load", this.onLoad, this);
22240 ds.on("loadexception", this.onLoadError, this);
22241 ds.on("remove", this.updateInfo, this);
22242 ds.on("add", this.updateInfo, this);
22253 * @class Roo.bootstrap.MessageBar
22254 * @extends Roo.bootstrap.Component
22255 * Bootstrap MessageBar class
22256 * @cfg {String} html contents of the MessageBar
22257 * @cfg {String} weight (info | success | warning | danger) default info
22258 * @cfg {String} beforeClass insert the bar before the given class
22259 * @cfg {Boolean} closable (true | false) default false
22260 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22263 * Create a new Element
22264 * @param {Object} config The config object
22267 Roo.bootstrap.MessageBar = function(config){
22268 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22271 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22277 beforeClass: 'bootstrap-sticky-wrap',
22279 getAutoCreate : function(){
22283 cls: 'alert alert-dismissable alert-' + this.weight,
22288 html: this.html || ''
22294 cfg.cls += ' alert-messages-fixed';
22308 onRender : function(ct, position)
22310 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22313 var cfg = Roo.apply({}, this.getAutoCreate());
22317 cfg.cls += ' ' + this.cls;
22320 cfg.style = this.style;
22322 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22324 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22327 this.el.select('>button.close').on('click', this.hide, this);
22333 if (!this.rendered) {
22339 this.fireEvent('show', this);
22345 if (!this.rendered) {
22351 this.fireEvent('hide', this);
22354 update : function()
22356 // var e = this.el.dom.firstChild;
22358 // if(this.closable){
22359 // e = e.nextSibling;
22362 // e.data = this.html || '';
22364 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22380 * @class Roo.bootstrap.Graph
22381 * @extends Roo.bootstrap.Component
22382 * Bootstrap Graph class
22386 @cfg {String} graphtype bar | vbar | pie
22387 @cfg {number} g_x coodinator | centre x (pie)
22388 @cfg {number} g_y coodinator | centre y (pie)
22389 @cfg {number} g_r radius (pie)
22390 @cfg {number} g_height height of the chart (respected by all elements in the set)
22391 @cfg {number} g_width width of the chart (respected by all elements in the set)
22392 @cfg {Object} title The title of the chart
22395 -opts (object) options for the chart
22397 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22398 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22400 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.
22401 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22403 o stretch (boolean)
22405 -opts (object) options for the pie
22408 o startAngle (number)
22409 o endAngle (number)
22413 * Create a new Input
22414 * @param {Object} config The config object
22417 Roo.bootstrap.Graph = function(config){
22418 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22424 * The img click event for the img.
22425 * @param {Roo.EventObject} e
22431 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22442 //g_colors: this.colors,
22449 getAutoCreate : function(){
22460 onRender : function(ct,position){
22461 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22462 this.raphael = Raphael(this.el.dom);
22464 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22465 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22466 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22467 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22469 r.text(160, 10, "Single Series Chart").attr(txtattr);
22470 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22471 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22472 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22474 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22475 r.barchart(330, 10, 300, 220, data1);
22476 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22477 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22480 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22481 // r.barchart(30, 30, 560, 250, xdata, {
22482 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22483 // axis : "0 0 1 1",
22484 // axisxlabels : xdata
22485 // //yvalues : cols,
22488 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22490 // this.load(null,xdata,{
22491 // axis : "0 0 1 1",
22492 // axisxlabels : xdata
22497 load : function(graphtype,xdata,opts){
22498 this.raphael.clear();
22500 graphtype = this.graphtype;
22505 var r = this.raphael,
22506 fin = function () {
22507 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22509 fout = function () {
22510 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22512 pfin = function() {
22513 this.sector.stop();
22514 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22517 this.label[0].stop();
22518 this.label[0].attr({ r: 7.5 });
22519 this.label[1].attr({ "font-weight": 800 });
22522 pfout = function() {
22523 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22526 this.label[0].animate({ r: 5 }, 500, "bounce");
22527 this.label[1].attr({ "font-weight": 400 });
22533 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22536 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22539 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
22540 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22542 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22549 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22554 setTitle: function(o)
22559 initEvents: function() {
22562 this.el.on('click', this.onClick, this);
22566 onClick : function(e)
22568 Roo.log('img onclick');
22569 this.fireEvent('click', this, e);
22581 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22584 * @class Roo.bootstrap.dash.NumberBox
22585 * @extends Roo.bootstrap.Component
22586 * Bootstrap NumberBox class
22587 * @cfg {String} headline Box headline
22588 * @cfg {String} content Box content
22589 * @cfg {String} icon Box icon
22590 * @cfg {String} footer Footer text
22591 * @cfg {String} fhref Footer href
22594 * Create a new NumberBox
22595 * @param {Object} config The config object
22599 Roo.bootstrap.dash.NumberBox = function(config){
22600 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22604 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
22613 getAutoCreate : function(){
22617 cls : 'small-box ',
22625 cls : 'roo-headline',
22626 html : this.headline
22630 cls : 'roo-content',
22631 html : this.content
22645 cls : 'ion ' + this.icon
22654 cls : 'small-box-footer',
22655 href : this.fhref || '#',
22659 cfg.cn.push(footer);
22666 onRender : function(ct,position){
22667 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22674 setHeadline: function (value)
22676 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22679 setFooter: function (value, href)
22681 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22684 this.el.select('a.small-box-footer',true).first().attr('href', href);
22689 setContent: function (value)
22691 this.el.select('.roo-content',true).first().dom.innerHTML = value;
22694 initEvents: function()
22708 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22711 * @class Roo.bootstrap.dash.TabBox
22712 * @extends Roo.bootstrap.Component
22713 * Bootstrap TabBox class
22714 * @cfg {String} title Title of the TabBox
22715 * @cfg {String} icon Icon of the TabBox
22716 * @cfg {Boolean} showtabs (true|false) show the tabs default true
22717 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22720 * Create a new TabBox
22721 * @param {Object} config The config object
22725 Roo.bootstrap.dash.TabBox = function(config){
22726 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22731 * When a pane is added
22732 * @param {Roo.bootstrap.dash.TabPane} pane
22736 * @event activatepane
22737 * When a pane is activated
22738 * @param {Roo.bootstrap.dash.TabPane} pane
22740 "activatepane" : true
22748 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
22753 tabScrollable : false,
22755 getChildContainer : function()
22757 return this.el.select('.tab-content', true).first();
22760 getAutoCreate : function(){
22764 cls: 'pull-left header',
22772 cls: 'fa ' + this.icon
22778 cls: 'nav nav-tabs pull-right',
22784 if(this.tabScrollable){
22791 cls: 'nav nav-tabs pull-right',
22802 cls: 'nav-tabs-custom',
22807 cls: 'tab-content no-padding',
22815 initEvents : function()
22817 //Roo.log('add add pane handler');
22818 this.on('addpane', this.onAddPane, this);
22821 * Updates the box title
22822 * @param {String} html to set the title to.
22824 setTitle : function(value)
22826 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22828 onAddPane : function(pane)
22830 this.panes.push(pane);
22831 //Roo.log('addpane');
22833 // tabs are rendere left to right..
22834 if(!this.showtabs){
22838 var ctr = this.el.select('.nav-tabs', true).first();
22841 var existing = ctr.select('.nav-tab',true);
22842 var qty = existing.getCount();;
22845 var tab = ctr.createChild({
22847 cls : 'nav-tab' + (qty ? '' : ' active'),
22855 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22858 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22860 pane.el.addClass('active');
22865 onTabClick : function(ev,un,ob,pane)
22867 //Roo.log('tab - prev default');
22868 ev.preventDefault();
22871 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22872 pane.tab.addClass('active');
22873 //Roo.log(pane.title);
22874 this.getChildContainer().select('.tab-pane',true).removeClass('active');
22875 // technically we should have a deactivate event.. but maybe add later.
22876 // and it should not de-activate the selected tab...
22877 this.fireEvent('activatepane', pane);
22878 pane.el.addClass('active');
22879 pane.fireEvent('activate');
22884 getActivePane : function()
22887 Roo.each(this.panes, function(p) {
22888 if(p.el.hasClass('active')){
22909 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22911 * @class Roo.bootstrap.TabPane
22912 * @extends Roo.bootstrap.Component
22913 * Bootstrap TabPane class
22914 * @cfg {Boolean} active (false | true) Default false
22915 * @cfg {String} title title of panel
22919 * Create a new TabPane
22920 * @param {Object} config The config object
22923 Roo.bootstrap.dash.TabPane = function(config){
22924 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22930 * When a pane is activated
22931 * @param {Roo.bootstrap.dash.TabPane} pane
22938 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
22943 // the tabBox that this is attached to.
22946 getAutoCreate : function()
22954 cfg.cls += ' active';
22959 initEvents : function()
22961 //Roo.log('trigger add pane handler');
22962 this.parent().fireEvent('addpane', this)
22966 * Updates the tab title
22967 * @param {String} html to set the title to.
22969 setTitle: function(str)
22975 this.tab.select('a', true).first().dom.innerHTML = str;
22992 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22995 * @class Roo.bootstrap.menu.Menu
22996 * @extends Roo.bootstrap.Component
22997 * Bootstrap Menu class - container for Menu
22998 * @cfg {String} html Text of the menu
22999 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23000 * @cfg {String} icon Font awesome icon
23001 * @cfg {String} pos Menu align to (top | bottom) default bottom
23005 * Create a new Menu
23006 * @param {Object} config The config object
23010 Roo.bootstrap.menu.Menu = function(config){
23011 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23015 * @event beforeshow
23016 * Fires before this menu is displayed
23017 * @param {Roo.bootstrap.menu.Menu} this
23021 * @event beforehide
23022 * Fires before this menu is hidden
23023 * @param {Roo.bootstrap.menu.Menu} this
23028 * Fires after this menu is displayed
23029 * @param {Roo.bootstrap.menu.Menu} this
23034 * Fires after this menu is hidden
23035 * @param {Roo.bootstrap.menu.Menu} this
23040 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23041 * @param {Roo.bootstrap.menu.Menu} this
23042 * @param {Roo.EventObject} e
23049 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
23053 weight : 'default',
23058 getChildContainer : function() {
23059 if(this.isSubMenu){
23063 return this.el.select('ul.dropdown-menu', true).first();
23066 getAutoCreate : function()
23071 cls : 'roo-menu-text',
23079 cls : 'fa ' + this.icon
23090 cls : 'dropdown-button btn btn-' + this.weight,
23095 cls : 'dropdown-toggle btn btn-' + this.weight,
23105 cls : 'dropdown-menu'
23111 if(this.pos == 'top'){
23112 cfg.cls += ' dropup';
23115 if(this.isSubMenu){
23118 cls : 'dropdown-menu'
23125 onRender : function(ct, position)
23127 this.isSubMenu = ct.hasClass('dropdown-submenu');
23129 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23132 initEvents : function()
23134 if(this.isSubMenu){
23138 this.hidden = true;
23140 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23141 this.triggerEl.on('click', this.onTriggerPress, this);
23143 this.buttonEl = this.el.select('button.dropdown-button', true).first();
23144 this.buttonEl.on('click', this.onClick, this);
23150 if(this.isSubMenu){
23154 return this.el.select('ul.dropdown-menu', true).first();
23157 onClick : function(e)
23159 this.fireEvent("click", this, e);
23162 onTriggerPress : function(e)
23164 if (this.isVisible()) {
23171 isVisible : function(){
23172 return !this.hidden;
23177 this.fireEvent("beforeshow", this);
23179 this.hidden = false;
23180 this.el.addClass('open');
23182 Roo.get(document).on("mouseup", this.onMouseUp, this);
23184 this.fireEvent("show", this);
23191 this.fireEvent("beforehide", this);
23193 this.hidden = true;
23194 this.el.removeClass('open');
23196 Roo.get(document).un("mouseup", this.onMouseUp);
23198 this.fireEvent("hide", this);
23201 onMouseUp : function()
23215 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23218 * @class Roo.bootstrap.menu.Item
23219 * @extends Roo.bootstrap.Component
23220 * Bootstrap MenuItem class
23221 * @cfg {Boolean} submenu (true | false) default false
23222 * @cfg {String} html text of the item
23223 * @cfg {String} href the link
23224 * @cfg {Boolean} disable (true | false) default false
23225 * @cfg {Boolean} preventDefault (true | false) default true
23226 * @cfg {String} icon Font awesome icon
23227 * @cfg {String} pos Submenu align to (left | right) default right
23231 * Create a new Item
23232 * @param {Object} config The config object
23236 Roo.bootstrap.menu.Item = function(config){
23237 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23241 * Fires when the mouse is hovering over this menu
23242 * @param {Roo.bootstrap.menu.Item} this
23243 * @param {Roo.EventObject} e
23248 * Fires when the mouse exits this menu
23249 * @param {Roo.bootstrap.menu.Item} this
23250 * @param {Roo.EventObject} e
23256 * The raw click event for the entire grid.
23257 * @param {Roo.EventObject} e
23263 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23268 preventDefault: true,
23273 getAutoCreate : function()
23278 cls : 'roo-menu-item-text',
23286 cls : 'fa ' + this.icon
23295 href : this.href || '#',
23302 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23306 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23308 if(this.pos == 'left'){
23309 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23316 initEvents : function()
23318 this.el.on('mouseover', this.onMouseOver, this);
23319 this.el.on('mouseout', this.onMouseOut, this);
23321 this.el.select('a', true).first().on('click', this.onClick, this);
23325 onClick : function(e)
23327 if(this.preventDefault){
23328 e.preventDefault();
23331 this.fireEvent("click", this, e);
23334 onMouseOver : function(e)
23336 if(this.submenu && this.pos == 'left'){
23337 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23340 this.fireEvent("mouseover", this, e);
23343 onMouseOut : function(e)
23345 this.fireEvent("mouseout", this, e);
23357 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23360 * @class Roo.bootstrap.menu.Separator
23361 * @extends Roo.bootstrap.Component
23362 * Bootstrap Separator class
23365 * Create a new Separator
23366 * @param {Object} config The config object
23370 Roo.bootstrap.menu.Separator = function(config){
23371 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23374 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23376 getAutoCreate : function(){
23397 * @class Roo.bootstrap.Tooltip
23398 * Bootstrap Tooltip class
23399 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23400 * to determine which dom element triggers the tooltip.
23402 * It needs to add support for additional attributes like tooltip-position
23405 * Create a new Toolti
23406 * @param {Object} config The config object
23409 Roo.bootstrap.Tooltip = function(config){
23410 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23413 Roo.apply(Roo.bootstrap.Tooltip, {
23415 * @function init initialize tooltip monitoring.
23419 currentTip : false,
23420 currentRegion : false,
23426 Roo.get(document).on('mouseover', this.enter ,this);
23427 Roo.get(document).on('mouseout', this.leave, this);
23430 this.currentTip = new Roo.bootstrap.Tooltip();
23433 enter : function(ev)
23435 var dom = ev.getTarget();
23437 //Roo.log(['enter',dom]);
23438 var el = Roo.fly(dom);
23439 if (this.currentEl) {
23441 //Roo.log(this.currentEl);
23442 //Roo.log(this.currentEl.contains(dom));
23443 if (this.currentEl == el) {
23446 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23452 if (this.currentTip.el) {
23453 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23458 // you can not look for children, as if el is the body.. then everythign is the child..
23459 if (!el.attr('tooltip')) { //
23460 if (!el.select("[tooltip]").elements.length) {
23463 // is the mouse over this child...?
23464 bindEl = el.select("[tooltip]").first();
23465 var xy = ev.getXY();
23466 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23467 //Roo.log("not in region.");
23470 //Roo.log("child element over..");
23473 this.currentEl = bindEl;
23474 this.currentTip.bind(bindEl);
23475 this.currentRegion = Roo.lib.Region.getRegion(dom);
23476 this.currentTip.enter();
23479 leave : function(ev)
23481 var dom = ev.getTarget();
23482 //Roo.log(['leave',dom]);
23483 if (!this.currentEl) {
23488 if (dom != this.currentEl.dom) {
23491 var xy = ev.getXY();
23492 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23495 // only activate leave if mouse cursor is outside... bounding box..
23500 if (this.currentTip) {
23501 this.currentTip.leave();
23503 //Roo.log('clear currentEl');
23504 this.currentEl = false;
23509 'left' : ['r-l', [-2,0], 'right'],
23510 'right' : ['l-r', [2,0], 'left'],
23511 'bottom' : ['t-b', [0,2], 'top'],
23512 'top' : [ 'b-t', [0,-2], 'bottom']
23518 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
23523 delay : null, // can be { show : 300 , hide: 500}
23527 hoverState : null, //???
23529 placement : 'bottom',
23531 getAutoCreate : function(){
23538 cls : 'tooltip-arrow'
23541 cls : 'tooltip-inner'
23548 bind : function(el)
23554 enter : function () {
23556 if (this.timeout != null) {
23557 clearTimeout(this.timeout);
23560 this.hoverState = 'in';
23561 //Roo.log("enter - show");
23562 if (!this.delay || !this.delay.show) {
23567 this.timeout = setTimeout(function () {
23568 if (_t.hoverState == 'in') {
23571 }, this.delay.show);
23575 clearTimeout(this.timeout);
23577 this.hoverState = 'out';
23578 if (!this.delay || !this.delay.hide) {
23584 this.timeout = setTimeout(function () {
23585 //Roo.log("leave - timeout");
23587 if (_t.hoverState == 'out') {
23589 Roo.bootstrap.Tooltip.currentEl = false;
23597 this.render(document.body);
23600 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23602 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23604 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23606 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23608 var placement = typeof this.placement == 'function' ?
23609 this.placement.call(this, this.el, on_el) :
23612 var autoToken = /\s?auto?\s?/i;
23613 var autoPlace = autoToken.test(placement);
23615 placement = placement.replace(autoToken, '') || 'top';
23619 //this.el.setXY([0,0]);
23621 //this.el.dom.style.display='block';
23623 //this.el.appendTo(on_el);
23625 var p = this.getPosition();
23626 var box = this.el.getBox();
23632 var align = Roo.bootstrap.Tooltip.alignment[placement];
23634 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23636 if(placement == 'top' || placement == 'bottom'){
23638 placement = 'right';
23641 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23642 placement = 'left';
23646 align = Roo.bootstrap.Tooltip.alignment[placement];
23648 this.el.alignTo(this.bindEl, align[0],align[1]);
23649 //var arrow = this.el.select('.arrow',true).first();
23650 //arrow.set(align[2],
23652 this.el.addClass(placement);
23654 this.el.addClass('in fade');
23656 this.hoverState = null;
23658 if (this.el.hasClass('fade')) {
23669 //this.el.setXY([0,0]);
23670 this.el.removeClass('in');
23686 * @class Roo.bootstrap.LocationPicker
23687 * @extends Roo.bootstrap.Component
23688 * Bootstrap LocationPicker class
23689 * @cfg {Number} latitude Position when init default 0
23690 * @cfg {Number} longitude Position when init default 0
23691 * @cfg {Number} zoom default 15
23692 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23693 * @cfg {Boolean} mapTypeControl default false
23694 * @cfg {Boolean} disableDoubleClickZoom default false
23695 * @cfg {Boolean} scrollwheel default true
23696 * @cfg {Boolean} streetViewControl default false
23697 * @cfg {Number} radius default 0
23698 * @cfg {String} locationName
23699 * @cfg {Boolean} draggable default true
23700 * @cfg {Boolean} enableAutocomplete default false
23701 * @cfg {Boolean} enableReverseGeocode default true
23702 * @cfg {String} markerTitle
23705 * Create a new LocationPicker
23706 * @param {Object} config The config object
23710 Roo.bootstrap.LocationPicker = function(config){
23712 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23717 * Fires when the picker initialized.
23718 * @param {Roo.bootstrap.LocationPicker} this
23719 * @param {Google Location} location
23723 * @event positionchanged
23724 * Fires when the picker position changed.
23725 * @param {Roo.bootstrap.LocationPicker} this
23726 * @param {Google Location} location
23728 positionchanged : true,
23731 * Fires when the map resize.
23732 * @param {Roo.bootstrap.LocationPicker} this
23737 * Fires when the map show.
23738 * @param {Roo.bootstrap.LocationPicker} this
23743 * Fires when the map hide.
23744 * @param {Roo.bootstrap.LocationPicker} this
23749 * Fires when click the map.
23750 * @param {Roo.bootstrap.LocationPicker} this
23751 * @param {Map event} e
23755 * @event mapRightClick
23756 * Fires when right click the map.
23757 * @param {Roo.bootstrap.LocationPicker} this
23758 * @param {Map event} e
23760 mapRightClick : true,
23762 * @event markerClick
23763 * Fires when click the marker.
23764 * @param {Roo.bootstrap.LocationPicker} this
23765 * @param {Map event} e
23767 markerClick : true,
23769 * @event markerRightClick
23770 * Fires when right click the marker.
23771 * @param {Roo.bootstrap.LocationPicker} this
23772 * @param {Map event} e
23774 markerRightClick : true,
23776 * @event OverlayViewDraw
23777 * Fires when OverlayView Draw
23778 * @param {Roo.bootstrap.LocationPicker} this
23780 OverlayViewDraw : true,
23782 * @event OverlayViewOnAdd
23783 * Fires when OverlayView Draw
23784 * @param {Roo.bootstrap.LocationPicker} this
23786 OverlayViewOnAdd : true,
23788 * @event OverlayViewOnRemove
23789 * Fires when OverlayView Draw
23790 * @param {Roo.bootstrap.LocationPicker} this
23792 OverlayViewOnRemove : true,
23794 * @event OverlayViewShow
23795 * Fires when OverlayView Draw
23796 * @param {Roo.bootstrap.LocationPicker} this
23797 * @param {Pixel} cpx
23799 OverlayViewShow : true,
23801 * @event OverlayViewHide
23802 * Fires when OverlayView Draw
23803 * @param {Roo.bootstrap.LocationPicker} this
23805 OverlayViewHide : true,
23807 * @event loadexception
23808 * Fires when load google lib failed.
23809 * @param {Roo.bootstrap.LocationPicker} this
23811 loadexception : true
23816 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
23818 gMapContext: false,
23824 mapTypeControl: false,
23825 disableDoubleClickZoom: false,
23827 streetViewControl: false,
23831 enableAutocomplete: false,
23832 enableReverseGeocode: true,
23835 getAutoCreate: function()
23840 cls: 'roo-location-picker'
23846 initEvents: function(ct, position)
23848 if(!this.el.getWidth() || this.isApplied()){
23852 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23857 initial: function()
23859 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
23860 this.fireEvent('loadexception', this);
23864 if(!this.mapTypeId){
23865 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23868 this.gMapContext = this.GMapContext();
23870 this.initOverlayView();
23872 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23876 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23877 _this.setPosition(_this.gMapContext.marker.position);
23880 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23881 _this.fireEvent('mapClick', this, event);
23885 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23886 _this.fireEvent('mapRightClick', this, event);
23890 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23891 _this.fireEvent('markerClick', this, event);
23895 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23896 _this.fireEvent('markerRightClick', this, event);
23900 this.setPosition(this.gMapContext.location);
23902 this.fireEvent('initial', this, this.gMapContext.location);
23905 initOverlayView: function()
23909 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23913 _this.fireEvent('OverlayViewDraw', _this);
23918 _this.fireEvent('OverlayViewOnAdd', _this);
23921 onRemove: function()
23923 _this.fireEvent('OverlayViewOnRemove', _this);
23926 show: function(cpx)
23928 _this.fireEvent('OverlayViewShow', _this, cpx);
23933 _this.fireEvent('OverlayViewHide', _this);
23939 fromLatLngToContainerPixel: function(event)
23941 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23944 isApplied: function()
23946 return this.getGmapContext() == false ? false : true;
23949 getGmapContext: function()
23951 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
23954 GMapContext: function()
23956 var position = new google.maps.LatLng(this.latitude, this.longitude);
23958 var _map = new google.maps.Map(this.el.dom, {
23961 mapTypeId: this.mapTypeId,
23962 mapTypeControl: this.mapTypeControl,
23963 disableDoubleClickZoom: this.disableDoubleClickZoom,
23964 scrollwheel: this.scrollwheel,
23965 streetViewControl: this.streetViewControl,
23966 locationName: this.locationName,
23967 draggable: this.draggable,
23968 enableAutocomplete: this.enableAutocomplete,
23969 enableReverseGeocode: this.enableReverseGeocode
23972 var _marker = new google.maps.Marker({
23973 position: position,
23975 title: this.markerTitle,
23976 draggable: this.draggable
23983 location: position,
23984 radius: this.radius,
23985 locationName: this.locationName,
23986 addressComponents: {
23987 formatted_address: null,
23988 addressLine1: null,
23989 addressLine2: null,
23991 streetNumber: null,
23995 stateOrProvince: null
23998 domContainer: this.el.dom,
23999 geodecoder: new google.maps.Geocoder()
24003 drawCircle: function(center, radius, options)
24005 if (this.gMapContext.circle != null) {
24006 this.gMapContext.circle.setMap(null);
24010 options = Roo.apply({}, options, {
24011 strokeColor: "#0000FF",
24012 strokeOpacity: .35,
24014 fillColor: "#0000FF",
24018 options.map = this.gMapContext.map;
24019 options.radius = radius;
24020 options.center = center;
24021 this.gMapContext.circle = new google.maps.Circle(options);
24022 return this.gMapContext.circle;
24028 setPosition: function(location)
24030 this.gMapContext.location = location;
24031 this.gMapContext.marker.setPosition(location);
24032 this.gMapContext.map.panTo(location);
24033 this.drawCircle(location, this.gMapContext.radius, {});
24037 if (this.gMapContext.settings.enableReverseGeocode) {
24038 this.gMapContext.geodecoder.geocode({
24039 latLng: this.gMapContext.location
24040 }, function(results, status) {
24042 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24043 _this.gMapContext.locationName = results[0].formatted_address;
24044 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24046 _this.fireEvent('positionchanged', this, location);
24053 this.fireEvent('positionchanged', this, location);
24058 google.maps.event.trigger(this.gMapContext.map, "resize");
24060 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24062 this.fireEvent('resize', this);
24065 setPositionByLatLng: function(latitude, longitude)
24067 this.setPosition(new google.maps.LatLng(latitude, longitude));
24070 getCurrentPosition: function()
24073 latitude: this.gMapContext.location.lat(),
24074 longitude: this.gMapContext.location.lng()
24078 getAddressName: function()
24080 return this.gMapContext.locationName;
24083 getAddressComponents: function()
24085 return this.gMapContext.addressComponents;
24088 address_component_from_google_geocode: function(address_components)
24092 for (var i = 0; i < address_components.length; i++) {
24093 var component = address_components[i];
24094 if (component.types.indexOf("postal_code") >= 0) {
24095 result.postalCode = component.short_name;
24096 } else if (component.types.indexOf("street_number") >= 0) {
24097 result.streetNumber = component.short_name;
24098 } else if (component.types.indexOf("route") >= 0) {
24099 result.streetName = component.short_name;
24100 } else if (component.types.indexOf("neighborhood") >= 0) {
24101 result.city = component.short_name;
24102 } else if (component.types.indexOf("locality") >= 0) {
24103 result.city = component.short_name;
24104 } else if (component.types.indexOf("sublocality") >= 0) {
24105 result.district = component.short_name;
24106 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24107 result.stateOrProvince = component.short_name;
24108 } else if (component.types.indexOf("country") >= 0) {
24109 result.country = component.short_name;
24113 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24114 result.addressLine2 = "";
24118 setZoomLevel: function(zoom)
24120 this.gMapContext.map.setZoom(zoom);
24133 this.fireEvent('show', this);
24144 this.fireEvent('hide', this);
24149 Roo.apply(Roo.bootstrap.LocationPicker, {
24151 OverlayView : function(map, options)
24153 options = options || {};
24167 * @class Roo.bootstrap.Alert
24168 * @extends Roo.bootstrap.Component
24169 * Bootstrap Alert class
24170 * @cfg {String} title The title of alert
24171 * @cfg {String} html The content of alert
24172 * @cfg {String} weight ( success | info | warning | danger )
24173 * @cfg {String} faicon font-awesomeicon
24176 * Create a new alert
24177 * @param {Object} config The config object
24181 Roo.bootstrap.Alert = function(config){
24182 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24186 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
24193 getAutoCreate : function()
24202 cls : 'roo-alert-icon'
24207 cls : 'roo-alert-title',
24212 cls : 'roo-alert-text',
24219 cfg.cn[0].cls += ' fa ' + this.faicon;
24223 cfg.cls += ' alert-' + this.weight;
24229 initEvents: function()
24231 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24234 setTitle : function(str)
24236 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24239 setText : function(str)
24241 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24244 setWeight : function(weight)
24247 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24250 this.weight = weight;
24252 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24255 setIcon : function(icon)
24258 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24261 this.faicon = icon;
24263 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24284 * @class Roo.bootstrap.UploadCropbox
24285 * @extends Roo.bootstrap.Component
24286 * Bootstrap UploadCropbox class
24287 * @cfg {String} emptyText show when image has been loaded
24288 * @cfg {String} rotateNotify show when image too small to rotate
24289 * @cfg {Number} errorTimeout default 3000
24290 * @cfg {Number} minWidth default 300
24291 * @cfg {Number} minHeight default 300
24292 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24293 * @cfg {Boolean} isDocument (true|false) default false
24294 * @cfg {String} url action url
24295 * @cfg {String} paramName default 'imageUpload'
24296 * @cfg {String} method default POST
24297 * @cfg {Boolean} loadMask (true|false) default true
24298 * @cfg {Boolean} loadingText default 'Loading...'
24301 * Create a new UploadCropbox
24302 * @param {Object} config The config object
24305 Roo.bootstrap.UploadCropbox = function(config){
24306 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24310 * @event beforeselectfile
24311 * Fire before select file
24312 * @param {Roo.bootstrap.UploadCropbox} this
24314 "beforeselectfile" : true,
24317 * Fire after initEvent
24318 * @param {Roo.bootstrap.UploadCropbox} this
24323 * Fire after initEvent
24324 * @param {Roo.bootstrap.UploadCropbox} this
24325 * @param {String} data
24330 * Fire when preparing the file data
24331 * @param {Roo.bootstrap.UploadCropbox} this
24332 * @param {Object} file
24337 * Fire when get exception
24338 * @param {Roo.bootstrap.UploadCropbox} this
24339 * @param {XMLHttpRequest} xhr
24341 "exception" : true,
24343 * @event beforeloadcanvas
24344 * Fire before load the canvas
24345 * @param {Roo.bootstrap.UploadCropbox} this
24346 * @param {String} src
24348 "beforeloadcanvas" : true,
24351 * Fire when trash image
24352 * @param {Roo.bootstrap.UploadCropbox} this
24357 * Fire when download the image
24358 * @param {Roo.bootstrap.UploadCropbox} this
24362 * @event footerbuttonclick
24363 * Fire when footerbuttonclick
24364 * @param {Roo.bootstrap.UploadCropbox} this
24365 * @param {String} type
24367 "footerbuttonclick" : true,
24371 * @param {Roo.bootstrap.UploadCropbox} this
24376 * Fire when rotate the image
24377 * @param {Roo.bootstrap.UploadCropbox} this
24378 * @param {String} pos
24383 * Fire when inspect the file
24384 * @param {Roo.bootstrap.UploadCropbox} this
24385 * @param {Object} file
24390 * Fire when xhr upload the file
24391 * @param {Roo.bootstrap.UploadCropbox} this
24392 * @param {Object} data
24397 * Fire when arrange the file data
24398 * @param {Roo.bootstrap.UploadCropbox} this
24399 * @param {Object} formData
24404 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24407 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24409 emptyText : 'Click to upload image',
24410 rotateNotify : 'Image is too small to rotate',
24411 errorTimeout : 3000,
24425 cropType : 'image/jpeg',
24427 canvasLoaded : false,
24428 isDocument : false,
24430 paramName : 'imageUpload',
24432 loadingText : 'Loading...',
24435 getAutoCreate : function()
24439 cls : 'roo-upload-cropbox',
24443 cls : 'roo-upload-cropbox-selector',
24448 cls : 'roo-upload-cropbox-body',
24449 style : 'cursor:pointer',
24453 cls : 'roo-upload-cropbox-preview'
24457 cls : 'roo-upload-cropbox-thumb'
24461 cls : 'roo-upload-cropbox-empty-notify',
24462 html : this.emptyText
24466 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24467 html : this.rotateNotify
24473 cls : 'roo-upload-cropbox-footer',
24476 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24486 onRender : function(ct, position)
24488 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24490 if (this.buttons.length) {
24492 Roo.each(this.buttons, function(bb) {
24494 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24496 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24502 this.maskEl = this.el;
24506 initEvents : function()
24508 this.urlAPI = (window.createObjectURL && window) ||
24509 (window.URL && URL.revokeObjectURL && URL) ||
24510 (window.webkitURL && webkitURL);
24512 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24513 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24515 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24516 this.selectorEl.hide();
24518 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24519 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24521 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24522 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24523 this.thumbEl.hide();
24525 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24526 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24528 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24529 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24530 this.errorEl.hide();
24532 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24533 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24534 this.footerEl.hide();
24536 this.setThumbBoxSize();
24542 this.fireEvent('initial', this);
24549 window.addEventListener("resize", function() { _this.resize(); } );
24551 this.bodyEl.on('click', this.beforeSelectFile, this);
24554 this.bodyEl.on('touchstart', this.onTouchStart, this);
24555 this.bodyEl.on('touchmove', this.onTouchMove, this);
24556 this.bodyEl.on('touchend', this.onTouchEnd, this);
24560 this.bodyEl.on('mousedown', this.onMouseDown, this);
24561 this.bodyEl.on('mousemove', this.onMouseMove, this);
24562 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24563 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24564 Roo.get(document).on('mouseup', this.onMouseUp, this);
24567 this.selectorEl.on('change', this.onFileSelected, this);
24573 this.baseScale = 1;
24575 this.baseRotate = 1;
24576 this.dragable = false;
24577 this.pinching = false;
24580 this.cropData = false;
24581 this.notifyEl.dom.innerHTML = this.emptyText;
24583 this.selectorEl.dom.value = '';
24587 resize : function()
24589 if(this.fireEvent('resize', this) != false){
24590 this.setThumbBoxPosition();
24591 this.setCanvasPosition();
24595 onFooterButtonClick : function(e, el, o, type)
24598 case 'rotate-left' :
24599 this.onRotateLeft(e);
24601 case 'rotate-right' :
24602 this.onRotateRight(e);
24605 this.beforeSelectFile(e);
24620 this.fireEvent('footerbuttonclick', this, type);
24623 beforeSelectFile : function(e)
24625 e.preventDefault();
24627 if(this.fireEvent('beforeselectfile', this) != false){
24628 this.selectorEl.dom.click();
24632 onFileSelected : function(e)
24634 e.preventDefault();
24636 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24640 var file = this.selectorEl.dom.files[0];
24642 if(this.fireEvent('inspect', this, file) != false){
24643 this.prepare(file);
24648 trash : function(e)
24650 this.fireEvent('trash', this);
24653 download : function(e)
24655 this.fireEvent('download', this);
24658 loadCanvas : function(src)
24660 if(this.fireEvent('beforeloadcanvas', this, src) != false){
24664 this.imageEl = document.createElement('img');
24668 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24670 this.imageEl.src = src;
24674 onLoadCanvas : function()
24676 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24677 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24679 this.bodyEl.un('click', this.beforeSelectFile, this);
24681 this.notifyEl.hide();
24682 this.thumbEl.show();
24683 this.footerEl.show();
24685 this.baseRotateLevel();
24687 if(this.isDocument){
24688 this.setThumbBoxSize();
24691 this.setThumbBoxPosition();
24693 this.baseScaleLevel();
24699 this.canvasLoaded = true;
24702 this.maskEl.unmask();
24707 setCanvasPosition : function()
24709 if(!this.canvasEl){
24713 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24714 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24716 this.previewEl.setLeft(pw);
24717 this.previewEl.setTop(ph);
24721 onMouseDown : function(e)
24725 this.dragable = true;
24726 this.pinching = false;
24728 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24729 this.dragable = false;
24733 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24734 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24738 onMouseMove : function(e)
24742 if(!this.canvasLoaded){
24746 if (!this.dragable){
24750 var minX = Math.ceil(this.thumbEl.getLeft(true));
24751 var minY = Math.ceil(this.thumbEl.getTop(true));
24753 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24754 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24756 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24757 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24759 x = x - this.mouseX;
24760 y = y - this.mouseY;
24762 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24763 var bgY = Math.ceil(y + this.previewEl.getTop(true));
24765 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24766 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24768 this.previewEl.setLeft(bgX);
24769 this.previewEl.setTop(bgY);
24771 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24772 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24775 onMouseUp : function(e)
24779 this.dragable = false;
24782 onMouseWheel : function(e)
24786 this.startScale = this.scale;
24788 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24790 if(!this.zoomable()){
24791 this.scale = this.startScale;
24800 zoomable : function()
24802 var minScale = this.thumbEl.getWidth() / this.minWidth;
24804 if(this.minWidth < this.minHeight){
24805 minScale = this.thumbEl.getHeight() / this.minHeight;
24808 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
24809 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
24813 (this.rotate == 0 || this.rotate == 180) &&
24815 width > this.imageEl.OriginWidth ||
24816 height > this.imageEl.OriginHeight ||
24817 (width < this.minWidth && height < this.minHeight)
24825 (this.rotate == 90 || this.rotate == 270) &&
24827 width > this.imageEl.OriginWidth ||
24828 height > this.imageEl.OriginHeight ||
24829 (width < this.minHeight && height < this.minWidth)
24836 !this.isDocument &&
24837 (this.rotate == 0 || this.rotate == 180) &&
24839 width < this.minWidth ||
24840 width > this.imageEl.OriginWidth ||
24841 height < this.minHeight ||
24842 height > this.imageEl.OriginHeight
24849 !this.isDocument &&
24850 (this.rotate == 90 || this.rotate == 270) &&
24852 width < this.minHeight ||
24853 width > this.imageEl.OriginWidth ||
24854 height < this.minWidth ||
24855 height > this.imageEl.OriginHeight
24865 onRotateLeft : function(e)
24867 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24869 var minScale = this.thumbEl.getWidth() / this.minWidth;
24871 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24872 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24874 this.startScale = this.scale;
24876 while (this.getScaleLevel() < minScale){
24878 this.scale = this.scale + 1;
24880 if(!this.zoomable()){
24885 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24886 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24891 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24898 this.scale = this.startScale;
24900 this.onRotateFail();
24905 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24907 if(this.isDocument){
24908 this.setThumbBoxSize();
24909 this.setThumbBoxPosition();
24910 this.setCanvasPosition();
24915 this.fireEvent('rotate', this, 'left');
24919 onRotateRight : function(e)
24921 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24923 var minScale = this.thumbEl.getWidth() / this.minWidth;
24925 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24926 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24928 this.startScale = this.scale;
24930 while (this.getScaleLevel() < minScale){
24932 this.scale = this.scale + 1;
24934 if(!this.zoomable()){
24939 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24940 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24945 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24952 this.scale = this.startScale;
24954 this.onRotateFail();
24959 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24961 if(this.isDocument){
24962 this.setThumbBoxSize();
24963 this.setThumbBoxPosition();
24964 this.setCanvasPosition();
24969 this.fireEvent('rotate', this, 'right');
24972 onRotateFail : function()
24974 this.errorEl.show(true);
24978 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
24983 this.previewEl.dom.innerHTML = '';
24985 var canvasEl = document.createElement("canvas");
24987 var contextEl = canvasEl.getContext("2d");
24989 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24990 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24991 var center = this.imageEl.OriginWidth / 2;
24993 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
24994 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24995 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24996 center = this.imageEl.OriginHeight / 2;
24999 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25001 contextEl.translate(center, center);
25002 contextEl.rotate(this.rotate * Math.PI / 180);
25004 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25006 this.canvasEl = document.createElement("canvas");
25008 this.contextEl = this.canvasEl.getContext("2d");
25010 switch (this.rotate) {
25013 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25014 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25016 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25021 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25022 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25024 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25025 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);
25029 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25034 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25035 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25037 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25038 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);
25042 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);
25047 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25048 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25050 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25051 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25055 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);
25062 this.previewEl.appendChild(this.canvasEl);
25064 this.setCanvasPosition();
25069 if(!this.canvasLoaded){
25073 var imageCanvas = document.createElement("canvas");
25075 var imageContext = imageCanvas.getContext("2d");
25077 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25078 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25080 var center = imageCanvas.width / 2;
25082 imageContext.translate(center, center);
25084 imageContext.rotate(this.rotate * Math.PI / 180);
25086 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25088 var canvas = document.createElement("canvas");
25090 var context = canvas.getContext("2d");
25092 canvas.width = this.minWidth;
25093 canvas.height = this.minHeight;
25095 switch (this.rotate) {
25098 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25099 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25101 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25102 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25104 var targetWidth = this.minWidth - 2 * x;
25105 var targetHeight = this.minHeight - 2 * y;
25109 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25110 scale = targetWidth / width;
25113 if(x > 0 && y == 0){
25114 scale = targetHeight / height;
25117 if(x > 0 && y > 0){
25118 scale = targetWidth / width;
25120 if(width < height){
25121 scale = targetHeight / height;
25125 context.scale(scale, scale);
25127 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25128 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25130 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25131 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25133 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25138 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25139 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25141 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25142 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25144 var targetWidth = this.minWidth - 2 * x;
25145 var targetHeight = this.minHeight - 2 * y;
25149 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25150 scale = targetWidth / width;
25153 if(x > 0 && y == 0){
25154 scale = targetHeight / height;
25157 if(x > 0 && y > 0){
25158 scale = targetWidth / width;
25160 if(width < height){
25161 scale = targetHeight / height;
25165 context.scale(scale, scale);
25167 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25168 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25170 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25171 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25173 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25175 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25180 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25181 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25183 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25184 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25186 var targetWidth = this.minWidth - 2 * x;
25187 var targetHeight = this.minHeight - 2 * y;
25191 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25192 scale = targetWidth / width;
25195 if(x > 0 && y == 0){
25196 scale = targetHeight / height;
25199 if(x > 0 && y > 0){
25200 scale = targetWidth / width;
25202 if(width < height){
25203 scale = targetHeight / height;
25207 context.scale(scale, scale);
25209 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25210 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25212 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25213 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25215 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25216 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25218 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25223 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25224 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25226 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25227 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25229 var targetWidth = this.minWidth - 2 * x;
25230 var targetHeight = this.minHeight - 2 * y;
25234 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25235 scale = targetWidth / width;
25238 if(x > 0 && y == 0){
25239 scale = targetHeight / height;
25242 if(x > 0 && y > 0){
25243 scale = targetWidth / width;
25245 if(width < height){
25246 scale = targetHeight / height;
25250 context.scale(scale, scale);
25252 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25253 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25255 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25256 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25258 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25260 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25267 this.cropData = canvas.toDataURL(this.cropType);
25269 if(this.fireEvent('crop', this, this.cropData) !== false){
25270 this.process(this.file, this.cropData);
25277 setThumbBoxSize : function()
25281 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25282 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25283 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25285 this.minWidth = width;
25286 this.minHeight = height;
25288 if(this.rotate == 90 || this.rotate == 270){
25289 this.minWidth = height;
25290 this.minHeight = width;
25295 width = Math.ceil(this.minWidth * height / this.minHeight);
25297 if(this.minWidth > this.minHeight){
25299 height = Math.ceil(this.minHeight * width / this.minWidth);
25302 this.thumbEl.setStyle({
25303 width : width + 'px',
25304 height : height + 'px'
25311 setThumbBoxPosition : function()
25313 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25314 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25316 this.thumbEl.setLeft(x);
25317 this.thumbEl.setTop(y);
25321 baseRotateLevel : function()
25323 this.baseRotate = 1;
25326 typeof(this.exif) != 'undefined' &&
25327 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25328 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25330 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25333 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25337 baseScaleLevel : function()
25341 if(this.isDocument){
25343 if(this.baseRotate == 6 || this.baseRotate == 8){
25345 height = this.thumbEl.getHeight();
25346 this.baseScale = height / this.imageEl.OriginWidth;
25348 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25349 width = this.thumbEl.getWidth();
25350 this.baseScale = width / this.imageEl.OriginHeight;
25356 height = this.thumbEl.getHeight();
25357 this.baseScale = height / this.imageEl.OriginHeight;
25359 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25360 width = this.thumbEl.getWidth();
25361 this.baseScale = width / this.imageEl.OriginWidth;
25367 if(this.baseRotate == 6 || this.baseRotate == 8){
25369 width = this.thumbEl.getHeight();
25370 this.baseScale = width / this.imageEl.OriginHeight;
25372 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25373 height = this.thumbEl.getWidth();
25374 this.baseScale = height / this.imageEl.OriginHeight;
25377 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25378 height = this.thumbEl.getWidth();
25379 this.baseScale = height / this.imageEl.OriginHeight;
25381 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25382 width = this.thumbEl.getHeight();
25383 this.baseScale = width / this.imageEl.OriginWidth;
25390 width = this.thumbEl.getWidth();
25391 this.baseScale = width / this.imageEl.OriginWidth;
25393 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25394 height = this.thumbEl.getHeight();
25395 this.baseScale = height / this.imageEl.OriginHeight;
25398 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25400 height = this.thumbEl.getHeight();
25401 this.baseScale = height / this.imageEl.OriginHeight;
25403 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25404 width = this.thumbEl.getWidth();
25405 this.baseScale = width / this.imageEl.OriginWidth;
25413 getScaleLevel : function()
25415 return this.baseScale * Math.pow(1.1, this.scale);
25418 onTouchStart : function(e)
25420 if(!this.canvasLoaded){
25421 this.beforeSelectFile(e);
25425 var touches = e.browserEvent.touches;
25431 if(touches.length == 1){
25432 this.onMouseDown(e);
25436 if(touches.length != 2){
25442 for(var i = 0, finger; finger = touches[i]; i++){
25443 coords.push(finger.pageX, finger.pageY);
25446 var x = Math.pow(coords[0] - coords[2], 2);
25447 var y = Math.pow(coords[1] - coords[3], 2);
25449 this.startDistance = Math.sqrt(x + y);
25451 this.startScale = this.scale;
25453 this.pinching = true;
25454 this.dragable = false;
25458 onTouchMove : function(e)
25460 if(!this.pinching && !this.dragable){
25464 var touches = e.browserEvent.touches;
25471 this.onMouseMove(e);
25477 for(var i = 0, finger; finger = touches[i]; i++){
25478 coords.push(finger.pageX, finger.pageY);
25481 var x = Math.pow(coords[0] - coords[2], 2);
25482 var y = Math.pow(coords[1] - coords[3], 2);
25484 this.endDistance = Math.sqrt(x + y);
25486 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25488 if(!this.zoomable()){
25489 this.scale = this.startScale;
25497 onTouchEnd : function(e)
25499 this.pinching = false;
25500 this.dragable = false;
25504 process : function(file, crop)
25507 this.maskEl.mask(this.loadingText);
25510 this.xhr = new XMLHttpRequest();
25512 file.xhr = this.xhr;
25514 this.xhr.open(this.method, this.url, true);
25517 "Accept": "application/json",
25518 "Cache-Control": "no-cache",
25519 "X-Requested-With": "XMLHttpRequest"
25522 for (var headerName in headers) {
25523 var headerValue = headers[headerName];
25525 this.xhr.setRequestHeader(headerName, headerValue);
25531 this.xhr.onload = function()
25533 _this.xhrOnLoad(_this.xhr);
25536 this.xhr.onerror = function()
25538 _this.xhrOnError(_this.xhr);
25541 var formData = new FormData();
25543 formData.append('returnHTML', 'NO');
25546 formData.append('crop', crop);
25549 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25550 formData.append(this.paramName, file, file.name);
25553 if(typeof(file.filename) != 'undefined'){
25554 formData.append('filename', file.filename);
25557 if(typeof(file.mimetype) != 'undefined'){
25558 formData.append('mimetype', file.mimetype);
25561 if(this.fireEvent('arrange', this, formData) != false){
25562 this.xhr.send(formData);
25566 xhrOnLoad : function(xhr)
25569 this.maskEl.unmask();
25572 if (xhr.readyState !== 4) {
25573 this.fireEvent('exception', this, xhr);
25577 var response = Roo.decode(xhr.responseText);
25579 if(!response.success){
25580 this.fireEvent('exception', this, xhr);
25584 var response = Roo.decode(xhr.responseText);
25586 this.fireEvent('upload', this, response);
25590 xhrOnError : function()
25593 this.maskEl.unmask();
25596 Roo.log('xhr on error');
25598 var response = Roo.decode(xhr.responseText);
25604 prepare : function(file)
25607 this.maskEl.mask(this.loadingText);
25613 if(typeof(file) === 'string'){
25614 this.loadCanvas(file);
25618 if(!file || !this.urlAPI){
25623 this.cropType = file.type;
25627 if(this.fireEvent('prepare', this, this.file) != false){
25629 var reader = new FileReader();
25631 reader.onload = function (e) {
25632 if (e.target.error) {
25633 Roo.log(e.target.error);
25637 var buffer = e.target.result,
25638 dataView = new DataView(buffer),
25640 maxOffset = dataView.byteLength - 4,
25644 if (dataView.getUint16(0) === 0xffd8) {
25645 while (offset < maxOffset) {
25646 markerBytes = dataView.getUint16(offset);
25648 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25649 markerLength = dataView.getUint16(offset + 2) + 2;
25650 if (offset + markerLength > dataView.byteLength) {
25651 Roo.log('Invalid meta data: Invalid segment size.');
25655 if(markerBytes == 0xffe1){
25656 _this.parseExifData(
25663 offset += markerLength;
25673 var url = _this.urlAPI.createObjectURL(_this.file);
25675 _this.loadCanvas(url);
25680 reader.readAsArrayBuffer(this.file);
25686 parseExifData : function(dataView, offset, length)
25688 var tiffOffset = offset + 10,
25692 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25693 // No Exif data, might be XMP data instead
25697 // Check for the ASCII code for "Exif" (0x45786966):
25698 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25699 // No Exif data, might be XMP data instead
25702 if (tiffOffset + 8 > dataView.byteLength) {
25703 Roo.log('Invalid Exif data: Invalid segment size.');
25706 // Check for the two null bytes:
25707 if (dataView.getUint16(offset + 8) !== 0x0000) {
25708 Roo.log('Invalid Exif data: Missing byte alignment offset.');
25711 // Check the byte alignment:
25712 switch (dataView.getUint16(tiffOffset)) {
25714 littleEndian = true;
25717 littleEndian = false;
25720 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25723 // Check for the TIFF tag marker (0x002A):
25724 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25725 Roo.log('Invalid Exif data: Missing TIFF marker.');
25728 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25729 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25731 this.parseExifTags(
25734 tiffOffset + dirOffset,
25739 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25744 if (dirOffset + 6 > dataView.byteLength) {
25745 Roo.log('Invalid Exif data: Invalid directory offset.');
25748 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25749 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25750 if (dirEndOffset + 4 > dataView.byteLength) {
25751 Roo.log('Invalid Exif data: Invalid directory size.');
25754 for (i = 0; i < tagsNumber; i += 1) {
25758 dirOffset + 2 + 12 * i, // tag offset
25762 // Return the offset to the next directory:
25763 return dataView.getUint32(dirEndOffset, littleEndian);
25766 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
25768 var tag = dataView.getUint16(offset, littleEndian);
25770 this.exif[tag] = this.getExifValue(
25774 dataView.getUint16(offset + 2, littleEndian), // tag type
25775 dataView.getUint32(offset + 4, littleEndian), // tag length
25780 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25782 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25791 Roo.log('Invalid Exif data: Invalid tag type.');
25795 tagSize = tagType.size * length;
25796 // Determine if the value is contained in the dataOffset bytes,
25797 // or if the value at the dataOffset is a pointer to the actual data:
25798 dataOffset = tagSize > 4 ?
25799 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25800 if (dataOffset + tagSize > dataView.byteLength) {
25801 Roo.log('Invalid Exif data: Invalid data offset.');
25804 if (length === 1) {
25805 return tagType.getValue(dataView, dataOffset, littleEndian);
25808 for (i = 0; i < length; i += 1) {
25809 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
25812 if (tagType.ascii) {
25814 // Concatenate the chars:
25815 for (i = 0; i < values.length; i += 1) {
25817 // Ignore the terminating NULL byte(s):
25818 if (c === '\u0000') {
25830 Roo.apply(Roo.bootstrap.UploadCropbox, {
25832 'Orientation': 0x0112
25836 1: 0, //'top-left',
25838 3: 180, //'bottom-right',
25839 // 4: 'bottom-left',
25841 6: 90, //'right-top',
25842 // 7: 'right-bottom',
25843 8: 270 //'left-bottom'
25847 // byte, 8-bit unsigned int:
25849 getValue: function (dataView, dataOffset) {
25850 return dataView.getUint8(dataOffset);
25854 // ascii, 8-bit byte:
25856 getValue: function (dataView, dataOffset) {
25857 return String.fromCharCode(dataView.getUint8(dataOffset));
25862 // short, 16 bit int:
25864 getValue: function (dataView, dataOffset, littleEndian) {
25865 return dataView.getUint16(dataOffset, littleEndian);
25869 // long, 32 bit int:
25871 getValue: function (dataView, dataOffset, littleEndian) {
25872 return dataView.getUint32(dataOffset, littleEndian);
25876 // rational = two long values, first is numerator, second is denominator:
25878 getValue: function (dataView, dataOffset, littleEndian) {
25879 return dataView.getUint32(dataOffset, littleEndian) /
25880 dataView.getUint32(dataOffset + 4, littleEndian);
25884 // slong, 32 bit signed int:
25886 getValue: function (dataView, dataOffset, littleEndian) {
25887 return dataView.getInt32(dataOffset, littleEndian);
25891 // srational, two slongs, first is numerator, second is denominator:
25893 getValue: function (dataView, dataOffset, littleEndian) {
25894 return dataView.getInt32(dataOffset, littleEndian) /
25895 dataView.getInt32(dataOffset + 4, littleEndian);
25905 cls : 'btn-group roo-upload-cropbox-rotate-left',
25906 action : 'rotate-left',
25910 cls : 'btn btn-default',
25911 html : '<i class="fa fa-undo"></i>'
25917 cls : 'btn-group roo-upload-cropbox-picture',
25918 action : 'picture',
25922 cls : 'btn btn-default',
25923 html : '<i class="fa fa-picture-o"></i>'
25929 cls : 'btn-group roo-upload-cropbox-rotate-right',
25930 action : 'rotate-right',
25934 cls : 'btn btn-default',
25935 html : '<i class="fa fa-repeat"></i>'
25943 cls : 'btn-group roo-upload-cropbox-rotate-left',
25944 action : 'rotate-left',
25948 cls : 'btn btn-default',
25949 html : '<i class="fa fa-undo"></i>'
25955 cls : 'btn-group roo-upload-cropbox-download',
25956 action : 'download',
25960 cls : 'btn btn-default',
25961 html : '<i class="fa fa-download"></i>'
25967 cls : 'btn-group roo-upload-cropbox-crop',
25972 cls : 'btn btn-default',
25973 html : '<i class="fa fa-crop"></i>'
25979 cls : 'btn-group roo-upload-cropbox-trash',
25984 cls : 'btn btn-default',
25985 html : '<i class="fa fa-trash"></i>'
25991 cls : 'btn-group roo-upload-cropbox-rotate-right',
25992 action : 'rotate-right',
25996 cls : 'btn btn-default',
25997 html : '<i class="fa fa-repeat"></i>'
26005 cls : 'btn-group roo-upload-cropbox-rotate-left',
26006 action : 'rotate-left',
26010 cls : 'btn btn-default',
26011 html : '<i class="fa fa-undo"></i>'
26017 cls : 'btn-group roo-upload-cropbox-rotate-right',
26018 action : 'rotate-right',
26022 cls : 'btn btn-default',
26023 html : '<i class="fa fa-repeat"></i>'
26036 * @class Roo.bootstrap.DocumentManager
26037 * @extends Roo.bootstrap.Component
26038 * Bootstrap DocumentManager class
26039 * @cfg {String} paramName default 'imageUpload'
26040 * @cfg {String} method default POST
26041 * @cfg {String} url action url
26042 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26043 * @cfg {Boolean} multiple multiple upload default true
26044 * @cfg {Number} thumbSize default 300
26045 * @cfg {String} fieldLabel
26046 * @cfg {Number} labelWidth default 4
26047 * @cfg {String} labelAlign (left|top) default left
26048 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26051 * Create a new DocumentManager
26052 * @param {Object} config The config object
26055 Roo.bootstrap.DocumentManager = function(config){
26056 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26061 * Fire when initial the DocumentManager
26062 * @param {Roo.bootstrap.DocumentManager} this
26067 * inspect selected file
26068 * @param {Roo.bootstrap.DocumentManager} this
26069 * @param {File} file
26074 * Fire when xhr load exception
26075 * @param {Roo.bootstrap.DocumentManager} this
26076 * @param {XMLHttpRequest} xhr
26078 "exception" : true,
26081 * prepare the form data
26082 * @param {Roo.bootstrap.DocumentManager} this
26083 * @param {Object} formData
26088 * Fire when remove the file
26089 * @param {Roo.bootstrap.DocumentManager} this
26090 * @param {Object} file
26095 * Fire after refresh the file
26096 * @param {Roo.bootstrap.DocumentManager} this
26101 * Fire after click the image
26102 * @param {Roo.bootstrap.DocumentManager} this
26103 * @param {Object} file
26108 * Fire when upload a image and editable set to true
26109 * @param {Roo.bootstrap.DocumentManager} this
26110 * @param {Object} file
26114 * @event beforeselectfile
26115 * Fire before select file
26116 * @param {Roo.bootstrap.DocumentManager} this
26118 "beforeselectfile" : true,
26121 * Fire before process file
26122 * @param {Roo.bootstrap.DocumentManager} this
26123 * @param {Object} file
26130 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
26139 paramName : 'imageUpload',
26142 labelAlign : 'left',
26149 getAutoCreate : function()
26151 var managerWidget = {
26153 cls : 'roo-document-manager',
26157 cls : 'roo-document-manager-selector',
26162 cls : 'roo-document-manager-uploader',
26166 cls : 'roo-document-manager-upload-btn',
26167 html : '<i class="fa fa-plus"></i>'
26178 cls : 'column col-md-12',
26183 if(this.fieldLabel.length){
26188 cls : 'column col-md-12',
26189 html : this.fieldLabel
26193 cls : 'column col-md-12',
26198 if(this.labelAlign == 'left'){
26202 cls : 'column col-md-' + this.labelWidth,
26203 html : this.fieldLabel
26207 cls : 'column col-md-' + (12 - this.labelWidth),
26217 cls : 'row clearfix',
26225 initEvents : function()
26227 this.managerEl = this.el.select('.roo-document-manager', true).first();
26228 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26230 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26231 this.selectorEl.hide();
26234 this.selectorEl.attr('multiple', 'multiple');
26237 this.selectorEl.on('change', this.onFileSelected, this);
26239 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26240 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26242 this.uploader.on('click', this.onUploaderClick, this);
26244 this.renderProgressDialog();
26248 window.addEventListener("resize", function() { _this.refresh(); } );
26250 this.fireEvent('initial', this);
26253 renderProgressDialog : function()
26257 this.progressDialog = new Roo.bootstrap.Modal({
26258 cls : 'roo-document-manager-progress-dialog',
26259 allow_close : false,
26269 btnclick : function() {
26270 _this.uploadCancel();
26276 this.progressDialog.render(Roo.get(document.body));
26278 this.progress = new Roo.bootstrap.Progress({
26279 cls : 'roo-document-manager-progress',
26284 this.progress.render(this.progressDialog.getChildContainer());
26286 this.progressBar = new Roo.bootstrap.ProgressBar({
26287 cls : 'roo-document-manager-progress-bar',
26290 aria_valuemax : 12,
26294 this.progressBar.render(this.progress.getChildContainer());
26297 onUploaderClick : function(e)
26299 e.preventDefault();
26301 if(this.fireEvent('beforeselectfile', this) != false){
26302 this.selectorEl.dom.click();
26307 onFileSelected : function(e)
26309 e.preventDefault();
26311 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26315 Roo.each(this.selectorEl.dom.files, function(file){
26316 if(this.fireEvent('inspect', this, file) != false){
26317 this.files.push(file);
26327 this.selectorEl.dom.value = '';
26329 if(!this.files.length){
26333 if(this.boxes > 0 && this.files.length > this.boxes){
26334 this.files = this.files.slice(0, this.boxes);
26337 this.uploader.show();
26339 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26340 this.uploader.hide();
26349 Roo.each(this.files, function(file){
26351 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26352 var f = this.renderPreview(file);
26357 if(file.type.indexOf('image') != -1){
26358 this.delegates.push(
26360 _this.process(file);
26361 }).createDelegate(this)
26369 _this.process(file);
26370 }).createDelegate(this)
26375 this.files = files;
26377 this.delegates = this.delegates.concat(docs);
26379 if(!this.delegates.length){
26384 this.progressBar.aria_valuemax = this.delegates.length;
26391 arrange : function()
26393 if(!this.delegates.length){
26394 this.progressDialog.hide();
26399 var delegate = this.delegates.shift();
26401 this.progressDialog.show();
26403 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26405 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26410 refresh : function()
26412 this.uploader.show();
26414 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26415 this.uploader.hide();
26418 Roo.isTouch ? this.closable(false) : this.closable(true);
26420 this.fireEvent('refresh', this);
26423 onRemove : function(e, el, o)
26425 e.preventDefault();
26427 this.fireEvent('remove', this, o);
26431 remove : function(o)
26435 Roo.each(this.files, function(file){
26436 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26445 this.files = files;
26452 Roo.each(this.files, function(file){
26457 file.target.remove();
26466 onClick : function(e, el, o)
26468 e.preventDefault();
26470 this.fireEvent('click', this, o);
26474 closable : function(closable)
26476 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26478 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26490 xhrOnLoad : function(xhr)
26492 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26496 if (xhr.readyState !== 4) {
26498 this.fireEvent('exception', this, xhr);
26502 var response = Roo.decode(xhr.responseText);
26504 if(!response.success){
26506 this.fireEvent('exception', this, xhr);
26510 var file = this.renderPreview(response.data);
26512 this.files.push(file);
26518 xhrOnError : function()
26520 Roo.log('xhr on error');
26522 var response = Roo.decode(xhr.responseText);
26529 process : function(file)
26531 if(this.fireEvent('process', this, file) !== false){
26532 if(this.editable && file.type.indexOf('image') != -1){
26533 this.fireEvent('edit', this, file);
26537 this.uploadStart(file, false);
26544 uploadStart : function(file, crop)
26546 this.xhr = new XMLHttpRequest();
26548 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26553 file.xhr = this.xhr;
26555 this.managerEl.createChild({
26557 cls : 'roo-document-manager-loading',
26561 tooltip : file.name,
26562 cls : 'roo-document-manager-thumb',
26563 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26569 this.xhr.open(this.method, this.url, true);
26572 "Accept": "application/json",
26573 "Cache-Control": "no-cache",
26574 "X-Requested-With": "XMLHttpRequest"
26577 for (var headerName in headers) {
26578 var headerValue = headers[headerName];
26580 this.xhr.setRequestHeader(headerName, headerValue);
26586 this.xhr.onload = function()
26588 _this.xhrOnLoad(_this.xhr);
26591 this.xhr.onerror = function()
26593 _this.xhrOnError(_this.xhr);
26596 var formData = new FormData();
26598 formData.append('returnHTML', 'NO');
26601 formData.append('crop', crop);
26604 formData.append(this.paramName, file, file.name);
26606 if(this.fireEvent('prepare', this, formData) != false){
26607 this.xhr.send(formData);
26611 uploadCancel : function()
26618 this.delegates = [];
26620 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26627 renderPreview : function(file)
26629 if(typeof(file.target) != 'undefined' && file.target){
26633 var previewEl = this.managerEl.createChild({
26635 cls : 'roo-document-manager-preview',
26639 tooltip : file.filename,
26640 cls : 'roo-document-manager-thumb',
26641 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26646 html : '<i class="fa fa-times-circle"></i>'
26651 var close = previewEl.select('button.close', true).first();
26653 close.on('click', this.onRemove, this, file);
26655 file.target = previewEl;
26657 var image = previewEl.select('img', true).first();
26661 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26663 image.on('click', this.onClick, this, file);
26669 onPreviewLoad : function(file, image)
26671 if(typeof(file.target) == 'undefined' || !file.target){
26675 var width = image.dom.naturalWidth || image.dom.width;
26676 var height = image.dom.naturalHeight || image.dom.height;
26678 if(width > height){
26679 file.target.addClass('wide');
26683 file.target.addClass('tall');
26688 uploadFromSource : function(file, crop)
26690 this.xhr = new XMLHttpRequest();
26692 this.managerEl.createChild({
26694 cls : 'roo-document-manager-loading',
26698 tooltip : file.name,
26699 cls : 'roo-document-manager-thumb',
26700 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26706 this.xhr.open(this.method, this.url, true);
26709 "Accept": "application/json",
26710 "Cache-Control": "no-cache",
26711 "X-Requested-With": "XMLHttpRequest"
26714 for (var headerName in headers) {
26715 var headerValue = headers[headerName];
26717 this.xhr.setRequestHeader(headerName, headerValue);
26723 this.xhr.onload = function()
26725 _this.xhrOnLoad(_this.xhr);
26728 this.xhr.onerror = function()
26730 _this.xhrOnError(_this.xhr);
26733 var formData = new FormData();
26735 formData.append('returnHTML', 'NO');
26737 formData.append('crop', crop);
26739 if(typeof(file.filename) != 'undefined'){
26740 formData.append('filename', file.filename);
26743 if(typeof(file.mimetype) != 'undefined'){
26744 formData.append('mimetype', file.mimetype);
26747 if(this.fireEvent('prepare', this, formData) != false){
26748 this.xhr.send(formData);
26758 * @class Roo.bootstrap.DocumentViewer
26759 * @extends Roo.bootstrap.Component
26760 * Bootstrap DocumentViewer class
26763 * Create a new DocumentViewer
26764 * @param {Object} config The config object
26767 Roo.bootstrap.DocumentViewer = function(config){
26768 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26773 * Fire after initEvent
26774 * @param {Roo.bootstrap.DocumentViewer} this
26780 * @param {Roo.bootstrap.DocumentViewer} this
26785 * Fire after trash button
26786 * @param {Roo.bootstrap.DocumentViewer} this
26793 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
26795 getAutoCreate : function()
26799 cls : 'roo-document-viewer',
26803 cls : 'roo-document-viewer-body',
26807 cls : 'roo-document-viewer-thumb',
26811 cls : 'roo-document-viewer-image'
26819 cls : 'roo-document-viewer-footer',
26822 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
26830 cls : 'btn btn-default roo-document-viewer-trash',
26831 html : '<i class="fa fa-trash"></i>'
26844 initEvents : function()
26847 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
26848 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26850 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
26851 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26853 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
26854 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26856 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
26857 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26859 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
26860 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26862 this.bodyEl.on('click', this.onClick, this);
26864 this.trashBtn.on('click', this.onTrash, this);
26868 initial : function()
26870 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
26873 this.fireEvent('initial', this);
26877 onClick : function(e)
26879 e.preventDefault();
26881 this.fireEvent('click', this);
26884 onTrash : function(e)
26886 e.preventDefault();
26888 this.fireEvent('trash', this);
26900 * @class Roo.bootstrap.NavProgressBar
26901 * @extends Roo.bootstrap.Component
26902 * Bootstrap NavProgressBar class
26905 * Create a new nav progress bar
26906 * @param {Object} config The config object
26909 Roo.bootstrap.NavProgressBar = function(config){
26910 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
26912 this.bullets = this.bullets || [];
26914 // Roo.bootstrap.NavProgressBar.register(this);
26918 * Fires when the active item changes
26919 * @param {Roo.bootstrap.NavProgressBar} this
26920 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
26921 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
26928 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
26933 getAutoCreate : function()
26935 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
26939 cls : 'roo-navigation-bar-group',
26943 cls : 'roo-navigation-top-bar'
26947 cls : 'roo-navigation-bullets-bar',
26951 cls : 'roo-navigation-bar'
26958 cls : 'roo-navigation-bottom-bar'
26968 initEvents: function()
26973 onRender : function(ct, position)
26975 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
26977 if(this.bullets.length){
26978 Roo.each(this.bullets, function(b){
26987 addItem : function(cfg)
26989 var item = new Roo.bootstrap.NavProgressItem(cfg);
26991 item.parentId = this.id;
26992 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
26995 var top = new Roo.bootstrap.Element({
26997 cls : 'roo-navigation-bar-text'
27000 var bottom = new Roo.bootstrap.Element({
27002 cls : 'roo-navigation-bar-text'
27005 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27006 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27008 var topText = new Roo.bootstrap.Element({
27010 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27013 var bottomText = new Roo.bootstrap.Element({
27015 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27018 topText.onRender(top.el, null);
27019 bottomText.onRender(bottom.el, null);
27022 item.bottomEl = bottom;
27025 this.barItems.push(item);
27030 getActive : function()
27032 var active = false;
27034 Roo.each(this.barItems, function(v){
27036 if (!v.isActive()) {
27048 setActiveItem : function(item)
27052 Roo.each(this.barItems, function(v){
27053 if (v.rid == item.rid) {
27057 if (v.isActive()) {
27058 v.setActive(false);
27063 item.setActive(true);
27065 this.fireEvent('changed', this, item, prev);
27068 getBarItem: function(rid)
27072 Roo.each(this.barItems, function(e) {
27073 if (e.rid != rid) {
27084 indexOfItem : function(item)
27088 Roo.each(this.barItems, function(v, i){
27090 if (v.rid != item.rid) {
27101 setActiveNext : function()
27103 var i = this.indexOfItem(this.getActive());
27105 if (i > this.barItems.length) {
27109 this.setActiveItem(this.barItems[i+1]);
27112 setActivePrev : function()
27114 var i = this.indexOfItem(this.getActive());
27120 this.setActiveItem(this.barItems[i-1]);
27123 format : function()
27125 if(!this.barItems.length){
27129 var width = 100 / this.barItems.length;
27131 Roo.each(this.barItems, function(i){
27132 i.el.setStyle('width', width + '%');
27133 i.topEl.el.setStyle('width', width + '%');
27134 i.bottomEl.el.setStyle('width', width + '%');
27143 * Nav Progress Item
27148 * @class Roo.bootstrap.NavProgressItem
27149 * @extends Roo.bootstrap.Component
27150 * Bootstrap NavProgressItem class
27151 * @cfg {String} rid the reference id
27152 * @cfg {Boolean} active (true|false) Is item active default false
27153 * @cfg {Boolean} disabled (true|false) Is item active default false
27154 * @cfg {String} html
27155 * @cfg {String} position (top|bottom) text position default bottom
27156 * @cfg {String} icon show icon instead of number
27159 * Create a new NavProgressItem
27160 * @param {Object} config The config object
27162 Roo.bootstrap.NavProgressItem = function(config){
27163 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27168 * The raw click event for the entire grid.
27169 * @param {Roo.bootstrap.NavProgressItem} this
27170 * @param {Roo.EventObject} e
27177 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
27183 position : 'bottom',
27186 getAutoCreate : function()
27188 var iconCls = 'roo-navigation-bar-item-icon';
27190 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27194 cls: 'roo-navigation-bar-item',
27204 cfg.cls += ' active';
27207 cfg.cls += ' disabled';
27213 disable : function()
27215 this.setDisabled(true);
27218 enable : function()
27220 this.setDisabled(false);
27223 initEvents: function()
27225 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27227 this.iconEl.on('click', this.onClick, this);
27230 onClick : function(e)
27232 e.preventDefault();
27238 if(this.fireEvent('click', this, e) === false){
27242 this.parent().setActiveItem(this);
27245 isActive: function ()
27247 return this.active;
27250 setActive : function(state)
27252 if(this.active == state){
27256 this.active = state;
27259 this.el.addClass('active');
27263 this.el.removeClass('active');
27268 setDisabled : function(state)
27270 if(this.disabled == state){
27274 this.disabled = state;
27277 this.el.addClass('disabled');
27281 this.el.removeClass('disabled');
27284 tooltipEl : function()
27286 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27299 * @class Roo.bootstrap.FieldLabel
27300 * @extends Roo.bootstrap.Component
27301 * Bootstrap FieldLabel class
27302 * @cfg {String} html contents of the element
27303 * @cfg {String} tag tag of the element default label
27304 * @cfg {String} cls class of the element
27305 * @cfg {String} target label target
27306 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27307 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27308 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27309 * @cfg {String} iconTooltip default "This field is required"
27312 * Create a new FieldLabel
27313 * @param {Object} config The config object
27316 Roo.bootstrap.FieldLabel = function(config){
27317 Roo.bootstrap.Element.superclass.constructor.call(this, config);
27322 * Fires after the field has been marked as invalid.
27323 * @param {Roo.form.FieldLabel} this
27324 * @param {String} msg The validation message
27329 * Fires after the field has been validated with no errors.
27330 * @param {Roo.form.FieldLabel} this
27336 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
27343 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27344 validClass : 'text-success fa fa-lg fa-check',
27345 iconTooltip : 'This field is required',
27347 getAutoCreate : function(){
27351 cls : 'roo-bootstrap-field-label ' + this.cls,
27357 tooltip : this.iconTooltip
27369 initEvents: function()
27371 Roo.bootstrap.Element.superclass.initEvents.call(this);
27373 this.iconEl = this.el.select('i', true).first();
27375 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27377 Roo.bootstrap.FieldLabel.register(this);
27381 * Mark this field as valid
27383 markValid : function()
27385 this.iconEl.show();
27387 this.iconEl.removeClass(this.invalidClass);
27389 this.iconEl.addClass(this.validClass);
27391 this.fireEvent('valid', this);
27395 * Mark this field as invalid
27396 * @param {String} msg The validation message
27398 markInvalid : function(msg)
27400 this.iconEl.show();
27402 this.iconEl.removeClass(this.validClass);
27404 this.iconEl.addClass(this.invalidClass);
27406 this.fireEvent('invalid', this, msg);
27412 Roo.apply(Roo.bootstrap.FieldLabel, {
27417 * register a FieldLabel Group
27418 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27420 register : function(label)
27422 if(this.groups.hasOwnProperty(label.target)){
27426 this.groups[label.target] = label;
27430 * fetch a FieldLabel Group based on the target
27431 * @param {string} target
27432 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27434 get: function(target) {
27435 if (typeof(this.groups[target]) == 'undefined') {
27439 return this.groups[target] ;