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');
1968 this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1970 this.el.on("mouseover", this.onMouseOver, this);
1971 this.el.on("mouseout", this.onMouseOut, this);
1975 findTargetItem : function(e){
1976 var t = e.getTarget(".dropdown-menu-item", this.el, true);
1980 //Roo.log(t); Roo.log(t.id);
1982 //Roo.log(this.menuitems);
1983 return this.menuitems.get(t.id);
1985 //return this.items.get(t.menuItemId);
1990 onClick : function(e){
1991 Roo.log("menu.onClick");
1992 var t = this.findTargetItem(e);
1993 if(!t || t.isContainer){
1998 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
1999 if(t == this.activeItem && t.shouldDeactivate(e)){
2000 this.activeItem.deactivate();
2001 delete this.activeItem;
2005 this.setActiveItem(t, true);
2013 Roo.log('pass click event');
2017 this.fireEvent("click", this, t, e);
2021 onMouseOver : function(e){
2022 var t = this.findTargetItem(e);
2025 // if(t.canActivate && !t.disabled){
2026 // this.setActiveItem(t, true);
2030 this.fireEvent("mouseover", this, e, t);
2032 isVisible : function(){
2033 return !this.hidden;
2035 onMouseOut : function(e){
2036 var t = this.findTargetItem(e);
2039 // if(t == this.activeItem && t.shouldDeactivate(e)){
2040 // this.activeItem.deactivate();
2041 // delete this.activeItem;
2044 this.fireEvent("mouseout", this, e, t);
2049 * Displays this menu relative to another element
2050 * @param {String/HTMLElement/Roo.Element} element The element to align to
2051 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2052 * the element (defaults to this.defaultAlign)
2053 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2055 show : function(el, pos, parentMenu){
2056 this.parentMenu = parentMenu;
2060 this.fireEvent("beforeshow", this);
2061 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2064 * Displays this menu at a specific xy position
2065 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2066 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2068 showAt : function(xy, parentMenu, /* private: */_e){
2069 this.parentMenu = parentMenu;
2074 this.fireEvent("beforeshow", this);
2075 //xy = this.el.adjustForConstraints(xy);
2079 this.hideMenuItems();
2080 this.hidden = false;
2081 this.triggerEl.addClass('open');
2083 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2084 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2089 this.fireEvent("show", this);
2095 this.doFocus.defer(50, this);
2099 doFocus : function(){
2101 this.focusEl.focus();
2106 * Hides this menu and optionally all parent menus
2107 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2109 hide : function(deep){
2111 this.hideMenuItems();
2112 if(this.el && this.isVisible()){
2113 this.fireEvent("beforehide", this);
2114 if(this.activeItem){
2115 this.activeItem.deactivate();
2116 this.activeItem = null;
2118 this.triggerEl.removeClass('open');;
2120 this.fireEvent("hide", this);
2122 if(deep === true && this.parentMenu){
2123 this.parentMenu.hide(true);
2127 onTriggerPress : function(e)
2130 Roo.log('trigger press');
2131 //Roo.log(e.getTarget());
2132 // Roo.log(this.triggerEl.dom);
2133 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2137 if (this.isVisible()) {
2142 this.show(this.triggerEl, false, false);
2151 hideMenuItems : function()
2153 //$(backdrop).remove()
2154 Roo.select('.open',true).each(function(aa) {
2156 aa.removeClass('open');
2157 //var parent = getParent($(this))
2158 //var relatedTarget = { relatedTarget: this }
2160 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2161 //if (e.isDefaultPrevented()) return
2162 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2165 addxtypeChild : function (tree, cntr) {
2166 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2168 this.menuitems.add(comp);
2189 * @class Roo.bootstrap.MenuItem
2190 * @extends Roo.bootstrap.Component
2191 * Bootstrap MenuItem class
2192 * @cfg {String} html the menu label
2193 * @cfg {String} href the link
2194 * @cfg {Boolean} preventDefault (true | false) default true
2195 * @cfg {Boolean} isContainer (true | false) default false
2199 * Create a new MenuItem
2200 * @param {Object} config The config object
2204 Roo.bootstrap.MenuItem = function(config){
2205 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2210 * The raw click event for the entire grid.
2211 * @param {Roo.bootstrap.MenuItem} this
2212 * @param {Roo.EventObject} e
2218 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2222 preventDefault: true,
2223 isContainer : false,
2225 getAutoCreate : function(){
2227 if(this.isContainer){
2230 cls: 'dropdown-menu-item'
2236 cls: 'dropdown-menu-item',
2245 if (this.parent().type == 'treeview') {
2246 cfg.cls = 'treeview-menu';
2249 cfg.cn[0].href = this.href || cfg.cn[0].href ;
2250 cfg.cn[0].html = this.html || cfg.cn[0].html ;
2254 initEvents: function() {
2256 //this.el.select('a').on('click', this.onClick, this);
2259 onClick : function(e)
2261 Roo.log('item on click ');
2262 //if(this.preventDefault){
2263 // e.preventDefault();
2265 //this.parent().hideMenuItems();
2267 this.fireEvent('click', this, e);
2286 * @class Roo.bootstrap.MenuSeparator
2287 * @extends Roo.bootstrap.Component
2288 * Bootstrap MenuSeparator class
2291 * Create a new MenuItem
2292 * @param {Object} config The config object
2296 Roo.bootstrap.MenuSeparator = function(config){
2297 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2300 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2302 getAutoCreate : function(){
2321 * @class Roo.bootstrap.Modal
2322 * @extends Roo.bootstrap.Component
2323 * Bootstrap Modal class
2324 * @cfg {String} title Title of dialog
2325 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2326 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2327 * @cfg {Boolean} specificTitle default false
2328 * @cfg {Array} buttons Array of buttons or standard button set..
2329 * @cfg {String} buttonPosition (left|right|center) default right
2330 * @cfg {Boolean} animate default true
2331 * @cfg {Boolean} allow_close default true
2334 * Create a new Modal Dialog
2335 * @param {Object} config The config object
2338 Roo.bootstrap.Modal = function(config){
2339 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2344 * The raw btnclick event for the button
2345 * @param {Roo.EventObject} e
2349 this.buttons = this.buttons || [];
2352 this.tmpl = Roo.factory(this.tmpl);
2357 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2359 title : 'test dialog',
2369 specificTitle: false,
2371 buttonPosition: 'right',
2385 onRender : function(ct, position)
2387 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2390 var cfg = Roo.apply({}, this.getAutoCreate());
2393 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2395 //if (!cfg.name.length) {
2399 cfg.cls += ' ' + this.cls;
2402 cfg.style = this.style;
2404 this.el = Roo.get(document.body).createChild(cfg, position);
2406 //var type = this.el.dom.type;
2411 if(this.tabIndex !== undefined){
2412 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2416 this.bodyEl = this.el.select('.modal-body',true).first();
2417 this.closeEl = this.el.select('.modal-header .close', true).first();
2418 this.footerEl = this.el.select('.modal-footer',true).first();
2419 this.titleEl = this.el.select('.modal-title',true).first();
2423 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2424 this.maskEl.enableDisplayMode("block");
2426 //this.el.addClass("x-dlg-modal");
2428 if (this.buttons.length) {
2429 Roo.each(this.buttons, function(bb) {
2430 var b = Roo.apply({}, bb);
2431 b.xns = b.xns || Roo.bootstrap;
2432 b.xtype = b.xtype || 'Button';
2433 if (typeof(b.listeners) == 'undefined') {
2434 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2437 var btn = Roo.factory(b);
2439 btn.onRender(this.el.select('.modal-footer div').first());
2443 // render the children.
2446 if(typeof(this.items) != 'undefined'){
2447 var items = this.items;
2450 for(var i =0;i < items.length;i++) {
2451 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2455 this.items = nitems;
2457 // where are these used - they used to be body/close/footer
2461 //this.el.addClass([this.fieldClass, this.cls]);
2465 getAutoCreate : function(){
2470 html : this.html || ''
2475 cls : 'modal-title',
2479 if(this.specificTitle){
2485 if (this.allow_close) {
2496 style : 'display: none',
2499 cls: "modal-dialog",
2502 cls : "modal-content",
2505 cls : 'modal-header',
2510 cls : 'modal-footer',
2514 cls: 'btn-' + this.buttonPosition
2531 modal.cls += ' fade';
2537 getChildContainer : function() {
2542 getButtonContainer : function() {
2543 return this.el.select('.modal-footer div',true).first();
2546 initEvents : function()
2548 if (this.allow_close) {
2549 this.closeEl.on('click', this.hide, this);
2555 if (!this.rendered) {
2559 this.el.setStyle('display', 'block');
2563 (function(){ _this.el.addClass('in'); }).defer(50);
2565 this.el.addClass('in');
2568 // not sure how we can show data in here..
2570 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2573 Roo.get(document.body).addClass("x-body-masked");
2574 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2576 this.el.setStyle('zIndex', '10001');
2578 this.fireEvent('show', this);
2585 Roo.get(document.body).removeClass("x-body-masked");
2586 this.el.removeClass('in');
2590 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2592 this.el.setStyle('display', 'none');
2595 this.fireEvent('hide', this);
2598 addButton : function(str, cb)
2602 var b = Roo.apply({}, { html : str } );
2603 b.xns = b.xns || Roo.bootstrap;
2604 b.xtype = b.xtype || 'Button';
2605 if (typeof(b.listeners) == 'undefined') {
2606 b.listeners = { click : cb.createDelegate(this) };
2609 var btn = Roo.factory(b);
2611 btn.onRender(this.el.select('.modal-footer div').first());
2617 setDefaultButton : function(btn)
2619 //this.el.select('.modal-footer').()
2621 resizeTo: function(w,h)
2625 setContentSize : function(w, h)
2629 onButtonClick: function(btn,e)
2632 this.fireEvent('btnclick', btn.name, e);
2635 * Set the title of the Dialog
2636 * @param {String} str new Title
2638 setTitle: function(str) {
2639 this.titleEl.dom.innerHTML = str;
2642 * Set the body of the Dialog
2643 * @param {String} str new Title
2645 setBody: function(str) {
2646 this.bodyEl.dom.innerHTML = str;
2649 * Set the body of the Dialog using the template
2650 * @param {Obj} data - apply this data to the template and replace the body contents.
2652 applyBody: function(obj)
2655 Roo.log("Error - using apply Body without a template");
2658 this.tmpl.overwrite(this.bodyEl, obj);
2664 Roo.apply(Roo.bootstrap.Modal, {
2666 * Button config that displays a single OK button
2675 * Button config that displays Yes and No buttons
2691 * Button config that displays OK and Cancel buttons
2706 * Button config that displays Yes, No and Cancel buttons
2729 * messagebox - can be used as a replace
2733 * @class Roo.MessageBox
2734 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2738 Roo.Msg.alert('Status', 'Changes saved successfully.');
2740 // Prompt for user data:
2741 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2743 // process text value...
2747 // Show a dialog using config options:
2749 title:'Save Changes?',
2750 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2751 buttons: Roo.Msg.YESNOCANCEL,
2758 Roo.bootstrap.MessageBox = function(){
2759 var dlg, opt, mask, waitTimer;
2760 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2761 var buttons, activeTextEl, bwidth;
2765 var handleButton = function(button){
2767 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2771 var handleHide = function(){
2773 dlg.el.removeClass(opt.cls);
2776 // Roo.TaskMgr.stop(waitTimer);
2777 // waitTimer = null;
2782 var updateButtons = function(b){
2785 buttons["ok"].hide();
2786 buttons["cancel"].hide();
2787 buttons["yes"].hide();
2788 buttons["no"].hide();
2789 //dlg.footer.dom.style.display = 'none';
2792 dlg.footerEl.dom.style.display = '';
2793 for(var k in buttons){
2794 if(typeof buttons[k] != "function"){
2797 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2798 width += buttons[k].el.getWidth()+15;
2808 var handleEsc = function(d, k, e){
2809 if(opt && opt.closable !== false){
2819 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2820 * @return {Roo.BasicDialog} The BasicDialog element
2822 getDialog : function(){
2824 dlg = new Roo.bootstrap.Modal( {
2827 //constraintoviewport:false,
2829 //collapsible : false,
2834 //buttonAlign:"center",
2835 closeClick : function(){
2836 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2839 handleButton("cancel");
2844 dlg.on("hide", handleHide);
2846 //dlg.addKeyListener(27, handleEsc);
2848 this.buttons = buttons;
2849 var bt = this.buttonText;
2850 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2851 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2852 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2853 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2855 bodyEl = dlg.bodyEl.createChild({
2857 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2858 '<textarea class="roo-mb-textarea"></textarea>' +
2859 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2861 msgEl = bodyEl.dom.firstChild;
2862 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2863 textboxEl.enableDisplayMode();
2864 textboxEl.addKeyListener([10,13], function(){
2865 if(dlg.isVisible() && opt && opt.buttons){
2868 }else if(opt.buttons.yes){
2869 handleButton("yes");
2873 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2874 textareaEl.enableDisplayMode();
2875 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2876 progressEl.enableDisplayMode();
2877 var pf = progressEl.dom.firstChild;
2879 pp = Roo.get(pf.firstChild);
2880 pp.setHeight(pf.offsetHeight);
2888 * Updates the message box body text
2889 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2890 * the XHTML-compliant non-breaking space character '&#160;')
2891 * @return {Roo.MessageBox} This message box
2893 updateText : function(text){
2894 if(!dlg.isVisible() && !opt.width){
2895 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2897 msgEl.innerHTML = text || ' ';
2899 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2900 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2902 Math.min(opt.width || cw , this.maxWidth),
2903 Math.max(opt.minWidth || this.minWidth, bwidth)
2906 activeTextEl.setWidth(w);
2908 if(dlg.isVisible()){
2909 dlg.fixedcenter = false;
2911 // to big, make it scroll. = But as usual stupid IE does not support
2914 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2915 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2916 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2918 bodyEl.dom.style.height = '';
2919 bodyEl.dom.style.overflowY = '';
2922 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2924 bodyEl.dom.style.overflowX = '';
2927 dlg.setContentSize(w, bodyEl.getHeight());
2928 if(dlg.isVisible()){
2929 dlg.fixedcenter = true;
2935 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
2936 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2937 * @param {Number} value Any number between 0 and 1 (e.g., .5)
2938 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2939 * @return {Roo.MessageBox} This message box
2941 updateProgress : function(value, text){
2943 this.updateText(text);
2945 if (pp) { // weird bug on my firefox - for some reason this is not defined
2946 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2952 * Returns true if the message box is currently displayed
2953 * @return {Boolean} True if the message box is visible, else false
2955 isVisible : function(){
2956 return dlg && dlg.isVisible();
2960 * Hides the message box if it is displayed
2963 if(this.isVisible()){
2969 * Displays a new message box, or reinitializes an existing message box, based on the config options
2970 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2971 * The following config object properties are supported:
2973 Property Type Description
2974 ---------- --------------- ------------------------------------------------------------------------------------
2975 animEl String/Element An id or Element from which the message box should animate as it opens and
2976 closes (defaults to undefined)
2977 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2978 cancel:'Bar'}), or false to not show any buttons (defaults to false)
2979 closable Boolean False to hide the top-right close button (defaults to true). Note that
2980 progress and wait dialogs will ignore this property and always hide the
2981 close button as they can only be closed programmatically.
2982 cls String A custom CSS class to apply to the message box element
2983 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
2984 displayed (defaults to 75)
2985 fn Function A callback function to execute after closing the dialog. The arguments to the
2986 function will be btn (the name of the button that was clicked, if applicable,
2987 e.g. "ok"), and text (the value of the active text field, if applicable).
2988 Progress and wait dialogs will ignore this option since they do not respond to
2989 user actions and can only be closed programmatically, so any required function
2990 should be called by the same code after it closes the dialog.
2991 icon String A CSS class that provides a background image to be used as an icon for
2992 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2993 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
2994 minWidth Number The minimum width in pixels of the message box (defaults to 100)
2995 modal Boolean False to allow user interaction with the page while the message box is
2996 displayed (defaults to true)
2997 msg String A string that will replace the existing message box body text (defaults
2998 to the XHTML-compliant non-breaking space character ' ')
2999 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3000 progress Boolean True to display a progress bar (defaults to false)
3001 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3002 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3003 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3004 title String The title text
3005 value String The string value to set into the active textbox element if displayed
3006 wait Boolean True to display a progress bar (defaults to false)
3007 width Number The width of the dialog in pixels
3014 msg: 'Please enter your address:',
3016 buttons: Roo.MessageBox.OKCANCEL,
3019 animEl: 'addAddressBtn'
3022 * @param {Object} config Configuration options
3023 * @return {Roo.MessageBox} This message box
3025 show : function(options)
3028 // this causes nightmares if you show one dialog after another
3029 // especially on callbacks..
3031 if(this.isVisible()){
3034 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3035 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3036 Roo.log("New Dialog Message:" + options.msg )
3037 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3038 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3041 var d = this.getDialog();
3043 d.setTitle(opt.title || " ");
3044 d.closeEl.setDisplayed(opt.closable !== false);
3045 activeTextEl = textboxEl;
3046 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3051 textareaEl.setHeight(typeof opt.multiline == "number" ?
3052 opt.multiline : this.defaultTextHeight);
3053 activeTextEl = textareaEl;
3062 progressEl.setDisplayed(opt.progress === true);
3063 this.updateProgress(0);
3064 activeTextEl.dom.value = opt.value || "";
3066 dlg.setDefaultButton(activeTextEl);
3068 var bs = opt.buttons;
3072 }else if(bs && bs.yes){
3073 db = buttons["yes"];
3075 dlg.setDefaultButton(db);
3077 bwidth = updateButtons(opt.buttons);
3078 this.updateText(opt.msg);
3080 d.el.addClass(opt.cls);
3082 d.proxyDrag = opt.proxyDrag === true;
3083 d.modal = opt.modal !== false;
3084 d.mask = opt.modal !== false ? mask : false;
3086 // force it to the end of the z-index stack so it gets a cursor in FF
3087 document.body.appendChild(dlg.el.dom);
3088 d.animateTarget = null;
3089 d.show(options.animEl);
3095 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3096 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3097 * and closing the message box when the process is complete.
3098 * @param {String} title The title bar text
3099 * @param {String} msg The message box body text
3100 * @return {Roo.MessageBox} This message box
3102 progress : function(title, msg){
3109 minWidth: this.minProgressWidth,
3116 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3117 * If a callback function is passed it will be called after the user clicks the button, and the
3118 * id of the button that was clicked will be passed as the only parameter to the callback
3119 * (could also be the top-right close button).
3120 * @param {String} title The title bar text
3121 * @param {String} msg The message box body text
3122 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3123 * @param {Object} scope (optional) The scope of the callback function
3124 * @return {Roo.MessageBox} This message box
3126 alert : function(title, msg, fn, scope){
3139 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3140 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3141 * You are responsible for closing the message box when the process is complete.
3142 * @param {String} msg The message box body text
3143 * @param {String} title (optional) The title bar text
3144 * @return {Roo.MessageBox} This message box
3146 wait : function(msg, title){
3157 waitTimer = Roo.TaskMgr.start({
3159 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3167 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3168 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3169 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3170 * @param {String} title The title bar text
3171 * @param {String} msg The message box body text
3172 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3173 * @param {Object} scope (optional) The scope of the callback function
3174 * @return {Roo.MessageBox} This message box
3176 confirm : function(title, msg, fn, scope){
3180 buttons: this.YESNO,
3189 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3190 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3191 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3192 * (could also be the top-right close button) and the text that was entered will be passed as the two
3193 * parameters to the callback.
3194 * @param {String} title The title bar text
3195 * @param {String} msg The message box body text
3196 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3197 * @param {Object} scope (optional) The scope of the callback function
3198 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3199 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3200 * @return {Roo.MessageBox} This message box
3202 prompt : function(title, msg, fn, scope, multiline){
3206 buttons: this.OKCANCEL,
3211 multiline: multiline,
3218 * Button config that displays a single OK button
3223 * Button config that displays Yes and No buttons
3226 YESNO : {yes:true, no:true},
3228 * Button config that displays OK and Cancel buttons
3231 OKCANCEL : {ok:true, cancel:true},
3233 * Button config that displays Yes, No and Cancel buttons
3236 YESNOCANCEL : {yes:true, no:true, cancel:true},
3239 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3242 defaultTextHeight : 75,
3244 * The maximum width in pixels of the message box (defaults to 600)
3249 * The minimum width in pixels of the message box (defaults to 100)
3254 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3255 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3258 minProgressWidth : 250,
3260 * An object containing the default button text strings that can be overriden for localized language support.
3261 * Supported properties are: ok, cancel, yes and no.
3262 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3275 * Shorthand for {@link Roo.MessageBox}
3277 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3278 Roo.Msg = Roo.Msg || Roo.MessageBox;
3287 * @class Roo.bootstrap.Navbar
3288 * @extends Roo.bootstrap.Component
3289 * Bootstrap Navbar class
3292 * Create a new Navbar
3293 * @param {Object} config The config object
3297 Roo.bootstrap.Navbar = function(config){
3298 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3302 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3311 getAutoCreate : function(){
3314 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3318 initEvents :function ()
3320 //Roo.log(this.el.select('.navbar-toggle',true));
3321 this.el.select('.navbar-toggle',true).on('click', function() {
3322 // Roo.log('click');
3323 this.el.select('.navbar-collapse',true).toggleClass('in');
3331 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3333 var size = this.el.getSize();
3334 this.maskEl.setSize(size.width, size.height);
3335 this.maskEl.enableDisplayMode("block");
3344 getChildContainer : function()
3346 if (this.el.select('.collapse').getCount()) {
3347 return this.el.select('.collapse',true).first();
3380 * @class Roo.bootstrap.NavSimplebar
3381 * @extends Roo.bootstrap.Navbar
3382 * Bootstrap Sidebar class
3384 * @cfg {Boolean} inverse is inverted color
3386 * @cfg {String} type (nav | pills | tabs)
3387 * @cfg {Boolean} arrangement stacked | justified
3388 * @cfg {String} align (left | right) alignment
3390 * @cfg {Boolean} main (true|false) main nav bar? default false
3391 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3393 * @cfg {String} tag (header|footer|nav|div) default is nav
3399 * Create a new Sidebar
3400 * @param {Object} config The config object
3404 Roo.bootstrap.NavSimplebar = function(config){
3405 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3408 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3424 getAutoCreate : function(){
3428 tag : this.tag || 'div',
3441 this.type = this.type || 'nav';
3442 if (['tabs','pills'].indexOf(this.type)!==-1) {
3443 cfg.cn[0].cls += ' nav-' + this.type
3447 if (this.type!=='nav') {
3448 Roo.log('nav type must be nav/tabs/pills')
3450 cfg.cn[0].cls += ' navbar-nav'
3456 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3457 cfg.cn[0].cls += ' nav-' + this.arrangement;
3461 if (this.align === 'right') {
3462 cfg.cn[0].cls += ' navbar-right';
3466 cfg.cls += ' navbar-inverse';
3493 * @class Roo.bootstrap.NavHeaderbar
3494 * @extends Roo.bootstrap.NavSimplebar
3495 * Bootstrap Sidebar class
3497 * @cfg {String} brand what is brand
3498 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3499 * @cfg {String} brand_href href of the brand
3500 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3501 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3502 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3503 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3506 * Create a new Sidebar
3507 * @param {Object} config The config object
3511 Roo.bootstrap.NavHeaderbar = function(config){
3512 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3516 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3523 desktopCenter : false,
3526 getAutoCreate : function(){
3529 tag: this.nav || 'nav',
3536 if (this.desktopCenter) {
3537 cn.push({cls : 'container', cn : []});
3544 cls: 'navbar-header',
3549 cls: 'navbar-toggle',
3550 'data-toggle': 'collapse',
3555 html: 'Toggle navigation'
3577 cls: 'collapse navbar-collapse',
3581 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3583 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3584 cfg.cls += ' navbar-' + this.position;
3586 // tag can override this..
3588 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3591 if (this.brand !== '') {
3594 href: this.brand_href ? this.brand_href : '#',
3595 cls: 'navbar-brand',
3603 cfg.cls += ' main-nav';
3611 getHeaderChildContainer : function()
3613 if (this.el.select('.navbar-header').getCount()) {
3614 return this.el.select('.navbar-header',true).first();
3617 return this.getChildContainer();
3621 initEvents : function()
3623 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3625 if (this.autohide) {
3630 Roo.get(document).on('scroll',function(e) {
3631 var ns = Roo.get(document).getScroll().top;
3632 var os = prevScroll;
3636 ft.removeClass('slideDown');
3637 ft.addClass('slideUp');
3640 ft.removeClass('slideUp');
3641 ft.addClass('slideDown');
3662 * @class Roo.bootstrap.NavSidebar
3663 * @extends Roo.bootstrap.Navbar
3664 * Bootstrap Sidebar class
3667 * Create a new Sidebar
3668 * @param {Object} config The config object
3672 Roo.bootstrap.NavSidebar = function(config){
3673 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3676 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3678 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3680 getAutoCreate : function(){
3685 cls: 'sidebar sidebar-nav'
3707 * @class Roo.bootstrap.NavGroup
3708 * @extends Roo.bootstrap.Component
3709 * Bootstrap NavGroup class
3710 * @cfg {String} align (left|right)
3711 * @cfg {Boolean} inverse
3712 * @cfg {String} type (nav|pills|tab) default nav
3713 * @cfg {String} navId - reference Id for navbar.
3717 * Create a new nav group
3718 * @param {Object} config The config object
3721 Roo.bootstrap.NavGroup = function(config){
3722 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3725 Roo.bootstrap.NavGroup.register(this);
3729 * Fires when the active item changes
3730 * @param {Roo.bootstrap.NavGroup} this
3731 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3732 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3739 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3750 getAutoCreate : function()
3752 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3759 if (['tabs','pills'].indexOf(this.type)!==-1) {
3760 cfg.cls += ' nav-' + this.type
3762 if (this.type!=='nav') {
3763 Roo.log('nav type must be nav/tabs/pills')
3765 cfg.cls += ' navbar-nav'
3768 if (this.parent().sidebar) {
3771 cls: 'dashboard-menu sidebar-menu'
3777 if (this.form === true) {
3783 if (this.align === 'right') {
3784 cfg.cls += ' navbar-right';
3786 cfg.cls += ' navbar-left';
3790 if (this.align === 'right') {
3791 cfg.cls += ' navbar-right';
3795 cfg.cls += ' navbar-inverse';
3803 * sets the active Navigation item
3804 * @param {Roo.bootstrap.NavItem} the new current navitem
3806 setActiveItem : function(item)
3809 Roo.each(this.navItems, function(v){
3814 v.setActive(false, true);
3821 item.setActive(true, true);
3822 this.fireEvent('changed', this, item, prev);
3827 * gets the active Navigation item
3828 * @return {Roo.bootstrap.NavItem} the current navitem
3830 getActive : function()
3834 Roo.each(this.navItems, function(v){
3845 indexOfNav : function()
3849 Roo.each(this.navItems, function(v,i){
3860 * adds a Navigation item
3861 * @param {Roo.bootstrap.NavItem} the navitem to add
3863 addItem : function(cfg)
3865 var cn = new Roo.bootstrap.NavItem(cfg);
3867 cn.parentId = this.id;
3868 cn.onRender(this.el, null);
3872 * register a Navigation item
3873 * @param {Roo.bootstrap.NavItem} the navitem to add
3875 register : function(item)
3877 this.navItems.push( item);
3878 item.navId = this.navId;
3883 * clear all the Navigation item
3886 clearAll : function()
3889 this.el.dom.innerHTML = '';
3892 getNavItem: function(tabId)
3895 Roo.each(this.navItems, function(e) {
3896 if (e.tabId == tabId) {
3906 setActiveNext : function()
3908 var i = this.indexOfNav(this.getActive());
3909 if (i > this.navItems.length) {
3912 this.setActiveItem(this.navItems[i+1]);
3914 setActivePrev : function()
3916 var i = this.indexOfNav(this.getActive());
3920 this.setActiveItem(this.navItems[i-1]);
3922 clearWasActive : function(except) {
3923 Roo.each(this.navItems, function(e) {
3924 if (e.tabId != except.tabId && e.was_active) {
3925 e.was_active = false;
3932 getWasActive : function ()
3935 Roo.each(this.navItems, function(e) {
3950 Roo.apply(Roo.bootstrap.NavGroup, {
3954 * register a Navigation Group
3955 * @param {Roo.bootstrap.NavGroup} the navgroup to add
3957 register : function(navgrp)
3959 this.groups[navgrp.navId] = navgrp;
3963 * fetch a Navigation Group based on the navigation ID
3964 * @param {string} the navgroup to add
3965 * @returns {Roo.bootstrap.NavGroup} the navgroup
3967 get: function(navId) {
3968 if (typeof(this.groups[navId]) == 'undefined') {
3970 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3972 return this.groups[navId] ;
3987 * @class Roo.bootstrap.NavItem
3988 * @extends Roo.bootstrap.Component
3989 * Bootstrap Navbar.NavItem class
3990 * @cfg {String} href link to
3991 * @cfg {String} html content of button
3992 * @cfg {String} badge text inside badge
3993 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3994 * @cfg {String} glyphicon name of glyphicon
3995 * @cfg {String} icon name of font awesome icon
3996 * @cfg {Boolean} active Is item active
3997 * @cfg {Boolean} disabled Is item disabled
3999 * @cfg {Boolean} preventDefault (true | false) default false
4000 * @cfg {String} tabId the tab that this item activates.
4001 * @cfg {String} tagtype (a|span) render as a href or span?
4002 * @cfg {Boolean} animateRef (true|false) link to element default false
4005 * Create a new Navbar Item
4006 * @param {Object} config The config object
4008 Roo.bootstrap.NavItem = function(config){
4009 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4014 * The raw click event for the entire grid.
4015 * @param {Roo.EventObject} e
4020 * Fires when the active item active state changes
4021 * @param {Roo.bootstrap.NavItem} this
4022 * @param {boolean} state the new state
4028 * Fires when scroll to element
4029 * @param {Roo.bootstrap.NavItem} this
4030 * @param {Object} options
4031 * @param {Roo.EventObject} e
4039 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4047 preventDefault : false,
4054 getAutoCreate : function(){
4062 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4064 if (this.disabled) {
4065 cfg.cls += ' disabled';
4068 if (this.href || this.html || this.glyphicon || this.icon) {
4072 href : this.href || "#",
4073 html: this.html || ''
4078 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4081 if(this.glyphicon) {
4082 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4087 cfg.cn[0].html += " <span class='caret'></span>";
4091 if (this.badge !== '') {
4093 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4101 initEvents: function()
4103 if (typeof (this.menu) != 'undefined') {
4104 this.menu.parentType = this.xtype;
4105 this.menu.triggerEl = this.el;
4106 this.menu = this.addxtype(Roo.apply({}, this.menu));
4109 this.el.select('a',true).on('click', this.onClick, this);
4111 if(this.tagtype == 'span'){
4112 this.el.select('span',true).on('click', this.onClick, this);
4115 // at this point parent should be available..
4116 this.parent().register(this);
4119 onClick : function(e)
4122 this.preventDefault ||
4129 if (this.disabled) {
4133 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4134 if (tg && tg.transition) {
4135 Roo.log("waiting for the transitionend");
4141 //Roo.log("fire event clicked");
4142 if(this.fireEvent('click', this, e) === false){
4146 if(this.tagtype == 'span'){
4150 //Roo.log(this.href);
4151 var ael = this.el.select('a',true).first();
4154 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4155 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4156 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4157 return; // ignore... - it's a 'hash' to another page.
4161 this.scrollToElement(e);
4165 var p = this.parent();
4167 if (['tabs','pills'].indexOf(p.type)!==-1) {
4168 if (typeof(p.setActiveItem) !== 'undefined') {
4169 p.setActiveItem(this);
4173 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4174 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4175 // remove the collapsed menu expand...
4176 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4180 isActive: function () {
4183 setActive : function(state, fire, is_was_active)
4185 if (this.active && !state & this.navId) {
4186 this.was_active = true;
4187 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4189 nv.clearWasActive(this);
4193 this.active = state;
4196 this.el.removeClass('active');
4197 } else if (!this.el.hasClass('active')) {
4198 this.el.addClass('active');
4201 this.fireEvent('changed', this, state);
4204 // show a panel if it's registered and related..
4206 if (!this.navId || !this.tabId || !state || is_was_active) {
4210 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4214 var pan = tg.getPanelByName(this.tabId);
4218 // if we can not flip to new panel - go back to old nav highlight..
4219 if (false == tg.showPanel(pan)) {
4220 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4222 var onav = nv.getWasActive();
4224 onav.setActive(true, false, true);
4233 // this should not be here...
4234 setDisabled : function(state)
4236 this.disabled = state;
4238 this.el.removeClass('disabled');
4239 } else if (!this.el.hasClass('disabled')) {
4240 this.el.addClass('disabled');
4246 * Fetch the element to display the tooltip on.
4247 * @return {Roo.Element} defaults to this.el
4249 tooltipEl : function()
4251 return this.el.select('' + this.tagtype + '', true).first();
4254 scrollToElement : function(e)
4256 var c = document.body;
4259 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4261 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4262 c = document.documentElement;
4265 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4271 var o = target.calcOffsetsTo(c);
4278 this.fireEvent('scrollto', this, options, e);
4280 Roo.get(c).scrollTo('top', options.value, true);
4293 * <span> icon </span>
4294 * <span> text </span>
4295 * <span>badge </span>
4299 * @class Roo.bootstrap.NavSidebarItem
4300 * @extends Roo.bootstrap.NavItem
4301 * Bootstrap Navbar.NavSidebarItem class
4303 * Create a new Navbar Button
4304 * @param {Object} config The config object
4306 Roo.bootstrap.NavSidebarItem = function(config){
4307 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4312 * The raw click event for the entire grid.
4313 * @param {Roo.EventObject} e
4318 * Fires when the active item active state changes
4319 * @param {Roo.bootstrap.NavSidebarItem} this
4320 * @param {boolean} state the new state
4328 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4331 getAutoCreate : function(){
4336 href : this.href || '#',
4348 html : this.html || ''
4353 cfg.cls += ' active';
4357 if (this.glyphicon || this.icon) {
4358 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4359 a.cn.push({ tag : 'i', cls : c }) ;
4364 if (this.badge !== '') {
4365 a.cn.push({ tag: 'span', cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge });
4369 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4370 a.cls += 'dropdown-toggle treeview' ;
4394 * @class Roo.bootstrap.Row
4395 * @extends Roo.bootstrap.Component
4396 * Bootstrap Row class (contains columns...)
4400 * @param {Object} config The config object
4403 Roo.bootstrap.Row = function(config){
4404 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4407 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4409 getAutoCreate : function(){
4428 * @class Roo.bootstrap.Element
4429 * @extends Roo.bootstrap.Component
4430 * Bootstrap Element class
4431 * @cfg {String} html contents of the element
4432 * @cfg {String} tag tag of the element
4433 * @cfg {String} cls class of the element
4434 * @cfg {Boolean} preventDefault (true|false) default false
4435 * @cfg {Boolean} clickable (true|false) default false
4438 * Create a new Element
4439 * @param {Object} config The config object
4442 Roo.bootstrap.Element = function(config){
4443 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4449 * When a element is chick
4450 * @param {Roo.bootstrap.Element} this
4451 * @param {Roo.EventObject} e
4457 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4462 preventDefault: false,
4465 getAutoCreate : function(){
4476 initEvents: function()
4478 Roo.bootstrap.Element.superclass.initEvents.call(this);
4481 this.el.on('click', this.onClick, this);
4486 onClick : function(e)
4488 if(this.preventDefault){
4492 this.fireEvent('click', this, e);
4495 getValue : function()
4497 return this.el.dom.innerHTML;
4500 setValue : function(value)
4502 this.el.dom.innerHTML = value;
4517 * @class Roo.bootstrap.Pagination
4518 * @extends Roo.bootstrap.Component
4519 * Bootstrap Pagination class
4520 * @cfg {String} size xs | sm | md | lg
4521 * @cfg {Boolean} inverse false | true
4524 * Create a new Pagination
4525 * @param {Object} config The config object
4528 Roo.bootstrap.Pagination = function(config){
4529 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4532 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4538 getAutoCreate : function(){
4544 cfg.cls += ' inverse';
4550 cfg.cls += " " + this.cls;
4568 * @class Roo.bootstrap.PaginationItem
4569 * @extends Roo.bootstrap.Component
4570 * Bootstrap PaginationItem class
4571 * @cfg {String} html text
4572 * @cfg {String} href the link
4573 * @cfg {Boolean} preventDefault (true | false) default true
4574 * @cfg {Boolean} active (true | false) default false
4575 * @cfg {Boolean} disabled default false
4579 * Create a new PaginationItem
4580 * @param {Object} config The config object
4584 Roo.bootstrap.PaginationItem = function(config){
4585 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4590 * The raw click event for the entire grid.
4591 * @param {Roo.EventObject} e
4597 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4601 preventDefault: true,
4606 getAutoCreate : function(){
4612 href : this.href ? this.href : '#',
4613 html : this.html ? this.html : ''
4623 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4627 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4633 initEvents: function() {
4635 this.el.on('click', this.onClick, this);
4638 onClick : function(e)
4640 Roo.log('PaginationItem on click ');
4641 if(this.preventDefault){
4649 this.fireEvent('click', this, e);
4665 * @class Roo.bootstrap.Slider
4666 * @extends Roo.bootstrap.Component
4667 * Bootstrap Slider class
4670 * Create a new Slider
4671 * @param {Object} config The config object
4674 Roo.bootstrap.Slider = function(config){
4675 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4678 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4680 getAutoCreate : function(){
4684 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4688 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4700 * Ext JS Library 1.1.1
4701 * Copyright(c) 2006-2007, Ext JS, LLC.
4703 * Originally Released Under LGPL - original licence link has changed is not relivant.
4706 * <script type="text/javascript">
4711 * @class Roo.grid.ColumnModel
4712 * @extends Roo.util.Observable
4713 * This is the default implementation of a ColumnModel used by the Grid. It defines
4714 * the columns in the grid.
4717 var colModel = new Roo.grid.ColumnModel([
4718 {header: "Ticker", width: 60, sortable: true, locked: true},
4719 {header: "Company Name", width: 150, sortable: true},
4720 {header: "Market Cap.", width: 100, sortable: true},
4721 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4722 {header: "Employees", width: 100, sortable: true, resizable: false}
4727 * The config options listed for this class are options which may appear in each
4728 * individual column definition.
4729 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4731 * @param {Object} config An Array of column config objects. See this class's
4732 * config objects for details.
4734 Roo.grid.ColumnModel = function(config){
4736 * The config passed into the constructor
4738 this.config = config;
4741 // if no id, create one
4742 // if the column does not have a dataIndex mapping,
4743 // map it to the order it is in the config
4744 for(var i = 0, len = config.length; i < len; i++){
4746 if(typeof c.dataIndex == "undefined"){
4749 if(typeof c.renderer == "string"){
4750 c.renderer = Roo.util.Format[c.renderer];
4752 if(typeof c.id == "undefined"){
4755 if(c.editor && c.editor.xtype){
4756 c.editor = Roo.factory(c.editor, Roo.grid);
4758 if(c.editor && c.editor.isFormField){
4759 c.editor = new Roo.grid.GridEditor(c.editor);
4761 this.lookup[c.id] = c;
4765 * The width of columns which have no width specified (defaults to 100)
4768 this.defaultWidth = 100;
4771 * Default sortable of columns which have no sortable specified (defaults to false)
4774 this.defaultSortable = false;
4778 * @event widthchange
4779 * Fires when the width of a column changes.
4780 * @param {ColumnModel} this
4781 * @param {Number} columnIndex The column index
4782 * @param {Number} newWidth The new width
4784 "widthchange": true,
4786 * @event headerchange
4787 * Fires when the text of a header changes.
4788 * @param {ColumnModel} this
4789 * @param {Number} columnIndex The column index
4790 * @param {Number} newText The new header text
4792 "headerchange": true,
4794 * @event hiddenchange
4795 * Fires when a column is hidden or "unhidden".
4796 * @param {ColumnModel} this
4797 * @param {Number} columnIndex The column index
4798 * @param {Boolean} hidden true if hidden, false otherwise
4800 "hiddenchange": true,
4802 * @event columnmoved
4803 * Fires when a column is moved.
4804 * @param {ColumnModel} this
4805 * @param {Number} oldIndex
4806 * @param {Number} newIndex
4808 "columnmoved" : true,
4810 * @event columlockchange
4811 * Fires when a column's locked state is changed
4812 * @param {ColumnModel} this
4813 * @param {Number} colIndex
4814 * @param {Boolean} locked true if locked
4816 "columnlockchange" : true
4818 Roo.grid.ColumnModel.superclass.constructor.call(this);
4820 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4822 * @cfg {String} header The header text to display in the Grid view.
4825 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4826 * {@link Roo.data.Record} definition from which to draw the column's value. If not
4827 * specified, the column's index is used as an index into the Record's data Array.
4830 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4831 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4834 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4835 * Defaults to the value of the {@link #defaultSortable} property.
4836 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4839 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
4842 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
4845 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4848 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4851 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4852 * given the cell's data value. See {@link #setRenderer}. If not specified, the
4853 * default renderer uses the raw data value. If an object is returned (bootstrap only)
4854 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4857 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
4860 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
4863 * @cfg {String} cursor (Optional)
4866 * @cfg {String} tooltip (Optional)
4869 * Returns the id of the column at the specified index.
4870 * @param {Number} index The column index
4871 * @return {String} the id
4873 getColumnId : function(index){
4874 return this.config[index].id;
4878 * Returns the column for a specified id.
4879 * @param {String} id The column id
4880 * @return {Object} the column
4882 getColumnById : function(id){
4883 return this.lookup[id];
4888 * Returns the column for a specified dataIndex.
4889 * @param {String} dataIndex The column dataIndex
4890 * @return {Object|Boolean} the column or false if not found
4892 getColumnByDataIndex: function(dataIndex){
4893 var index = this.findColumnIndex(dataIndex);
4894 return index > -1 ? this.config[index] : false;
4898 * Returns the index for a specified column id.
4899 * @param {String} id The column id
4900 * @return {Number} the index, or -1 if not found
4902 getIndexById : function(id){
4903 for(var i = 0, len = this.config.length; i < len; i++){
4904 if(this.config[i].id == id){
4912 * Returns the index for a specified column dataIndex.
4913 * @param {String} dataIndex The column dataIndex
4914 * @return {Number} the index, or -1 if not found
4917 findColumnIndex : function(dataIndex){
4918 for(var i = 0, len = this.config.length; i < len; i++){
4919 if(this.config[i].dataIndex == dataIndex){
4927 moveColumn : function(oldIndex, newIndex){
4928 var c = this.config[oldIndex];
4929 this.config.splice(oldIndex, 1);
4930 this.config.splice(newIndex, 0, c);
4931 this.dataMap = null;
4932 this.fireEvent("columnmoved", this, oldIndex, newIndex);
4935 isLocked : function(colIndex){
4936 return this.config[colIndex].locked === true;
4939 setLocked : function(colIndex, value, suppressEvent){
4940 if(this.isLocked(colIndex) == value){
4943 this.config[colIndex].locked = value;
4945 this.fireEvent("columnlockchange", this, colIndex, value);
4949 getTotalLockedWidth : function(){
4951 for(var i = 0; i < this.config.length; i++){
4952 if(this.isLocked(i) && !this.isHidden(i)){
4953 this.totalWidth += this.getColumnWidth(i);
4959 getLockedCount : function(){
4960 for(var i = 0, len = this.config.length; i < len; i++){
4961 if(!this.isLocked(i)){
4968 * Returns the number of columns.
4971 getColumnCount : function(visibleOnly){
4972 if(visibleOnly === true){
4974 for(var i = 0, len = this.config.length; i < len; i++){
4975 if(!this.isHidden(i)){
4981 return this.config.length;
4985 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4986 * @param {Function} fn
4987 * @param {Object} scope (optional)
4988 * @return {Array} result
4990 getColumnsBy : function(fn, scope){
4992 for(var i = 0, len = this.config.length; i < len; i++){
4993 var c = this.config[i];
4994 if(fn.call(scope||this, c, i) === true){
5002 * Returns true if the specified column is sortable.
5003 * @param {Number} col The column index
5006 isSortable : function(col){
5007 if(typeof this.config[col].sortable == "undefined"){
5008 return this.defaultSortable;
5010 return this.config[col].sortable;
5014 * Returns the rendering (formatting) function defined for the column.
5015 * @param {Number} col The column index.
5016 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5018 getRenderer : function(col){
5019 if(!this.config[col].renderer){
5020 return Roo.grid.ColumnModel.defaultRenderer;
5022 return this.config[col].renderer;
5026 * Sets the rendering (formatting) function for a column.
5027 * @param {Number} col The column index
5028 * @param {Function} fn The function to use to process the cell's raw data
5029 * to return HTML markup for the grid view. The render function is called with
5030 * the following parameters:<ul>
5031 * <li>Data value.</li>
5032 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5033 * <li>css A CSS style string to apply to the table cell.</li>
5034 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5035 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5036 * <li>Row index</li>
5037 * <li>Column index</li>
5038 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5040 setRenderer : function(col, fn){
5041 this.config[col].renderer = fn;
5045 * Returns the width for the specified column.
5046 * @param {Number} col The column index
5049 getColumnWidth : function(col){
5050 return this.config[col].width * 1 || this.defaultWidth;
5054 * Sets the width for a column.
5055 * @param {Number} col The column index
5056 * @param {Number} width The new width
5058 setColumnWidth : function(col, width, suppressEvent){
5059 this.config[col].width = width;
5060 this.totalWidth = null;
5062 this.fireEvent("widthchange", this, col, width);
5067 * Returns the total width of all columns.
5068 * @param {Boolean} includeHidden True to include hidden column widths
5071 getTotalWidth : function(includeHidden){
5072 if(!this.totalWidth){
5073 this.totalWidth = 0;
5074 for(var i = 0, len = this.config.length; i < len; i++){
5075 if(includeHidden || !this.isHidden(i)){
5076 this.totalWidth += this.getColumnWidth(i);
5080 return this.totalWidth;
5084 * Returns the header for the specified column.
5085 * @param {Number} col The column index
5088 getColumnHeader : function(col){
5089 return this.config[col].header;
5093 * Sets the header for a column.
5094 * @param {Number} col The column index
5095 * @param {String} header The new header
5097 setColumnHeader : function(col, header){
5098 this.config[col].header = header;
5099 this.fireEvent("headerchange", this, col, header);
5103 * Returns the tooltip for the specified column.
5104 * @param {Number} col The column index
5107 getColumnTooltip : function(col){
5108 return this.config[col].tooltip;
5111 * Sets the tooltip for a column.
5112 * @param {Number} col The column index
5113 * @param {String} tooltip The new tooltip
5115 setColumnTooltip : function(col, tooltip){
5116 this.config[col].tooltip = tooltip;
5120 * Returns the dataIndex for the specified column.
5121 * @param {Number} col The column index
5124 getDataIndex : function(col){
5125 return this.config[col].dataIndex;
5129 * Sets the dataIndex for a column.
5130 * @param {Number} col The column index
5131 * @param {Number} dataIndex The new dataIndex
5133 setDataIndex : function(col, dataIndex){
5134 this.config[col].dataIndex = dataIndex;
5140 * Returns true if the cell is editable.
5141 * @param {Number} colIndex The column index
5142 * @param {Number} rowIndex The row index
5145 isCellEditable : function(colIndex, rowIndex){
5146 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5150 * Returns the editor defined for the cell/column.
5151 * return false or null to disable editing.
5152 * @param {Number} colIndex The column index
5153 * @param {Number} rowIndex The row index
5156 getCellEditor : function(colIndex, rowIndex){
5157 return this.config[colIndex].editor;
5161 * Sets if a column is editable.
5162 * @param {Number} col The column index
5163 * @param {Boolean} editable True if the column is editable
5165 setEditable : function(col, editable){
5166 this.config[col].editable = editable;
5171 * Returns true if the column is hidden.
5172 * @param {Number} colIndex The column index
5175 isHidden : function(colIndex){
5176 return this.config[colIndex].hidden;
5181 * Returns true if the column width cannot be changed
5183 isFixed : function(colIndex){
5184 return this.config[colIndex].fixed;
5188 * Returns true if the column can be resized
5191 isResizable : function(colIndex){
5192 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5195 * Sets if a column is hidden.
5196 * @param {Number} colIndex The column index
5197 * @param {Boolean} hidden True if the column is hidden
5199 setHidden : function(colIndex, hidden){
5200 this.config[colIndex].hidden = hidden;
5201 this.totalWidth = null;
5202 this.fireEvent("hiddenchange", this, colIndex, hidden);
5206 * Sets the editor for a column.
5207 * @param {Number} col The column index
5208 * @param {Object} editor The editor object
5210 setEditor : function(col, editor){
5211 this.config[col].editor = editor;
5215 Roo.grid.ColumnModel.defaultRenderer = function(value){
5216 if(typeof value == "string" && value.length < 1){
5222 // Alias for backwards compatibility
5223 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5226 * Ext JS Library 1.1.1
5227 * Copyright(c) 2006-2007, Ext JS, LLC.
5229 * Originally Released Under LGPL - original licence link has changed is not relivant.
5232 * <script type="text/javascript">
5236 * @class Roo.LoadMask
5237 * A simple utility class for generically masking elements while loading data. If the element being masked has
5238 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5239 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5240 * element's UpdateManager load indicator and will be destroyed after the initial load.
5242 * Create a new LoadMask
5243 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5244 * @param {Object} config The config object
5246 Roo.LoadMask = function(el, config){
5247 this.el = Roo.get(el);
5248 Roo.apply(this, config);
5250 this.store.on('beforeload', this.onBeforeLoad, this);
5251 this.store.on('load', this.onLoad, this);
5252 this.store.on('loadexception', this.onLoadException, this);
5253 this.removeMask = false;
5255 var um = this.el.getUpdateManager();
5256 um.showLoadIndicator = false; // disable the default indicator
5257 um.on('beforeupdate', this.onBeforeLoad, this);
5258 um.on('update', this.onLoad, this);
5259 um.on('failure', this.onLoad, this);
5260 this.removeMask = true;
5264 Roo.LoadMask.prototype = {
5266 * @cfg {Boolean} removeMask
5267 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5268 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5272 * The text to display in a centered loading message box (defaults to 'Loading...')
5276 * @cfg {String} msgCls
5277 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5279 msgCls : 'x-mask-loading',
5282 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5288 * Disables the mask to prevent it from being displayed
5290 disable : function(){
5291 this.disabled = true;
5295 * Enables the mask so that it can be displayed
5297 enable : function(){
5298 this.disabled = false;
5301 onLoadException : function()
5305 if (typeof(arguments[3]) != 'undefined') {
5306 Roo.MessageBox.alert("Error loading",arguments[3]);
5310 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5311 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5320 this.el.unmask(this.removeMask);
5325 this.el.unmask(this.removeMask);
5329 onBeforeLoad : function(){
5331 this.el.mask(this.msg, this.msgCls);
5336 destroy : function(){
5338 this.store.un('beforeload', this.onBeforeLoad, this);
5339 this.store.un('load', this.onLoad, this);
5340 this.store.un('loadexception', this.onLoadException, this);
5342 var um = this.el.getUpdateManager();
5343 um.un('beforeupdate', this.onBeforeLoad, this);
5344 um.un('update', this.onLoad, this);
5345 um.un('failure', this.onLoad, this);
5356 * @class Roo.bootstrap.Table
5357 * @extends Roo.bootstrap.Component
5358 * Bootstrap Table class
5359 * @cfg {String} cls table class
5360 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5361 * @cfg {String} bgcolor Specifies the background color for a table
5362 * @cfg {Number} border Specifies whether the table cells should have borders or not
5363 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5364 * @cfg {Number} cellspacing Specifies the space between cells
5365 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5366 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5367 * @cfg {String} sortable Specifies that the table should be sortable
5368 * @cfg {String} summary Specifies a summary of the content of a table
5369 * @cfg {Number} width Specifies the width of a table
5370 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5372 * @cfg {boolean} striped Should the rows be alternative striped
5373 * @cfg {boolean} bordered Add borders to the table
5374 * @cfg {boolean} hover Add hover highlighting
5375 * @cfg {boolean} condensed Format condensed
5376 * @cfg {boolean} responsive Format condensed
5377 * @cfg {Boolean} loadMask (true|false) default false
5378 * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
5379 * @cfg {Boolean} thead (true|false) generate thead, default true
5380 * @cfg {Boolean} RowSelection (true|false) default false
5381 * @cfg {Boolean} CellSelection (true|false) default false
5382 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5386 * Create a new Table
5387 * @param {Object} config The config object
5390 Roo.bootstrap.Table = function(config){
5391 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5394 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5395 this.sm = this.selModel;
5396 this.sm.xmodule = this.xmodule || false;
5398 if (this.cm && typeof(this.cm.config) == 'undefined') {
5399 this.colModel = new Roo.grid.ColumnModel(this.cm);
5400 this.cm = this.colModel;
5401 this.cm.xmodule = this.xmodule || false;
5404 this.store= Roo.factory(this.store, Roo.data);
5405 this.ds = this.store;
5406 this.ds.xmodule = this.xmodule || false;
5409 if (this.footer && this.store) {
5410 this.footer.dataSource = this.ds;
5411 this.footer = Roo.factory(this.footer);
5418 * Fires when a cell is clicked
5419 * @param {Roo.bootstrap.Table} this
5420 * @param {Roo.Element} el
5421 * @param {Number} rowIndex
5422 * @param {Number} columnIndex
5423 * @param {Roo.EventObject} e
5427 * @event celldblclick
5428 * Fires when a cell is double clicked
5429 * @param {Roo.bootstrap.Table} this
5430 * @param {Roo.Element} el
5431 * @param {Number} rowIndex
5432 * @param {Number} columnIndex
5433 * @param {Roo.EventObject} e
5435 "celldblclick" : true,
5438 * Fires when a row is clicked
5439 * @param {Roo.bootstrap.Table} this
5440 * @param {Roo.Element} el
5441 * @param {Number} rowIndex
5442 * @param {Roo.EventObject} e
5446 * @event rowdblclick
5447 * Fires when a row is double clicked
5448 * @param {Roo.bootstrap.Table} this
5449 * @param {Roo.Element} el
5450 * @param {Number} rowIndex
5451 * @param {Roo.EventObject} e
5453 "rowdblclick" : true,
5456 * Fires when a mouseover occur
5457 * @param {Roo.bootstrap.Table} this
5458 * @param {Roo.Element} el
5459 * @param {Number} rowIndex
5460 * @param {Number} columnIndex
5461 * @param {Roo.EventObject} e
5466 * Fires when a mouseout occur
5467 * @param {Roo.bootstrap.Table} this
5468 * @param {Roo.Element} el
5469 * @param {Number} rowIndex
5470 * @param {Number} columnIndex
5471 * @param {Roo.EventObject} e
5476 * Fires when a row is rendered, so you can change add a style to it.
5477 * @param {Roo.bootstrap.Table} this
5478 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5482 * @event rowsrendered
5483 * Fires when all the rows have been rendered
5484 * @param {Roo.bootstrap.Table} this
5486 'rowsrendered' : true
5491 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5515 RowSelection : false,
5516 CellSelection : false,
5519 // Roo.Element - the tbody
5522 getAutoCreate : function(){
5523 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5532 cfg.cls += ' table-striped';
5536 cfg.cls += ' table-hover';
5538 if (this.bordered) {
5539 cfg.cls += ' table-bordered';
5541 if (this.condensed) {
5542 cfg.cls += ' table-condensed';
5544 if (this.responsive) {
5545 cfg.cls += ' table-responsive';
5549 cfg.cls+= ' ' +this.cls;
5552 // this lot should be simplifed...
5555 cfg.align=this.align;
5558 cfg.bgcolor=this.bgcolor;
5561 cfg.border=this.border;
5563 if (this.cellpadding) {
5564 cfg.cellpadding=this.cellpadding;
5566 if (this.cellspacing) {
5567 cfg.cellspacing=this.cellspacing;
5570 cfg.frame=this.frame;
5573 cfg.rules=this.rules;
5575 if (this.sortable) {
5576 cfg.sortable=this.sortable;
5579 cfg.summary=this.summary;
5582 cfg.width=this.width;
5585 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5588 if(this.store || this.cm){
5590 cfg.cn.push(this.renderHeader());
5593 cfg.cn.push(this.renderBody());
5596 cfg.cn.push(this.renderFooter());
5599 cfg.cls+= ' TableGrid';
5602 return { cn : [ cfg ] };
5605 initEvents : function()
5607 if(!this.store || !this.cm){
5611 //Roo.log('initEvents with ds!!!!');
5613 this.mainBody = this.el.select('tbody', true).first();
5618 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5619 e.on('click', _this.sort, _this);
5622 this.el.on("click", this.onClick, this);
5623 this.el.on("dblclick", this.onDblClick, this);
5625 // why is this done????? = it breaks dialogs??
5626 //this.parent().el.setStyle('position', 'relative');
5630 this.footer.parentId = this.id;
5631 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5634 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5636 this.store.on('load', this.onLoad, this);
5637 this.store.on('beforeload', this.onBeforeLoad, this);
5638 this.store.on('update', this.onUpdate, this);
5639 this.store.on('add', this.onAdd, this);
5643 onMouseover : function(e, el)
5645 var cell = Roo.get(el);
5651 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5652 cell = cell.findParent('td', false, true);
5655 var row = cell.findParent('tr', false, true);
5656 var cellIndex = cell.dom.cellIndex;
5657 var rowIndex = row.dom.rowIndex - 1; // start from 0
5659 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5663 onMouseout : function(e, el)
5665 var cell = Roo.get(el);
5671 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5672 cell = cell.findParent('td', false, true);
5675 var row = cell.findParent('tr', false, true);
5676 var cellIndex = cell.dom.cellIndex;
5677 var rowIndex = row.dom.rowIndex - 1; // start from 0
5679 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5683 onClick : function(e, el)
5685 var cell = Roo.get(el);
5687 if(!cell || (!this.CellSelection && !this.RowSelection)){
5691 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5692 cell = cell.findParent('td', false, true);
5695 if(!cell || typeof(cell) == 'undefined'){
5699 var row = cell.findParent('tr', false, true);
5701 if(!row || typeof(row) == 'undefined'){
5705 var cellIndex = cell.dom.cellIndex;
5706 var rowIndex = this.getRowIndex(row);
5708 if(this.CellSelection){
5709 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5712 if(this.RowSelection){
5713 this.fireEvent('rowclick', this, row, rowIndex, e);
5719 onDblClick : function(e,el)
5721 var cell = Roo.get(el);
5723 if(!cell || (!this.CellSelection && !this.RowSelection)){
5727 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5728 cell = cell.findParent('td', false, true);
5731 if(!cell || typeof(cell) == 'undefined'){
5735 var row = cell.findParent('tr', false, true);
5737 if(!row || typeof(row) == 'undefined'){
5741 var cellIndex = cell.dom.cellIndex;
5742 var rowIndex = this.getRowIndex(row);
5744 if(this.CellSelection){
5745 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5748 if(this.RowSelection){
5749 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5753 sort : function(e,el)
5755 var col = Roo.get(el);
5757 if(!col.hasClass('sortable')){
5761 var sort = col.attr('sort');
5764 if(col.hasClass('glyphicon-arrow-up')){
5768 this.store.sortInfo = {field : sort, direction : dir};
5771 Roo.log("calling footer first");
5772 this.footer.onClick('first');
5775 this.store.load({ params : { start : 0 } });
5779 renderHeader : function()
5788 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5790 var config = cm.config[i];
5795 html: cm.getColumnHeader(i)
5798 if(typeof(config.tooltip) != 'undefined'){
5799 c.tooltip = config.tooltip;
5802 if(typeof(config.colspan) != 'undefined'){
5803 c.colspan = config.colspan;
5806 if(typeof(config.hidden) != 'undefined' && config.hidden){
5807 c.style += ' display:none;';
5810 if(typeof(config.dataIndex) != 'undefined'){
5811 c.sort = config.dataIndex;
5814 if(typeof(config.sortable) != 'undefined' && config.sortable){
5818 if(typeof(config.align) != 'undefined' && config.align.length){
5819 c.style += ' text-align:' + config.align + ';';
5822 if(typeof(config.width) != 'undefined'){
5823 c.style += ' width:' + config.width + 'px;';
5826 if(typeof(config.cls) != 'undefined'){
5827 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5836 renderBody : function()
5846 colspan : this.cm.getColumnCount()
5856 renderFooter : function()
5866 colspan : this.cm.getColumnCount()
5880 Roo.log('ds onload');
5885 var ds = this.store;
5887 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5888 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5890 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5891 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5894 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5895 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5899 var tbody = this.mainBody;
5901 if(ds.getCount() > 0){
5902 ds.data.each(function(d,rowIndex){
5903 var row = this.renderRow(cm, ds, rowIndex);
5905 tbody.createChild(row);
5909 if(row.cellObjects.length){
5910 Roo.each(row.cellObjects, function(r){
5911 _this.renderCellObject(r);
5918 Roo.each(this.el.select('tbody td', true).elements, function(e){
5919 e.on('mouseover', _this.onMouseover, _this);
5922 Roo.each(this.el.select('tbody td', true).elements, function(e){
5923 e.on('mouseout', _this.onMouseout, _this);
5925 this.fireEvent('rowsrendered', this);
5926 //if(this.loadMask){
5927 // this.maskEl.hide();
5932 onUpdate : function(ds,record)
5934 this.refreshRow(record);
5937 onRemove : function(ds, record, index, isUpdate){
5938 if(isUpdate !== true){
5939 this.fireEvent("beforerowremoved", this, index, record);
5941 var bt = this.mainBody.dom;
5943 var rows = this.el.select('tbody > tr', true).elements;
5945 if(typeof(rows[index]) != 'undefined'){
5946 bt.removeChild(rows[index].dom);
5949 // if(bt.rows[index]){
5950 // bt.removeChild(bt.rows[index]);
5953 if(isUpdate !== true){
5954 //this.stripeRows(index);
5955 //this.syncRowHeights(index, index);
5957 this.fireEvent("rowremoved", this, index, record);
5961 onAdd : function(ds, records, rowIndex)
5963 //Roo.log('on Add called');
5964 // - note this does not handle multiple adding very well..
5965 var bt = this.mainBody.dom;
5966 for (var i =0 ; i < records.length;i++) {
5967 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5968 //Roo.log(records[i]);
5969 //Roo.log(this.store.getAt(rowIndex+i));
5970 this.insertRow(this.store, rowIndex + i, false);
5977 refreshRow : function(record){
5978 var ds = this.store, index;
5979 if(typeof record == 'number'){
5981 record = ds.getAt(index);
5983 index = ds.indexOf(record);
5985 this.insertRow(ds, index, true);
5986 this.onRemove(ds, record, index+1, true);
5987 //this.syncRowHeights(index, index);
5989 this.fireEvent("rowupdated", this, index, record);
5992 insertRow : function(dm, rowIndex, isUpdate){
5995 this.fireEvent("beforerowsinserted", this, rowIndex);
5997 //var s = this.getScrollState();
5998 var row = this.renderRow(this.cm, this.store, rowIndex);
5999 // insert before rowIndex..
6000 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6004 if(row.cellObjects.length){
6005 Roo.each(row.cellObjects, function(r){
6006 _this.renderCellObject(r);
6011 this.fireEvent("rowsinserted", this, rowIndex);
6012 //this.syncRowHeights(firstRow, lastRow);
6013 //this.stripeRows(firstRow);
6020 getRowDom : function(rowIndex)
6022 var rows = this.el.select('tbody > tr', true).elements;
6024 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6027 // returns the object tree for a tr..
6030 renderRow : function(cm, ds, rowIndex)
6033 var d = ds.getAt(rowIndex);
6040 var cellObjects = [];
6042 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6043 var config = cm.config[i];
6045 var renderer = cm.getRenderer(i);
6049 if(typeof(renderer) !== 'undefined'){
6050 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6052 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6053 // and are rendered into the cells after the row is rendered - using the id for the element.
6055 if(typeof(value) === 'object'){
6065 rowIndex : rowIndex,
6070 this.fireEvent('rowclass', this, rowcfg);
6074 cls : rowcfg.rowClass,
6076 html: (typeof(value) === 'object') ? '' : value
6083 if(typeof(config.colspan) != 'undefined'){
6084 td.colspan = config.colspan;
6087 if(typeof(config.hidden) != 'undefined' && config.hidden){
6088 td.style += ' display:none;';
6091 if(typeof(config.align) != 'undefined' && config.align.length){
6092 td.style += ' text-align:' + config.align + ';';
6095 if(typeof(config.width) != 'undefined'){
6096 td.style += ' width:' + config.width + 'px;';
6099 if(typeof(config.cursor) != 'undefined'){
6100 td.style += ' cursor:' + config.cursor + ';';
6103 if(typeof(config.cls) != 'undefined'){
6104 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6111 row.cellObjects = cellObjects;
6119 onBeforeLoad : function()
6121 //Roo.log('ds onBeforeLoad');
6125 //if(this.loadMask){
6126 // this.maskEl.show();
6134 this.el.select('tbody', true).first().dom.innerHTML = '';
6137 * Show or hide a row.
6138 * @param {Number} rowIndex to show or hide
6139 * @param {Boolean} state hide
6141 setRowVisibility : function(rowIndex, state)
6143 var bt = this.mainBody.dom;
6145 var rows = this.el.select('tbody > tr', true).elements;
6147 if(typeof(rows[rowIndex]) == 'undefined'){
6150 rows[rowIndex].dom.style.display = state ? '' : 'none';
6154 getSelectionModel : function(){
6156 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6158 return this.selModel;
6161 * Render the Roo.bootstrap object from renderder
6163 renderCellObject : function(r)
6167 var t = r.cfg.render(r.container);
6170 Roo.each(r.cfg.cn, function(c){
6172 container: t.getChildContainer(),
6175 _this.renderCellObject(child);
6180 getRowIndex : function(row)
6184 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6207 * @class Roo.bootstrap.TableCell
6208 * @extends Roo.bootstrap.Component
6209 * Bootstrap TableCell class
6210 * @cfg {String} html cell contain text
6211 * @cfg {String} cls cell class
6212 * @cfg {String} tag cell tag (td|th) default td
6213 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6214 * @cfg {String} align Aligns the content in a cell
6215 * @cfg {String} axis Categorizes cells
6216 * @cfg {String} bgcolor Specifies the background color of a cell
6217 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6218 * @cfg {Number} colspan Specifies the number of columns a cell should span
6219 * @cfg {String} headers Specifies one or more header cells a cell is related to
6220 * @cfg {Number} height Sets the height of a cell
6221 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6222 * @cfg {Number} rowspan Sets the number of rows a cell should span
6223 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6224 * @cfg {String} valign Vertical aligns the content in a cell
6225 * @cfg {Number} width Specifies the width of a cell
6228 * Create a new TableCell
6229 * @param {Object} config The config object
6232 Roo.bootstrap.TableCell = function(config){
6233 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6236 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6256 getAutoCreate : function(){
6257 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6277 cfg.align=this.align
6283 cfg.bgcolor=this.bgcolor
6286 cfg.charoff=this.charoff
6289 cfg.colspan=this.colspan
6292 cfg.headers=this.headers
6295 cfg.height=this.height
6298 cfg.nowrap=this.nowrap
6301 cfg.rowspan=this.rowspan
6304 cfg.scope=this.scope
6307 cfg.valign=this.valign
6310 cfg.width=this.width
6329 * @class Roo.bootstrap.TableRow
6330 * @extends Roo.bootstrap.Component
6331 * Bootstrap TableRow class
6332 * @cfg {String} cls row class
6333 * @cfg {String} align Aligns the content in a table row
6334 * @cfg {String} bgcolor Specifies a background color for a table row
6335 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6336 * @cfg {String} valign Vertical aligns the content in a table row
6339 * Create a new TableRow
6340 * @param {Object} config The config object
6343 Roo.bootstrap.TableRow = function(config){
6344 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6347 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6355 getAutoCreate : function(){
6356 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6366 cfg.align = this.align;
6369 cfg.bgcolor = this.bgcolor;
6372 cfg.charoff = this.charoff;
6375 cfg.valign = this.valign;
6393 * @class Roo.bootstrap.TableBody
6394 * @extends Roo.bootstrap.Component
6395 * Bootstrap TableBody class
6396 * @cfg {String} cls element class
6397 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6398 * @cfg {String} align Aligns the content inside the element
6399 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6400 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6403 * Create a new TableBody
6404 * @param {Object} config The config object
6407 Roo.bootstrap.TableBody = function(config){
6408 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6411 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6419 getAutoCreate : function(){
6420 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6434 cfg.align = this.align;
6437 cfg.charoff = this.charoff;
6440 cfg.valign = this.valign;
6447 // initEvents : function()
6454 // this.store = Roo.factory(this.store, Roo.data);
6455 // this.store.on('load', this.onLoad, this);
6457 // this.store.load();
6461 // onLoad: function ()
6463 // this.fireEvent('load', this);
6473 * Ext JS Library 1.1.1
6474 * Copyright(c) 2006-2007, Ext JS, LLC.
6476 * Originally Released Under LGPL - original licence link has changed is not relivant.
6479 * <script type="text/javascript">
6482 // as we use this in bootstrap.
6483 Roo.namespace('Roo.form');
6485 * @class Roo.form.Action
6486 * Internal Class used to handle form actions
6488 * @param {Roo.form.BasicForm} el The form element or its id
6489 * @param {Object} config Configuration options
6494 // define the action interface
6495 Roo.form.Action = function(form, options){
6497 this.options = options || {};
6500 * Client Validation Failed
6503 Roo.form.Action.CLIENT_INVALID = 'client';
6505 * Server Validation Failed
6508 Roo.form.Action.SERVER_INVALID = 'server';
6510 * Connect to Server Failed
6513 Roo.form.Action.CONNECT_FAILURE = 'connect';
6515 * Reading Data from Server Failed
6518 Roo.form.Action.LOAD_FAILURE = 'load';
6520 Roo.form.Action.prototype = {
6522 failureType : undefined,
6523 response : undefined,
6527 run : function(options){
6532 success : function(response){
6537 handleResponse : function(response){
6541 // default connection failure
6542 failure : function(response){
6544 this.response = response;
6545 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6546 this.form.afterAction(this, false);
6549 processResponse : function(response){
6550 this.response = response;
6551 if(!response.responseText){
6554 this.result = this.handleResponse(response);
6558 // utility functions used internally
6559 getUrl : function(appendParams){
6560 var url = this.options.url || this.form.url || this.form.el.dom.action;
6562 var p = this.getParams();
6564 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6570 getMethod : function(){
6571 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6574 getParams : function(){
6575 var bp = this.form.baseParams;
6576 var p = this.options.params;
6578 if(typeof p == "object"){
6579 p = Roo.urlEncode(Roo.applyIf(p, bp));
6580 }else if(typeof p == 'string' && bp){
6581 p += '&' + Roo.urlEncode(bp);
6584 p = Roo.urlEncode(bp);
6589 createCallback : function(){
6591 success: this.success,
6592 failure: this.failure,
6594 timeout: (this.form.timeout*1000),
6595 upload: this.form.fileUpload ? this.success : undefined
6600 Roo.form.Action.Submit = function(form, options){
6601 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6604 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6607 haveProgress : false,
6608 uploadComplete : false,
6610 // uploadProgress indicator.
6611 uploadProgress : function()
6613 if (!this.form.progressUrl) {
6617 if (!this.haveProgress) {
6618 Roo.MessageBox.progress("Uploading", "Uploading");
6620 if (this.uploadComplete) {
6621 Roo.MessageBox.hide();
6625 this.haveProgress = true;
6627 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6629 var c = new Roo.data.Connection();
6631 url : this.form.progressUrl,
6636 success : function(req){
6637 //console.log(data);
6641 rdata = Roo.decode(req.responseText)
6643 Roo.log("Invalid data from server..");
6647 if (!rdata || !rdata.success) {
6649 Roo.MessageBox.alert(Roo.encode(rdata));
6652 var data = rdata.data;
6654 if (this.uploadComplete) {
6655 Roo.MessageBox.hide();
6660 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6661 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6664 this.uploadProgress.defer(2000,this);
6667 failure: function(data) {
6668 Roo.log('progress url failed ');
6679 // run get Values on the form, so it syncs any secondary forms.
6680 this.form.getValues();
6682 var o = this.options;
6683 var method = this.getMethod();
6684 var isPost = method == 'POST';
6685 if(o.clientValidation === false || this.form.isValid()){
6687 if (this.form.progressUrl) {
6688 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6689 (new Date() * 1) + '' + Math.random());
6694 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6695 form:this.form.el.dom,
6696 url:this.getUrl(!isPost),
6698 params:isPost ? this.getParams() : null,
6699 isUpload: this.form.fileUpload
6702 this.uploadProgress();
6704 }else if (o.clientValidation !== false){ // client validation failed
6705 this.failureType = Roo.form.Action.CLIENT_INVALID;
6706 this.form.afterAction(this, false);
6710 success : function(response)
6712 this.uploadComplete= true;
6713 if (this.haveProgress) {
6714 Roo.MessageBox.hide();
6718 var result = this.processResponse(response);
6719 if(result === true || result.success){
6720 this.form.afterAction(this, true);
6724 this.form.markInvalid(result.errors);
6725 this.failureType = Roo.form.Action.SERVER_INVALID;
6727 this.form.afterAction(this, false);
6729 failure : function(response)
6731 this.uploadComplete= true;
6732 if (this.haveProgress) {
6733 Roo.MessageBox.hide();
6736 this.response = response;
6737 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6738 this.form.afterAction(this, false);
6741 handleResponse : function(response){
6742 if(this.form.errorReader){
6743 var rs = this.form.errorReader.read(response);
6746 for(var i = 0, len = rs.records.length; i < len; i++) {
6747 var r = rs.records[i];
6751 if(errors.length < 1){
6755 success : rs.success,
6761 ret = Roo.decode(response.responseText);
6765 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6775 Roo.form.Action.Load = function(form, options){
6776 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6777 this.reader = this.form.reader;
6780 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6785 Roo.Ajax.request(Roo.apply(
6786 this.createCallback(), {
6787 method:this.getMethod(),
6788 url:this.getUrl(false),
6789 params:this.getParams()
6793 success : function(response){
6795 var result = this.processResponse(response);
6796 if(result === true || !result.success || !result.data){
6797 this.failureType = Roo.form.Action.LOAD_FAILURE;
6798 this.form.afterAction(this, false);
6801 this.form.clearInvalid();
6802 this.form.setValues(result.data);
6803 this.form.afterAction(this, true);
6806 handleResponse : function(response){
6807 if(this.form.reader){
6808 var rs = this.form.reader.read(response);
6809 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6811 success : rs.success,
6815 return Roo.decode(response.responseText);
6819 Roo.form.Action.ACTION_TYPES = {
6820 'load' : Roo.form.Action.Load,
6821 'submit' : Roo.form.Action.Submit
6830 * @class Roo.bootstrap.Form
6831 * @extends Roo.bootstrap.Component
6832 * Bootstrap Form class
6833 * @cfg {String} method GET | POST (default POST)
6834 * @cfg {String} labelAlign top | left (default top)
6835 * @cfg {String} align left | right - for navbars
6836 * @cfg {Boolean} loadMask load mask when submit (default true)
6841 * @param {Object} config The config object
6845 Roo.bootstrap.Form = function(config){
6846 Roo.bootstrap.Form.superclass.constructor.call(this, config);
6849 * @event clientvalidation
6850 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6851 * @param {Form} this
6852 * @param {Boolean} valid true if the form has passed client-side validation
6854 clientvalidation: true,
6856 * @event beforeaction
6857 * Fires before any action is performed. Return false to cancel the action.
6858 * @param {Form} this
6859 * @param {Action} action The action to be performed
6863 * @event actionfailed
6864 * Fires when an action fails.
6865 * @param {Form} this
6866 * @param {Action} action The action that failed
6868 actionfailed : true,
6870 * @event actioncomplete
6871 * Fires when an action is completed.
6872 * @param {Form} this
6873 * @param {Action} action The action that completed
6875 actioncomplete : true
6880 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
6883 * @cfg {String} method
6884 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6889 * The URL to use for form actions if one isn't supplied in the action options.
6892 * @cfg {Boolean} fileUpload
6893 * Set to true if this form is a file upload.
6897 * @cfg {Object} baseParams
6898 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6902 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6906 * @cfg {Sting} align (left|right) for navbar forms
6911 activeAction : null,
6914 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6915 * element by passing it or its id or mask the form itself by passing in true.
6918 waitMsgTarget : false,
6922 getAutoCreate : function(){
6926 method : this.method || 'POST',
6927 id : this.id || Roo.id(),
6930 if (this.parent().xtype.match(/^Nav/)) {
6931 cfg.cls = 'navbar-form navbar-' + this.align;
6935 if (this.labelAlign == 'left' ) {
6936 cfg.cls += ' form-horizontal';
6942 initEvents : function()
6944 this.el.on('submit', this.onSubmit, this);
6945 // this was added as random key presses on the form where triggering form submit.
6946 this.el.on('keypress', function(e) {
6947 if (e.getCharCode() != 13) {
6950 // we might need to allow it for textareas.. and some other items.
6951 // check e.getTarget().
6953 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6957 Roo.log("keypress blocked");
6965 onSubmit : function(e){
6970 * Returns true if client-side validation on the form is successful.
6973 isValid : function(){
6974 var items = this.getItems();
6976 items.each(function(f){
6985 * Returns true if any fields in this form have changed since their original load.
6988 isDirty : function(){
6990 var items = this.getItems();
6991 items.each(function(f){
7001 * Performs a predefined action (submit or load) or custom actions you define on this form.
7002 * @param {String} actionName The name of the action type
7003 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7004 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7005 * accept other config options):
7007 Property Type Description
7008 ---------------- --------------- ----------------------------------------------------------------------------------
7009 url String The url for the action (defaults to the form's url)
7010 method String The form method to use (defaults to the form's method, or POST if not defined)
7011 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7012 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7013 validate the form on the client (defaults to false)
7015 * @return {BasicForm} this
7017 doAction : function(action, options){
7018 if(typeof action == 'string'){
7019 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7021 if(this.fireEvent('beforeaction', this, action) !== false){
7022 this.beforeAction(action);
7023 action.run.defer(100, action);
7029 beforeAction : function(action){
7030 var o = action.options;
7033 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7035 // not really supported yet.. ??
7037 //if(this.waitMsgTarget === true){
7038 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7039 //}else if(this.waitMsgTarget){
7040 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7041 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7043 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7049 afterAction : function(action, success){
7050 this.activeAction = null;
7051 var o = action.options;
7053 //if(this.waitMsgTarget === true){
7055 //}else if(this.waitMsgTarget){
7056 // this.waitMsgTarget.unmask();
7058 // Roo.MessageBox.updateProgress(1);
7059 // Roo.MessageBox.hide();
7066 Roo.callback(o.success, o.scope, [this, action]);
7067 this.fireEvent('actioncomplete', this, action);
7071 // failure condition..
7072 // we have a scenario where updates need confirming.
7073 // eg. if a locking scenario exists..
7074 // we look for { errors : { needs_confirm : true }} in the response.
7076 (typeof(action.result) != 'undefined') &&
7077 (typeof(action.result.errors) != 'undefined') &&
7078 (typeof(action.result.errors.needs_confirm) != 'undefined')
7081 Roo.log("not supported yet");
7084 Roo.MessageBox.confirm(
7085 "Change requires confirmation",
7086 action.result.errorMsg,
7091 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7101 Roo.callback(o.failure, o.scope, [this, action]);
7102 // show an error message if no failed handler is set..
7103 if (!this.hasListener('actionfailed')) {
7104 Roo.log("need to add dialog support");
7106 Roo.MessageBox.alert("Error",
7107 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7108 action.result.errorMsg :
7109 "Saving Failed, please check your entries or try again"
7114 this.fireEvent('actionfailed', this, action);
7119 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7120 * @param {String} id The value to search for
7123 findField : function(id){
7124 var items = this.getItems();
7125 var field = items.get(id);
7127 items.each(function(f){
7128 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7135 return field || null;
7138 * Mark fields in this form invalid in bulk.
7139 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7140 * @return {BasicForm} this
7142 markInvalid : function(errors){
7143 if(errors instanceof Array){
7144 for(var i = 0, len = errors.length; i < len; i++){
7145 var fieldError = errors[i];
7146 var f = this.findField(fieldError.id);
7148 f.markInvalid(fieldError.msg);
7154 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7155 field.markInvalid(errors[id]);
7159 //Roo.each(this.childForms || [], function (f) {
7160 // f.markInvalid(errors);
7167 * Set values for fields in this form in bulk.
7168 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7169 * @return {BasicForm} this
7171 setValues : function(values){
7172 if(values instanceof Array){ // array of objects
7173 for(var i = 0, len = values.length; i < len; i++){
7175 var f = this.findField(v.id);
7177 f.setValue(v.value);
7178 if(this.trackResetOnLoad){
7179 f.originalValue = f.getValue();
7183 }else{ // object hash
7186 if(typeof values[id] != 'function' && (field = this.findField(id))){
7188 if (field.setFromData &&
7190 field.displayField &&
7191 // combos' with local stores can
7192 // be queried via setValue()
7193 // to set their value..
7194 (field.store && !field.store.isLocal)
7198 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7199 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7200 field.setFromData(sd);
7203 field.setValue(values[id]);
7207 if(this.trackResetOnLoad){
7208 field.originalValue = field.getValue();
7214 //Roo.each(this.childForms || [], function (f) {
7215 // f.setValues(values);
7222 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7223 * they are returned as an array.
7224 * @param {Boolean} asString
7227 getValues : function(asString){
7228 //if (this.childForms) {
7229 // copy values from the child forms
7230 // Roo.each(this.childForms, function (f) {
7231 // this.setValues(f.getValues());
7237 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7238 if(asString === true){
7241 return Roo.urlDecode(fs);
7245 * Returns the fields in this form as an object with key/value pairs.
7246 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7249 getFieldValues : function(with_hidden)
7251 var items = this.getItems();
7253 items.each(function(f){
7257 var v = f.getValue();
7258 if (f.inputType =='radio') {
7259 if (typeof(ret[f.getName()]) == 'undefined') {
7260 ret[f.getName()] = ''; // empty..
7263 if (!f.el.dom.checked) {
7271 // not sure if this supported any more..
7272 if ((typeof(v) == 'object') && f.getRawValue) {
7273 v = f.getRawValue() ; // dates..
7275 // combo boxes where name != hiddenName...
7276 if (f.name != f.getName()) {
7277 ret[f.name] = f.getRawValue();
7279 ret[f.getName()] = v;
7286 * Clears all invalid messages in this form.
7287 * @return {BasicForm} this
7289 clearInvalid : function(){
7290 var items = this.getItems();
7292 items.each(function(f){
7303 * @return {BasicForm} this
7306 var items = this.getItems();
7307 items.each(function(f){
7311 Roo.each(this.childForms || [], function (f) {
7318 getItems : function()
7320 var r=new Roo.util.MixedCollection(false, function(o){
7321 return o.id || (o.id = Roo.id());
7323 var iter = function(el) {
7330 Roo.each(el.items,function(e) {
7350 * Ext JS Library 1.1.1
7351 * Copyright(c) 2006-2007, Ext JS, LLC.
7353 * Originally Released Under LGPL - original licence link has changed is not relivant.
7356 * <script type="text/javascript">
7359 * @class Roo.form.VTypes
7360 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7363 Roo.form.VTypes = function(){
7364 // closure these in so they are only created once.
7365 var alpha = /^[a-zA-Z_]+$/;
7366 var alphanum = /^[a-zA-Z0-9_]+$/;
7367 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7368 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7370 // All these messages and functions are configurable
7373 * The function used to validate email addresses
7374 * @param {String} value The email address
7376 'email' : function(v){
7377 return email.test(v);
7380 * The error text to display when the email validation function returns false
7383 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7385 * The keystroke filter mask to be applied on email input
7388 'emailMask' : /[a-z0-9_\.\-@]/i,
7391 * The function used to validate URLs
7392 * @param {String} value The URL
7394 'url' : function(v){
7398 * The error text to display when the url validation function returns false
7401 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7404 * The function used to validate alpha values
7405 * @param {String} value The value
7407 'alpha' : function(v){
7408 return alpha.test(v);
7411 * The error text to display when the alpha validation function returns false
7414 'alphaText' : 'This field should only contain letters and _',
7416 * The keystroke filter mask to be applied on alpha input
7419 'alphaMask' : /[a-z_]/i,
7422 * The function used to validate alphanumeric values
7423 * @param {String} value The value
7425 'alphanum' : function(v){
7426 return alphanum.test(v);
7429 * The error text to display when the alphanumeric validation function returns false
7432 'alphanumText' : 'This field should only contain letters, numbers and _',
7434 * The keystroke filter mask to be applied on alphanumeric input
7437 'alphanumMask' : /[a-z0-9_]/i
7447 * @class Roo.bootstrap.Input
7448 * @extends Roo.bootstrap.Component
7449 * Bootstrap Input class
7450 * @cfg {Boolean} disabled is it disabled
7451 * @cfg {String} fieldLabel - the label associated
7452 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7453 * @cfg {String} name name of the input
7454 * @cfg {string} fieldLabel - the label associated
7455 * @cfg {string} inputType - input / file submit ...
7456 * @cfg {string} placeholder - placeholder to put in text.
7457 * @cfg {string} before - input group add on before
7458 * @cfg {string} after - input group add on after
7459 * @cfg {string} size - (lg|sm) or leave empty..
7460 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7461 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7462 * @cfg {Number} md colspan out of 12 for computer-sized screens
7463 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7464 * @cfg {string} value default value of the input
7465 * @cfg {Number} labelWidth set the width of label (0-12)
7466 * @cfg {String} labelAlign (top|left)
7467 * @cfg {Boolean} readOnly Specifies that the field should be read-only
7468 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7470 * @cfg {String} align (left|center|right) Default left
7475 * Create a new Input
7476 * @param {Object} config The config object
7479 Roo.bootstrap.Input = function(config){
7480 Roo.bootstrap.Input.superclass.constructor.call(this, config);
7485 * Fires when this field receives input focus.
7486 * @param {Roo.form.Field} this
7491 * Fires when this field loses input focus.
7492 * @param {Roo.form.Field} this
7497 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
7498 * {@link Roo.EventObject#getKey} to determine which key was pressed.
7499 * @param {Roo.form.Field} this
7500 * @param {Roo.EventObject} e The event object
7505 * Fires just before the field blurs if the field value has changed.
7506 * @param {Roo.form.Field} this
7507 * @param {Mixed} newValue The new value
7508 * @param {Mixed} oldValue The original value
7513 * Fires after the field has been marked as invalid.
7514 * @param {Roo.form.Field} this
7515 * @param {String} msg The validation message
7520 * Fires after the field has been validated with no errors.
7521 * @param {Roo.form.Field} this
7526 * Fires after the key up
7527 * @param {Roo.form.Field} this
7528 * @param {Roo.EventObject} e The event Object
7534 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
7536 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7537 automatic validation (defaults to "keyup").
7539 validationEvent : "keyup",
7541 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7543 validateOnBlur : true,
7545 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7547 validationDelay : 250,
7549 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7551 focusClass : "x-form-focus", // not needed???
7555 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7557 invalidClass : "has-warning",
7560 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7562 validClass : "has-success",
7565 * @cfg {Boolean} hasFeedback (true|false) default true
7570 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7572 invalidFeedbackClass : "glyphicon-warning-sign",
7575 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7577 validFeedbackClass : "glyphicon-ok",
7580 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7582 selectOnFocus : false,
7585 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7589 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7594 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7596 disableKeyFilter : false,
7599 * @cfg {Boolean} disabled True to disable the field (defaults to false).
7603 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7607 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7609 blankText : "This field is required",
7612 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7616 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7618 maxLength : Number.MAX_VALUE,
7620 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7622 minLengthText : "The minimum length for this field is {0}",
7624 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7626 maxLengthText : "The maximum length for this field is {0}",
7630 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7631 * If available, this function will be called only after the basic validators all return true, and will be passed the
7632 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7636 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7637 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7638 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
7642 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7646 autocomplete: false,
7665 formatedValue : false,
7667 parentLabelAlign : function()
7670 while (parent.parent()) {
7671 parent = parent.parent();
7672 if (typeof(parent.labelAlign) !='undefined') {
7673 return parent.labelAlign;
7680 getAutoCreate : function(){
7682 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7688 if(this.inputType != 'hidden'){
7689 cfg.cls = 'form-group' //input-group
7695 type : this.inputType,
7697 cls : 'form-control',
7698 placeholder : this.placeholder || '',
7699 autocomplete : this.autocomplete || 'new-password'
7704 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7707 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7708 input.maxLength = this.maxLength;
7711 if (this.disabled) {
7712 input.disabled=true;
7715 if (this.readOnly) {
7716 input.readonly=true;
7720 input.name = this.name;
7723 input.cls += ' input-' + this.size;
7726 ['xs','sm','md','lg'].map(function(size){
7727 if (settings[size]) {
7728 cfg.cls += ' col-' + size + '-' + settings[size];
7732 var inputblock = input;
7736 cls: 'glyphicon form-control-feedback'
7739 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7742 cls : 'has-feedback',
7750 if (this.before || this.after) {
7753 cls : 'input-group',
7757 if (this.before && typeof(this.before) == 'string') {
7759 inputblock.cn.push({
7761 cls : 'roo-input-before input-group-addon',
7765 if (this.before && typeof(this.before) == 'object') {
7766 this.before = Roo.factory(this.before);
7767 Roo.log(this.before);
7768 inputblock.cn.push({
7770 cls : 'roo-input-before input-group-' +
7771 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7775 inputblock.cn.push(input);
7777 if (this.after && typeof(this.after) == 'string') {
7778 inputblock.cn.push({
7780 cls : 'roo-input-after input-group-addon',
7784 if (this.after && typeof(this.after) == 'object') {
7785 this.after = Roo.factory(this.after);
7786 Roo.log(this.after);
7787 inputblock.cn.push({
7789 cls : 'roo-input-after input-group-' +
7790 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7794 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7795 inputblock.cls += ' has-feedback';
7796 inputblock.cn.push(feedback);
7800 if (align ==='left' && this.fieldLabel.length) {
7801 Roo.log("left and has label");
7807 cls : 'control-label col-sm-' + this.labelWidth,
7808 html : this.fieldLabel
7812 cls : "col-sm-" + (12 - this.labelWidth),
7819 } else if ( this.fieldLabel.length) {
7825 //cls : 'input-group-addon',
7826 html : this.fieldLabel
7836 Roo.log(" no label && no align");
7845 Roo.log('input-parentType: ' + this.parentType);
7847 if (this.parentType === 'Navbar' && this.parent().bar) {
7848 cfg.cls += ' navbar-form';
7856 * return the real input element.
7858 inputEl: function ()
7860 return this.el.select('input.form-control',true).first();
7863 tooltipEl : function()
7865 return this.inputEl();
7868 setDisabled : function(v)
7870 var i = this.inputEl().dom;
7872 i.removeAttribute('disabled');
7876 i.setAttribute('disabled','true');
7878 initEvents : function()
7881 this.inputEl().on("keydown" , this.fireKey, this);
7882 this.inputEl().on("focus", this.onFocus, this);
7883 this.inputEl().on("blur", this.onBlur, this);
7885 this.inputEl().relayEvent('keyup', this);
7887 // reference to original value for reset
7888 this.originalValue = this.getValue();
7889 //Roo.form.TextField.superclass.initEvents.call(this);
7890 if(this.validationEvent == 'keyup'){
7891 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7892 this.inputEl().on('keyup', this.filterValidation, this);
7894 else if(this.validationEvent !== false){
7895 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7898 if(this.selectOnFocus){
7899 this.on("focus", this.preFocus, this);
7902 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7903 this.inputEl().on("keypress", this.filterKeys, this);
7906 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
7907 this.el.on("click", this.autoSize, this);
7910 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7911 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7914 if (typeof(this.before) == 'object') {
7915 this.before.render(this.el.select('.roo-input-before',true).first());
7917 if (typeof(this.after) == 'object') {
7918 this.after.render(this.el.select('.roo-input-after',true).first());
7923 filterValidation : function(e){
7924 if(!e.isNavKeyPress()){
7925 this.validationTask.delay(this.validationDelay);
7929 * Validates the field value
7930 * @return {Boolean} True if the value is valid, else false
7932 validate : function(){
7933 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7934 if(this.disabled || this.validateValue(this.getRawValue())){
7945 * Validates a value according to the field's validation rules and marks the field as invalid
7946 * if the validation fails
7947 * @param {Mixed} value The value to validate
7948 * @return {Boolean} True if the value is valid, else false
7950 validateValue : function(value){
7951 if(value.length < 1) { // if it's blank
7952 if(this.allowBlank){
7958 if(value.length < this.minLength){
7961 if(value.length > this.maxLength){
7965 var vt = Roo.form.VTypes;
7966 if(!vt[this.vtype](value, this)){
7970 if(typeof this.validator == "function"){
7971 var msg = this.validator(value);
7977 if(this.regex && !this.regex.test(value)){
7987 fireKey : function(e){
7988 //Roo.log('field ' + e.getKey());
7989 if(e.isNavKeyPress()){
7990 this.fireEvent("specialkey", this, e);
7993 focus : function (selectText){
7995 this.inputEl().focus();
7996 if(selectText === true){
7997 this.inputEl().dom.select();
8003 onFocus : function(){
8004 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8005 // this.el.addClass(this.focusClass);
8008 this.hasFocus = true;
8009 this.startValue = this.getValue();
8010 this.fireEvent("focus", this);
8014 beforeBlur : Roo.emptyFn,
8018 onBlur : function(){
8020 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8021 //this.el.removeClass(this.focusClass);
8023 this.hasFocus = false;
8024 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8027 var v = this.getValue();
8028 if(String(v) !== String(this.startValue)){
8029 this.fireEvent('change', this, v, this.startValue);
8031 this.fireEvent("blur", this);
8035 * Resets the current field value to the originally loaded value and clears any validation messages
8038 this.setValue(this.originalValue);
8042 * Returns the name of the field
8043 * @return {Mixed} name The name field
8045 getName: function(){
8049 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8050 * @return {Mixed} value The field value
8052 getValue : function(){
8054 var v = this.inputEl().getValue();
8059 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8060 * @return {Mixed} value The field value
8062 getRawValue : function(){
8063 var v = this.inputEl().getValue();
8069 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8070 * @param {Mixed} value The value to set
8072 setRawValue : function(v){
8073 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8076 selectText : function(start, end){
8077 var v = this.getRawValue();
8079 start = start === undefined ? 0 : start;
8080 end = end === undefined ? v.length : end;
8081 var d = this.inputEl().dom;
8082 if(d.setSelectionRange){
8083 d.setSelectionRange(start, end);
8084 }else if(d.createTextRange){
8085 var range = d.createTextRange();
8086 range.moveStart("character", start);
8087 range.moveEnd("character", v.length-end);
8094 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8095 * @param {Mixed} value The value to set
8097 setValue : function(v){
8100 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8106 processValue : function(value){
8107 if(this.stripCharsRe){
8108 var newValue = value.replace(this.stripCharsRe, '');
8109 if(newValue !== value){
8110 this.setRawValue(newValue);
8117 preFocus : function(){
8119 if(this.selectOnFocus){
8120 this.inputEl().dom.select();
8123 filterKeys : function(e){
8125 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8128 var c = e.getCharCode(), cc = String.fromCharCode(c);
8129 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8132 if(!this.maskRe.test(cc)){
8137 * Clear any invalid styles/messages for this field
8139 clearInvalid : function(){
8141 if(!this.el || this.preventMark){ // not rendered
8144 this.el.removeClass(this.invalidClass);
8146 this.fireEvent('valid', this);
8150 * Mark this field as valid
8152 markValid : function(){
8153 if(!this.el || this.preventMark){ // not rendered
8157 this.el.removeClass([this.invalidClass, this.validClass]);
8159 if(this.disabled || this.allowBlank){
8163 this.el.addClass(this.validClass);
8165 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && this.getValue().length){
8167 var feedback = this.el.select('.form-control-feedback', true).first();
8170 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8171 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8176 this.fireEvent('valid', this);
8180 * Mark this field as invalid
8181 * @param {String} msg The validation message
8183 markInvalid : function(msg){
8184 if(!this.el || this.preventMark){ // not rendered
8188 this.el.removeClass([this.invalidClass, this.validClass]);
8190 if(this.disabled || this.allowBlank){
8194 this.el.addClass(this.invalidClass);
8196 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8198 var feedback = this.el.select('.form-control-feedback', true).first();
8201 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8203 if(this.getValue().length){
8204 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8211 this.fireEvent('invalid', this, msg);
8214 SafariOnKeyDown : function(event)
8216 // this is a workaround for a password hang bug on chrome/ webkit.
8218 var isSelectAll = false;
8220 if(this.inputEl().dom.selectionEnd > 0){
8221 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8223 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8224 event.preventDefault();
8229 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8231 event.preventDefault();
8232 // this is very hacky as keydown always get's upper case.
8234 var cc = String.fromCharCode(event.getCharCode());
8235 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8239 adjustWidth : function(tag, w){
8240 tag = tag.toLowerCase();
8241 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8242 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8246 if(tag == 'textarea'){
8249 }else if(Roo.isOpera){
8253 if(tag == 'textarea'){
8272 * @class Roo.bootstrap.TextArea
8273 * @extends Roo.bootstrap.Input
8274 * Bootstrap TextArea class
8275 * @cfg {Number} cols Specifies the visible width of a text area
8276 * @cfg {Number} rows Specifies the visible number of lines in a text area
8277 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8278 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8279 * @cfg {string} html text
8282 * Create a new TextArea
8283 * @param {Object} config The config object
8286 Roo.bootstrap.TextArea = function(config){
8287 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8291 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8301 getAutoCreate : function(){
8303 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8314 value : this.value || '',
8315 html: this.html || '',
8316 cls : 'form-control',
8317 placeholder : this.placeholder || ''
8321 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8322 input.maxLength = this.maxLength;
8326 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8330 input.cols = this.cols;
8333 if (this.readOnly) {
8334 input.readonly = true;
8338 input.name = this.name;
8342 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8346 ['xs','sm','md','lg'].map(function(size){
8347 if (settings[size]) {
8348 cfg.cls += ' col-' + size + '-' + settings[size];
8352 var inputblock = input;
8354 if(this.hasFeedback && !this.allowBlank){
8358 cls: 'glyphicon form-control-feedback'
8362 cls : 'has-feedback',
8371 if (this.before || this.after) {
8374 cls : 'input-group',
8378 inputblock.cn.push({
8380 cls : 'input-group-addon',
8385 inputblock.cn.push(input);
8387 if(this.hasFeedback && !this.allowBlank){
8388 inputblock.cls += ' has-feedback';
8389 inputblock.cn.push(feedback);
8393 inputblock.cn.push({
8395 cls : 'input-group-addon',
8402 if (align ==='left' && this.fieldLabel.length) {
8403 Roo.log("left and has label");
8409 cls : 'control-label col-sm-' + this.labelWidth,
8410 html : this.fieldLabel
8414 cls : "col-sm-" + (12 - this.labelWidth),
8421 } else if ( this.fieldLabel.length) {
8427 //cls : 'input-group-addon',
8428 html : this.fieldLabel
8438 Roo.log(" no label && no align");
8448 if (this.disabled) {
8449 input.disabled=true;
8456 * return the real textarea element.
8458 inputEl: function ()
8460 return this.el.select('textarea.form-control',true).first();
8468 * trigger field - base class for combo..
8473 * @class Roo.bootstrap.TriggerField
8474 * @extends Roo.bootstrap.Input
8475 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8476 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8477 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8478 * for which you can provide a custom implementation. For example:
8480 var trigger = new Roo.bootstrap.TriggerField();
8481 trigger.onTriggerClick = myTriggerFn;
8482 trigger.applyTo('my-field');
8485 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8486 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8487 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
8488 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8489 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8492 * Create a new TriggerField.
8493 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8494 * to the base TextField)
8496 Roo.bootstrap.TriggerField = function(config){
8497 this.mimicing = false;
8498 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8501 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
8503 * @cfg {String} triggerClass A CSS class to apply to the trigger
8506 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8511 * @cfg {Boolean} removable (true|false) special filter default false
8515 /** @cfg {Boolean} grow @hide */
8516 /** @cfg {Number} growMin @hide */
8517 /** @cfg {Number} growMax @hide */
8523 autoSize: Roo.emptyFn,
8530 actionMode : 'wrap',
8535 getAutoCreate : function(){
8537 var align = this.labelAlign || this.parentLabelAlign();
8542 cls: 'form-group' //input-group
8549 type : this.inputType,
8550 cls : 'form-control',
8551 autocomplete: 'new-password',
8552 placeholder : this.placeholder || ''
8556 input.name = this.name;
8559 input.cls += ' input-' + this.size;
8562 if (this.disabled) {
8563 input.disabled=true;
8566 var inputblock = input;
8568 if(this.hasFeedback && !this.allowBlank){
8572 cls: 'glyphicon form-control-feedback'
8575 if(this.removable && !this.editable && !this.tickable){
8577 cls : 'has-feedback',
8583 cls : 'roo-combo-removable-btn close'
8590 cls : 'has-feedback',
8599 if(this.removable && !this.editable && !this.tickable){
8601 cls : 'roo-removable',
8607 cls : 'roo-combo-removable-btn close'
8614 if (this.before || this.after) {
8617 cls : 'input-group',
8621 inputblock.cn.push({
8623 cls : 'input-group-addon',
8628 inputblock.cn.push(input);
8630 if(this.hasFeedback && !this.allowBlank){
8631 inputblock.cls += ' has-feedback';
8632 inputblock.cn.push(feedback);
8636 inputblock.cn.push({
8638 cls : 'input-group-addon',
8651 cls: 'form-hidden-field'
8659 Roo.log('multiple');
8667 cls: 'form-hidden-field'
8671 cls: 'select2-choices',
8675 cls: 'select2-search-field',
8688 cls: 'select2-container input-group',
8693 // cls: 'typeahead typeahead-long dropdown-menu',
8694 // style: 'display:none'
8699 if(!this.multiple && this.showToggleBtn){
8705 if (this.caret != false) {
8708 cls: 'fa fa-' + this.caret
8715 cls : 'input-group-addon btn dropdown-toggle',
8720 cls: 'combobox-clear',
8734 combobox.cls += ' select2-container-multi';
8737 if (align ==='left' && this.fieldLabel.length) {
8739 Roo.log("left and has label");
8745 cls : 'control-label col-sm-' + this.labelWidth,
8746 html : this.fieldLabel
8750 cls : "col-sm-" + (12 - this.labelWidth),
8757 } else if ( this.fieldLabel.length) {
8763 //cls : 'input-group-addon',
8764 html : this.fieldLabel
8774 Roo.log(" no label && no align");
8781 ['xs','sm','md','lg'].map(function(size){
8782 if (settings[size]) {
8783 cfg.cls += ' col-' + size + '-' + settings[size];
8794 onResize : function(w, h){
8795 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8796 // if(typeof w == 'number'){
8797 // var x = w - this.trigger.getWidth();
8798 // this.inputEl().setWidth(this.adjustWidth('input', x));
8799 // this.trigger.setStyle('left', x+'px');
8804 adjustSize : Roo.BoxComponent.prototype.adjustSize,
8807 getResizeEl : function(){
8808 return this.inputEl();
8812 getPositionEl : function(){
8813 return this.inputEl();
8817 alignErrorIcon : function(){
8818 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8822 initEvents : function(){
8826 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8827 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8828 if(!this.multiple && this.showToggleBtn){
8829 this.trigger = this.el.select('span.dropdown-toggle',true).first();
8830 if(this.hideTrigger){
8831 this.trigger.setDisplayed(false);
8833 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8837 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8840 if(this.removable && !this.editable && !this.tickable){
8841 var close = this.closeTriggerEl();
8844 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
8845 close.on('click', this.removeBtnClick, this, close);
8849 //this.trigger.addClassOnOver('x-form-trigger-over');
8850 //this.trigger.addClassOnClick('x-form-trigger-click');
8853 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8857 closeTriggerEl : function()
8859 var close = this.el.select('.roo-combo-removable-btn', true).first();
8860 return close ? close : false;
8863 removeBtnClick : function(e, h, el)
8867 if(this.fireEvent("remove", this) !== false){
8872 createList : function()
8874 this.list = Roo.get(document.body).createChild({
8876 cls: 'typeahead typeahead-long dropdown-menu',
8877 style: 'display:none'
8880 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8885 initTrigger : function(){
8890 onDestroy : function(){
8892 this.trigger.removeAllListeners();
8893 // this.trigger.remove();
8896 // this.wrap.remove();
8898 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8902 onFocus : function(){
8903 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8906 this.wrap.addClass('x-trigger-wrap-focus');
8907 this.mimicing = true;
8908 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8909 if(this.monitorTab){
8910 this.el.on("keydown", this.checkTab, this);
8917 checkTab : function(e){
8918 if(e.getKey() == e.TAB){
8924 onBlur : function(){
8929 mimicBlur : function(e, t){
8931 if(!this.wrap.contains(t) && this.validateBlur()){
8938 triggerBlur : function(){
8939 this.mimicing = false;
8940 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8941 if(this.monitorTab){
8942 this.el.un("keydown", this.checkTab, this);
8944 //this.wrap.removeClass('x-trigger-wrap-focus');
8945 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8949 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8950 validateBlur : function(e, t){
8955 onDisable : function(){
8956 this.inputEl().dom.disabled = true;
8957 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8959 // this.wrap.addClass('x-item-disabled');
8964 onEnable : function(){
8965 this.inputEl().dom.disabled = false;
8966 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8968 // this.el.removeClass('x-item-disabled');
8973 onShow : function(){
8974 var ae = this.getActionEl();
8977 ae.dom.style.display = '';
8978 ae.dom.style.visibility = 'visible';
8984 onHide : function(){
8985 var ae = this.getActionEl();
8986 ae.dom.style.display = 'none';
8990 * The function that should handle the trigger's click event. This method does nothing by default until overridden
8991 * by an implementing function.
8993 * @param {EventObject} e
8995 onTriggerClick : Roo.emptyFn
8999 * Ext JS Library 1.1.1
9000 * Copyright(c) 2006-2007, Ext JS, LLC.
9002 * Originally Released Under LGPL - original licence link has changed is not relivant.
9005 * <script type="text/javascript">
9010 * @class Roo.data.SortTypes
9012 * Defines the default sorting (casting?) comparison functions used when sorting data.
9014 Roo.data.SortTypes = {
9016 * Default sort that does nothing
9017 * @param {Mixed} s The value being converted
9018 * @return {Mixed} The comparison value
9025 * The regular expression used to strip tags
9029 stripTagsRE : /<\/?[^>]+>/gi,
9032 * Strips all HTML tags to sort on text only
9033 * @param {Mixed} s The value being converted
9034 * @return {String} The comparison value
9036 asText : function(s){
9037 return String(s).replace(this.stripTagsRE, "");
9041 * Strips all HTML tags to sort on text only - Case insensitive
9042 * @param {Mixed} s The value being converted
9043 * @return {String} The comparison value
9045 asUCText : function(s){
9046 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9050 * Case insensitive string
9051 * @param {Mixed} s The value being converted
9052 * @return {String} The comparison value
9054 asUCString : function(s) {
9055 return String(s).toUpperCase();
9060 * @param {Mixed} s The value being converted
9061 * @return {Number} The comparison value
9063 asDate : function(s) {
9067 if(s instanceof Date){
9070 return Date.parse(String(s));
9075 * @param {Mixed} s The value being converted
9076 * @return {Float} The comparison value
9078 asFloat : function(s) {
9079 var val = parseFloat(String(s).replace(/,/g, ""));
9080 if(isNaN(val)) val = 0;
9086 * @param {Mixed} s The value being converted
9087 * @return {Number} The comparison value
9089 asInt : function(s) {
9090 var val = parseInt(String(s).replace(/,/g, ""));
9091 if(isNaN(val)) val = 0;
9096 * Ext JS Library 1.1.1
9097 * Copyright(c) 2006-2007, Ext JS, LLC.
9099 * Originally Released Under LGPL - original licence link has changed is not relivant.
9102 * <script type="text/javascript">
9106 * @class Roo.data.Record
9107 * Instances of this class encapsulate both record <em>definition</em> information, and record
9108 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9109 * to access Records cached in an {@link Roo.data.Store} object.<br>
9111 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9112 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9115 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9117 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9118 * {@link #create}. The parameters are the same.
9119 * @param {Array} data An associative Array of data values keyed by the field name.
9120 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9121 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9122 * not specified an integer id is generated.
9124 Roo.data.Record = function(data, id){
9125 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9130 * Generate a constructor for a specific record layout.
9131 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9132 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9133 * Each field definition object may contain the following properties: <ul>
9134 * <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,
9135 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9136 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9137 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9138 * is being used, then this is a string containing the javascript expression to reference the data relative to
9139 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9140 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9141 * this may be omitted.</p></li>
9142 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9143 * <ul><li>auto (Default, implies no conversion)</li>
9148 * <li>date</li></ul></p></li>
9149 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9150 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9151 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9152 * by the Reader into an object that will be stored in the Record. It is passed the
9153 * following parameters:<ul>
9154 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9156 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9158 * <br>usage:<br><pre><code>
9159 var TopicRecord = Roo.data.Record.create(
9160 {name: 'title', mapping: 'topic_title'},
9161 {name: 'author', mapping: 'username'},
9162 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9163 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9164 {name: 'lastPoster', mapping: 'user2'},
9165 {name: 'excerpt', mapping: 'post_text'}
9168 var myNewRecord = new TopicRecord({
9169 title: 'Do my job please',
9172 lastPost: new Date(),
9173 lastPoster: 'Animal',
9174 excerpt: 'No way dude!'
9176 myStore.add(myNewRecord);
9181 Roo.data.Record.create = function(o){
9183 f.superclass.constructor.apply(this, arguments);
9185 Roo.extend(f, Roo.data.Record);
9186 var p = f.prototype;
9187 p.fields = new Roo.util.MixedCollection(false, function(field){
9190 for(var i = 0, len = o.length; i < len; i++){
9191 p.fields.add(new Roo.data.Field(o[i]));
9193 f.getField = function(name){
9194 return p.fields.get(name);
9199 Roo.data.Record.AUTO_ID = 1000;
9200 Roo.data.Record.EDIT = 'edit';
9201 Roo.data.Record.REJECT = 'reject';
9202 Roo.data.Record.COMMIT = 'commit';
9204 Roo.data.Record.prototype = {
9206 * Readonly flag - true if this record has been modified.
9215 join : function(store){
9220 * Set the named field to the specified value.
9221 * @param {String} name The name of the field to set.
9222 * @param {Object} value The value to set the field to.
9224 set : function(name, value){
9225 if(this.data[name] == value){
9232 if(typeof this.modified[name] == 'undefined'){
9233 this.modified[name] = this.data[name];
9235 this.data[name] = value;
9236 if(!this.editing && this.store){
9237 this.store.afterEdit(this);
9242 * Get the value of the named field.
9243 * @param {String} name The name of the field to get the value of.
9244 * @return {Object} The value of the field.
9246 get : function(name){
9247 return this.data[name];
9251 beginEdit : function(){
9252 this.editing = true;
9257 cancelEdit : function(){
9258 this.editing = false;
9259 delete this.modified;
9263 endEdit : function(){
9264 this.editing = false;
9265 if(this.dirty && this.store){
9266 this.store.afterEdit(this);
9271 * Usually called by the {@link Roo.data.Store} which owns the Record.
9272 * Rejects all changes made to the Record since either creation, or the last commit operation.
9273 * Modified fields are reverted to their original values.
9275 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9276 * of reject operations.
9278 reject : function(){
9279 var m = this.modified;
9281 if(typeof m[n] != "function"){
9282 this.data[n] = m[n];
9286 delete this.modified;
9287 this.editing = false;
9289 this.store.afterReject(this);
9294 * Usually called by the {@link Roo.data.Store} which owns the Record.
9295 * Commits all changes made to the Record since either creation, or the last commit operation.
9297 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9298 * of commit operations.
9300 commit : function(){
9302 delete this.modified;
9303 this.editing = false;
9305 this.store.afterCommit(this);
9310 hasError : function(){
9311 return this.error != null;
9315 clearError : function(){
9320 * Creates a copy of this record.
9321 * @param {String} id (optional) A new record id if you don't want to use this record's id
9324 copy : function(newId) {
9325 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9329 * Ext JS Library 1.1.1
9330 * Copyright(c) 2006-2007, Ext JS, LLC.
9332 * Originally Released Under LGPL - original licence link has changed is not relivant.
9335 * <script type="text/javascript">
9341 * @class Roo.data.Store
9342 * @extends Roo.util.Observable
9343 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9344 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9346 * 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
9347 * has no knowledge of the format of the data returned by the Proxy.<br>
9349 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9350 * instances from the data object. These records are cached and made available through accessor functions.
9352 * Creates a new Store.
9353 * @param {Object} config A config object containing the objects needed for the Store to access data,
9354 * and read the data into Records.
9356 Roo.data.Store = function(config){
9357 this.data = new Roo.util.MixedCollection(false);
9358 this.data.getKey = function(o){
9361 this.baseParams = {};
9368 "multisort" : "_multisort"
9371 if(config && config.data){
9372 this.inlineData = config.data;
9376 Roo.apply(this, config);
9378 if(this.reader){ // reader passed
9379 this.reader = Roo.factory(this.reader, Roo.data);
9380 this.reader.xmodule = this.xmodule || false;
9381 if(!this.recordType){
9382 this.recordType = this.reader.recordType;
9384 if(this.reader.onMetaChange){
9385 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9389 if(this.recordType){
9390 this.fields = this.recordType.prototype.fields;
9396 * @event datachanged
9397 * Fires when the data cache has changed, and a widget which is using this Store
9398 * as a Record cache should refresh its view.
9399 * @param {Store} this
9404 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9405 * @param {Store} this
9406 * @param {Object} meta The JSON metadata
9411 * Fires when Records have been added to the Store
9412 * @param {Store} this
9413 * @param {Roo.data.Record[]} records The array of Records added
9414 * @param {Number} index The index at which the record(s) were added
9419 * Fires when a Record has been removed from the Store
9420 * @param {Store} this
9421 * @param {Roo.data.Record} record The Record that was removed
9422 * @param {Number} index The index at which the record was removed
9427 * Fires when a Record has been updated
9428 * @param {Store} this
9429 * @param {Roo.data.Record} record The Record that was updated
9430 * @param {String} operation The update operation being performed. Value may be one of:
9432 Roo.data.Record.EDIT
9433 Roo.data.Record.REJECT
9434 Roo.data.Record.COMMIT
9440 * Fires when the data cache has been cleared.
9441 * @param {Store} this
9446 * Fires before a request is made for a new data object. If the beforeload handler returns false
9447 * the load action will be canceled.
9448 * @param {Store} this
9449 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9453 * @event beforeloadadd
9454 * Fires after a new set of Records has been loaded.
9455 * @param {Store} this
9456 * @param {Roo.data.Record[]} records The Records that were loaded
9457 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9459 beforeloadadd : true,
9462 * Fires after a new set of Records has been loaded, before they are added to the store.
9463 * @param {Store} this
9464 * @param {Roo.data.Record[]} records The Records that were loaded
9465 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9466 * @params {Object} return from reader
9470 * @event loadexception
9471 * Fires if an exception occurs in the Proxy during loading.
9472 * Called with the signature of the Proxy's "loadexception" event.
9473 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9476 * @param {Object} return from JsonData.reader() - success, totalRecords, records
9477 * @param {Object} load options
9478 * @param {Object} jsonData from your request (normally this contains the Exception)
9480 loadexception : true
9484 this.proxy = Roo.factory(this.proxy, Roo.data);
9485 this.proxy.xmodule = this.xmodule || false;
9486 this.relayEvents(this.proxy, ["loadexception"]);
9488 this.sortToggle = {};
9489 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9491 Roo.data.Store.superclass.constructor.call(this);
9493 if(this.inlineData){
9494 this.loadData(this.inlineData);
9495 delete this.inlineData;
9499 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9501 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
9502 * without a remote query - used by combo/forms at present.
9506 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9509 * @cfg {Array} data Inline data to be loaded when the store is initialized.
9512 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9513 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9516 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9517 * on any HTTP request
9520 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9523 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9527 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9528 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9533 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9534 * loaded or when a record is removed. (defaults to false).
9536 pruneModifiedRecords : false,
9542 * Add Records to the Store and fires the add event.
9543 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9545 add : function(records){
9546 records = [].concat(records);
9547 for(var i = 0, len = records.length; i < len; i++){
9548 records[i].join(this);
9550 var index = this.data.length;
9551 this.data.addAll(records);
9552 this.fireEvent("add", this, records, index);
9556 * Remove a Record from the Store and fires the remove event.
9557 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9559 remove : function(record){
9560 var index = this.data.indexOf(record);
9561 this.data.removeAt(index);
9562 if(this.pruneModifiedRecords){
9563 this.modified.remove(record);
9565 this.fireEvent("remove", this, record, index);
9569 * Remove all Records from the Store and fires the clear event.
9571 removeAll : function(){
9573 if(this.pruneModifiedRecords){
9576 this.fireEvent("clear", this);
9580 * Inserts Records to the Store at the given index and fires the add event.
9581 * @param {Number} index The start index at which to insert the passed Records.
9582 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9584 insert : function(index, records){
9585 records = [].concat(records);
9586 for(var i = 0, len = records.length; i < len; i++){
9587 this.data.insert(index, records[i]);
9588 records[i].join(this);
9590 this.fireEvent("add", this, records, index);
9594 * Get the index within the cache of the passed Record.
9595 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9596 * @return {Number} The index of the passed Record. Returns -1 if not found.
9598 indexOf : function(record){
9599 return this.data.indexOf(record);
9603 * Get the index within the cache of the Record with the passed id.
9604 * @param {String} id The id of the Record to find.
9605 * @return {Number} The index of the Record. Returns -1 if not found.
9607 indexOfId : function(id){
9608 return this.data.indexOfKey(id);
9612 * Get the Record with the specified id.
9613 * @param {String} id The id of the Record to find.
9614 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9616 getById : function(id){
9617 return this.data.key(id);
9621 * Get the Record at the specified index.
9622 * @param {Number} index The index of the Record to find.
9623 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9625 getAt : function(index){
9626 return this.data.itemAt(index);
9630 * Returns a range of Records between specified indices.
9631 * @param {Number} startIndex (optional) The starting index (defaults to 0)
9632 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9633 * @return {Roo.data.Record[]} An array of Records
9635 getRange : function(start, end){
9636 return this.data.getRange(start, end);
9640 storeOptions : function(o){
9641 o = Roo.apply({}, o);
9644 this.lastOptions = o;
9648 * Loads the Record cache from the configured Proxy using the configured Reader.
9650 * If using remote paging, then the first load call must specify the <em>start</em>
9651 * and <em>limit</em> properties in the options.params property to establish the initial
9652 * position within the dataset, and the number of Records to cache on each read from the Proxy.
9654 * <strong>It is important to note that for remote data sources, loading is asynchronous,
9655 * and this call will return before the new data has been loaded. Perform any post-processing
9656 * in a callback function, or in a "load" event handler.</strong>
9658 * @param {Object} options An object containing properties which control loading options:<ul>
9659 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9660 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9661 * passed the following arguments:<ul>
9662 * <li>r : Roo.data.Record[]</li>
9663 * <li>options: Options object from the load call</li>
9664 * <li>success: Boolean success indicator</li></ul></li>
9665 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9666 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9669 load : function(options){
9670 options = options || {};
9671 if(this.fireEvent("beforeload", this, options) !== false){
9672 this.storeOptions(options);
9673 var p = Roo.apply(options.params || {}, this.baseParams);
9674 // if meta was not loaded from remote source.. try requesting it.
9675 if (!this.reader.metaFromRemote) {
9678 if(this.sortInfo && this.remoteSort){
9679 var pn = this.paramNames;
9680 p[pn["sort"]] = this.sortInfo.field;
9681 p[pn["dir"]] = this.sortInfo.direction;
9683 if (this.multiSort) {
9684 var pn = this.paramNames;
9685 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9688 this.proxy.load(p, this.reader, this.loadRecords, this, options);
9693 * Reloads the Record cache from the configured Proxy using the configured Reader and
9694 * the options from the last load operation performed.
9695 * @param {Object} options (optional) An object containing properties which may override the options
9696 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9697 * the most recently used options are reused).
9699 reload : function(options){
9700 this.load(Roo.applyIf(options||{}, this.lastOptions));
9704 // Called as a callback by the Reader during a load operation.
9705 loadRecords : function(o, options, success){
9706 if(!o || success === false){
9707 if(success !== false){
9708 this.fireEvent("load", this, [], options, o);
9710 if(options.callback){
9711 options.callback.call(options.scope || this, [], options, false);
9715 // if data returned failure - throw an exception.
9716 if (o.success === false) {
9717 // show a message if no listener is registered.
9718 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9719 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9721 // loadmask wil be hooked into this..
9722 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9725 var r = o.records, t = o.totalRecords || r.length;
9727 this.fireEvent("beforeloadadd", this, r, options, o);
9729 if(!options || options.add !== true){
9730 if(this.pruneModifiedRecords){
9733 for(var i = 0, len = r.length; i < len; i++){
9737 this.data = this.snapshot;
9738 delete this.snapshot;
9741 this.data.addAll(r);
9742 this.totalLength = t;
9744 this.fireEvent("datachanged", this);
9746 this.totalLength = Math.max(t, this.data.length+r.length);
9749 this.fireEvent("load", this, r, options, o);
9750 if(options.callback){
9751 options.callback.call(options.scope || this, r, options, true);
9757 * Loads data from a passed data block. A Reader which understands the format of the data
9758 * must have been configured in the constructor.
9759 * @param {Object} data The data block from which to read the Records. The format of the data expected
9760 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9761 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9763 loadData : function(o, append){
9764 var r = this.reader.readRecords(o);
9765 this.loadRecords(r, {add: append}, true);
9769 * Gets the number of cached records.
9771 * <em>If using paging, this may not be the total size of the dataset. If the data object
9772 * used by the Reader contains the dataset size, then the getTotalCount() function returns
9773 * the data set size</em>
9775 getCount : function(){
9776 return this.data.length || 0;
9780 * Gets the total number of records in the dataset as returned by the server.
9782 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9783 * the dataset size</em>
9785 getTotalCount : function(){
9786 return this.totalLength || 0;
9790 * Returns the sort state of the Store as an object with two properties:
9792 field {String} The name of the field by which the Records are sorted
9793 direction {String} The sort order, "ASC" or "DESC"
9796 getSortState : function(){
9797 return this.sortInfo;
9801 applySort : function(){
9802 if(this.sortInfo && !this.remoteSort){
9803 var s = this.sortInfo, f = s.field;
9804 var st = this.fields.get(f).sortType;
9805 var fn = function(r1, r2){
9806 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9807 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9809 this.data.sort(s.direction, fn);
9810 if(this.snapshot && this.snapshot != this.data){
9811 this.snapshot.sort(s.direction, fn);
9817 * Sets the default sort column and order to be used by the next load operation.
9818 * @param {String} fieldName The name of the field to sort by.
9819 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9821 setDefaultSort : function(field, dir){
9822 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9827 * If remote sorting is used, the sort is performed on the server, and the cache is
9828 * reloaded. If local sorting is used, the cache is sorted internally.
9829 * @param {String} fieldName The name of the field to sort by.
9830 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9832 sort : function(fieldName, dir){
9833 var f = this.fields.get(fieldName);
9835 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9837 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9838 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9843 this.sortToggle[f.name] = dir;
9844 this.sortInfo = {field: f.name, direction: dir};
9845 if(!this.remoteSort){
9847 this.fireEvent("datachanged", this);
9849 this.load(this.lastOptions);
9854 * Calls the specified function for each of the Records in the cache.
9855 * @param {Function} fn The function to call. The Record is passed as the first parameter.
9856 * Returning <em>false</em> aborts and exits the iteration.
9857 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9859 each : function(fn, scope){
9860 this.data.each(fn, scope);
9864 * Gets all records modified since the last commit. Modified records are persisted across load operations
9865 * (e.g., during paging).
9866 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9868 getModifiedRecords : function(){
9869 return this.modified;
9873 createFilterFn : function(property, value, anyMatch){
9874 if(!value.exec){ // not a regex
9875 value = String(value);
9876 if(value.length == 0){
9879 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9882 return value.test(r.data[property]);
9887 * Sums the value of <i>property</i> for each record between start and end and returns the result.
9888 * @param {String} property A field on your records
9889 * @param {Number} start The record index to start at (defaults to 0)
9890 * @param {Number} end The last record index to include (defaults to length - 1)
9891 * @return {Number} The sum
9893 sum : function(property, start, end){
9894 var rs = this.data.items, v = 0;
9896 end = (end || end === 0) ? end : rs.length-1;
9898 for(var i = start; i <= end; i++){
9899 v += (rs[i].data[property] || 0);
9905 * Filter the records by a specified property.
9906 * @param {String} field A field on your records
9907 * @param {String/RegExp} value Either a string that the field
9908 * should start with or a RegExp to test against the field
9909 * @param {Boolean} anyMatch True to match any part not just the beginning
9911 filter : function(property, value, anyMatch){
9912 var fn = this.createFilterFn(property, value, anyMatch);
9913 return fn ? this.filterBy(fn) : this.clearFilter();
9917 * Filter by a function. The specified function will be called with each
9918 * record in this data source. If the function returns true the record is included,
9919 * otherwise it is filtered.
9920 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9921 * @param {Object} scope (optional) The scope of the function (defaults to this)
9923 filterBy : function(fn, scope){
9924 this.snapshot = this.snapshot || this.data;
9925 this.data = this.queryBy(fn, scope||this);
9926 this.fireEvent("datachanged", this);
9930 * Query the records by a specified property.
9931 * @param {String} field A field on your records
9932 * @param {String/RegExp} value Either a string that the field
9933 * should start with or a RegExp to test against the field
9934 * @param {Boolean} anyMatch True to match any part not just the beginning
9935 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9937 query : function(property, value, anyMatch){
9938 var fn = this.createFilterFn(property, value, anyMatch);
9939 return fn ? this.queryBy(fn) : this.data.clone();
9943 * Query by a function. The specified function will be called with each
9944 * record in this data source. If the function returns true the record is included
9946 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9947 * @param {Object} scope (optional) The scope of the function (defaults to this)
9948 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9950 queryBy : function(fn, scope){
9951 var data = this.snapshot || this.data;
9952 return data.filterBy(fn, scope||this);
9956 * Collects unique values for a particular dataIndex from this store.
9957 * @param {String} dataIndex The property to collect
9958 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9959 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9960 * @return {Array} An array of the unique values
9962 collect : function(dataIndex, allowNull, bypassFilter){
9963 var d = (bypassFilter === true && this.snapshot) ?
9964 this.snapshot.items : this.data.items;
9965 var v, sv, r = [], l = {};
9966 for(var i = 0, len = d.length; i < len; i++){
9967 v = d[i].data[dataIndex];
9969 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9978 * Revert to a view of the Record cache with no filtering applied.
9979 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9981 clearFilter : function(suppressEvent){
9982 if(this.snapshot && this.snapshot != this.data){
9983 this.data = this.snapshot;
9984 delete this.snapshot;
9985 if(suppressEvent !== true){
9986 this.fireEvent("datachanged", this);
9992 afterEdit : function(record){
9993 if(this.modified.indexOf(record) == -1){
9994 this.modified.push(record);
9996 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10000 afterReject : function(record){
10001 this.modified.remove(record);
10002 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10006 afterCommit : function(record){
10007 this.modified.remove(record);
10008 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10012 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10013 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10015 commitChanges : function(){
10016 var m = this.modified.slice(0);
10017 this.modified = [];
10018 for(var i = 0, len = m.length; i < len; i++){
10024 * Cancel outstanding changes on all changed records.
10026 rejectChanges : function(){
10027 var m = this.modified.slice(0);
10028 this.modified = [];
10029 for(var i = 0, len = m.length; i < len; i++){
10034 onMetaChange : function(meta, rtype, o){
10035 this.recordType = rtype;
10036 this.fields = rtype.prototype.fields;
10037 delete this.snapshot;
10038 this.sortInfo = meta.sortInfo || this.sortInfo;
10039 this.modified = [];
10040 this.fireEvent('metachange', this, this.reader.meta);
10043 moveIndex : function(data, type)
10045 var index = this.indexOf(data);
10047 var newIndex = index + type;
10051 this.insert(newIndex, data);
10056 * Ext JS Library 1.1.1
10057 * Copyright(c) 2006-2007, Ext JS, LLC.
10059 * Originally Released Under LGPL - original licence link has changed is not relivant.
10062 * <script type="text/javascript">
10066 * @class Roo.data.SimpleStore
10067 * @extends Roo.data.Store
10068 * Small helper class to make creating Stores from Array data easier.
10069 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10070 * @cfg {Array} fields An array of field definition objects, or field name strings.
10071 * @cfg {Array} data The multi-dimensional array of data
10073 * @param {Object} config
10075 Roo.data.SimpleStore = function(config){
10076 Roo.data.SimpleStore.superclass.constructor.call(this, {
10078 reader: new Roo.data.ArrayReader({
10081 Roo.data.Record.create(config.fields)
10083 proxy : new Roo.data.MemoryProxy(config.data)
10087 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10089 * Ext JS Library 1.1.1
10090 * Copyright(c) 2006-2007, Ext JS, LLC.
10092 * Originally Released Under LGPL - original licence link has changed is not relivant.
10095 * <script type="text/javascript">
10100 * @extends Roo.data.Store
10101 * @class Roo.data.JsonStore
10102 * Small helper class to make creating Stores for JSON data easier. <br/>
10104 var store = new Roo.data.JsonStore({
10105 url: 'get-images.php',
10107 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10110 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10111 * JsonReader and HttpProxy (unless inline data is provided).</b>
10112 * @cfg {Array} fields An array of field definition objects, or field name strings.
10114 * @param {Object} config
10116 Roo.data.JsonStore = function(c){
10117 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10118 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10119 reader: new Roo.data.JsonReader(c, c.fields)
10122 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10124 * Ext JS Library 1.1.1
10125 * Copyright(c) 2006-2007, Ext JS, LLC.
10127 * Originally Released Under LGPL - original licence link has changed is not relivant.
10130 * <script type="text/javascript">
10134 Roo.data.Field = function(config){
10135 if(typeof config == "string"){
10136 config = {name: config};
10138 Roo.apply(this, config);
10141 this.type = "auto";
10144 var st = Roo.data.SortTypes;
10145 // named sortTypes are supported, here we look them up
10146 if(typeof this.sortType == "string"){
10147 this.sortType = st[this.sortType];
10150 // set default sortType for strings and dates
10151 if(!this.sortType){
10154 this.sortType = st.asUCString;
10157 this.sortType = st.asDate;
10160 this.sortType = st.none;
10165 var stripRe = /[\$,%]/g;
10167 // prebuilt conversion function for this field, instead of
10168 // switching every time we're reading a value
10170 var cv, dateFormat = this.dateFormat;
10175 cv = function(v){ return v; };
10178 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10182 return v !== undefined && v !== null && v !== '' ?
10183 parseInt(String(v).replace(stripRe, ""), 10) : '';
10188 return v !== undefined && v !== null && v !== '' ?
10189 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10194 cv = function(v){ return v === true || v === "true" || v == 1; };
10201 if(v instanceof Date){
10205 if(dateFormat == "timestamp"){
10206 return new Date(v*1000);
10208 return Date.parseDate(v, dateFormat);
10210 var parsed = Date.parse(v);
10211 return parsed ? new Date(parsed) : null;
10220 Roo.data.Field.prototype = {
10228 * Ext JS Library 1.1.1
10229 * Copyright(c) 2006-2007, Ext JS, LLC.
10231 * Originally Released Under LGPL - original licence link has changed is not relivant.
10234 * <script type="text/javascript">
10237 // Base class for reading structured data from a data source. This class is intended to be
10238 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10241 * @class Roo.data.DataReader
10242 * Base class for reading structured data from a data source. This class is intended to be
10243 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10246 Roo.data.DataReader = function(meta, recordType){
10250 this.recordType = recordType instanceof Array ?
10251 Roo.data.Record.create(recordType) : recordType;
10254 Roo.data.DataReader.prototype = {
10256 * Create an empty record
10257 * @param {Object} data (optional) - overlay some values
10258 * @return {Roo.data.Record} record created.
10260 newRow : function(d) {
10262 this.recordType.prototype.fields.each(function(c) {
10264 case 'int' : da[c.name] = 0; break;
10265 case 'date' : da[c.name] = new Date(); break;
10266 case 'float' : da[c.name] = 0.0; break;
10267 case 'boolean' : da[c.name] = false; break;
10268 default : da[c.name] = ""; break;
10272 return new this.recordType(Roo.apply(da, d));
10277 * Ext JS Library 1.1.1
10278 * Copyright(c) 2006-2007, Ext JS, LLC.
10280 * Originally Released Under LGPL - original licence link has changed is not relivant.
10283 * <script type="text/javascript">
10287 * @class Roo.data.DataProxy
10288 * @extends Roo.data.Observable
10289 * This class is an abstract base class for implementations which provide retrieval of
10290 * unformatted data objects.<br>
10292 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10293 * (of the appropriate type which knows how to parse the data object) to provide a block of
10294 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10296 * Custom implementations must implement the load method as described in
10297 * {@link Roo.data.HttpProxy#load}.
10299 Roo.data.DataProxy = function(){
10302 * @event beforeload
10303 * Fires before a network request is made to retrieve a data object.
10304 * @param {Object} This DataProxy object.
10305 * @param {Object} params The params parameter to the load function.
10310 * Fires before the load method's callback is called.
10311 * @param {Object} This DataProxy object.
10312 * @param {Object} o The data object.
10313 * @param {Object} arg The callback argument object passed to the load function.
10317 * @event loadexception
10318 * Fires if an Exception occurs during data retrieval.
10319 * @param {Object} This DataProxy object.
10320 * @param {Object} o The data object.
10321 * @param {Object} arg The callback argument object passed to the load function.
10322 * @param {Object} e The Exception.
10324 loadexception : true
10326 Roo.data.DataProxy.superclass.constructor.call(this);
10329 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10332 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10336 * Ext JS Library 1.1.1
10337 * Copyright(c) 2006-2007, Ext JS, LLC.
10339 * Originally Released Under LGPL - original licence link has changed is not relivant.
10342 * <script type="text/javascript">
10345 * @class Roo.data.MemoryProxy
10346 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10347 * to the Reader when its load method is called.
10349 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10351 Roo.data.MemoryProxy = function(data){
10355 Roo.data.MemoryProxy.superclass.constructor.call(this);
10359 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10361 * Load data from the requested source (in this case an in-memory
10362 * data object passed to the constructor), read the data object into
10363 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10364 * process that block using the passed callback.
10365 * @param {Object} params This parameter is not used by the MemoryProxy class.
10366 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10367 * object into a block of Roo.data.Records.
10368 * @param {Function} callback The function into which to pass the block of Roo.data.records.
10369 * The function must be passed <ul>
10370 * <li>The Record block object</li>
10371 * <li>The "arg" argument from the load function</li>
10372 * <li>A boolean success indicator</li>
10374 * @param {Object} scope The scope in which to call the callback
10375 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10377 load : function(params, reader, callback, scope, arg){
10378 params = params || {};
10381 result = reader.readRecords(this.data);
10383 this.fireEvent("loadexception", this, arg, null, e);
10384 callback.call(scope, null, arg, false);
10387 callback.call(scope, result, arg, true);
10391 update : function(params, records){
10396 * Ext JS Library 1.1.1
10397 * Copyright(c) 2006-2007, Ext JS, LLC.
10399 * Originally Released Under LGPL - original licence link has changed is not relivant.
10402 * <script type="text/javascript">
10405 * @class Roo.data.HttpProxy
10406 * @extends Roo.data.DataProxy
10407 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10408 * configured to reference a certain URL.<br><br>
10410 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10411 * from which the running page was served.<br><br>
10413 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10415 * Be aware that to enable the browser to parse an XML document, the server must set
10416 * the Content-Type header in the HTTP response to "text/xml".
10418 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10419 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
10420 * will be used to make the request.
10422 Roo.data.HttpProxy = function(conn){
10423 Roo.data.HttpProxy.superclass.constructor.call(this);
10424 // is conn a conn config or a real conn?
10426 this.useAjax = !conn || !conn.events;
10430 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10431 // thse are take from connection...
10434 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10437 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10438 * extra parameters to each request made by this object. (defaults to undefined)
10441 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10442 * to each request made by this object. (defaults to undefined)
10445 * @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)
10448 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10451 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10457 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10461 * Return the {@link Roo.data.Connection} object being used by this Proxy.
10462 * @return {Connection} The Connection object. This object may be used to subscribe to events on
10463 * a finer-grained basis than the DataProxy events.
10465 getConnection : function(){
10466 return this.useAjax ? Roo.Ajax : this.conn;
10470 * Load data from the configured {@link Roo.data.Connection}, read the data object into
10471 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10472 * process that block using the passed callback.
10473 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10474 * for the request to the remote server.
10475 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10476 * object into a block of Roo.data.Records.
10477 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10478 * The function must be passed <ul>
10479 * <li>The Record block object</li>
10480 * <li>The "arg" argument from the load function</li>
10481 * <li>A boolean success indicator</li>
10483 * @param {Object} scope The scope in which to call the callback
10484 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10486 load : function(params, reader, callback, scope, arg){
10487 if(this.fireEvent("beforeload", this, params) !== false){
10489 params : params || {},
10491 callback : callback,
10496 callback : this.loadResponse,
10500 Roo.applyIf(o, this.conn);
10501 if(this.activeRequest){
10502 Roo.Ajax.abort(this.activeRequest);
10504 this.activeRequest = Roo.Ajax.request(o);
10506 this.conn.request(o);
10509 callback.call(scope||this, null, arg, false);
10514 loadResponse : function(o, success, response){
10515 delete this.activeRequest;
10517 this.fireEvent("loadexception", this, o, response);
10518 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10523 result = o.reader.read(response);
10525 this.fireEvent("loadexception", this, o, response, e);
10526 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10530 this.fireEvent("load", this, o, o.request.arg);
10531 o.request.callback.call(o.request.scope, result, o.request.arg, true);
10535 update : function(dataSet){
10540 updateResponse : function(dataSet){
10545 * Ext JS Library 1.1.1
10546 * Copyright(c) 2006-2007, Ext JS, LLC.
10548 * Originally Released Under LGPL - original licence link has changed is not relivant.
10551 * <script type="text/javascript">
10555 * @class Roo.data.ScriptTagProxy
10556 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10557 * other than the originating domain of the running page.<br><br>
10559 * <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
10560 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10562 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10563 * source code that is used as the source inside a <script> tag.<br><br>
10565 * In order for the browser to process the returned data, the server must wrap the data object
10566 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10567 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10568 * depending on whether the callback name was passed:
10571 boolean scriptTag = false;
10572 String cb = request.getParameter("callback");
10575 response.setContentType("text/javascript");
10577 response.setContentType("application/x-json");
10579 Writer out = response.getWriter();
10581 out.write(cb + "(");
10583 out.print(dataBlock.toJsonString());
10590 * @param {Object} config A configuration object.
10592 Roo.data.ScriptTagProxy = function(config){
10593 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10594 Roo.apply(this, config);
10595 this.head = document.getElementsByTagName("head")[0];
10598 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10600 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10602 * @cfg {String} url The URL from which to request the data object.
10605 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10609 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10610 * the server the name of the callback function set up by the load call to process the returned data object.
10611 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10612 * javascript output which calls this named function passing the data object as its only parameter.
10614 callbackParam : "callback",
10616 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10617 * name to the request.
10622 * Load data from the configured URL, read the data object into
10623 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10624 * process that block using the passed callback.
10625 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10626 * for the request to the remote server.
10627 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10628 * object into a block of Roo.data.Records.
10629 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10630 * The function must be passed <ul>
10631 * <li>The Record block object</li>
10632 * <li>The "arg" argument from the load function</li>
10633 * <li>A boolean success indicator</li>
10635 * @param {Object} scope The scope in which to call the callback
10636 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10638 load : function(params, reader, callback, scope, arg){
10639 if(this.fireEvent("beforeload", this, params) !== false){
10641 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10643 var url = this.url;
10644 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10646 url += "&_dc=" + (new Date().getTime());
10648 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10651 cb : "stcCallback"+transId,
10652 scriptId : "stcScript"+transId,
10656 callback : callback,
10662 window[trans.cb] = function(o){
10663 conn.handleResponse(o, trans);
10666 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10668 if(this.autoAbort !== false){
10672 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10674 var script = document.createElement("script");
10675 script.setAttribute("src", url);
10676 script.setAttribute("type", "text/javascript");
10677 script.setAttribute("id", trans.scriptId);
10678 this.head.appendChild(script);
10680 this.trans = trans;
10682 callback.call(scope||this, null, arg, false);
10687 isLoading : function(){
10688 return this.trans ? true : false;
10692 * Abort the current server request.
10694 abort : function(){
10695 if(this.isLoading()){
10696 this.destroyTrans(this.trans);
10701 destroyTrans : function(trans, isLoaded){
10702 this.head.removeChild(document.getElementById(trans.scriptId));
10703 clearTimeout(trans.timeoutId);
10705 window[trans.cb] = undefined;
10707 delete window[trans.cb];
10710 // if hasn't been loaded, wait for load to remove it to prevent script error
10711 window[trans.cb] = function(){
10712 window[trans.cb] = undefined;
10714 delete window[trans.cb];
10721 handleResponse : function(o, trans){
10722 this.trans = false;
10723 this.destroyTrans(trans, true);
10726 result = trans.reader.readRecords(o);
10728 this.fireEvent("loadexception", this, o, trans.arg, e);
10729 trans.callback.call(trans.scope||window, null, trans.arg, false);
10732 this.fireEvent("load", this, o, trans.arg);
10733 trans.callback.call(trans.scope||window, result, trans.arg, true);
10737 handleFailure : function(trans){
10738 this.trans = false;
10739 this.destroyTrans(trans, false);
10740 this.fireEvent("loadexception", this, null, trans.arg);
10741 trans.callback.call(trans.scope||window, null, trans.arg, false);
10745 * Ext JS Library 1.1.1
10746 * Copyright(c) 2006-2007, Ext JS, LLC.
10748 * Originally Released Under LGPL - original licence link has changed is not relivant.
10751 * <script type="text/javascript">
10755 * @class Roo.data.JsonReader
10756 * @extends Roo.data.DataReader
10757 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10758 * based on mappings in a provided Roo.data.Record constructor.
10760 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10761 * in the reply previously.
10766 var RecordDef = Roo.data.Record.create([
10767 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
10768 {name: 'occupation'} // This field will use "occupation" as the mapping.
10770 var myReader = new Roo.data.JsonReader({
10771 totalProperty: "results", // The property which contains the total dataset size (optional)
10772 root: "rows", // The property which contains an Array of row objects
10773 id: "id" // The property within each row object that provides an ID for the record (optional)
10777 * This would consume a JSON file like this:
10779 { 'results': 2, 'rows': [
10780 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10781 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10784 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10785 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10786 * paged from the remote server.
10787 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10788 * @cfg {String} root name of the property which contains the Array of row objects.
10789 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10790 * @cfg {Array} fields Array of field definition objects
10792 * Create a new JsonReader
10793 * @param {Object} meta Metadata configuration options
10794 * @param {Object} recordType Either an Array of field definition objects,
10795 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10797 Roo.data.JsonReader = function(meta, recordType){
10800 // set some defaults:
10801 Roo.applyIf(meta, {
10802 totalProperty: 'total',
10803 successProperty : 'success',
10808 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10810 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10813 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
10814 * Used by Store query builder to append _requestMeta to params.
10817 metaFromRemote : false,
10819 * This method is only used by a DataProxy which has retrieved data from a remote server.
10820 * @param {Object} response The XHR object which contains the JSON data in its responseText.
10821 * @return {Object} data A data block which is used by an Roo.data.Store object as
10822 * a cache of Roo.data.Records.
10824 read : function(response){
10825 var json = response.responseText;
10827 var o = /* eval:var:o */ eval("("+json+")");
10829 throw {message: "JsonReader.read: Json object not found"};
10835 this.metaFromRemote = true;
10836 this.meta = o.metaData;
10837 this.recordType = Roo.data.Record.create(o.metaData.fields);
10838 this.onMetaChange(this.meta, this.recordType, o);
10840 return this.readRecords(o);
10843 // private function a store will implement
10844 onMetaChange : function(meta, recordType, o){
10851 simpleAccess: function(obj, subsc) {
10858 getJsonAccessor: function(){
10860 return function(expr) {
10862 return(re.test(expr))
10863 ? new Function("obj", "return obj." + expr)
10868 return Roo.emptyFn;
10873 * Create a data block containing Roo.data.Records from an XML document.
10874 * @param {Object} o An object which contains an Array of row objects in the property specified
10875 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10876 * which contains the total size of the dataset.
10877 * @return {Object} data A data block which is used by an Roo.data.Store object as
10878 * a cache of Roo.data.Records.
10880 readRecords : function(o){
10882 * After any data loads, the raw JSON data is available for further custom processing.
10886 var s = this.meta, Record = this.recordType,
10887 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10889 // Generate extraction functions for the totalProperty, the root, the id, and for each field
10891 if(s.totalProperty) {
10892 this.getTotal = this.getJsonAccessor(s.totalProperty);
10894 if(s.successProperty) {
10895 this.getSuccess = this.getJsonAccessor(s.successProperty);
10897 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10899 var g = this.getJsonAccessor(s.id);
10900 this.getId = function(rec) {
10902 return (r === undefined || r === "") ? null : r;
10905 this.getId = function(){return null;};
10908 for(var jj = 0; jj < fl; jj++){
10910 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10911 this.ef[jj] = this.getJsonAccessor(map);
10915 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10916 if(s.totalProperty){
10917 var vt = parseInt(this.getTotal(o), 10);
10922 if(s.successProperty){
10923 var vs = this.getSuccess(o);
10924 if(vs === false || vs === 'false'){
10929 for(var i = 0; i < c; i++){
10932 var id = this.getId(n);
10933 for(var j = 0; j < fl; j++){
10935 var v = this.ef[j](n);
10937 Roo.log('missing convert for ' + f.name);
10941 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10943 var record = new Record(values, id);
10945 records[i] = record;
10951 totalRecords : totalRecords
10956 * Ext JS Library 1.1.1
10957 * Copyright(c) 2006-2007, Ext JS, LLC.
10959 * Originally Released Under LGPL - original licence link has changed is not relivant.
10962 * <script type="text/javascript">
10966 * @class Roo.data.ArrayReader
10967 * @extends Roo.data.DataReader
10968 * Data reader class to create an Array of Roo.data.Record objects from an Array.
10969 * Each element of that Array represents a row of data fields. The
10970 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10971 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10975 var RecordDef = Roo.data.Record.create([
10976 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
10977 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
10979 var myReader = new Roo.data.ArrayReader({
10980 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
10984 * This would consume an Array like this:
10986 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10988 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10990 * Create a new JsonReader
10991 * @param {Object} meta Metadata configuration options.
10992 * @param {Object} recordType Either an Array of field definition objects
10993 * as specified to {@link Roo.data.Record#create},
10994 * or an {@link Roo.data.Record} object
10995 * created using {@link Roo.data.Record#create}.
10997 Roo.data.ArrayReader = function(meta, recordType){
10998 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11001 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11003 * Create a data block containing Roo.data.Records from an XML document.
11004 * @param {Object} o An Array of row objects which represents the dataset.
11005 * @return {Object} data A data block which is used by an Roo.data.Store object as
11006 * a cache of Roo.data.Records.
11008 readRecords : function(o){
11009 var sid = this.meta ? this.meta.id : null;
11010 var recordType = this.recordType, fields = recordType.prototype.fields;
11013 for(var i = 0; i < root.length; i++){
11016 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11017 for(var j = 0, jlen = fields.length; j < jlen; j++){
11018 var f = fields.items[j];
11019 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11020 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11022 values[f.name] = v;
11024 var record = new recordType(values, id);
11026 records[records.length] = record;
11030 totalRecords : records.length
11039 * @class Roo.bootstrap.ComboBox
11040 * @extends Roo.bootstrap.TriggerField
11041 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11042 * @cfg {Boolean} append (true|false) default false
11043 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11044 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11045 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11046 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11047 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11048 * @cfg {Boolean} animate default true
11049 * @cfg {Boolean} emptyResultText only for touch device
11051 * Create a new ComboBox.
11052 * @param {Object} config Configuration options
11054 Roo.bootstrap.ComboBox = function(config){
11055 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11059 * Fires when the dropdown list is expanded
11060 * @param {Roo.bootstrap.ComboBox} combo This combo box
11065 * Fires when the dropdown list is collapsed
11066 * @param {Roo.bootstrap.ComboBox} combo This combo box
11070 * @event beforeselect
11071 * Fires before a list item is selected. Return false to cancel the selection.
11072 * @param {Roo.bootstrap.ComboBox} combo This combo box
11073 * @param {Roo.data.Record} record The data record returned from the underlying store
11074 * @param {Number} index The index of the selected item in the dropdown list
11076 'beforeselect' : true,
11079 * Fires when a list item is selected
11080 * @param {Roo.bootstrap.ComboBox} combo This combo box
11081 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11082 * @param {Number} index The index of the selected item in the dropdown list
11086 * @event beforequery
11087 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11088 * The event object passed has these properties:
11089 * @param {Roo.bootstrap.ComboBox} combo This combo box
11090 * @param {String} query The query
11091 * @param {Boolean} forceAll true to force "all" query
11092 * @param {Boolean} cancel true to cancel the query
11093 * @param {Object} e The query event object
11095 'beforequery': true,
11098 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11099 * @param {Roo.bootstrap.ComboBox} combo This combo box
11104 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11105 * @param {Roo.bootstrap.ComboBox} combo This combo box
11106 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11111 * Fires when the remove value from the combobox array
11112 * @param {Roo.bootstrap.ComboBox} combo This combo box
11116 * @event specialfilter
11117 * Fires when specialfilter
11118 * @param {Roo.bootstrap.ComboBox} combo This combo box
11120 'specialfilter' : true
11125 this.tickItems = [];
11127 this.selectedIndex = -1;
11128 if(this.mode == 'local'){
11129 if(config.queryDelay === undefined){
11130 this.queryDelay = 10;
11132 if(config.minChars === undefined){
11138 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11141 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11142 * rendering into an Roo.Editor, defaults to false)
11145 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11146 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11149 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11152 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11153 * the dropdown list (defaults to undefined, with no header element)
11157 * @cfg {String/Roo.Template} tpl The template to use to render the output
11161 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11163 listWidth: undefined,
11165 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11166 * mode = 'remote' or 'text' if mode = 'local')
11168 displayField: undefined,
11171 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11172 * mode = 'remote' or 'value' if mode = 'local').
11173 * Note: use of a valueField requires the user make a selection
11174 * in order for a value to be mapped.
11176 valueField: undefined,
11180 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11181 * field's data value (defaults to the underlying DOM element's name)
11183 hiddenName: undefined,
11185 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11189 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11191 selectedClass: 'active',
11194 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11198 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11199 * anchor positions (defaults to 'tl-bl')
11201 listAlign: 'tl-bl?',
11203 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11207 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11208 * query specified by the allQuery config option (defaults to 'query')
11210 triggerAction: 'query',
11212 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11213 * (defaults to 4, does not apply if editable = false)
11217 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11218 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11222 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11223 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11227 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11228 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11232 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11233 * when editable = true (defaults to false)
11235 selectOnFocus:false,
11237 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11239 queryParam: 'query',
11241 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
11242 * when mode = 'remote' (defaults to 'Loading...')
11244 loadingText: 'Loading...',
11246 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11250 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11254 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11255 * traditional select (defaults to true)
11259 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11263 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11267 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11268 * listWidth has a higher value)
11272 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11273 * allow the user to set arbitrary text into the field (defaults to false)
11275 forceSelection:false,
11277 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11278 * if typeAhead = true (defaults to 250)
11280 typeAheadDelay : 250,
11282 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11283 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11285 valueNotFoundText : undefined,
11287 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11289 blockFocus : false,
11292 * @cfg {Boolean} disableClear Disable showing of clear button.
11294 disableClear : false,
11296 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
11298 alwaysQuery : false,
11301 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
11306 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11308 invalidClass : "has-warning",
11311 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11313 validClass : "has-success",
11316 * @cfg {Boolean} specialFilter (true|false) special filter default false
11318 specialFilter : false,
11321 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11323 mobileTouchView : true,
11335 btnPosition : 'right',
11336 triggerList : true,
11337 showToggleBtn : true,
11339 emptyResultText: 'Empty',
11340 // element that contains real text value.. (when hidden is used..)
11342 getAutoCreate : function()
11350 if(Roo.isTouch && this.mobileTouchView){
11351 cfg = this.getAutoCreateTouchView();
11358 if(!this.tickable){
11359 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11364 * ComboBox with tickable selections
11367 var align = this.labelAlign || this.parentLabelAlign();
11370 cls : 'form-group roo-combobox-tickable' //input-group
11375 cls : 'tickable-buttons',
11380 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11387 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11394 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11401 buttons.cn.unshift({
11403 cls: 'select2-search-field-input'
11409 Roo.each(buttons.cn, function(c){
11411 c.cls += ' btn-' + _this.size;
11414 if (_this.disabled) {
11425 cls: 'form-hidden-field'
11429 cls: 'select2-choices',
11433 cls: 'select2-search-field',
11445 cls: 'select2-container input-group select2-container-multi',
11450 // cls: 'typeahead typeahead-long dropdown-menu',
11451 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
11456 if(this.hasFeedback && !this.allowBlank){
11460 cls: 'glyphicon form-control-feedback'
11463 combobox.cn.push(feedback);
11466 if (align ==='left' && this.fieldLabel.length) {
11468 Roo.log("left and has label");
11474 cls : 'control-label col-sm-' + this.labelWidth,
11475 html : this.fieldLabel
11479 cls : "col-sm-" + (12 - this.labelWidth),
11486 } else if ( this.fieldLabel.length) {
11492 //cls : 'input-group-addon',
11493 html : this.fieldLabel
11503 Roo.log(" no label && no align");
11510 ['xs','sm','md','lg'].map(function(size){
11511 if (settings[size]) {
11512 cfg.cls += ' col-' + size + '-' + settings[size];
11521 initEvents: function()
11525 throw "can not find store for combo";
11528 this.store = Roo.factory(this.store, Roo.data);
11534 if(Roo.isTouch && this.mobileTouchView){
11535 this.initTouchView();
11540 this.initTickableEvents();
11544 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11546 if(this.hiddenName){
11548 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11550 this.hiddenField.dom.value =
11551 this.hiddenValue !== undefined ? this.hiddenValue :
11552 this.value !== undefined ? this.value : '';
11554 // prevent input submission
11555 this.el.dom.removeAttribute('name');
11556 this.hiddenField.dom.setAttribute('name', this.hiddenName);
11561 // this.el.dom.setAttribute('autocomplete', 'off');
11564 var cls = 'x-combo-list';
11566 //this.list = new Roo.Layer({
11567 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11573 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11574 _this.list.setWidth(lw);
11577 this.list.on('mouseover', this.onViewOver, this);
11578 this.list.on('mousemove', this.onViewMove, this);
11580 this.list.on('scroll', this.onViewScroll, this);
11583 this.list.swallowEvent('mousewheel');
11584 this.assetHeight = 0;
11587 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11588 this.assetHeight += this.header.getHeight();
11591 this.innerList = this.list.createChild({cls:cls+'-inner'});
11592 this.innerList.on('mouseover', this.onViewOver, this);
11593 this.innerList.on('mousemove', this.onViewMove, this);
11594 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11596 if(this.allowBlank && !this.pageSize && !this.disableClear){
11597 this.footer = this.list.createChild({cls:cls+'-ft'});
11598 this.pageTb = new Roo.Toolbar(this.footer);
11602 this.footer = this.list.createChild({cls:cls+'-ft'});
11603 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11604 {pageSize: this.pageSize});
11608 if (this.pageTb && this.allowBlank && !this.disableClear) {
11610 this.pageTb.add(new Roo.Toolbar.Fill(), {
11611 cls: 'x-btn-icon x-btn-clear',
11613 handler: function()
11616 _this.clearValue();
11617 _this.onSelect(false, -1);
11622 this.assetHeight += this.footer.getHeight();
11627 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11630 this.view = new Roo.View(this.list, this.tpl, {
11631 singleSelect:true, store: this.store, selectedClass: this.selectedClass
11633 //this.view.wrapEl.setDisplayed(false);
11634 this.view.on('click', this.onViewClick, this);
11638 this.store.on('beforeload', this.onBeforeLoad, this);
11639 this.store.on('load', this.onLoad, this);
11640 this.store.on('loadexception', this.onLoadException, this);
11642 if(this.resizable){
11643 this.resizer = new Roo.Resizable(this.list, {
11644 pinned:true, handles:'se'
11646 this.resizer.on('resize', function(r, w, h){
11647 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11648 this.listWidth = w;
11649 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11650 this.restrictHeight();
11652 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11655 if(!this.editable){
11656 this.editable = true;
11657 this.setEditable(false);
11662 if (typeof(this.events.add.listeners) != 'undefined') {
11664 this.addicon = this.wrap.createChild(
11665 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
11667 this.addicon.on('click', function(e) {
11668 this.fireEvent('add', this);
11671 if (typeof(this.events.edit.listeners) != 'undefined') {
11673 this.editicon = this.wrap.createChild(
11674 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
11675 if (this.addicon) {
11676 this.editicon.setStyle('margin-left', '40px');
11678 this.editicon.on('click', function(e) {
11680 // we fire even if inothing is selected..
11681 this.fireEvent('edit', this, this.lastData );
11687 this.keyNav = new Roo.KeyNav(this.inputEl(), {
11688 "up" : function(e){
11689 this.inKeyMode = true;
11693 "down" : function(e){
11694 if(!this.isExpanded()){
11695 this.onTriggerClick();
11697 this.inKeyMode = true;
11702 "enter" : function(e){
11703 // this.onViewClick();
11707 if(this.fireEvent("specialkey", this, e)){
11708 this.onViewClick(false);
11714 "esc" : function(e){
11718 "tab" : function(e){
11721 if(this.fireEvent("specialkey", this, e)){
11722 this.onViewClick(false);
11730 doRelay : function(foo, bar, hname){
11731 if(hname == 'down' || this.scope.isExpanded()){
11732 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11741 this.queryDelay = Math.max(this.queryDelay || 10,
11742 this.mode == 'local' ? 10 : 250);
11745 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11747 if(this.typeAhead){
11748 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11750 if(this.editable !== false){
11751 this.inputEl().on("keyup", this.onKeyUp, this);
11753 if(this.forceSelection){
11754 this.inputEl().on('blur', this.doForce, this);
11758 this.choices = this.el.select('ul.select2-choices', true).first();
11759 this.searchField = this.el.select('ul li.select2-search-field', true).first();
11763 initTickableEvents: function()
11767 if(this.hiddenName){
11769 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11771 this.hiddenField.dom.value =
11772 this.hiddenValue !== undefined ? this.hiddenValue :
11773 this.value !== undefined ? this.value : '';
11775 // prevent input submission
11776 this.el.dom.removeAttribute('name');
11777 this.hiddenField.dom.setAttribute('name', this.hiddenName);
11782 // this.list = this.el.select('ul.dropdown-menu',true).first();
11784 this.choices = this.el.select('ul.select2-choices', true).first();
11785 this.searchField = this.el.select('ul li.select2-search-field', true).first();
11786 if(this.triggerList){
11787 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11790 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11791 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11793 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11794 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11796 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11797 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11799 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11800 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11801 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11804 this.cancelBtn.hide();
11809 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11810 _this.list.setWidth(lw);
11813 this.list.on('mouseover', this.onViewOver, this);
11814 this.list.on('mousemove', this.onViewMove, this);
11816 this.list.on('scroll', this.onViewScroll, this);
11819 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>';
11822 this.view = new Roo.View(this.list, this.tpl, {
11823 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11826 //this.view.wrapEl.setDisplayed(false);
11827 this.view.on('click', this.onViewClick, this);
11831 this.store.on('beforeload', this.onBeforeLoad, this);
11832 this.store.on('load', this.onLoad, this);
11833 this.store.on('loadexception', this.onLoadException, this);
11836 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11837 "up" : function(e){
11838 this.inKeyMode = true;
11842 "down" : function(e){
11843 this.inKeyMode = true;
11847 "enter" : function(e){
11848 if(this.fireEvent("specialkey", this, e)){
11849 this.onViewClick(false);
11855 "esc" : function(e){
11856 this.onTickableFooterButtonClick(e, false, false);
11859 "tab" : function(e){
11860 this.fireEvent("specialkey", this, e);
11862 this.onTickableFooterButtonClick(e, false, false);
11869 doRelay : function(e, fn, key){
11870 if(this.scope.isExpanded()){
11871 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11880 this.queryDelay = Math.max(this.queryDelay || 10,
11881 this.mode == 'local' ? 10 : 250);
11884 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11886 if(this.typeAhead){
11887 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11890 if(this.editable !== false){
11891 this.tickableInputEl().on("keyup", this.onKeyUp, this);
11896 onDestroy : function(){
11898 this.view.setStore(null);
11899 this.view.el.removeAllListeners();
11900 this.view.el.remove();
11901 this.view.purgeListeners();
11904 this.list.dom.innerHTML = '';
11908 this.store.un('beforeload', this.onBeforeLoad, this);
11909 this.store.un('load', this.onLoad, this);
11910 this.store.un('loadexception', this.onLoadException, this);
11912 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11916 fireKey : function(e){
11917 if(e.isNavKeyPress() && !this.list.isVisible()){
11918 this.fireEvent("specialkey", this, e);
11923 onResize: function(w, h){
11924 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11926 // if(typeof w != 'number'){
11927 // // we do not handle it!?!?
11930 // var tw = this.trigger.getWidth();
11931 // // tw += this.addicon ? this.addicon.getWidth() : 0;
11932 // // tw += this.editicon ? this.editicon.getWidth() : 0;
11934 // this.inputEl().setWidth( this.adjustWidth('input', x));
11936 // //this.trigger.setStyle('left', x+'px');
11938 // if(this.list && this.listWidth === undefined){
11939 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11940 // this.list.setWidth(lw);
11941 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11949 * Allow or prevent the user from directly editing the field text. If false is passed,
11950 * the user will only be able to select from the items defined in the dropdown list. This method
11951 * is the runtime equivalent of setting the 'editable' config option at config time.
11952 * @param {Boolean} value True to allow the user to directly edit the field text
11954 setEditable : function(value){
11955 if(value == this.editable){
11958 this.editable = value;
11960 this.inputEl().dom.setAttribute('readOnly', true);
11961 this.inputEl().on('mousedown', this.onTriggerClick, this);
11962 this.inputEl().addClass('x-combo-noedit');
11964 this.inputEl().dom.setAttribute('readOnly', false);
11965 this.inputEl().un('mousedown', this.onTriggerClick, this);
11966 this.inputEl().removeClass('x-combo-noedit');
11972 onBeforeLoad : function(combo,opts){
11973 if(!this.hasFocus){
11977 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11979 this.restrictHeight();
11980 this.selectedIndex = -1;
11984 onLoad : function(){
11986 this.hasQuery = false;
11988 if(!this.hasFocus){
11992 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11993 this.loading.hide();
11996 if(this.store.getCount() > 0){
11998 this.restrictHeight();
11999 if(this.lastQuery == this.allQuery){
12000 if(this.editable && !this.tickable){
12001 this.inputEl().dom.select();
12005 !this.selectByValue(this.value, true) &&
12008 !this.store.lastOptions ||
12009 typeof(this.store.lastOptions.add) == 'undefined' ||
12010 this.store.lastOptions.add != true
12013 this.select(0, true);
12016 if(this.autoFocus){
12019 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12020 this.taTask.delay(this.typeAheadDelay);
12024 this.onEmptyResults();
12030 onLoadException : function()
12032 this.hasQuery = false;
12034 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12035 this.loading.hide();
12038 if(this.tickable && this.editable){
12044 Roo.log(this.store.reader.jsonData);
12045 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12047 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12053 onTypeAhead : function(){
12054 if(this.store.getCount() > 0){
12055 var r = this.store.getAt(0);
12056 var newValue = r.data[this.displayField];
12057 var len = newValue.length;
12058 var selStart = this.getRawValue().length;
12060 if(selStart != len){
12061 this.setRawValue(newValue);
12062 this.selectText(selStart, newValue.length);
12068 onSelect : function(record, index){
12070 if(this.fireEvent('beforeselect', this, record, index) !== false){
12072 this.setFromData(index > -1 ? record.data : false);
12075 this.fireEvent('select', this, record, index);
12080 * Returns the currently selected field value or empty string if no value is set.
12081 * @return {String} value The selected value
12083 getValue : function(){
12086 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12089 if(this.valueField){
12090 return typeof this.value != 'undefined' ? this.value : '';
12092 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12097 * Clears any text/value currently set in the field
12099 clearValue : function(){
12100 if(this.hiddenField){
12101 this.hiddenField.dom.value = '';
12104 this.setRawValue('');
12105 this.lastSelectionText = '';
12106 this.lastData = false;
12108 var close = this.closeTriggerEl();
12117 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12118 * will be displayed in the field. If the value does not match the data value of an existing item,
12119 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12120 * Otherwise the field will be blank (although the value will still be set).
12121 * @param {String} value The value to match
12123 setValue : function(v){
12130 if(this.valueField){
12131 var r = this.findRecord(this.valueField, v);
12133 text = r.data[this.displayField];
12134 }else if(this.valueNotFoundText !== undefined){
12135 text = this.valueNotFoundText;
12138 this.lastSelectionText = text;
12139 if(this.hiddenField){
12140 this.hiddenField.dom.value = v;
12142 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12145 var close = this.closeTriggerEl();
12148 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12152 * @property {Object} the last set data for the element
12157 * Sets the value of the field based on a object which is related to the record format for the store.
12158 * @param {Object} value the value to set as. or false on reset?
12160 setFromData : function(o){
12167 var dv = ''; // display value
12168 var vv = ''; // value value..
12170 if (this.displayField) {
12171 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12173 // this is an error condition!!!
12174 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12177 if(this.valueField){
12178 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12181 var close = this.closeTriggerEl();
12184 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12187 if(this.hiddenField){
12188 this.hiddenField.dom.value = vv;
12190 this.lastSelectionText = dv;
12191 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12195 // no hidden field.. - we store the value in 'value', but still display
12196 // display field!!!!
12197 this.lastSelectionText = dv;
12198 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12205 reset : function(){
12206 // overridden so that last data is reset..
12213 this.setValue(this.originalValue);
12214 this.clearInvalid();
12215 this.lastData = false;
12217 this.view.clearSelections();
12221 findRecord : function(prop, value){
12223 if(this.store.getCount() > 0){
12224 this.store.each(function(r){
12225 if(r.data[prop] == value){
12235 getName: function()
12237 // returns hidden if it's set..
12238 if (!this.rendered) {return ''};
12239 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
12243 onViewMove : function(e, t){
12244 this.inKeyMode = false;
12248 onViewOver : function(e, t){
12249 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12252 var item = this.view.findItemFromChild(t);
12255 var index = this.view.indexOf(item);
12256 this.select(index, false);
12261 onViewClick : function(view, doFocus, el, e)
12263 var index = this.view.getSelectedIndexes()[0];
12265 var r = this.store.getAt(index);
12269 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12276 Roo.each(this.tickItems, function(v,k){
12278 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12279 _this.tickItems.splice(k, 1);
12281 if(typeof(e) == 'undefined' && view == false){
12282 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12294 this.tickItems.push(r.data);
12296 if(typeof(e) == 'undefined' && view == false){
12297 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12304 this.onSelect(r, index);
12306 if(doFocus !== false && !this.blockFocus){
12307 this.inputEl().focus();
12312 restrictHeight : function(){
12313 //this.innerList.dom.style.height = '';
12314 //var inner = this.innerList.dom;
12315 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12316 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12317 //this.list.beginUpdate();
12318 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12319 this.list.alignTo(this.inputEl(), this.listAlign);
12320 this.list.alignTo(this.inputEl(), this.listAlign);
12321 //this.list.endUpdate();
12325 onEmptyResults : function(){
12327 if(this.tickable && this.editable){
12328 this.restrictHeight();
12336 * Returns true if the dropdown list is expanded, else false.
12338 isExpanded : function(){
12339 return this.list.isVisible();
12343 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12344 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12345 * @param {String} value The data value of the item to select
12346 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12347 * selected item if it is not currently in view (defaults to true)
12348 * @return {Boolean} True if the value matched an item in the list, else false
12350 selectByValue : function(v, scrollIntoView){
12351 if(v !== undefined && v !== null){
12352 var r = this.findRecord(this.valueField || this.displayField, v);
12354 this.select(this.store.indexOf(r), scrollIntoView);
12362 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12363 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12364 * @param {Number} index The zero-based index of the list item to select
12365 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12366 * selected item if it is not currently in view (defaults to true)
12368 select : function(index, scrollIntoView){
12369 this.selectedIndex = index;
12370 this.view.select(index);
12371 if(scrollIntoView !== false){
12372 var el = this.view.getNode(index);
12374 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12377 this.list.scrollChildIntoView(el, false);
12383 selectNext : function(){
12384 var ct = this.store.getCount();
12386 if(this.selectedIndex == -1){
12388 }else if(this.selectedIndex < ct-1){
12389 this.select(this.selectedIndex+1);
12395 selectPrev : function(){
12396 var ct = this.store.getCount();
12398 if(this.selectedIndex == -1){
12400 }else if(this.selectedIndex != 0){
12401 this.select(this.selectedIndex-1);
12407 onKeyUp : function(e){
12408 if(this.editable !== false && !e.isSpecialKey()){
12409 this.lastKey = e.getKey();
12410 this.dqTask.delay(this.queryDelay);
12415 validateBlur : function(){
12416 return !this.list || !this.list.isVisible();
12420 initQuery : function(){
12422 var v = this.getRawValue();
12424 if(this.tickable && this.editable){
12425 v = this.tickableInputEl().getValue();
12432 doForce : function(){
12433 if(this.inputEl().dom.value.length > 0){
12434 this.inputEl().dom.value =
12435 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12441 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
12442 * query allowing the query action to be canceled if needed.
12443 * @param {String} query The SQL query to execute
12444 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12445 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
12446 * saved in the current store (defaults to false)
12448 doQuery : function(q, forceAll){
12450 if(q === undefined || q === null){
12455 forceAll: forceAll,
12459 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12464 forceAll = qe.forceAll;
12465 if(forceAll === true || (q.length >= this.minChars)){
12467 this.hasQuery = true;
12469 if(this.lastQuery != q || this.alwaysQuery){
12470 this.lastQuery = q;
12471 if(this.mode == 'local'){
12472 this.selectedIndex = -1;
12474 this.store.clearFilter();
12477 if(this.specialFilter){
12478 this.fireEvent('specialfilter', this);
12483 this.store.filter(this.displayField, q);
12486 this.store.fireEvent("datachanged", this.store);
12493 this.store.baseParams[this.queryParam] = q;
12495 var options = {params : this.getParams(q)};
12498 options.add = true;
12499 options.params.start = this.page * this.pageSize;
12502 this.store.load(options);
12505 * this code will make the page width larger, at the beginning, the list not align correctly,
12506 * we should expand the list on onLoad
12507 * so command out it
12512 this.selectedIndex = -1;
12517 this.loadNext = false;
12521 getParams : function(q){
12523 //p[this.queryParam] = q;
12527 p.limit = this.pageSize;
12533 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12535 collapse : function(){
12536 if(!this.isExpanded()){
12543 this.hasFocus = false;
12545 this.cancelBtn.hide();
12546 this.trigger.show();
12549 this.tickableInputEl().dom.value = '';
12550 this.tickableInputEl().blur();
12555 Roo.get(document).un('mousedown', this.collapseIf, this);
12556 Roo.get(document).un('mousewheel', this.collapseIf, this);
12557 if (!this.editable) {
12558 Roo.get(document).un('keydown', this.listKeyPress, this);
12560 this.fireEvent('collapse', this);
12564 collapseIf : function(e){
12565 var in_combo = e.within(this.el);
12566 var in_list = e.within(this.list);
12567 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12569 if (in_combo || in_list || is_list) {
12570 //e.stopPropagation();
12575 this.onTickableFooterButtonClick(e, false, false);
12583 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12585 expand : function(){
12587 if(this.isExpanded() || !this.hasFocus){
12591 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12592 this.list.setWidth(lw);
12599 this.restrictHeight();
12603 this.tickItems = Roo.apply([], this.item);
12606 this.cancelBtn.show();
12607 this.trigger.hide();
12610 this.tickableInputEl().focus();
12615 Roo.get(document).on('mousedown', this.collapseIf, this);
12616 Roo.get(document).on('mousewheel', this.collapseIf, this);
12617 if (!this.editable) {
12618 Roo.get(document).on('keydown', this.listKeyPress, this);
12621 this.fireEvent('expand', this);
12625 // Implements the default empty TriggerField.onTriggerClick function
12626 onTriggerClick : function(e)
12628 Roo.log('trigger click');
12630 if(this.disabled || !this.triggerList){
12635 this.loadNext = false;
12637 if(this.isExpanded()){
12639 if (!this.blockFocus) {
12640 this.inputEl().focus();
12644 this.hasFocus = true;
12645 if(this.triggerAction == 'all') {
12646 this.doQuery(this.allQuery, true);
12648 this.doQuery(this.getRawValue());
12650 if (!this.blockFocus) {
12651 this.inputEl().focus();
12656 onTickableTriggerClick : function(e)
12663 this.loadNext = false;
12664 this.hasFocus = true;
12666 if(this.triggerAction == 'all') {
12667 this.doQuery(this.allQuery, true);
12669 this.doQuery(this.getRawValue());
12673 onSearchFieldClick : function(e)
12675 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12676 this.onTickableFooterButtonClick(e, false, false);
12680 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12685 this.loadNext = false;
12686 this.hasFocus = true;
12688 if(this.triggerAction == 'all') {
12689 this.doQuery(this.allQuery, true);
12691 this.doQuery(this.getRawValue());
12695 listKeyPress : function(e)
12697 //Roo.log('listkeypress');
12698 // scroll to first matching element based on key pres..
12699 if (e.isSpecialKey()) {
12702 var k = String.fromCharCode(e.getKey()).toUpperCase();
12705 var csel = this.view.getSelectedNodes();
12706 var cselitem = false;
12708 var ix = this.view.indexOf(csel[0]);
12709 cselitem = this.store.getAt(ix);
12710 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12716 this.store.each(function(v) {
12718 // start at existing selection.
12719 if (cselitem.id == v.id) {
12725 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12726 match = this.store.indexOf(v);
12732 if (match === false) {
12733 return true; // no more action?
12736 this.view.select(match);
12737 var sn = Roo.get(this.view.getSelectedNodes()[0])
12738 sn.scrollIntoView(sn.dom.parentNode, false);
12741 onViewScroll : function(e, t){
12743 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){
12747 this.hasQuery = true;
12749 this.loading = this.list.select('.loading', true).first();
12751 if(this.loading === null){
12752 this.list.createChild({
12754 cls: 'loading select2-more-results select2-active',
12755 html: 'Loading more results...'
12758 this.loading = this.list.select('.loading', true).first();
12760 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12762 this.loading.hide();
12765 this.loading.show();
12770 this.loadNext = true;
12772 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12777 addItem : function(o)
12779 var dv = ''; // display value
12781 if (this.displayField) {
12782 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12784 // this is an error condition!!!
12785 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12792 var choice = this.choices.createChild({
12794 cls: 'select2-search-choice',
12803 cls: 'select2-search-choice-close',
12808 }, this.searchField);
12810 var close = choice.select('a.select2-search-choice-close', true).first()
12812 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12820 this.inputEl().dom.value = '';
12825 onRemoveItem : function(e, _self, o)
12827 e.preventDefault();
12829 this.lastItem = Roo.apply([], this.item);
12831 var index = this.item.indexOf(o.data) * 1;
12834 Roo.log('not this item?!');
12838 this.item.splice(index, 1);
12843 this.fireEvent('remove', this, e);
12849 syncValue : function()
12851 if(!this.item.length){
12858 Roo.each(this.item, function(i){
12859 if(_this.valueField){
12860 value.push(i[_this.valueField]);
12867 this.value = value.join(',');
12869 if(this.hiddenField){
12870 this.hiddenField.dom.value = this.value;
12873 this.store.fireEvent("datachanged", this.store);
12876 clearItem : function()
12878 if(!this.multiple){
12884 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12893 inputEl: function ()
12895 if(Roo.isTouch && this.mobileTouchView){
12896 return this.el.select('input.form-control',true).first();
12900 return this.searchField;
12903 return this.el.select('input.form-control',true).first();
12907 onTickableFooterButtonClick : function(e, btn, el)
12909 e.preventDefault();
12911 this.lastItem = Roo.apply([], this.item);
12913 if(btn && btn.name == 'cancel'){
12914 this.tickItems = Roo.apply([], this.item);
12923 Roo.each(this.tickItems, function(o){
12931 validate : function()
12933 var v = this.getRawValue();
12936 v = this.getValue();
12939 if(this.disabled || this.allowBlank || v.length){
12944 this.markInvalid();
12948 tickableInputEl : function()
12950 if(!this.tickable || !this.editable){
12951 return this.inputEl();
12954 return this.inputEl().select('.select2-search-field-input', true).first();
12958 getAutoCreateTouchView : function()
12963 cls: 'form-group' //input-group
12969 type : this.inputType,
12970 cls : 'form-control x-combo-noedit',
12971 autocomplete: 'new-password',
12972 placeholder : this.placeholder || '',
12977 input.name = this.name;
12981 input.cls += ' input-' + this.size;
12984 if (this.disabled) {
12985 input.disabled = true;
12996 inputblock.cls += ' input-group';
12998 inputblock.cn.unshift({
13000 cls : 'input-group-addon',
13005 if(this.removable && !this.multiple){
13006 inputblock.cls += ' roo-removable';
13008 inputblock.cn.push({
13011 cls : 'roo-combo-removable-btn close'
13015 if(this.hasFeedback && !this.allowBlank){
13017 inputblock.cls += ' has-feedback';
13019 inputblock.cn.push({
13021 cls: 'glyphicon form-control-feedback'
13028 inputblock.cls += (this.before) ? '' : ' input-group';
13030 inputblock.cn.push({
13032 cls : 'input-group-addon',
13043 cls: 'form-hidden-field'
13057 cls: 'form-hidden-field'
13061 cls: 'select2-choices',
13065 cls: 'select2-search-field',
13078 cls: 'select2-container input-group',
13085 combobox.cls += ' select2-container-multi';
13088 var align = this.labelAlign || this.parentLabelAlign();
13092 if(this.fieldLabel.length){
13094 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13095 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13100 cls : 'control-label ' + lw,
13101 html : this.fieldLabel
13113 var settings = this;
13115 ['xs','sm','md','lg'].map(function(size){
13116 if (settings[size]) {
13117 cfg.cls += ' col-' + size + '-' + settings[size];
13124 initTouchView : function()
13126 this.renderTouchView();
13128 this.touchViewEl.on('scroll', function(){
13129 this.el.dom.scrollTop = 0;
13132 this.inputEl().on("click", this.showTouchView, this);
13133 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13134 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13136 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13138 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13139 this.store.on('load', this.onTouchViewLoad, this);
13140 this.store.on('loadexception', this.onTouchViewLoadException, this);
13142 if(this.hiddenName){
13144 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13146 this.hiddenField.dom.value =
13147 this.hiddenValue !== undefined ? this.hiddenValue :
13148 this.value !== undefined ? this.value : '';
13150 this.el.dom.removeAttribute('name');
13151 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13155 this.choices = this.el.select('ul.select2-choices', true).first();
13156 this.searchField = this.el.select('ul li.select2-search-field', true).first();
13159 if(this.removable && !this.multiple){
13160 var close = this.closeTriggerEl();
13162 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13163 close.on('click', this.removeBtnClick, this, close);
13172 renderTouchView : function()
13174 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13175 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13177 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13178 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13180 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13181 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13182 this.touchViewBodyEl.setStyle('overflow', 'auto');
13184 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13185 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13187 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13188 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13192 showTouchView : function()
13194 this.touchViewHeaderEl.hide();
13196 if(this.fieldLabel.length){
13197 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13198 this.touchViewHeaderEl.show();
13201 this.touchViewEl.show();
13203 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13204 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13206 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13208 if(this.fieldLabel.length){
13209 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13212 this.touchViewBodyEl.setHeight(bodyHeight);
13216 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13218 this.touchViewEl.addClass('in');
13221 this.doTouchViewQuery();
13225 hideTouchView : function()
13227 this.touchViewEl.removeClass('in');
13231 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13233 this.touchViewEl.setStyle('display', 'none');
13238 setTouchViewValue : function()
13245 Roo.each(this.tickItems, function(o){
13250 this.hideTouchView();
13253 doTouchViewQuery : function()
13262 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13266 if(!this.alwaysQuery || this.mode == 'local'){
13267 this.onTouchViewLoad();
13274 onTouchViewBeforeLoad : function(combo,opts)
13280 onTouchViewLoad : function()
13282 if(this.store.getCount() < 1){
13283 this.onTouchViewEmptyResults();
13287 this.clearTouchView();
13289 var rawValue = this.getRawValue();
13291 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13293 this.tickItems = [];
13295 this.store.data.each(function(d, rowIndex){
13296 var row = this.touchViewListGroup.createChild(template);
13298 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13299 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13302 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13303 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13306 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13307 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13308 this.tickItems.push(d.data);
13311 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13315 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13317 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13319 if(this.fieldLabel.length){
13320 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13323 var listHeight = this.touchViewListGroup.getHeight();
13325 if(firstChecked && listHeight > bodyHeight){
13326 (function() { firstChecked.findParent('li').scrollIntoView(this.touchViewListGroup.dom); }).defer(500);
13331 onTouchViewLoadException : function()
13333 this.hideTouchView();
13336 onTouchViewEmptyResults : function()
13338 this.clearTouchView();
13340 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13342 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13346 clearTouchView : function()
13348 this.touchViewListGroup.dom.innerHTML = '';
13351 onTouchViewClick : function(e, el, o)
13353 e.preventDefault();
13356 var rowIndex = o.rowIndex;
13358 var r = this.store.getAt(rowIndex);
13360 if(!this.multiple){
13361 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13362 c.dom.removeAttribute('checked');
13365 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13367 this.setFromData(r.data);
13369 var close = this.closeTriggerEl();
13375 this.hideTouchView();
13377 this.fireEvent('select', this, r, rowIndex);
13382 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13383 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13384 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13388 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13389 this.addItem(r.data);
13390 this.tickItems.push(r.data);
13396 * @cfg {Boolean} grow
13400 * @cfg {Number} growMin
13404 * @cfg {Number} growMax
13413 Roo.apply(Roo.bootstrap.ComboBox, {
13417 cls: 'modal-header',
13439 cls: 'list-group-item',
13443 cls: 'roo-combobox-list-group-item-value'
13447 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13461 listItemCheckbox : {
13463 cls: 'list-group-item',
13467 cls: 'roo-combobox-list-group-item-value'
13471 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13487 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13492 cls: 'modal-footer',
13500 cls: 'col-xs-6 text-left',
13503 cls: 'btn btn-danger roo-touch-view-cancel',
13509 cls: 'col-xs-6 text-right',
13512 cls: 'btn btn-success roo-touch-view-ok',
13523 Roo.apply(Roo.bootstrap.ComboBox, {
13525 touchViewTemplate : {
13527 cls: 'modal fade roo-combobox-touch-view',
13531 cls: 'modal-dialog',
13535 cls: 'modal-content',
13537 Roo.bootstrap.ComboBox.header,
13538 Roo.bootstrap.ComboBox.body,
13539 Roo.bootstrap.ComboBox.footer
13548 * Ext JS Library 1.1.1
13549 * Copyright(c) 2006-2007, Ext JS, LLC.
13551 * Originally Released Under LGPL - original licence link has changed is not relivant.
13554 * <script type="text/javascript">
13559 * @extends Roo.util.Observable
13560 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
13561 * This class also supports single and multi selection modes. <br>
13562 * Create a data model bound view:
13564 var store = new Roo.data.Store(...);
13566 var view = new Roo.View({
13568 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
13570 singleSelect: true,
13571 selectedClass: "ydataview-selected",
13575 // listen for node click?
13576 view.on("click", function(vw, index, node, e){
13577 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13581 dataModel.load("foobar.xml");
13583 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13585 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13586 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13588 * Note: old style constructor is still suported (container, template, config)
13591 * Create a new View
13592 * @param {Object} config The config object
13595 Roo.View = function(config, depreciated_tpl, depreciated_config){
13597 this.parent = false;
13599 if (typeof(depreciated_tpl) == 'undefined') {
13600 // new way.. - universal constructor.
13601 Roo.apply(this, config);
13602 this.el = Roo.get(this.el);
13605 this.el = Roo.get(config);
13606 this.tpl = depreciated_tpl;
13607 Roo.apply(this, depreciated_config);
13609 this.wrapEl = this.el.wrap().wrap();
13610 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13613 if(typeof(this.tpl) == "string"){
13614 this.tpl = new Roo.Template(this.tpl);
13616 // support xtype ctors..
13617 this.tpl = new Roo.factory(this.tpl, Roo);
13621 this.tpl.compile();
13626 * @event beforeclick
13627 * Fires before a click is processed. Returns false to cancel the default action.
13628 * @param {Roo.View} this
13629 * @param {Number} index The index of the target node
13630 * @param {HTMLElement} node The target node
13631 * @param {Roo.EventObject} e The raw event object
13633 "beforeclick" : true,
13636 * Fires when a template node is clicked.
13637 * @param {Roo.View} this
13638 * @param {Number} index The index of the target node
13639 * @param {HTMLElement} node The target node
13640 * @param {Roo.EventObject} e The raw event object
13645 * Fires when a template node is double clicked.
13646 * @param {Roo.View} this
13647 * @param {Number} index The index of the target node
13648 * @param {HTMLElement} node The target node
13649 * @param {Roo.EventObject} e The raw event object
13653 * @event contextmenu
13654 * Fires when a template node is right clicked.
13655 * @param {Roo.View} this
13656 * @param {Number} index The index of the target node
13657 * @param {HTMLElement} node The target node
13658 * @param {Roo.EventObject} e The raw event object
13660 "contextmenu" : true,
13662 * @event selectionchange
13663 * Fires when the selected nodes change.
13664 * @param {Roo.View} this
13665 * @param {Array} selections Array of the selected nodes
13667 "selectionchange" : true,
13670 * @event beforeselect
13671 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13672 * @param {Roo.View} this
13673 * @param {HTMLElement} node The node to be selected
13674 * @param {Array} selections Array of currently selected nodes
13676 "beforeselect" : true,
13678 * @event preparedata
13679 * Fires on every row to render, to allow you to change the data.
13680 * @param {Roo.View} this
13681 * @param {Object} data to be rendered (change this)
13683 "preparedata" : true
13691 "click": this.onClick,
13692 "dblclick": this.onDblClick,
13693 "contextmenu": this.onContextMenu,
13697 this.selections = [];
13699 this.cmp = new Roo.CompositeElementLite([]);
13701 this.store = Roo.factory(this.store, Roo.data);
13702 this.setStore(this.store, true);
13705 if ( this.footer && this.footer.xtype) {
13707 var fctr = this.wrapEl.appendChild(document.createElement("div"));
13709 this.footer.dataSource = this.store
13710 this.footer.container = fctr;
13711 this.footer = Roo.factory(this.footer, Roo);
13712 fctr.insertFirst(this.el);
13714 // this is a bit insane - as the paging toolbar seems to detach the el..
13715 // dom.parentNode.parentNode.parentNode
13716 // they get detached?
13720 Roo.View.superclass.constructor.call(this);
13725 Roo.extend(Roo.View, Roo.util.Observable, {
13728 * @cfg {Roo.data.Store} store Data store to load data from.
13733 * @cfg {String|Roo.Element} el The container element.
13738 * @cfg {String|Roo.Template} tpl The template used by this View
13742 * @cfg {String} dataName the named area of the template to use as the data area
13743 * Works with domtemplates roo-name="name"
13747 * @cfg {String} selectedClass The css class to add to selected nodes
13749 selectedClass : "x-view-selected",
13751 * @cfg {String} emptyText The empty text to show when nothing is loaded.
13756 * @cfg {String} text to display on mask (default Loading)
13760 * @cfg {Boolean} multiSelect Allow multiple selection
13762 multiSelect : false,
13764 * @cfg {Boolean} singleSelect Allow single selection
13766 singleSelect: false,
13769 * @cfg {Boolean} toggleSelect - selecting
13771 toggleSelect : false,
13774 * @cfg {Boolean} tickable - selecting
13779 * Returns the element this view is bound to.
13780 * @return {Roo.Element}
13782 getEl : function(){
13783 return this.wrapEl;
13789 * Refreshes the view. - called by datachanged on the store. - do not call directly.
13791 refresh : function(){
13792 //Roo.log('refresh');
13795 // if we are using something like 'domtemplate', then
13796 // the what gets used is:
13797 // t.applySubtemplate(NAME, data, wrapping data..)
13798 // the outer template then get' applied with
13799 // the store 'extra data'
13800 // and the body get's added to the
13801 // roo-name="data" node?
13802 // <span class='roo-tpl-{name}'></span> ?????
13806 this.clearSelections();
13807 this.el.update("");
13809 var records = this.store.getRange();
13810 if(records.length < 1) {
13812 // is this valid?? = should it render a template??
13814 this.el.update(this.emptyText);
13818 if (this.dataName) {
13819 this.el.update(t.apply(this.store.meta)); //????
13820 el = this.el.child('.roo-tpl-' + this.dataName);
13823 for(var i = 0, len = records.length; i < len; i++){
13824 var data = this.prepareData(records[i].data, i, records[i]);
13825 this.fireEvent("preparedata", this, data, i, records[i]);
13827 var d = Roo.apply({}, data);
13830 Roo.apply(d, {'roo-id' : Roo.id()});
13834 Roo.each(this.parent.item, function(item){
13835 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
13838 Roo.apply(d, {'roo-data-checked' : 'checked'});
13842 html[html.length] = Roo.util.Format.trim(
13844 t.applySubtemplate(this.dataName, d, this.store.meta) :
13851 el.update(html.join(""));
13852 this.nodes = el.dom.childNodes;
13853 this.updateIndexes(0);
13858 * Function to override to reformat the data that is sent to
13859 * the template for each node.
13860 * DEPRICATED - use the preparedata event handler.
13861 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
13862 * a JSON object for an UpdateManager bound view).
13864 prepareData : function(data, index, record)
13866 this.fireEvent("preparedata", this, data, index, record);
13870 onUpdate : function(ds, record){
13871 // Roo.log('on update');
13872 this.clearSelections();
13873 var index = this.store.indexOf(record);
13874 var n = this.nodes[index];
13875 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
13876 n.parentNode.removeChild(n);
13877 this.updateIndexes(index, index);
13883 onAdd : function(ds, records, index)
13885 //Roo.log(['on Add', ds, records, index] );
13886 this.clearSelections();
13887 if(this.nodes.length == 0){
13891 var n = this.nodes[index];
13892 for(var i = 0, len = records.length; i < len; i++){
13893 var d = this.prepareData(records[i].data, i, records[i]);
13895 this.tpl.insertBefore(n, d);
13898 this.tpl.append(this.el, d);
13901 this.updateIndexes(index);
13904 onRemove : function(ds, record, index){
13905 // Roo.log('onRemove');
13906 this.clearSelections();
13907 var el = this.dataName ?
13908 this.el.child('.roo-tpl-' + this.dataName) :
13911 el.dom.removeChild(this.nodes[index]);
13912 this.updateIndexes(index);
13916 * Refresh an individual node.
13917 * @param {Number} index
13919 refreshNode : function(index){
13920 this.onUpdate(this.store, this.store.getAt(index));
13923 updateIndexes : function(startIndex, endIndex){
13924 var ns = this.nodes;
13925 startIndex = startIndex || 0;
13926 endIndex = endIndex || ns.length - 1;
13927 for(var i = startIndex; i <= endIndex; i++){
13928 ns[i].nodeIndex = i;
13933 * Changes the data store this view uses and refresh the view.
13934 * @param {Store} store
13936 setStore : function(store, initial){
13937 if(!initial && this.store){
13938 this.store.un("datachanged", this.refresh);
13939 this.store.un("add", this.onAdd);
13940 this.store.un("remove", this.onRemove);
13941 this.store.un("update", this.onUpdate);
13942 this.store.un("clear", this.refresh);
13943 this.store.un("beforeload", this.onBeforeLoad);
13944 this.store.un("load", this.onLoad);
13945 this.store.un("loadexception", this.onLoad);
13949 store.on("datachanged", this.refresh, this);
13950 store.on("add", this.onAdd, this);
13951 store.on("remove", this.onRemove, this);
13952 store.on("update", this.onUpdate, this);
13953 store.on("clear", this.refresh, this);
13954 store.on("beforeload", this.onBeforeLoad, this);
13955 store.on("load", this.onLoad, this);
13956 store.on("loadexception", this.onLoad, this);
13964 * onbeforeLoad - masks the loading area.
13967 onBeforeLoad : function(store,opts)
13969 //Roo.log('onBeforeLoad');
13971 this.el.update("");
13973 this.el.mask(this.mask ? this.mask : "Loading" );
13975 onLoad : function ()
13982 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
13983 * @param {HTMLElement} node
13984 * @return {HTMLElement} The template node
13986 findItemFromChild : function(node){
13987 var el = this.dataName ?
13988 this.el.child('.roo-tpl-' + this.dataName,true) :
13991 if(!node || node.parentNode == el){
13994 var p = node.parentNode;
13995 while(p && p != el){
13996 if(p.parentNode == el){
14005 onClick : function(e){
14006 var item = this.findItemFromChild(e.getTarget());
14008 var index = this.indexOf(item);
14009 if(this.onItemClick(item, index, e) !== false){
14010 this.fireEvent("click", this, index, item, e);
14013 this.clearSelections();
14018 onContextMenu : function(e){
14019 var item = this.findItemFromChild(e.getTarget());
14021 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14026 onDblClick : function(e){
14027 var item = this.findItemFromChild(e.getTarget());
14029 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14033 onItemClick : function(item, index, e)
14035 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14038 if (this.toggleSelect) {
14039 var m = this.isSelected(item) ? 'unselect' : 'select';
14042 _t[m](item, true, false);
14045 if(this.multiSelect || this.singleSelect){
14046 if(this.multiSelect && e.shiftKey && this.lastSelection){
14047 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14049 this.select(item, this.multiSelect && e.ctrlKey);
14050 this.lastSelection = item;
14053 if(!this.tickable){
14054 e.preventDefault();
14062 * Get the number of selected nodes.
14065 getSelectionCount : function(){
14066 return this.selections.length;
14070 * Get the currently selected nodes.
14071 * @return {Array} An array of HTMLElements
14073 getSelectedNodes : function(){
14074 return this.selections;
14078 * Get the indexes of the selected nodes.
14081 getSelectedIndexes : function(){
14082 var indexes = [], s = this.selections;
14083 for(var i = 0, len = s.length; i < len; i++){
14084 indexes.push(s[i].nodeIndex);
14090 * Clear all selections
14091 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14093 clearSelections : function(suppressEvent){
14094 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14095 this.cmp.elements = this.selections;
14096 this.cmp.removeClass(this.selectedClass);
14097 this.selections = [];
14098 if(!suppressEvent){
14099 this.fireEvent("selectionchange", this, this.selections);
14105 * Returns true if the passed node is selected
14106 * @param {HTMLElement/Number} node The node or node index
14107 * @return {Boolean}
14109 isSelected : function(node){
14110 var s = this.selections;
14114 node = this.getNode(node);
14115 return s.indexOf(node) !== -1;
14120 * @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
14121 * @param {Boolean} keepExisting (optional) true to keep existing selections
14122 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14124 select : function(nodeInfo, keepExisting, suppressEvent){
14125 if(nodeInfo instanceof Array){
14127 this.clearSelections(true);
14129 for(var i = 0, len = nodeInfo.length; i < len; i++){
14130 this.select(nodeInfo[i], true, true);
14134 var node = this.getNode(nodeInfo);
14135 if(!node || this.isSelected(node)){
14136 return; // already selected.
14139 this.clearSelections(true);
14142 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14143 Roo.fly(node).addClass(this.selectedClass);
14144 this.selections.push(node);
14145 if(!suppressEvent){
14146 this.fireEvent("selectionchange", this, this.selections);
14154 * @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
14155 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14156 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14158 unselect : function(nodeInfo, keepExisting, suppressEvent)
14160 if(nodeInfo instanceof Array){
14161 Roo.each(this.selections, function(s) {
14162 this.unselect(s, nodeInfo);
14166 var node = this.getNode(nodeInfo);
14167 if(!node || !this.isSelected(node)){
14168 //Roo.log("not selected");
14169 return; // not selected.
14173 Roo.each(this.selections, function(s) {
14175 Roo.fly(node).removeClass(this.selectedClass);
14182 this.selections= ns;
14183 this.fireEvent("selectionchange", this, this.selections);
14187 * Gets a template node.
14188 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14189 * @return {HTMLElement} The node or null if it wasn't found
14191 getNode : function(nodeInfo){
14192 if(typeof nodeInfo == "string"){
14193 return document.getElementById(nodeInfo);
14194 }else if(typeof nodeInfo == "number"){
14195 return this.nodes[nodeInfo];
14201 * Gets a range template nodes.
14202 * @param {Number} startIndex
14203 * @param {Number} endIndex
14204 * @return {Array} An array of nodes
14206 getNodes : function(start, end){
14207 var ns = this.nodes;
14208 start = start || 0;
14209 end = typeof end == "undefined" ? ns.length - 1 : end;
14212 for(var i = start; i <= end; i++){
14216 for(var i = start; i >= end; i--){
14224 * Finds the index of the passed node
14225 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14226 * @return {Number} The index of the node or -1
14228 indexOf : function(node){
14229 node = this.getNode(node);
14230 if(typeof node.nodeIndex == "number"){
14231 return node.nodeIndex;
14233 var ns = this.nodes;
14234 for(var i = 0, len = ns.length; i < len; i++){
14245 * based on jquery fullcalendar
14249 Roo.bootstrap = Roo.bootstrap || {};
14251 * @class Roo.bootstrap.Calendar
14252 * @extends Roo.bootstrap.Component
14253 * Bootstrap Calendar class
14254 * @cfg {Boolean} loadMask (true|false) default false
14255 * @cfg {Object} header generate the user specific header of the calendar, default false
14258 * Create a new Container
14259 * @param {Object} config The config object
14264 Roo.bootstrap.Calendar = function(config){
14265 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14269 * Fires when a date is selected
14270 * @param {DatePicker} this
14271 * @param {Date} date The selected date
14275 * @event monthchange
14276 * Fires when the displayed month changes
14277 * @param {DatePicker} this
14278 * @param {Date} date The selected month
14280 'monthchange': true,
14282 * @event evententer
14283 * Fires when mouse over an event
14284 * @param {Calendar} this
14285 * @param {event} Event
14287 'evententer': true,
14289 * @event eventleave
14290 * Fires when the mouse leaves an
14291 * @param {Calendar} this
14294 'eventleave': true,
14296 * @event eventclick
14297 * Fires when the mouse click an
14298 * @param {Calendar} this
14307 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
14310 * @cfg {Number} startDay
14311 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14319 getAutoCreate : function(){
14322 var fc_button = function(name, corner, style, content ) {
14323 return Roo.apply({},{
14325 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
14327 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14330 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14341 style : 'width:100%',
14348 cls : 'fc-header-left',
14350 fc_button('prev', 'left', 'arrow', '‹' ),
14351 fc_button('next', 'right', 'arrow', '›' ),
14352 { tag: 'span', cls: 'fc-header-space' },
14353 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
14361 cls : 'fc-header-center',
14365 cls: 'fc-header-title',
14368 html : 'month / year'
14376 cls : 'fc-header-right',
14378 /* fc_button('month', 'left', '', 'month' ),
14379 fc_button('week', '', '', 'week' ),
14380 fc_button('day', 'right', '', 'day' )
14392 header = this.header;
14395 var cal_heads = function() {
14397 // fixme - handle this.
14399 for (var i =0; i < Date.dayNames.length; i++) {
14400 var d = Date.dayNames[i];
14403 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14404 html : d.substring(0,3)
14408 ret[0].cls += ' fc-first';
14409 ret[6].cls += ' fc-last';
14412 var cal_cell = function(n) {
14415 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14420 cls: 'fc-day-number',
14424 cls: 'fc-day-content',
14428 style: 'position: relative;' // height: 17px;
14440 var cal_rows = function() {
14443 for (var r = 0; r < 6; r++) {
14450 for (var i =0; i < Date.dayNames.length; i++) {
14451 var d = Date.dayNames[i];
14452 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14455 row.cn[0].cls+=' fc-first';
14456 row.cn[0].cn[0].style = 'min-height:90px';
14457 row.cn[6].cls+=' fc-last';
14461 ret[0].cls += ' fc-first';
14462 ret[4].cls += ' fc-prev-last';
14463 ret[5].cls += ' fc-last';
14470 cls: 'fc-border-separate',
14471 style : 'width:100%',
14479 cls : 'fc-first fc-last',
14497 cls : 'fc-content',
14498 style : "position: relative;",
14501 cls : 'fc-view fc-view-month fc-grid',
14502 style : 'position: relative',
14503 unselectable : 'on',
14506 cls : 'fc-event-container',
14507 style : 'position:absolute;z-index:8;top:0;left:0;'
14525 initEvents : function()
14528 throw "can not find store for calendar";
14534 style: "text-align:center",
14538 style: "background-color:white;width:50%;margin:250 auto",
14542 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
14553 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14555 var size = this.el.select('.fc-content', true).first().getSize();
14556 this.maskEl.setSize(size.width, size.height);
14557 this.maskEl.enableDisplayMode("block");
14558 if(!this.loadMask){
14559 this.maskEl.hide();
14562 this.store = Roo.factory(this.store, Roo.data);
14563 this.store.on('load', this.onLoad, this);
14564 this.store.on('beforeload', this.onBeforeLoad, this);
14568 this.cells = this.el.select('.fc-day',true);
14569 //Roo.log(this.cells);
14570 this.textNodes = this.el.query('.fc-day-number');
14571 this.cells.addClassOnOver('fc-state-hover');
14573 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14574 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14575 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14576 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14578 this.on('monthchange', this.onMonthChange, this);
14580 this.update(new Date().clearTime());
14583 resize : function() {
14584 var sz = this.el.getSize();
14586 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14587 this.el.select('.fc-day-content div',true).setHeight(34);
14592 showPrevMonth : function(e){
14593 this.update(this.activeDate.add("mo", -1));
14595 showToday : function(e){
14596 this.update(new Date().clearTime());
14599 showNextMonth : function(e){
14600 this.update(this.activeDate.add("mo", 1));
14604 showPrevYear : function(){
14605 this.update(this.activeDate.add("y", -1));
14609 showNextYear : function(){
14610 this.update(this.activeDate.add("y", 1));
14615 update : function(date)
14617 var vd = this.activeDate;
14618 this.activeDate = date;
14619 // if(vd && this.el){
14620 // var t = date.getTime();
14621 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14622 // Roo.log('using add remove');
14624 // this.fireEvent('monthchange', this, date);
14626 // this.cells.removeClass("fc-state-highlight");
14627 // this.cells.each(function(c){
14628 // if(c.dateValue == t){
14629 // c.addClass("fc-state-highlight");
14630 // setTimeout(function(){
14631 // try{c.dom.firstChild.focus();}catch(e){}
14641 var days = date.getDaysInMonth();
14643 var firstOfMonth = date.getFirstDateOfMonth();
14644 var startingPos = firstOfMonth.getDay()-this.startDay;
14646 if(startingPos < this.startDay){
14650 var pm = date.add(Date.MONTH, -1);
14651 var prevStart = pm.getDaysInMonth()-startingPos;
14653 this.cells = this.el.select('.fc-day',true);
14654 this.textNodes = this.el.query('.fc-day-number');
14655 this.cells.addClassOnOver('fc-state-hover');
14657 var cells = this.cells.elements;
14658 var textEls = this.textNodes;
14660 Roo.each(cells, function(cell){
14661 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14664 days += startingPos;
14666 // convert everything to numbers so it's fast
14667 var day = 86400000;
14668 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14671 //Roo.log(prevStart);
14673 var today = new Date().clearTime().getTime();
14674 var sel = date.clearTime().getTime();
14675 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14676 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14677 var ddMatch = this.disabledDatesRE;
14678 var ddText = this.disabledDatesText;
14679 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14680 var ddaysText = this.disabledDaysText;
14681 var format = this.format;
14683 var setCellClass = function(cal, cell){
14687 //Roo.log('set Cell Class');
14689 var t = d.getTime();
14693 cell.dateValue = t;
14695 cell.className += " fc-today";
14696 cell.className += " fc-state-highlight";
14697 cell.title = cal.todayText;
14700 // disable highlight in other month..
14701 //cell.className += " fc-state-highlight";
14706 cell.className = " fc-state-disabled";
14707 cell.title = cal.minText;
14711 cell.className = " fc-state-disabled";
14712 cell.title = cal.maxText;
14716 if(ddays.indexOf(d.getDay()) != -1){
14717 cell.title = ddaysText;
14718 cell.className = " fc-state-disabled";
14721 if(ddMatch && format){
14722 var fvalue = d.dateFormat(format);
14723 if(ddMatch.test(fvalue)){
14724 cell.title = ddText.replace("%0", fvalue);
14725 cell.className = " fc-state-disabled";
14729 if (!cell.initialClassName) {
14730 cell.initialClassName = cell.dom.className;
14733 cell.dom.className = cell.initialClassName + ' ' + cell.className;
14738 for(; i < startingPos; i++) {
14739 textEls[i].innerHTML = (++prevStart);
14740 d.setDate(d.getDate()+1);
14742 cells[i].className = "fc-past fc-other-month";
14743 setCellClass(this, cells[i]);
14748 for(; i < days; i++){
14749 intDay = i - startingPos + 1;
14750 textEls[i].innerHTML = (intDay);
14751 d.setDate(d.getDate()+1);
14753 cells[i].className = ''; // "x-date-active";
14754 setCellClass(this, cells[i]);
14758 for(; i < 42; i++) {
14759 textEls[i].innerHTML = (++extraDays);
14760 d.setDate(d.getDate()+1);
14762 cells[i].className = "fc-future fc-other-month";
14763 setCellClass(this, cells[i]);
14766 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14768 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14770 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14771 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14773 if(totalRows != 6){
14774 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14775 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14778 this.fireEvent('monthchange', this, date);
14782 if(!this.internalRender){
14783 var main = this.el.dom.firstChild;
14784 var w = main.offsetWidth;
14785 this.el.setWidth(w + this.el.getBorderWidth("lr"));
14786 Roo.fly(main).setWidth(w);
14787 this.internalRender = true;
14788 // opera does not respect the auto grow header center column
14789 // then, after it gets a width opera refuses to recalculate
14790 // without a second pass
14791 if(Roo.isOpera && !this.secondPass){
14792 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14793 this.secondPass = true;
14794 this.update.defer(10, this, [date]);
14801 findCell : function(dt) {
14802 dt = dt.clearTime().getTime();
14804 this.cells.each(function(c){
14805 //Roo.log("check " +c.dateValue + '?=' + dt);
14806 if(c.dateValue == dt){
14816 findCells : function(ev) {
14817 var s = ev.start.clone().clearTime().getTime();
14819 var e= ev.end.clone().clearTime().getTime();
14822 this.cells.each(function(c){
14823 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
14825 if(c.dateValue > e){
14828 if(c.dateValue < s){
14837 // findBestRow: function(cells)
14841 // for (var i =0 ; i < cells.length;i++) {
14842 // ret = Math.max(cells[i].rows || 0,ret);
14849 addItem : function(ev)
14851 // look for vertical location slot in
14852 var cells = this.findCells(ev);
14854 // ev.row = this.findBestRow(cells);
14856 // work out the location.
14860 for(var i =0; i < cells.length; i++) {
14862 cells[i].row = cells[0].row;
14865 cells[i].row = cells[i].row + 1;
14875 if (crow.start.getY() == cells[i].getY()) {
14877 crow.end = cells[i];
14894 cells[0].events.push(ev);
14896 this.calevents.push(ev);
14899 clearEvents: function() {
14901 if(!this.calevents){
14905 Roo.each(this.cells.elements, function(c){
14911 Roo.each(this.calevents, function(e) {
14912 Roo.each(e.els, function(el) {
14913 el.un('mouseenter' ,this.onEventEnter, this);
14914 el.un('mouseleave' ,this.onEventLeave, this);
14919 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
14925 renderEvents: function()
14929 this.cells.each(function(c) {
14938 if(c.row != c.events.length){
14939 r = 4 - (4 - (c.row - c.events.length));
14942 c.events = ev.slice(0, r);
14943 c.more = ev.slice(r);
14945 if(c.more.length && c.more.length == 1){
14946 c.events.push(c.more.pop());
14949 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
14953 this.cells.each(function(c) {
14955 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
14958 for (var e = 0; e < c.events.length; e++){
14959 var ev = c.events[e];
14960 var rows = ev.rows;
14962 for(var i = 0; i < rows.length; i++) {
14964 // how many rows should it span..
14967 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
14968 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
14970 unselectable : "on",
14973 cls: 'fc-event-inner',
14977 // cls: 'fc-event-time',
14978 // html : cells.length > 1 ? '' : ev.time
14982 cls: 'fc-event-title',
14983 html : String.format('{0}', ev.title)
14990 cls: 'ui-resizable-handle ui-resizable-e',
14991 html : '  '
14998 cfg.cls += ' fc-event-start';
15000 if ((i+1) == rows.length) {
15001 cfg.cls += ' fc-event-end';
15004 var ctr = _this.el.select('.fc-event-container',true).first();
15005 var cg = ctr.createChild(cfg);
15007 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15008 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15010 var r = (c.more.length) ? 1 : 0;
15011 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15012 cg.setWidth(ebox.right - sbox.x -2);
15014 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15015 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15016 cg.on('click', _this.onEventClick, _this, ev);
15027 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15028 style : 'position: absolute',
15029 unselectable : "on",
15032 cls: 'fc-event-inner',
15036 cls: 'fc-event-title',
15044 cls: 'ui-resizable-handle ui-resizable-e',
15045 html : '  '
15051 var ctr = _this.el.select('.fc-event-container',true).first();
15052 var cg = ctr.createChild(cfg);
15054 var sbox = c.select('.fc-day-content',true).first().getBox();
15055 var ebox = c.select('.fc-day-content',true).first().getBox();
15057 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15058 cg.setWidth(ebox.right - sbox.x -2);
15060 cg.on('click', _this.onMoreEventClick, _this, c.more);
15070 onEventEnter: function (e, el,event,d) {
15071 this.fireEvent('evententer', this, el, event);
15074 onEventLeave: function (e, el,event,d) {
15075 this.fireEvent('eventleave', this, el, event);
15078 onEventClick: function (e, el,event,d) {
15079 this.fireEvent('eventclick', this, el, event);
15082 onMonthChange: function () {
15086 onMoreEventClick: function(e, el, more)
15090 this.calpopover.placement = 'right';
15091 this.calpopover.setTitle('More');
15093 this.calpopover.setContent('');
15095 var ctr = this.calpopover.el.select('.popover-content', true).first();
15097 Roo.each(more, function(m){
15099 cls : 'fc-event-hori fc-event-draggable',
15102 var cg = ctr.createChild(cfg);
15104 cg.on('click', _this.onEventClick, _this, m);
15107 this.calpopover.show(el);
15112 onLoad: function ()
15114 this.calevents = [];
15117 if(this.store.getCount() > 0){
15118 this.store.data.each(function(d){
15121 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15122 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15123 time : d.data.start_time,
15124 title : d.data.title,
15125 description : d.data.description,
15126 venue : d.data.venue
15131 this.renderEvents();
15133 if(this.calevents.length && this.loadMask){
15134 this.maskEl.hide();
15138 onBeforeLoad: function()
15140 this.clearEvents();
15142 this.maskEl.show();
15156 * @class Roo.bootstrap.Popover
15157 * @extends Roo.bootstrap.Component
15158 * Bootstrap Popover class
15159 * @cfg {String} html contents of the popover (or false to use children..)
15160 * @cfg {String} title of popover (or false to hide)
15161 * @cfg {String} placement how it is placed
15162 * @cfg {String} trigger click || hover (or false to trigger manually)
15163 * @cfg {String} over what (parent or false to trigger manually.)
15164 * @cfg {Number} delay - delay before showing
15167 * Create a new Popover
15168 * @param {Object} config The config object
15171 Roo.bootstrap.Popover = function(config){
15172 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15175 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
15177 title: 'Fill in a title',
15180 placement : 'right',
15181 trigger : 'hover', // hover
15187 can_build_overlaid : false,
15189 getChildContainer : function()
15191 return this.el.select('.popover-content',true).first();
15194 getAutoCreate : function(){
15195 Roo.log('make popover?');
15197 cls : 'popover roo-dynamic',
15198 style: 'display:block',
15204 cls : 'popover-inner',
15208 cls: 'popover-title',
15212 cls : 'popover-content',
15223 setTitle: function(str)
15226 this.el.select('.popover-title',true).first().dom.innerHTML = str;
15228 setContent: function(str)
15231 this.el.select('.popover-content',true).first().dom.innerHTML = str;
15233 // as it get's added to the bottom of the page.
15234 onRender : function(ct, position)
15236 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15238 var cfg = Roo.apply({}, this.getAutoCreate());
15242 cfg.cls += ' ' + this.cls;
15245 cfg.style = this.style;
15247 Roo.log("adding to ")
15248 this.el = Roo.get(document.body).createChild(cfg, position);
15254 initEvents : function()
15256 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15257 this.el.enableDisplayMode('block');
15259 if (this.over === false) {
15262 if (this.triggers === false) {
15265 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15266 var triggers = this.trigger ? this.trigger.split(' ') : [];
15267 Roo.each(triggers, function(trigger) {
15269 if (trigger == 'click') {
15270 on_el.on('click', this.toggle, this);
15271 } else if (trigger != 'manual') {
15272 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
15273 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15275 on_el.on(eventIn ,this.enter, this);
15276 on_el.on(eventOut, this.leave, this);
15287 toggle : function () {
15288 this.hoverState == 'in' ? this.leave() : this.enter();
15291 enter : function () {
15294 clearTimeout(this.timeout);
15296 this.hoverState = 'in';
15298 if (!this.delay || !this.delay.show) {
15303 this.timeout = setTimeout(function () {
15304 if (_t.hoverState == 'in') {
15307 }, this.delay.show)
15309 leave : function() {
15310 clearTimeout(this.timeout);
15312 this.hoverState = 'out';
15314 if (!this.delay || !this.delay.hide) {
15319 this.timeout = setTimeout(function () {
15320 if (_t.hoverState == 'out') {
15323 }, this.delay.hide)
15326 show : function (on_el)
15329 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15332 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15333 if (this.html !== false) {
15334 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15336 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15337 if (!this.title.length) {
15338 this.el.select('.popover-title',true).hide();
15341 var placement = typeof this.placement == 'function' ?
15342 this.placement.call(this, this.el, on_el) :
15345 var autoToken = /\s?auto?\s?/i;
15346 var autoPlace = autoToken.test(placement);
15348 placement = placement.replace(autoToken, '') || 'top';
15352 //this.el.setXY([0,0]);
15354 this.el.dom.style.display='block';
15355 this.el.addClass(placement);
15357 //this.el.appendTo(on_el);
15359 var p = this.getPosition();
15360 var box = this.el.getBox();
15365 var align = Roo.bootstrap.Popover.alignment[placement];
15366 this.el.alignTo(on_el, align[0],align[1]);
15367 //var arrow = this.el.select('.arrow',true).first();
15368 //arrow.set(align[2],
15370 this.el.addClass('in');
15373 if (this.el.hasClass('fade')) {
15380 this.el.setXY([0,0]);
15381 this.el.removeClass('in');
15383 this.hoverState = null;
15389 Roo.bootstrap.Popover.alignment = {
15390 'left' : ['r-l', [-10,0], 'right'],
15391 'right' : ['l-r', [10,0], 'left'],
15392 'bottom' : ['t-b', [0,10], 'top'],
15393 'top' : [ 'b-t', [0,-10], 'bottom']
15404 * @class Roo.bootstrap.Progress
15405 * @extends Roo.bootstrap.Component
15406 * Bootstrap Progress class
15407 * @cfg {Boolean} striped striped of the progress bar
15408 * @cfg {Boolean} active animated of the progress bar
15412 * Create a new Progress
15413 * @param {Object} config The config object
15416 Roo.bootstrap.Progress = function(config){
15417 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15420 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
15425 getAutoCreate : function(){
15433 cfg.cls += ' progress-striped';
15437 cfg.cls += ' active';
15456 * @class Roo.bootstrap.ProgressBar
15457 * @extends Roo.bootstrap.Component
15458 * Bootstrap ProgressBar class
15459 * @cfg {Number} aria_valuenow aria-value now
15460 * @cfg {Number} aria_valuemin aria-value min
15461 * @cfg {Number} aria_valuemax aria-value max
15462 * @cfg {String} label label for the progress bar
15463 * @cfg {String} panel (success | info | warning | danger )
15464 * @cfg {String} role role of the progress bar
15465 * @cfg {String} sr_only text
15469 * Create a new ProgressBar
15470 * @param {Object} config The config object
15473 Roo.bootstrap.ProgressBar = function(config){
15474 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15477 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
15481 aria_valuemax : 100,
15487 getAutoCreate : function()
15492 cls: 'progress-bar',
15493 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15505 cfg.role = this.role;
15508 if(this.aria_valuenow){
15509 cfg['aria-valuenow'] = this.aria_valuenow;
15512 if(this.aria_valuemin){
15513 cfg['aria-valuemin'] = this.aria_valuemin;
15516 if(this.aria_valuemax){
15517 cfg['aria-valuemax'] = this.aria_valuemax;
15520 if(this.label && !this.sr_only){
15521 cfg.html = this.label;
15525 cfg.cls += ' progress-bar-' + this.panel;
15531 update : function(aria_valuenow)
15533 this.aria_valuenow = aria_valuenow;
15535 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15550 * @class Roo.bootstrap.TabGroup
15551 * @extends Roo.bootstrap.Column
15552 * Bootstrap Column class
15553 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15554 * @cfg {Boolean} carousel true to make the group behave like a carousel
15555 * @cfg {Number} bullets show the panel pointer.. default 0
15556 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15557 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15558 * @cfg {Number} timer auto slide timer .. default 0 millisecond
15561 * Create a new TabGroup
15562 * @param {Object} config The config object
15565 Roo.bootstrap.TabGroup = function(config){
15566 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15568 this.navId = Roo.id();
15571 Roo.bootstrap.TabGroup.register(this);
15575 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
15578 transition : false,
15583 slideOnTouch : false,
15585 getAutoCreate : function()
15587 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15589 cfg.cls += ' tab-content';
15591 Roo.log('get auto create...............');
15593 if (this.carousel) {
15594 cfg.cls += ' carousel slide';
15597 cls : 'carousel-inner'
15600 if(this.bullets > 0 && !Roo.isTouch){
15603 cls : 'carousel-bullets',
15607 if(this.bullets_cls){
15608 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15611 for (var i = 0; i < this.bullets; i++){
15613 cls : 'bullet bullet-' + i
15621 cfg.cn[0].cn = bullets;
15628 initEvents: function()
15630 Roo.log('-------- init events on tab group ---------');
15632 if(this.bullets > 0 && !Roo.isTouch){
15638 if(Roo.isTouch && this.slideOnTouch){
15639 this.el.on("touchstart", this.onTouchStart, this);
15642 if(this.autoslide){
15645 this.slideFn = window.setInterval(function() {
15646 _this.showPanelNext();
15652 onTouchStart : function(e, el, o)
15654 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15658 this.showPanelNext();
15661 getChildContainer : function()
15663 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15667 * register a Navigation item
15668 * @param {Roo.bootstrap.NavItem} the navitem to add
15670 register : function(item)
15672 this.tabs.push( item);
15673 item.navId = this.navId; // not really needed..
15677 getActivePanel : function()
15680 Roo.each(this.tabs, function(t) {
15690 getPanelByName : function(n)
15693 Roo.each(this.tabs, function(t) {
15694 if (t.tabId == n) {
15702 indexOfPanel : function(p)
15705 Roo.each(this.tabs, function(t,i) {
15706 if (t.tabId == p.tabId) {
15715 * show a specific panel
15716 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15717 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15719 showPanel : function (pan)
15721 if(this.transition){
15722 Roo.log("waiting for the transitionend");
15726 if (typeof(pan) == 'number') {
15727 pan = this.tabs[pan];
15729 if (typeof(pan) == 'string') {
15730 pan = this.getPanelByName(pan);
15732 if (pan.tabId == this.getActivePanel().tabId) {
15735 var cur = this.getActivePanel();
15737 if (false === cur.fireEvent('beforedeactivate')) {
15741 if(this.bullets > 0 && !Roo.isTouch){
15742 this.setActiveBullet(this.indexOfPanel(pan));
15745 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15747 this.transition = true;
15748 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
15749 var lr = dir == 'next' ? 'left' : 'right';
15750 pan.el.addClass(dir); // or prev
15751 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
15752 cur.el.addClass(lr); // or right
15753 pan.el.addClass(lr);
15756 cur.el.on('transitionend', function() {
15757 Roo.log("trans end?");
15759 pan.el.removeClass([lr,dir]);
15760 pan.setActive(true);
15762 cur.el.removeClass([lr]);
15763 cur.setActive(false);
15765 _this.transition = false;
15767 }, this, { single: true } );
15772 cur.setActive(false);
15773 pan.setActive(true);
15778 showPanelNext : function()
15780 var i = this.indexOfPanel(this.getActivePanel());
15782 if (i >= this.tabs.length - 1 && !this.autoslide) {
15786 if (i >= this.tabs.length - 1 && this.autoslide) {
15790 this.showPanel(this.tabs[i+1]);
15793 showPanelPrev : function()
15795 var i = this.indexOfPanel(this.getActivePanel());
15797 if (i < 1 && !this.autoslide) {
15801 if (i < 1 && this.autoslide) {
15802 i = this.tabs.length;
15805 this.showPanel(this.tabs[i-1]);
15808 initBullet : function()
15816 for (var i = 0; i < this.bullets; i++){
15817 var bullet = this.el.select('.bullet-' + i, true).first();
15823 bullet.on('click', (function(e, el, o, ii, t){
15825 e.preventDefault();
15827 _this.showPanel(ii);
15829 if(_this.autoslide && _this.slideFn){
15830 clearInterval(_this.slideFn);
15831 _this.slideFn = window.setInterval(function() {
15832 _this.showPanelNext();
15836 }).createDelegate(this, [i, bullet], true));
15840 setActiveBullet : function(i)
15846 Roo.each(this.el.select('.bullet', true).elements, function(el){
15847 el.removeClass('selected');
15850 var bullet = this.el.select('.bullet-' + i, true).first();
15856 bullet.addClass('selected');
15867 Roo.apply(Roo.bootstrap.TabGroup, {
15871 * register a Navigation Group
15872 * @param {Roo.bootstrap.NavGroup} the navgroup to add
15874 register : function(navgrp)
15876 this.groups[navgrp.navId] = navgrp;
15880 * fetch a Navigation Group based on the navigation ID
15881 * if one does not exist , it will get created.
15882 * @param {string} the navgroup to add
15883 * @returns {Roo.bootstrap.NavGroup} the navgroup
15885 get: function(navId) {
15886 if (typeof(this.groups[navId]) == 'undefined') {
15887 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
15889 return this.groups[navId] ;
15904 * @class Roo.bootstrap.TabPanel
15905 * @extends Roo.bootstrap.Component
15906 * Bootstrap TabPanel class
15907 * @cfg {Boolean} active panel active
15908 * @cfg {String} html panel content
15909 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
15910 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
15914 * Create a new TabPanel
15915 * @param {Object} config The config object
15918 Roo.bootstrap.TabPanel = function(config){
15919 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
15923 * Fires when the active status changes
15924 * @param {Roo.bootstrap.TabPanel} this
15925 * @param {Boolean} state the new state
15930 * @event beforedeactivate
15931 * Fires before a tab is de-activated - can be used to do validation on a form.
15932 * @param {Roo.bootstrap.TabPanel} this
15933 * @return {Boolean} false if there is an error
15936 'beforedeactivate': true
15939 this.tabId = this.tabId || Roo.id();
15943 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
15950 getAutoCreate : function(){
15953 // item is needed for carousel - not sure if it has any effect otherwise
15954 cls: 'tab-pane item',
15955 html: this.html || ''
15959 cfg.cls += ' active';
15963 cfg.tabId = this.tabId;
15970 initEvents: function()
15972 Roo.log('-------- init events on tab panel ---------');
15974 var p = this.parent();
15975 this.navId = this.navId || p.navId;
15977 if (typeof(this.navId) != 'undefined') {
15978 // not really needed.. but just in case.. parent should be a NavGroup.
15979 var tg = Roo.bootstrap.TabGroup.get(this.navId);
15980 Roo.log(['register', tg, this]);
15983 var i = tg.tabs.length - 1;
15985 if(this.active && tg.bullets > 0 && i < tg.bullets){
15986 tg.setActiveBullet(i);
15993 onRender : function(ct, position)
15995 // Roo.log("Call onRender: " + this.xtype);
15997 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16005 setActive: function(state)
16007 Roo.log("panel - set active " + this.tabId + "=" + state);
16009 this.active = state;
16011 this.el.removeClass('active');
16013 } else if (!this.el.hasClass('active')) {
16014 this.el.addClass('active');
16017 this.fireEvent('changed', this, state);
16034 * @class Roo.bootstrap.DateField
16035 * @extends Roo.bootstrap.Input
16036 * Bootstrap DateField class
16037 * @cfg {Number} weekStart default 0
16038 * @cfg {String} viewMode default empty, (months|years)
16039 * @cfg {String} minViewMode default empty, (months|years)
16040 * @cfg {Number} startDate default -Infinity
16041 * @cfg {Number} endDate default Infinity
16042 * @cfg {Boolean} todayHighlight default false
16043 * @cfg {Boolean} todayBtn default false
16044 * @cfg {Boolean} calendarWeeks default false
16045 * @cfg {Object} daysOfWeekDisabled default empty
16046 * @cfg {Boolean} singleMode default false (true | false)
16048 * @cfg {Boolean} keyboardNavigation default true
16049 * @cfg {String} language default en
16052 * Create a new DateField
16053 * @param {Object} config The config object
16056 Roo.bootstrap.DateField = function(config){
16057 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16061 * Fires when this field show.
16062 * @param {Roo.bootstrap.DateField} this
16063 * @param {Mixed} date The date value
16068 * Fires when this field hide.
16069 * @param {Roo.bootstrap.DateField} this
16070 * @param {Mixed} date The date value
16075 * Fires when select a date.
16076 * @param {Roo.bootstrap.DateField} this
16077 * @param {Mixed} date The date value
16083 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16086 * @cfg {String} format
16087 * The default date format string which can be overriden for localization support. The format must be
16088 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16092 * @cfg {String} altFormats
16093 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16094 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16096 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16104 todayHighlight : false,
16110 keyboardNavigation: true,
16112 calendarWeeks: false,
16114 startDate: -Infinity,
16118 daysOfWeekDisabled: [],
16122 singleMode : false,
16124 UTCDate: function()
16126 return new Date(Date.UTC.apply(Date, arguments));
16129 UTCToday: function()
16131 var today = new Date();
16132 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16135 getDate: function() {
16136 var d = this.getUTCDate();
16137 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16140 getUTCDate: function() {
16144 setDate: function(d) {
16145 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16148 setUTCDate: function(d) {
16150 this.setValue(this.formatDate(this.date));
16153 onRender: function(ct, position)
16156 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16158 this.language = this.language || 'en';
16159 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16160 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16162 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16163 this.format = this.format || 'm/d/y';
16164 this.isInline = false;
16165 this.isInput = true;
16166 this.component = this.el.select('.add-on', true).first() || false;
16167 this.component = (this.component && this.component.length === 0) ? false : this.component;
16168 this.hasInput = this.component && this.inputEL().length;
16170 if (typeof(this.minViewMode === 'string')) {
16171 switch (this.minViewMode) {
16173 this.minViewMode = 1;
16176 this.minViewMode = 2;
16179 this.minViewMode = 0;
16184 if (typeof(this.viewMode === 'string')) {
16185 switch (this.viewMode) {
16198 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16200 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16202 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16204 this.picker().on('mousedown', this.onMousedown, this);
16205 this.picker().on('click', this.onClick, this);
16207 this.picker().addClass('datepicker-dropdown');
16209 this.startViewMode = this.viewMode;
16211 if(this.singleMode){
16212 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16213 v.setVisibilityMode(Roo.Element.DISPLAY)
16217 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16218 v.setStyle('width', '189px');
16222 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16223 if(!this.calendarWeeks){
16228 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16229 v.attr('colspan', function(i, val){
16230 return parseInt(val) + 1;
16235 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16237 this.setStartDate(this.startDate);
16238 this.setEndDate(this.endDate);
16240 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16247 if(this.isInline) {
16252 picker : function()
16254 return this.pickerEl;
16255 // return this.el.select('.datepicker', true).first();
16258 fillDow: function()
16260 var dowCnt = this.weekStart;
16269 if(this.calendarWeeks){
16277 while (dowCnt < this.weekStart + 7) {
16281 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16285 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16288 fillMonths: function()
16291 var months = this.picker().select('>.datepicker-months td', true).first();
16293 months.dom.innerHTML = '';
16299 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16302 months.createChild(month);
16309 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;
16311 if (this.date < this.startDate) {
16312 this.viewDate = new Date(this.startDate);
16313 } else if (this.date > this.endDate) {
16314 this.viewDate = new Date(this.endDate);
16316 this.viewDate = new Date(this.date);
16324 var d = new Date(this.viewDate),
16325 year = d.getUTCFullYear(),
16326 month = d.getUTCMonth(),
16327 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16328 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16329 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16330 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16331 currentDate = this.date && this.date.valueOf(),
16332 today = this.UTCToday();
16334 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16336 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16338 // this.picker.select('>tfoot th.today').
16339 // .text(dates[this.language].today)
16340 // .toggle(this.todayBtn !== false);
16342 this.updateNavArrows();
16345 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16347 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16349 prevMonth.setUTCDate(day);
16351 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16353 var nextMonth = new Date(prevMonth);
16355 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16357 nextMonth = nextMonth.valueOf();
16359 var fillMonths = false;
16361 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16363 while(prevMonth.valueOf() < nextMonth) {
16366 if (prevMonth.getUTCDay() === this.weekStart) {
16368 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16376 if(this.calendarWeeks){
16377 // ISO 8601: First week contains first thursday.
16378 // ISO also states week starts on Monday, but we can be more abstract here.
16380 // Start of current week: based on weekstart/current date
16381 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16382 // Thursday of this week
16383 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16384 // First Thursday of year, year from thursday
16385 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16386 // Calendar week: ms between thursdays, div ms per day, div 7 days
16387 calWeek = (th - yth) / 864e5 / 7 + 1;
16389 fillMonths.cn.push({
16397 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16399 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16402 if (this.todayHighlight &&
16403 prevMonth.getUTCFullYear() == today.getFullYear() &&
16404 prevMonth.getUTCMonth() == today.getMonth() &&
16405 prevMonth.getUTCDate() == today.getDate()) {
16406 clsName += ' today';
16409 if (currentDate && prevMonth.valueOf() === currentDate) {
16410 clsName += ' active';
16413 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16414 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16415 clsName += ' disabled';
16418 fillMonths.cn.push({
16420 cls: 'day ' + clsName,
16421 html: prevMonth.getDate()
16424 prevMonth.setDate(prevMonth.getDate()+1);
16427 var currentYear = this.date && this.date.getUTCFullYear();
16428 var currentMonth = this.date && this.date.getUTCMonth();
16430 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16432 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16433 v.removeClass('active');
16435 if(currentYear === year && k === currentMonth){
16436 v.addClass('active');
16439 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16440 v.addClass('disabled');
16446 year = parseInt(year/10, 10) * 10;
16448 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16450 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16453 for (var i = -1; i < 11; i++) {
16454 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16456 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16464 showMode: function(dir)
16467 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16470 Roo.each(this.picker().select('>div',true).elements, function(v){
16471 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16474 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16479 if(this.isInline) return;
16481 this.picker().removeClass(['bottom', 'top']);
16483 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16485 * place to the top of element!
16489 this.picker().addClass('top');
16490 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16495 this.picker().addClass('bottom');
16497 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16500 parseDate : function(value)
16502 if(!value || value instanceof Date){
16505 var v = Date.parseDate(value, this.format);
16506 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16507 v = Date.parseDate(value, 'Y-m-d');
16509 if(!v && this.altFormats){
16510 if(!this.altFormatsArray){
16511 this.altFormatsArray = this.altFormats.split("|");
16513 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16514 v = Date.parseDate(value, this.altFormatsArray[i]);
16520 formatDate : function(date, fmt)
16522 return (!date || !(date instanceof Date)) ?
16523 date : date.dateFormat(fmt || this.format);
16526 onFocus : function()
16528 Roo.bootstrap.DateField.superclass.onFocus.call(this);
16532 onBlur : function()
16534 Roo.bootstrap.DateField.superclass.onBlur.call(this);
16536 var d = this.inputEl().getValue();
16545 this.picker().show();
16549 this.fireEvent('show', this, this.date);
16554 if(this.isInline) return;
16555 this.picker().hide();
16556 this.viewMode = this.startViewMode;
16559 this.fireEvent('hide', this, this.date);
16563 onMousedown: function(e)
16565 e.stopPropagation();
16566 e.preventDefault();
16571 Roo.bootstrap.DateField.superclass.keyup.call(this);
16575 setValue: function(v)
16578 // v can be a string or a date..
16581 var d = new Date(this.parseDate(v) ).clearTime();
16583 if(isNaN(d.getTime())){
16584 this.date = this.viewDate = '';
16585 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16589 v = this.formatDate(d);
16591 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16593 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16597 this.fireEvent('select', this, this.date);
16601 getValue: function()
16603 return this.formatDate(this.date);
16606 fireKey: function(e)
16608 if (!this.picker().isVisible()){
16609 if (e.keyCode == 27) // allow escape to hide and re-show picker
16614 var dateChanged = false,
16616 newDate, newViewDate;
16621 e.preventDefault();
16625 if (!this.keyboardNavigation) break;
16626 dir = e.keyCode == 37 ? -1 : 1;
16629 newDate = this.moveYear(this.date, dir);
16630 newViewDate = this.moveYear(this.viewDate, dir);
16631 } else if (e.shiftKey){
16632 newDate = this.moveMonth(this.date, dir);
16633 newViewDate = this.moveMonth(this.viewDate, dir);
16635 newDate = new Date(this.date);
16636 newDate.setUTCDate(this.date.getUTCDate() + dir);
16637 newViewDate = new Date(this.viewDate);
16638 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16640 if (this.dateWithinRange(newDate)){
16641 this.date = newDate;
16642 this.viewDate = newViewDate;
16643 this.setValue(this.formatDate(this.date));
16645 e.preventDefault();
16646 dateChanged = true;
16651 if (!this.keyboardNavigation) break;
16652 dir = e.keyCode == 38 ? -1 : 1;
16654 newDate = this.moveYear(this.date, dir);
16655 newViewDate = this.moveYear(this.viewDate, dir);
16656 } else if (e.shiftKey){
16657 newDate = this.moveMonth(this.date, dir);
16658 newViewDate = this.moveMonth(this.viewDate, dir);
16660 newDate = new Date(this.date);
16661 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16662 newViewDate = new Date(this.viewDate);
16663 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16665 if (this.dateWithinRange(newDate)){
16666 this.date = newDate;
16667 this.viewDate = newViewDate;
16668 this.setValue(this.formatDate(this.date));
16670 e.preventDefault();
16671 dateChanged = true;
16675 this.setValue(this.formatDate(this.date));
16677 e.preventDefault();
16680 this.setValue(this.formatDate(this.date));
16694 onClick: function(e)
16696 e.stopPropagation();
16697 e.preventDefault();
16699 var target = e.getTarget();
16701 if(target.nodeName.toLowerCase() === 'i'){
16702 target = Roo.get(target).dom.parentNode;
16705 var nodeName = target.nodeName;
16706 var className = target.className;
16707 var html = target.innerHTML;
16708 //Roo.log(nodeName);
16710 switch(nodeName.toLowerCase()) {
16712 switch(className) {
16718 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16719 switch(this.viewMode){
16721 this.viewDate = this.moveMonth(this.viewDate, dir);
16725 this.viewDate = this.moveYear(this.viewDate, dir);
16731 var date = new Date();
16732 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16734 this.setValue(this.formatDate(this.date));
16741 if (className.indexOf('disabled') < 0) {
16742 this.viewDate.setUTCDate(1);
16743 if (className.indexOf('month') > -1) {
16744 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16746 var year = parseInt(html, 10) || 0;
16747 this.viewDate.setUTCFullYear(year);
16751 if(this.singleMode){
16752 this.setValue(this.formatDate(this.viewDate));
16763 //Roo.log(className);
16764 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
16765 var day = parseInt(html, 10) || 1;
16766 var year = this.viewDate.getUTCFullYear(),
16767 month = this.viewDate.getUTCMonth();
16769 if (className.indexOf('old') > -1) {
16776 } else if (className.indexOf('new') > -1) {
16784 //Roo.log([year,month,day]);
16785 this.date = this.UTCDate(year, month, day,0,0,0,0);
16786 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16788 //Roo.log(this.formatDate(this.date));
16789 this.setValue(this.formatDate(this.date));
16796 setStartDate: function(startDate)
16798 this.startDate = startDate || -Infinity;
16799 if (this.startDate !== -Infinity) {
16800 this.startDate = this.parseDate(this.startDate);
16803 this.updateNavArrows();
16806 setEndDate: function(endDate)
16808 this.endDate = endDate || Infinity;
16809 if (this.endDate !== Infinity) {
16810 this.endDate = this.parseDate(this.endDate);
16813 this.updateNavArrows();
16816 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
16818 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
16819 if (typeof(this.daysOfWeekDisabled) !== 'object') {
16820 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
16822 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
16823 return parseInt(d, 10);
16826 this.updateNavArrows();
16829 updateNavArrows: function()
16831 if(this.singleMode){
16835 var d = new Date(this.viewDate),
16836 year = d.getUTCFullYear(),
16837 month = d.getUTCMonth();
16839 Roo.each(this.picker().select('.prev', true).elements, function(v){
16841 switch (this.viewMode) {
16844 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
16850 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
16857 Roo.each(this.picker().select('.next', true).elements, function(v){
16859 switch (this.viewMode) {
16862 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
16868 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
16876 moveMonth: function(date, dir)
16878 if (!dir) return date;
16879 var new_date = new Date(date.valueOf()),
16880 day = new_date.getUTCDate(),
16881 month = new_date.getUTCMonth(),
16882 mag = Math.abs(dir),
16884 dir = dir > 0 ? 1 : -1;
16887 // If going back one month, make sure month is not current month
16888 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
16890 return new_date.getUTCMonth() == month;
16892 // If going forward one month, make sure month is as expected
16893 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
16895 return new_date.getUTCMonth() != new_month;
16897 new_month = month + dir;
16898 new_date.setUTCMonth(new_month);
16899 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
16900 if (new_month < 0 || new_month > 11)
16901 new_month = (new_month + 12) % 12;
16903 // For magnitudes >1, move one month at a time...
16904 for (var i=0; i<mag; i++)
16905 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
16906 new_date = this.moveMonth(new_date, dir);
16907 // ...then reset the day, keeping it in the new month
16908 new_month = new_date.getUTCMonth();
16909 new_date.setUTCDate(day);
16911 return new_month != new_date.getUTCMonth();
16914 // Common date-resetting loop -- if date is beyond end of month, make it
16917 new_date.setUTCDate(--day);
16918 new_date.setUTCMonth(new_month);
16923 moveYear: function(date, dir)
16925 return this.moveMonth(date, dir*12);
16928 dateWithinRange: function(date)
16930 return date >= this.startDate && date <= this.endDate;
16936 this.picker().remove();
16941 Roo.apply(Roo.bootstrap.DateField, {
16952 html: '<i class="fa fa-arrow-left"/>'
16962 html: '<i class="fa fa-arrow-right"/>'
17004 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17005 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17006 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17007 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17008 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17021 navFnc: 'FullYear',
17026 navFnc: 'FullYear',
17031 Roo.apply(Roo.bootstrap.DateField, {
17035 cls: 'datepicker dropdown-menu roo-dynamic',
17039 cls: 'datepicker-days',
17043 cls: 'table-condensed',
17045 Roo.bootstrap.DateField.head,
17049 Roo.bootstrap.DateField.footer
17056 cls: 'datepicker-months',
17060 cls: 'table-condensed',
17062 Roo.bootstrap.DateField.head,
17063 Roo.bootstrap.DateField.content,
17064 Roo.bootstrap.DateField.footer
17071 cls: 'datepicker-years',
17075 cls: 'table-condensed',
17077 Roo.bootstrap.DateField.head,
17078 Roo.bootstrap.DateField.content,
17079 Roo.bootstrap.DateField.footer
17098 * @class Roo.bootstrap.TimeField
17099 * @extends Roo.bootstrap.Input
17100 * Bootstrap DateField class
17104 * Create a new TimeField
17105 * @param {Object} config The config object
17108 Roo.bootstrap.TimeField = function(config){
17109 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17113 * Fires when this field show.
17114 * @param {Roo.bootstrap.DateField} thisthis
17115 * @param {Mixed} date The date value
17120 * Fires when this field hide.
17121 * @param {Roo.bootstrap.DateField} this
17122 * @param {Mixed} date The date value
17127 * Fires when select a date.
17128 * @param {Roo.bootstrap.DateField} this
17129 * @param {Mixed} date The date value
17135 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17138 * @cfg {String} format
17139 * The default time format string which can be overriden for localization support. The format must be
17140 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17144 onRender: function(ct, position)
17147 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17149 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17151 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17153 this.pop = this.picker().select('>.datepicker-time',true).first();
17154 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17156 this.picker().on('mousedown', this.onMousedown, this);
17157 this.picker().on('click', this.onClick, this);
17159 this.picker().addClass('datepicker-dropdown');
17164 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17165 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17166 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17167 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17168 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17169 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17173 fireKey: function(e){
17174 if (!this.picker().isVisible()){
17175 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17181 e.preventDefault();
17189 this.onTogglePeriod();
17192 this.onIncrementMinutes();
17195 this.onDecrementMinutes();
17204 onClick: function(e) {
17205 e.stopPropagation();
17206 e.preventDefault();
17209 picker : function()
17211 return this.el.select('.datepicker', true).first();
17214 fillTime: function()
17216 var time = this.pop.select('tbody', true).first();
17218 time.dom.innerHTML = '';
17233 cls: 'hours-up glyphicon glyphicon-chevron-up'
17253 cls: 'minutes-up glyphicon glyphicon-chevron-up'
17274 cls: 'timepicker-hour',
17289 cls: 'timepicker-minute',
17304 cls: 'btn btn-primary period',
17326 cls: 'hours-down glyphicon glyphicon-chevron-down'
17346 cls: 'minutes-down glyphicon glyphicon-chevron-down'
17364 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17371 var hours = this.time.getHours();
17372 var minutes = this.time.getMinutes();
17385 hours = hours - 12;
17389 hours = '0' + hours;
17393 minutes = '0' + minutes;
17396 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17397 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17398 this.pop.select('button', true).first().dom.innerHTML = period;
17404 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17406 var cls = ['bottom'];
17408 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17415 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17420 this.picker().addClass(cls.join('-'));
17424 Roo.each(cls, function(c){
17426 _this.picker().setTop(_this.inputEl().getHeight());
17430 _this.picker().setTop(0 - _this.picker().getHeight());
17435 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17439 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17446 onFocus : function()
17448 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17452 onBlur : function()
17454 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17460 this.picker().show();
17465 this.fireEvent('show', this, this.date);
17470 this.picker().hide();
17473 this.fireEvent('hide', this, this.date);
17476 setTime : function()
17479 this.setValue(this.time.format(this.format));
17481 this.fireEvent('select', this, this.date);
17486 onMousedown: function(e){
17487 e.stopPropagation();
17488 e.preventDefault();
17491 onIncrementHours: function()
17493 Roo.log('onIncrementHours');
17494 this.time = this.time.add(Date.HOUR, 1);
17499 onDecrementHours: function()
17501 Roo.log('onDecrementHours');
17502 this.time = this.time.add(Date.HOUR, -1);
17506 onIncrementMinutes: function()
17508 Roo.log('onIncrementMinutes');
17509 this.time = this.time.add(Date.MINUTE, 1);
17513 onDecrementMinutes: function()
17515 Roo.log('onDecrementMinutes');
17516 this.time = this.time.add(Date.MINUTE, -1);
17520 onTogglePeriod: function()
17522 Roo.log('onTogglePeriod');
17523 this.time = this.time.add(Date.HOUR, 12);
17530 Roo.apply(Roo.bootstrap.TimeField, {
17560 cls: 'btn btn-info ok',
17572 Roo.apply(Roo.bootstrap.TimeField, {
17576 cls: 'datepicker dropdown-menu',
17580 cls: 'datepicker-time',
17584 cls: 'table-condensed',
17586 Roo.bootstrap.TimeField.content,
17587 Roo.bootstrap.TimeField.footer
17606 * @class Roo.bootstrap.MonthField
17607 * @extends Roo.bootstrap.Input
17608 * Bootstrap MonthField class
17610 * @cfg {String} language default en
17613 * Create a new MonthField
17614 * @param {Object} config The config object
17617 Roo.bootstrap.MonthField = function(config){
17618 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17623 * Fires when this field show.
17624 * @param {Roo.bootstrap.MonthField} this
17625 * @param {Mixed} date The date value
17630 * Fires when this field hide.
17631 * @param {Roo.bootstrap.MonthField} this
17632 * @param {Mixed} date The date value
17637 * Fires when select a date.
17638 * @param {Roo.bootstrap.MonthField} this
17639 * @param {String} oldvalue The old value
17640 * @param {String} newvalue The new value
17646 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
17648 onRender: function(ct, position)
17651 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17653 this.language = this.language || 'en';
17654 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17655 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17657 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17658 this.isInline = false;
17659 this.isInput = true;
17660 this.component = this.el.select('.add-on', true).first() || false;
17661 this.component = (this.component && this.component.length === 0) ? false : this.component;
17662 this.hasInput = this.component && this.inputEL().length;
17664 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17666 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17668 this.picker().on('mousedown', this.onMousedown, this);
17669 this.picker().on('click', this.onClick, this);
17671 this.picker().addClass('datepicker-dropdown');
17673 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17674 v.setStyle('width', '189px');
17681 if(this.isInline) {
17687 setValue: function(v, suppressEvent)
17689 var o = this.getValue();
17691 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17695 if(suppressEvent !== true){
17696 this.fireEvent('select', this, o, v);
17701 getValue: function()
17706 onClick: function(e)
17708 e.stopPropagation();
17709 e.preventDefault();
17711 var target = e.getTarget();
17713 if(target.nodeName.toLowerCase() === 'i'){
17714 target = Roo.get(target).dom.parentNode;
17717 var nodeName = target.nodeName;
17718 var className = target.className;
17719 var html = target.innerHTML;
17721 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17725 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17727 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17733 picker : function()
17735 return this.pickerEl;
17738 fillMonths: function()
17741 var months = this.picker().select('>.datepicker-months td', true).first();
17743 months.dom.innerHTML = '';
17749 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
17752 months.createChild(month);
17761 if(typeof(this.vIndex) == 'undefined' && this.value.length){
17762 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
17765 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
17766 e.removeClass('active');
17768 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
17769 e.addClass('active');
17776 if(this.isInline) return;
17778 this.picker().removeClass(['bottom', 'top']);
17780 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17782 * place to the top of element!
17786 this.picker().addClass('top');
17787 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17792 this.picker().addClass('bottom');
17794 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17797 onFocus : function()
17799 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
17803 onBlur : function()
17805 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
17807 var d = this.inputEl().getValue();
17816 this.picker().show();
17817 this.picker().select('>.datepicker-months', true).first().show();
17821 this.fireEvent('show', this, this.date);
17826 if(this.isInline) return;
17827 this.picker().hide();
17828 this.fireEvent('hide', this, this.date);
17832 onMousedown: function(e)
17834 e.stopPropagation();
17835 e.preventDefault();
17840 Roo.bootstrap.MonthField.superclass.keyup.call(this);
17844 fireKey: function(e)
17846 if (!this.picker().isVisible()){
17847 if (e.keyCode == 27) // allow escape to hide and re-show picker
17857 e.preventDefault();
17861 dir = e.keyCode == 37 ? -1 : 1;
17863 this.vIndex = this.vIndex + dir;
17865 if(this.vIndex < 0){
17869 if(this.vIndex > 11){
17873 if(isNaN(this.vIndex)){
17877 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17883 dir = e.keyCode == 38 ? -1 : 1;
17885 this.vIndex = this.vIndex + dir * 4;
17887 if(this.vIndex < 0){
17891 if(this.vIndex > 11){
17895 if(isNaN(this.vIndex)){
17899 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17904 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17905 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17909 e.preventDefault();
17912 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17913 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17929 this.picker().remove();
17934 Roo.apply(Roo.bootstrap.MonthField, {
17953 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17954 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
17959 Roo.apply(Roo.bootstrap.MonthField, {
17963 cls: 'datepicker dropdown-menu roo-dynamic',
17967 cls: 'datepicker-months',
17971 cls: 'table-condensed',
17973 Roo.bootstrap.DateField.content
17993 * @class Roo.bootstrap.CheckBox
17994 * @extends Roo.bootstrap.Input
17995 * Bootstrap CheckBox class
17997 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
17998 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
17999 * @cfg {String} boxLabel The text that appears beside the checkbox
18000 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18001 * @cfg {Boolean} checked initnal the element
18002 * @cfg {Boolean} inline inline the element (default false)
18003 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18006 * Create a new CheckBox
18007 * @param {Object} config The config object
18010 Roo.bootstrap.CheckBox = function(config){
18011 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18016 * Fires when the element is checked or unchecked.
18017 * @param {Roo.bootstrap.CheckBox} this This input
18018 * @param {Boolean} checked The new checked value
18025 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18027 inputType: 'checkbox',
18035 getAutoCreate : function()
18037 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18043 cfg.cls = 'form-group ' + this.inputType; //input-group
18046 cfg.cls += ' ' + this.inputType + '-inline';
18052 type : this.inputType,
18053 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18054 cls : 'roo-' + this.inputType, //'form-box',
18055 placeholder : this.placeholder || ''
18059 if (this.weight) { // Validity check?
18060 cfg.cls += " " + this.inputType + "-" + this.weight;
18063 if (this.disabled) {
18064 input.disabled=true;
18068 input.checked = this.checked;
18072 input.name = this.name;
18076 input.cls += ' input-' + this.size;
18081 ['xs','sm','md','lg'].map(function(size){
18082 if (settings[size]) {
18083 cfg.cls += ' col-' + size + '-' + settings[size];
18087 var inputblock = input;
18089 if (this.before || this.after) {
18092 cls : 'input-group',
18097 inputblock.cn.push({
18099 cls : 'input-group-addon',
18104 inputblock.cn.push(input);
18107 inputblock.cn.push({
18109 cls : 'input-group-addon',
18116 if (align ==='left' && this.fieldLabel.length) {
18117 Roo.log("left and has label");
18123 cls : 'control-label col-md-' + this.labelWidth,
18124 html : this.fieldLabel
18128 cls : "col-md-" + (12 - this.labelWidth),
18135 } else if ( this.fieldLabel.length) {
18140 tag: this.boxLabel ? 'span' : 'label',
18142 cls: 'control-label box-input-label',
18143 //cls : 'input-group-addon',
18144 html : this.fieldLabel
18154 Roo.log(" no label && no align");
18155 cfg.cn = [ inputblock ] ;
18160 var boxLabelCfg = {
18162 //'for': id, // box label is handled by onclick - so no for...
18164 html: this.boxLabel
18168 boxLabelCfg.tooltip = this.tooltip;
18171 cfg.cn.push(boxLabelCfg);
18181 * return the real input element.
18183 inputEl: function ()
18185 return this.el.select('input.roo-' + this.inputType,true).first();
18188 labelEl: function()
18190 return this.el.select('label.control-label',true).first();
18192 /* depricated... */
18196 return this.labelEl();
18199 initEvents : function()
18201 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18203 this.inputEl().on('click', this.onClick, this);
18205 if (this.boxLabel) {
18206 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18209 this.startValue = this.getValue();
18212 Roo.bootstrap.CheckBox.register(this);
18216 onClick : function()
18218 this.setChecked(!this.checked);
18221 setChecked : function(state,suppressEvent)
18223 this.startValue = this.getValue();
18225 if(this.inputType == 'radio'){
18227 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18228 e.dom.checked = false;
18231 this.inputEl().dom.checked = true;
18233 this.inputEl().dom.value = this.inputValue;
18235 if(suppressEvent !== true){
18236 this.fireEvent('check', this, true);
18244 this.checked = state;
18246 this.inputEl().dom.checked = state;
18248 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18250 if(suppressEvent !== true){
18251 this.fireEvent('check', this, state);
18257 getValue : function()
18259 if(this.inputType == 'radio'){
18260 return this.getGroupValue();
18263 return this.inputEl().getValue();
18267 getGroupValue : function()
18269 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18273 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18276 setValue : function(v,suppressEvent)
18278 if(this.inputType == 'radio'){
18279 this.setGroupValue(v, suppressEvent);
18283 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18288 setGroupValue : function(v, suppressEvent)
18290 this.startValue = this.getValue();
18292 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18293 e.dom.checked = false;
18295 if(e.dom.value == v){
18296 e.dom.checked = true;
18300 if(suppressEvent !== true){
18301 this.fireEvent('check', this, true);
18309 validate : function()
18313 (this.inputType == 'radio' && this.validateRadio()) ||
18314 (this.inputType == 'checkbox' && this.validateCheckbox())
18320 this.markInvalid();
18324 validateRadio : function()
18328 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18329 if(!e.dom.checked){
18341 validateCheckbox : function()
18344 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18347 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18355 for(var i in group){
18360 r = (group[i].getValue() == group[i].inputValue) ? true : false;
18367 * Mark this field as valid
18369 markValid : function()
18371 if(this.allowBlank){
18377 this.fireEvent('valid', this);
18379 if(this.inputType == 'radio'){
18380 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18381 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18382 e.findParent('.form-group', false, true).addClass(_this.validClass);
18389 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18390 this.el.findParent('.form-group', false, true).addClass(this.validClass);
18394 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18400 for(var i in group){
18401 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18402 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18407 * Mark this field as invalid
18408 * @param {String} msg The validation message
18410 markInvalid : function(msg)
18412 if(this.allowBlank){
18418 this.fireEvent('invalid', this, msg);
18420 if(this.inputType == 'radio'){
18421 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18422 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18423 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18430 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18431 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18435 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18441 for(var i in group){
18442 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18443 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18450 Roo.apply(Roo.bootstrap.CheckBox, {
18455 * register a CheckBox Group
18456 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18458 register : function(checkbox)
18460 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18461 this.groups[checkbox.groupId] = {};
18464 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18468 this.groups[checkbox.groupId][checkbox.name] = checkbox;
18472 * fetch a CheckBox Group based on the group ID
18473 * @param {string} the group ID
18474 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18476 get: function(groupId) {
18477 if (typeof(this.groups[groupId]) == 'undefined') {
18481 return this.groups[groupId] ;
18493 *<div class="radio">
18495 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18496 Option one is this and that—be sure to include why it's great
18503 *<label class="radio-inline">fieldLabel</label>
18504 *<label class="radio-inline">
18505 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18513 * @class Roo.bootstrap.Radio
18514 * @extends Roo.bootstrap.CheckBox
18515 * Bootstrap Radio class
18518 * Create a new Radio
18519 * @param {Object} config The config object
18522 Roo.bootstrap.Radio = function(config){
18523 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18527 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
18529 inputType: 'radio',
18533 getAutoCreate : function()
18535 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18536 align = align || 'left'; // default...
18543 tag : this.inline ? 'span' : 'div',
18548 var inline = this.inline ? ' radio-inline' : '';
18552 // does not need for, as we wrap the input with it..
18554 cls : 'control-label box-label' + inline,
18557 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18561 //cls : 'control-label' + inline,
18562 html : this.fieldLabel,
18563 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18572 type : this.inputType,
18573 //value : (!this.checked) ? this.valueOff : this.inputValue,
18574 value : this.inputValue,
18576 placeholder : this.placeholder || '' // ?? needed????
18579 if (this.weight) { // Validity check?
18580 input.cls += " radio-" + this.weight;
18582 if (this.disabled) {
18583 input.disabled=true;
18587 input.checked = this.checked;
18591 input.name = this.name;
18595 input.cls += ' input-' + this.size;
18598 //?? can span's inline have a width??
18601 ['xs','sm','md','lg'].map(function(size){
18602 if (settings[size]) {
18603 cfg.cls += ' col-' + size + '-' + settings[size];
18607 var inputblock = input;
18609 if (this.before || this.after) {
18612 cls : 'input-group',
18617 inputblock.cn.push({
18619 cls : 'input-group-addon',
18623 inputblock.cn.push(input);
18625 inputblock.cn.push({
18627 cls : 'input-group-addon',
18635 if (this.fieldLabel && this.fieldLabel.length) {
18636 cfg.cn.push(fieldLabel);
18639 // normal bootstrap puts the input inside the label.
18640 // however with our styled version - it has to go after the input.
18642 //lbl.cn.push(inputblock);
18646 cls: 'radio' + inline,
18653 cfg.cn.push( lblwrap);
18658 html: this.boxLabel
18667 initEvents : function()
18669 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18671 this.inputEl().on('click', this.onClick, this);
18672 if (this.boxLabel) {
18673 Roo.log('find label')
18674 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
18679 inputEl: function ()
18681 return this.el.select('input.roo-radio',true).first();
18683 onClick : function()
18686 this.setChecked(true);
18689 setChecked : function(state,suppressEvent)
18692 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18693 v.dom.checked = false;
18696 Roo.log(this.inputEl().dom);
18697 this.checked = state;
18698 this.inputEl().dom.checked = state;
18700 if(suppressEvent !== true){
18701 this.fireEvent('check', this, state);
18704 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18708 getGroupValue : function()
18711 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18712 if(v.dom.checked == true){
18713 value = v.dom.value;
18721 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
18722 * @return {Mixed} value The field value
18724 getValue : function(){
18725 return this.getGroupValue();
18731 //<script type="text/javascript">
18734 * Based Ext JS Library 1.1.1
18735 * Copyright(c) 2006-2007, Ext JS, LLC.
18741 * @class Roo.HtmlEditorCore
18742 * @extends Roo.Component
18743 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18745 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18748 Roo.HtmlEditorCore = function(config){
18751 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
18756 * @event initialize
18757 * Fires when the editor is fully initialized (including the iframe)
18758 * @param {Roo.HtmlEditorCore} this
18763 * Fires when the editor is first receives the focus. Any insertion must wait
18764 * until after this event.
18765 * @param {Roo.HtmlEditorCore} this
18769 * @event beforesync
18770 * Fires before the textarea is updated with content from the editor iframe. Return false
18771 * to cancel the sync.
18772 * @param {Roo.HtmlEditorCore} this
18773 * @param {String} html
18777 * @event beforepush
18778 * Fires before the iframe editor is updated with content from the textarea. Return false
18779 * to cancel the push.
18780 * @param {Roo.HtmlEditorCore} this
18781 * @param {String} html
18786 * Fires when the textarea is updated with content from the editor iframe.
18787 * @param {Roo.HtmlEditorCore} this
18788 * @param {String} html
18793 * Fires when the iframe editor is updated with content from the textarea.
18794 * @param {Roo.HtmlEditorCore} this
18795 * @param {String} html
18800 * @event editorevent
18801 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18802 * @param {Roo.HtmlEditorCore} this
18808 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
18810 // defaults : white / black...
18811 this.applyBlacklists();
18818 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
18822 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
18828 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
18833 * @cfg {Number} height (in pixels)
18837 * @cfg {Number} width (in pixels)
18842 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18845 stylesheets: false,
18850 // private properties
18851 validationEvent : false,
18853 initialized : false,
18855 sourceEditMode : false,
18856 onFocus : Roo.emptyFn,
18858 hideMode:'offsets',
18862 // blacklist + whitelisted elements..
18869 * Protected method that will not generally be called directly. It
18870 * is called when the editor initializes the iframe with HTML contents. Override this method if you
18871 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
18873 getDocMarkup : function(){
18877 // inherit styels from page...??
18878 if (this.stylesheets === false) {
18880 Roo.get(document.head).select('style').each(function(node) {
18881 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18884 Roo.get(document.head).select('link').each(function(node) {
18885 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18888 } else if (!this.stylesheets.length) {
18890 st = '<style type="text/css">' +
18891 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18897 st += '<style type="text/css">' +
18898 'IMG { cursor: pointer } ' +
18902 return '<html><head>' + st +
18903 //<style type="text/css">' +
18904 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18906 ' </head><body class="roo-htmleditor-body"></body></html>';
18910 onRender : function(ct, position)
18913 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
18914 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
18917 this.el.dom.style.border = '0 none';
18918 this.el.dom.setAttribute('tabIndex', -1);
18919 this.el.addClass('x-hidden hide');
18923 if(Roo.isIE){ // fix IE 1px bogus margin
18924 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
18928 this.frameId = Roo.id();
18932 var iframe = this.owner.wrap.createChild({
18934 cls: 'form-control', // bootstrap..
18936 name: this.frameId,
18937 frameBorder : 'no',
18938 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
18943 this.iframe = iframe.dom;
18945 this.assignDocWin();
18947 this.doc.designMode = 'on';
18950 this.doc.write(this.getDocMarkup());
18954 var task = { // must defer to wait for browser to be ready
18956 //console.log("run task?" + this.doc.readyState);
18957 this.assignDocWin();
18958 if(this.doc.body || this.doc.readyState == 'complete'){
18960 this.doc.designMode="on";
18964 Roo.TaskMgr.stop(task);
18965 this.initEditor.defer(10, this);
18972 Roo.TaskMgr.start(task);
18977 onResize : function(w, h)
18979 Roo.log('resize: ' +w + ',' + h );
18980 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
18984 if(typeof w == 'number'){
18986 this.iframe.style.width = w + 'px';
18988 if(typeof h == 'number'){
18990 this.iframe.style.height = h + 'px';
18992 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
18999 * Toggles the editor between standard and source edit mode.
19000 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19002 toggleSourceEdit : function(sourceEditMode){
19004 this.sourceEditMode = sourceEditMode === true;
19006 if(this.sourceEditMode){
19008 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19011 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19012 //this.iframe.className = '';
19015 //this.setSize(this.owner.wrap.getSize());
19016 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19023 * Protected method that will not generally be called directly. If you need/want
19024 * custom HTML cleanup, this is the method you should override.
19025 * @param {String} html The HTML to be cleaned
19026 * return {String} The cleaned HTML
19028 cleanHtml : function(html){
19029 html = String(html);
19030 if(html.length > 5){
19031 if(Roo.isSafari){ // strip safari nonsense
19032 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19035 if(html == ' '){
19042 * HTML Editor -> Textarea
19043 * Protected method that will not generally be called directly. Syncs the contents
19044 * of the editor iframe with the textarea.
19046 syncValue : function(){
19047 if(this.initialized){
19048 var bd = (this.doc.body || this.doc.documentElement);
19049 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19050 var html = bd.innerHTML;
19052 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19053 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19055 html = '<div style="'+m[0]+'">' + html + '</div>';
19058 html = this.cleanHtml(html);
19059 // fix up the special chars.. normaly like back quotes in word...
19060 // however we do not want to do this with chinese..
19061 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19062 var cc = b.charCodeAt();
19064 (cc >= 0x4E00 && cc < 0xA000 ) ||
19065 (cc >= 0x3400 && cc < 0x4E00 ) ||
19066 (cc >= 0xf900 && cc < 0xfb00 )
19072 if(this.owner.fireEvent('beforesync', this, html) !== false){
19073 this.el.dom.value = html;
19074 this.owner.fireEvent('sync', this, html);
19080 * Protected method that will not generally be called directly. Pushes the value of the textarea
19081 * into the iframe editor.
19083 pushValue : function(){
19084 if(this.initialized){
19085 var v = this.el.dom.value.trim();
19087 // if(v.length < 1){
19091 if(this.owner.fireEvent('beforepush', this, v) !== false){
19092 var d = (this.doc.body || this.doc.documentElement);
19094 this.cleanUpPaste();
19095 this.el.dom.value = d.innerHTML;
19096 this.owner.fireEvent('push', this, v);
19102 deferFocus : function(){
19103 this.focus.defer(10, this);
19107 focus : function(){
19108 if(this.win && !this.sourceEditMode){
19115 assignDocWin: function()
19117 var iframe = this.iframe;
19120 this.doc = iframe.contentWindow.document;
19121 this.win = iframe.contentWindow;
19123 // if (!Roo.get(this.frameId)) {
19126 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19127 // this.win = Roo.get(this.frameId).dom.contentWindow;
19129 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19133 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19134 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19139 initEditor : function(){
19140 //console.log("INIT EDITOR");
19141 this.assignDocWin();
19145 this.doc.designMode="on";
19147 this.doc.write(this.getDocMarkup());
19150 var dbody = (this.doc.body || this.doc.documentElement);
19151 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19152 // this copies styles from the containing element into thsi one..
19153 // not sure why we need all of this..
19154 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19156 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19157 //ss['background-attachment'] = 'fixed'; // w3c
19158 dbody.bgProperties = 'fixed'; // ie
19159 //Roo.DomHelper.applyStyles(dbody, ss);
19160 Roo.EventManager.on(this.doc, {
19161 //'mousedown': this.onEditorEvent,
19162 'mouseup': this.onEditorEvent,
19163 'dblclick': this.onEditorEvent,
19164 'click': this.onEditorEvent,
19165 'keyup': this.onEditorEvent,
19170 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19172 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19173 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19175 this.initialized = true;
19177 this.owner.fireEvent('initialize', this);
19182 onDestroy : function(){
19188 //for (var i =0; i < this.toolbars.length;i++) {
19189 // // fixme - ask toolbars for heights?
19190 // this.toolbars[i].onDestroy();
19193 //this.wrap.dom.innerHTML = '';
19194 //this.wrap.remove();
19199 onFirstFocus : function(){
19201 this.assignDocWin();
19204 this.activated = true;
19207 if(Roo.isGecko){ // prevent silly gecko errors
19209 var s = this.win.getSelection();
19210 if(!s.focusNode || s.focusNode.nodeType != 3){
19211 var r = s.getRangeAt(0);
19212 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19217 this.execCmd('useCSS', true);
19218 this.execCmd('styleWithCSS', false);
19221 this.owner.fireEvent('activate', this);
19225 adjustFont: function(btn){
19226 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19227 //if(Roo.isSafari){ // safari
19230 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19231 if(Roo.isSafari){ // safari
19232 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19233 v = (v < 10) ? 10 : v;
19234 v = (v > 48) ? 48 : v;
19235 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19240 v = Math.max(1, v+adjust);
19242 this.execCmd('FontSize', v );
19245 onEditorEvent : function(e)
19247 this.owner.fireEvent('editorevent', this, e);
19248 // this.updateToolbar();
19249 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19252 insertTag : function(tg)
19254 // could be a bit smarter... -> wrap the current selected tRoo..
19255 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19257 range = this.createRange(this.getSelection());
19258 var wrappingNode = this.doc.createElement(tg.toLowerCase());
19259 wrappingNode.appendChild(range.extractContents());
19260 range.insertNode(wrappingNode);
19267 this.execCmd("formatblock", tg);
19271 insertText : function(txt)
19275 var range = this.createRange();
19276 range.deleteContents();
19277 //alert(Sender.getAttribute('label'));
19279 range.insertNode(this.doc.createTextNode(txt));
19285 * Executes a Midas editor command on the editor document and performs necessary focus and
19286 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19287 * @param {String} cmd The Midas command
19288 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19290 relayCmd : function(cmd, value){
19292 this.execCmd(cmd, value);
19293 this.owner.fireEvent('editorevent', this);
19294 //this.updateToolbar();
19295 this.owner.deferFocus();
19299 * Executes a Midas editor command directly on the editor document.
19300 * For visual commands, you should use {@link #relayCmd} instead.
19301 * <b>This should only be called after the editor is initialized.</b>
19302 * @param {String} cmd The Midas command
19303 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19305 execCmd : function(cmd, value){
19306 this.doc.execCommand(cmd, false, value === undefined ? null : value);
19313 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19315 * @param {String} text | dom node..
19317 insertAtCursor : function(text)
19322 if(!this.activated){
19328 var r = this.doc.selection.createRange();
19339 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19343 // from jquery ui (MIT licenced)
19345 var win = this.win;
19347 if (win.getSelection && win.getSelection().getRangeAt) {
19348 range = win.getSelection().getRangeAt(0);
19349 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19350 range.insertNode(node);
19351 } else if (win.document.selection && win.document.selection.createRange) {
19352 // no firefox support
19353 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19354 win.document.selection.createRange().pasteHTML(txt);
19356 // no firefox support
19357 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19358 this.execCmd('InsertHTML', txt);
19367 mozKeyPress : function(e){
19369 var c = e.getCharCode(), cmd;
19372 c = String.fromCharCode(c).toLowerCase();
19386 this.cleanUpPaste.defer(100, this);
19394 e.preventDefault();
19402 fixKeys : function(){ // load time branching for fastest keydown performance
19404 return function(e){
19405 var k = e.getKey(), r;
19408 r = this.doc.selection.createRange();
19411 r.pasteHTML('    ');
19418 r = this.doc.selection.createRange();
19420 var target = r.parentElement();
19421 if(!target || target.tagName.toLowerCase() != 'li'){
19423 r.pasteHTML('<br />');
19429 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19430 this.cleanUpPaste.defer(100, this);
19436 }else if(Roo.isOpera){
19437 return function(e){
19438 var k = e.getKey();
19442 this.execCmd('InsertHTML','    ');
19445 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19446 this.cleanUpPaste.defer(100, this);
19451 }else if(Roo.isSafari){
19452 return function(e){
19453 var k = e.getKey();
19457 this.execCmd('InsertText','\t');
19461 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19462 this.cleanUpPaste.defer(100, this);
19470 getAllAncestors: function()
19472 var p = this.getSelectedNode();
19475 a.push(p); // push blank onto stack..
19476 p = this.getParentElement();
19480 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19484 a.push(this.doc.body);
19488 lastSelNode : false,
19491 getSelection : function()
19493 this.assignDocWin();
19494 return Roo.isIE ? this.doc.selection : this.win.getSelection();
19497 getSelectedNode: function()
19499 // this may only work on Gecko!!!
19501 // should we cache this!!!!
19506 var range = this.createRange(this.getSelection()).cloneRange();
19509 var parent = range.parentElement();
19511 var testRange = range.duplicate();
19512 testRange.moveToElementText(parent);
19513 if (testRange.inRange(range)) {
19516 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19519 parent = parent.parentElement;
19524 // is ancestor a text element.
19525 var ac = range.commonAncestorContainer;
19526 if (ac.nodeType == 3) {
19527 ac = ac.parentNode;
19530 var ar = ac.childNodes;
19533 var other_nodes = [];
19534 var has_other_nodes = false;
19535 for (var i=0;i<ar.length;i++) {
19536 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
19539 // fullly contained node.
19541 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19546 // probably selected..
19547 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19548 other_nodes.push(ar[i]);
19552 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
19557 has_other_nodes = true;
19559 if (!nodes.length && other_nodes.length) {
19560 nodes= other_nodes;
19562 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19568 createRange: function(sel)
19570 // this has strange effects when using with
19571 // top toolbar - not sure if it's a great idea.
19572 //this.editor.contentWindow.focus();
19573 if (typeof sel != "undefined") {
19575 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19577 return this.doc.createRange();
19580 return this.doc.createRange();
19583 getParentElement: function()
19586 this.assignDocWin();
19587 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19589 var range = this.createRange(sel);
19592 var p = range.commonAncestorContainer;
19593 while (p.nodeType == 3) { // text node
19604 * Range intersection.. the hard stuff...
19608 * [ -- selected range --- ]
19612 * if end is before start or hits it. fail.
19613 * if start is after end or hits it fail.
19615 * if either hits (but other is outside. - then it's not
19621 // @see http://www.thismuchiknow.co.uk/?p=64.
19622 rangeIntersectsNode : function(range, node)
19624 var nodeRange = node.ownerDocument.createRange();
19626 nodeRange.selectNode(node);
19628 nodeRange.selectNodeContents(node);
19631 var rangeStartRange = range.cloneRange();
19632 rangeStartRange.collapse(true);
19634 var rangeEndRange = range.cloneRange();
19635 rangeEndRange.collapse(false);
19637 var nodeStartRange = nodeRange.cloneRange();
19638 nodeStartRange.collapse(true);
19640 var nodeEndRange = nodeRange.cloneRange();
19641 nodeEndRange.collapse(false);
19643 return rangeStartRange.compareBoundaryPoints(
19644 Range.START_TO_START, nodeEndRange) == -1 &&
19645 rangeEndRange.compareBoundaryPoints(
19646 Range.START_TO_START, nodeStartRange) == 1;
19650 rangeCompareNode : function(range, node)
19652 var nodeRange = node.ownerDocument.createRange();
19654 nodeRange.selectNode(node);
19656 nodeRange.selectNodeContents(node);
19660 range.collapse(true);
19662 nodeRange.collapse(true);
19664 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19665 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
19667 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19669 var nodeIsBefore = ss == 1;
19670 var nodeIsAfter = ee == -1;
19672 if (nodeIsBefore && nodeIsAfter)
19674 if (!nodeIsBefore && nodeIsAfter)
19675 return 1; //right trailed.
19677 if (nodeIsBefore && !nodeIsAfter)
19678 return 2; // left trailed.
19683 // private? - in a new class?
19684 cleanUpPaste : function()
19686 // cleans up the whole document..
19687 Roo.log('cleanuppaste');
19689 this.cleanUpChildren(this.doc.body);
19690 var clean = this.cleanWordChars(this.doc.body.innerHTML);
19691 if (clean != this.doc.body.innerHTML) {
19692 this.doc.body.innerHTML = clean;
19697 cleanWordChars : function(input) {// change the chars to hex code
19698 var he = Roo.HtmlEditorCore;
19700 var output = input;
19701 Roo.each(he.swapCodes, function(sw) {
19702 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19704 output = output.replace(swapper, sw[1]);
19711 cleanUpChildren : function (n)
19713 if (!n.childNodes.length) {
19716 for (var i = n.childNodes.length-1; i > -1 ; i--) {
19717 this.cleanUpChild(n.childNodes[i]);
19724 cleanUpChild : function (node)
19727 //console.log(node);
19728 if (node.nodeName == "#text") {
19729 // clean up silly Windows -- stuff?
19732 if (node.nodeName == "#comment") {
19733 node.parentNode.removeChild(node);
19734 // clean up silly Windows -- stuff?
19737 var lcname = node.tagName.toLowerCase();
19738 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19739 // whitelist of tags..
19741 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19743 node.parentNode.removeChild(node);
19748 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
19750 // remove <a name=....> as rendering on yahoo mailer is borked with this.
19751 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
19753 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
19754 // remove_keep_children = true;
19757 if (remove_keep_children) {
19758 this.cleanUpChildren(node);
19759 // inserts everything just before this node...
19760 while (node.childNodes.length) {
19761 var cn = node.childNodes[0];
19762 node.removeChild(cn);
19763 node.parentNode.insertBefore(cn, node);
19765 node.parentNode.removeChild(node);
19769 if (!node.attributes || !node.attributes.length) {
19770 this.cleanUpChildren(node);
19774 function cleanAttr(n,v)
19777 if (v.match(/^\./) || v.match(/^\//)) {
19780 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19783 if (v.match(/^#/)) {
19786 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19787 node.removeAttribute(n);
19791 var cwhite = this.cwhite;
19792 var cblack = this.cblack;
19794 function cleanStyle(n,v)
19796 if (v.match(/expression/)) { //XSS?? should we even bother..
19797 node.removeAttribute(n);
19801 var parts = v.split(/;/);
19804 Roo.each(parts, function(p) {
19805 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
19809 var l = p.split(':').shift().replace(/\s+/g,'');
19810 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
19812 if ( cwhite.length && cblack.indexOf(l) > -1) {
19813 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19814 //node.removeAttribute(n);
19818 // only allow 'c whitelisted system attributes'
19819 if ( cwhite.length && cwhite.indexOf(l) < 0) {
19820 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19821 //node.removeAttribute(n);
19831 if (clean.length) {
19832 node.setAttribute(n, clean.join(';'));
19834 node.removeAttribute(n);
19840 for (var i = node.attributes.length-1; i > -1 ; i--) {
19841 var a = node.attributes[i];
19844 if (a.name.toLowerCase().substr(0,2)=='on') {
19845 node.removeAttribute(a.name);
19848 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
19849 node.removeAttribute(a.name);
19852 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
19853 cleanAttr(a.name,a.value); // fixme..
19856 if (a.name == 'style') {
19857 cleanStyle(a.name,a.value);
19860 /// clean up MS crap..
19861 // tecnically this should be a list of valid class'es..
19864 if (a.name == 'class') {
19865 if (a.value.match(/^Mso/)) {
19866 node.className = '';
19869 if (a.value.match(/body/)) {
19870 node.className = '';
19881 this.cleanUpChildren(node);
19887 * Clean up MS wordisms...
19889 cleanWord : function(node)
19894 this.cleanWord(this.doc.body);
19897 if (node.nodeName == "#text") {
19898 // clean up silly Windows -- stuff?
19901 if (node.nodeName == "#comment") {
19902 node.parentNode.removeChild(node);
19903 // clean up silly Windows -- stuff?
19907 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
19908 node.parentNode.removeChild(node);
19912 // remove - but keep children..
19913 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
19914 while (node.childNodes.length) {
19915 var cn = node.childNodes[0];
19916 node.removeChild(cn);
19917 node.parentNode.insertBefore(cn, node);
19919 node.parentNode.removeChild(node);
19920 this.iterateChildren(node, this.cleanWord);
19924 if (node.className.length) {
19926 var cn = node.className.split(/\W+/);
19928 Roo.each(cn, function(cls) {
19929 if (cls.match(/Mso[a-zA-Z]+/)) {
19934 node.className = cna.length ? cna.join(' ') : '';
19936 node.removeAttribute("class");
19940 if (node.hasAttribute("lang")) {
19941 node.removeAttribute("lang");
19944 if (node.hasAttribute("style")) {
19946 var styles = node.getAttribute("style").split(";");
19948 Roo.each(styles, function(s) {
19949 if (!s.match(/:/)) {
19952 var kv = s.split(":");
19953 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
19956 // what ever is left... we allow.
19959 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19960 if (!nstyle.length) {
19961 node.removeAttribute('style');
19964 this.iterateChildren(node, this.cleanWord);
19970 * iterateChildren of a Node, calling fn each time, using this as the scole..
19971 * @param {DomNode} node node to iterate children of.
19972 * @param {Function} fn method of this class to call on each item.
19974 iterateChildren : function(node, fn)
19976 if (!node.childNodes.length) {
19979 for (var i = node.childNodes.length-1; i > -1 ; i--) {
19980 fn.call(this, node.childNodes[i])
19986 * cleanTableWidths.
19988 * Quite often pasting from word etc.. results in tables with column and widths.
19989 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
19992 cleanTableWidths : function(node)
19997 this.cleanTableWidths(this.doc.body);
20002 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20005 Roo.log(node.tagName);
20006 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20007 this.iterateChildren(node, this.cleanTableWidths);
20010 if (node.hasAttribute('width')) {
20011 node.removeAttribute('width');
20015 if (node.hasAttribute("style")) {
20018 var styles = node.getAttribute("style").split(";");
20020 Roo.each(styles, function(s) {
20021 if (!s.match(/:/)) {
20024 var kv = s.split(":");
20025 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20028 // what ever is left... we allow.
20031 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20032 if (!nstyle.length) {
20033 node.removeAttribute('style');
20037 this.iterateChildren(node, this.cleanTableWidths);
20045 domToHTML : function(currentElement, depth, nopadtext) {
20047 depth = depth || 0;
20048 nopadtext = nopadtext || false;
20050 if (!currentElement) {
20051 return this.domToHTML(this.doc.body);
20054 //Roo.log(currentElement);
20056 var allText = false;
20057 var nodeName = currentElement.nodeName;
20058 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20060 if (nodeName == '#text') {
20062 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20067 if (nodeName != 'BODY') {
20070 // Prints the node tagName, such as <A>, <IMG>, etc
20073 for(i = 0; i < currentElement.attributes.length;i++) {
20075 var aname = currentElement.attributes.item(i).name;
20076 if (!currentElement.attributes.item(i).value.length) {
20079 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20082 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20091 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20094 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20099 // Traverse the tree
20101 var currentElementChild = currentElement.childNodes.item(i);
20102 var allText = true;
20103 var innerHTML = '';
20105 while (currentElementChild) {
20106 // Formatting code (indent the tree so it looks nice on the screen)
20107 var nopad = nopadtext;
20108 if (lastnode == 'SPAN') {
20112 if (currentElementChild.nodeName == '#text') {
20113 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20114 toadd = nopadtext ? toadd : toadd.trim();
20115 if (!nopad && toadd.length > 80) {
20116 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20118 innerHTML += toadd;
20121 currentElementChild = currentElement.childNodes.item(i);
20127 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20129 // Recursively traverse the tree structure of the child node
20130 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20131 lastnode = currentElementChild.nodeName;
20133 currentElementChild=currentElement.childNodes.item(i);
20139 // The remaining code is mostly for formatting the tree
20140 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20145 ret+= "</"+tagName+">";
20151 applyBlacklists : function()
20153 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20154 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20158 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20159 if (b.indexOf(tag) > -1) {
20162 this.white.push(tag);
20166 Roo.each(w, function(tag) {
20167 if (b.indexOf(tag) > -1) {
20170 if (this.white.indexOf(tag) > -1) {
20173 this.white.push(tag);
20178 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20179 if (w.indexOf(tag) > -1) {
20182 this.black.push(tag);
20186 Roo.each(b, function(tag) {
20187 if (w.indexOf(tag) > -1) {
20190 if (this.black.indexOf(tag) > -1) {
20193 this.black.push(tag);
20198 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20199 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20203 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20204 if (b.indexOf(tag) > -1) {
20207 this.cwhite.push(tag);
20211 Roo.each(w, function(tag) {
20212 if (b.indexOf(tag) > -1) {
20215 if (this.cwhite.indexOf(tag) > -1) {
20218 this.cwhite.push(tag);
20223 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20224 if (w.indexOf(tag) > -1) {
20227 this.cblack.push(tag);
20231 Roo.each(b, function(tag) {
20232 if (w.indexOf(tag) > -1) {
20235 if (this.cblack.indexOf(tag) > -1) {
20238 this.cblack.push(tag);
20243 setStylesheets : function(stylesheets)
20245 if(typeof(stylesheets) == 'string'){
20246 Roo.get(this.iframe.contentDocument.head).createChild({
20248 rel : 'stylesheet',
20257 Roo.each(stylesheets, function(s) {
20262 Roo.get(_this.iframe.contentDocument.head).createChild({
20264 rel : 'stylesheet',
20273 removeStylesheets : function()
20277 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20282 // hide stuff that is not compatible
20296 * @event specialkey
20300 * @cfg {String} fieldClass @hide
20303 * @cfg {String} focusClass @hide
20306 * @cfg {String} autoCreate @hide
20309 * @cfg {String} inputType @hide
20312 * @cfg {String} invalidClass @hide
20315 * @cfg {String} invalidText @hide
20318 * @cfg {String} msgFx @hide
20321 * @cfg {String} validateOnBlur @hide
20325 Roo.HtmlEditorCore.white = [
20326 'area', 'br', 'img', 'input', 'hr', 'wbr',
20328 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
20329 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
20330 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
20331 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
20332 'table', 'ul', 'xmp',
20334 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
20337 'dir', 'menu', 'ol', 'ul', 'dl',
20343 Roo.HtmlEditorCore.black = [
20344 // 'embed', 'object', // enable - backend responsiblity to clean thiese
20346 'base', 'basefont', 'bgsound', 'blink', 'body',
20347 'frame', 'frameset', 'head', 'html', 'ilayer',
20348 'iframe', 'layer', 'link', 'meta', 'object',
20349 'script', 'style' ,'title', 'xml' // clean later..
20351 Roo.HtmlEditorCore.clean = [
20352 'script', 'style', 'title', 'xml'
20354 Roo.HtmlEditorCore.remove = [
20359 Roo.HtmlEditorCore.ablack = [
20363 Roo.HtmlEditorCore.aclean = [
20364 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
20368 Roo.HtmlEditorCore.pwhite= [
20369 'http', 'https', 'mailto'
20372 // white listed style attributes.
20373 Roo.HtmlEditorCore.cwhite= [
20374 // 'text-align', /// default is to allow most things..
20380 // black listed style attributes.
20381 Roo.HtmlEditorCore.cblack= [
20382 // 'font-size' -- this can be set by the project
20386 Roo.HtmlEditorCore.swapCodes =[
20405 * @class Roo.bootstrap.HtmlEditor
20406 * @extends Roo.bootstrap.TextArea
20407 * Bootstrap HtmlEditor class
20410 * Create a new HtmlEditor
20411 * @param {Object} config The config object
20414 Roo.bootstrap.HtmlEditor = function(config){
20415 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20416 if (!this.toolbars) {
20417 this.toolbars = [];
20419 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20422 * @event initialize
20423 * Fires when the editor is fully initialized (including the iframe)
20424 * @param {HtmlEditor} this
20429 * Fires when the editor is first receives the focus. Any insertion must wait
20430 * until after this event.
20431 * @param {HtmlEditor} this
20435 * @event beforesync
20436 * Fires before the textarea is updated with content from the editor iframe. Return false
20437 * to cancel the sync.
20438 * @param {HtmlEditor} this
20439 * @param {String} html
20443 * @event beforepush
20444 * Fires before the iframe editor is updated with content from the textarea. Return false
20445 * to cancel the push.
20446 * @param {HtmlEditor} this
20447 * @param {String} html
20452 * Fires when the textarea is updated with content from the editor iframe.
20453 * @param {HtmlEditor} this
20454 * @param {String} html
20459 * Fires when the iframe editor is updated with content from the textarea.
20460 * @param {HtmlEditor} this
20461 * @param {String} html
20465 * @event editmodechange
20466 * Fires when the editor switches edit modes
20467 * @param {HtmlEditor} this
20468 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20470 editmodechange: true,
20472 * @event editorevent
20473 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20474 * @param {HtmlEditor} this
20478 * @event firstfocus
20479 * Fires when on first focus - needed by toolbars..
20480 * @param {HtmlEditor} this
20485 * Auto save the htmlEditor value as a file into Events
20486 * @param {HtmlEditor} this
20490 * @event savedpreview
20491 * preview the saved version of htmlEditor
20492 * @param {HtmlEditor} this
20499 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
20503 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20508 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20513 * @cfg {Number} height (in pixels)
20517 * @cfg {Number} width (in pixels)
20522 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20525 stylesheets: false,
20530 // private properties
20531 validationEvent : false,
20533 initialized : false,
20536 onFocus : Roo.emptyFn,
20538 hideMode:'offsets',
20541 tbContainer : false,
20543 toolbarContainer :function() {
20544 return this.wrap.select('.x-html-editor-tb',true).first();
20548 * Protected method that will not generally be called directly. It
20549 * is called when the editor creates its toolbar. Override this method if you need to
20550 * add custom toolbar buttons.
20551 * @param {HtmlEditor} editor
20553 createToolbar : function(){
20555 Roo.log("create toolbars");
20557 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20558 this.toolbars[0].render(this.toolbarContainer());
20562 // if (!editor.toolbars || !editor.toolbars.length) {
20563 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20566 // for (var i =0 ; i < editor.toolbars.length;i++) {
20567 // editor.toolbars[i] = Roo.factory(
20568 // typeof(editor.toolbars[i]) == 'string' ?
20569 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
20570 // Roo.bootstrap.HtmlEditor);
20571 // editor.toolbars[i].init(editor);
20577 onRender : function(ct, position)
20579 // Roo.log("Call onRender: " + this.xtype);
20581 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20583 this.wrap = this.inputEl().wrap({
20584 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20587 this.editorcore.onRender(ct, position);
20589 if (this.resizable) {
20590 this.resizeEl = new Roo.Resizable(this.wrap, {
20594 minHeight : this.height,
20595 height: this.height,
20596 handles : this.resizable,
20599 resize : function(r, w, h) {
20600 _t.onResize(w,h); // -something
20606 this.createToolbar(this);
20609 if(!this.width && this.resizable){
20610 this.setSize(this.wrap.getSize());
20612 if (this.resizeEl) {
20613 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20614 // should trigger onReize..
20620 onResize : function(w, h)
20622 Roo.log('resize: ' +w + ',' + h );
20623 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20627 if(this.inputEl() ){
20628 if(typeof w == 'number'){
20629 var aw = w - this.wrap.getFrameWidth('lr');
20630 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20633 if(typeof h == 'number'){
20634 var tbh = -11; // fixme it needs to tool bar size!
20635 for (var i =0; i < this.toolbars.length;i++) {
20636 // fixme - ask toolbars for heights?
20637 tbh += this.toolbars[i].el.getHeight();
20638 //if (this.toolbars[i].footer) {
20639 // tbh += this.toolbars[i].footer.el.getHeight();
20647 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20648 ah -= 5; // knock a few pixes off for look..
20649 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20653 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20654 this.editorcore.onResize(ew,eh);
20659 * Toggles the editor between standard and source edit mode.
20660 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20662 toggleSourceEdit : function(sourceEditMode)
20664 this.editorcore.toggleSourceEdit(sourceEditMode);
20666 if(this.editorcore.sourceEditMode){
20667 Roo.log('editor - showing textarea');
20670 // Roo.log(this.syncValue());
20672 this.inputEl().removeClass(['hide', 'x-hidden']);
20673 this.inputEl().dom.removeAttribute('tabIndex');
20674 this.inputEl().focus();
20676 Roo.log('editor - hiding textarea');
20678 // Roo.log(this.pushValue());
20681 this.inputEl().addClass(['hide', 'x-hidden']);
20682 this.inputEl().dom.setAttribute('tabIndex', -1);
20683 //this.deferFocus();
20686 if(this.resizable){
20687 this.setSize(this.wrap.getSize());
20690 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20693 // private (for BoxComponent)
20694 adjustSize : Roo.BoxComponent.prototype.adjustSize,
20696 // private (for BoxComponent)
20697 getResizeEl : function(){
20701 // private (for BoxComponent)
20702 getPositionEl : function(){
20707 initEvents : function(){
20708 this.originalValue = this.getValue();
20712 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20715 // markInvalid : Roo.emptyFn,
20717 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20720 // clearInvalid : Roo.emptyFn,
20722 setValue : function(v){
20723 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20724 this.editorcore.pushValue();
20729 deferFocus : function(){
20730 this.focus.defer(10, this);
20734 focus : function(){
20735 this.editorcore.focus();
20741 onDestroy : function(){
20747 for (var i =0; i < this.toolbars.length;i++) {
20748 // fixme - ask toolbars for heights?
20749 this.toolbars[i].onDestroy();
20752 this.wrap.dom.innerHTML = '';
20753 this.wrap.remove();
20758 onFirstFocus : function(){
20759 //Roo.log("onFirstFocus");
20760 this.editorcore.onFirstFocus();
20761 for (var i =0; i < this.toolbars.length;i++) {
20762 this.toolbars[i].onFirstFocus();
20768 syncValue : function()
20770 this.editorcore.syncValue();
20773 pushValue : function()
20775 this.editorcore.pushValue();
20779 // hide stuff that is not compatible
20793 * @event specialkey
20797 * @cfg {String} fieldClass @hide
20800 * @cfg {String} focusClass @hide
20803 * @cfg {String} autoCreate @hide
20806 * @cfg {String} inputType @hide
20809 * @cfg {String} invalidClass @hide
20812 * @cfg {String} invalidText @hide
20815 * @cfg {String} msgFx @hide
20818 * @cfg {String} validateOnBlur @hide
20827 Roo.namespace('Roo.bootstrap.htmleditor');
20829 * @class Roo.bootstrap.HtmlEditorToolbar1
20834 new Roo.bootstrap.HtmlEditor({
20837 new Roo.bootstrap.HtmlEditorToolbar1({
20838 disable : { fonts: 1 , format: 1, ..., ... , ...],
20844 * @cfg {Object} disable List of elements to disable..
20845 * @cfg {Array} btns List of additional buttons.
20849 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
20852 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
20855 Roo.apply(this, config);
20857 // default disabled, based on 'good practice'..
20858 this.disable = this.disable || {};
20859 Roo.applyIf(this.disable, {
20862 specialElements : true
20864 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
20866 this.editor = config.editor;
20867 this.editorcore = config.editor.editorcore;
20869 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
20871 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
20872 // dont call parent... till later.
20874 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
20879 editorcore : false,
20884 "h1","h2","h3","h4","h5","h6",
20886 "abbr", "acronym", "address", "cite", "samp", "var",
20890 onRender : function(ct, position)
20892 // Roo.log("Call onRender: " + this.xtype);
20894 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
20896 this.el.dom.style.marginBottom = '0';
20898 var editorcore = this.editorcore;
20899 var editor= this.editor;
20902 var btn = function(id,cmd , toggle, handler){
20904 var event = toggle ? 'toggle' : 'click';
20909 xns: Roo.bootstrap,
20912 enableToggle:toggle !== false,
20914 pressed : toggle ? false : null,
20917 a.listeners[toggle ? 'toggle' : 'click'] = function() {
20918 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
20927 xns: Roo.bootstrap,
20928 glyphicon : 'font',
20932 xns: Roo.bootstrap,
20936 Roo.each(this.formats, function(f) {
20937 style.menu.items.push({
20939 xns: Roo.bootstrap,
20940 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
20945 editorcore.insertTag(this.tagname);
20952 children.push(style);
20955 btn('bold',false,true);
20956 btn('italic',false,true);
20957 btn('align-left', 'justifyleft',true);
20958 btn('align-center', 'justifycenter',true);
20959 btn('align-right' , 'justifyright',true);
20960 btn('link', false, false, function(btn) {
20961 //Roo.log("create link?");
20962 var url = prompt(this.createLinkText, this.defaultLinkValue);
20963 if(url && url != 'http:/'+'/'){
20964 this.editorcore.relayCmd('createlink', url);
20967 btn('list','insertunorderedlist',true);
20968 btn('pencil', false,true, function(btn){
20971 this.toggleSourceEdit(btn.pressed);
20977 xns: Roo.bootstrap,
20982 xns: Roo.bootstrap,
20987 cog.menu.items.push({
20989 xns: Roo.bootstrap,
20990 html : Clean styles,
20995 editorcore.insertTag(this.tagname);
21004 this.xtype = 'NavSimplebar';
21006 for(var i=0;i< children.length;i++) {
21008 this.buttons.add(this.addxtypeChild(children[i]));
21012 editor.on('editorevent', this.updateToolbar, this);
21014 onBtnClick : function(id)
21016 this.editorcore.relayCmd(id);
21017 this.editorcore.focus();
21021 * Protected method that will not generally be called directly. It triggers
21022 * a toolbar update by reading the markup state of the current selection in the editor.
21024 updateToolbar: function(){
21026 if(!this.editorcore.activated){
21027 this.editor.onFirstFocus(); // is this neeed?
21031 var btns = this.buttons;
21032 var doc = this.editorcore.doc;
21033 btns.get('bold').setActive(doc.queryCommandState('bold'));
21034 btns.get('italic').setActive(doc.queryCommandState('italic'));
21035 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21037 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21038 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21039 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21041 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21042 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21045 var ans = this.editorcore.getAllAncestors();
21046 if (this.formatCombo) {
21049 var store = this.formatCombo.store;
21050 this.formatCombo.setValue("");
21051 for (var i =0; i < ans.length;i++) {
21052 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21054 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21062 // hides menus... - so this cant be on a menu...
21063 Roo.bootstrap.MenuMgr.hideAll();
21065 Roo.bootstrap.MenuMgr.hideAll();
21066 //this.editorsyncValue();
21068 onFirstFocus: function() {
21069 this.buttons.each(function(item){
21073 toggleSourceEdit : function(sourceEditMode){
21076 if(sourceEditMode){
21077 Roo.log("disabling buttons");
21078 this.buttons.each( function(item){
21079 if(item.cmd != 'pencil'){
21085 Roo.log("enabling buttons");
21086 if(this.editorcore.initialized){
21087 this.buttons.each( function(item){
21093 Roo.log("calling toggole on editor");
21094 // tell the editor that it's been pressed..
21095 this.editor.toggleSourceEdit(sourceEditMode);
21105 * @class Roo.bootstrap.Table.AbstractSelectionModel
21106 * @extends Roo.util.Observable
21107 * Abstract base class for grid SelectionModels. It provides the interface that should be
21108 * implemented by descendant classes. This class should not be directly instantiated.
21111 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21112 this.locked = false;
21113 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21117 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21118 /** @ignore Called by the grid automatically. Do not call directly. */
21119 init : function(grid){
21125 * Locks the selections.
21128 this.locked = true;
21132 * Unlocks the selections.
21134 unlock : function(){
21135 this.locked = false;
21139 * Returns true if the selections are locked.
21140 * @return {Boolean}
21142 isLocked : function(){
21143 return this.locked;
21147 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21148 * @class Roo.bootstrap.Table.RowSelectionModel
21149 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21150 * It supports multiple selections and keyboard selection/navigation.
21152 * @param {Object} config
21155 Roo.bootstrap.Table.RowSelectionModel = function(config){
21156 Roo.apply(this, config);
21157 this.selections = new Roo.util.MixedCollection(false, function(o){
21162 this.lastActive = false;
21166 * @event selectionchange
21167 * Fires when the selection changes
21168 * @param {SelectionModel} this
21170 "selectionchange" : true,
21172 * @event afterselectionchange
21173 * Fires after the selection changes (eg. by key press or clicking)
21174 * @param {SelectionModel} this
21176 "afterselectionchange" : true,
21178 * @event beforerowselect
21179 * Fires when a row is selected being selected, return false to cancel.
21180 * @param {SelectionModel} this
21181 * @param {Number} rowIndex The selected index
21182 * @param {Boolean} keepExisting False if other selections will be cleared
21184 "beforerowselect" : true,
21187 * Fires when a row is selected.
21188 * @param {SelectionModel} this
21189 * @param {Number} rowIndex The selected index
21190 * @param {Roo.data.Record} r The record
21192 "rowselect" : true,
21194 * @event rowdeselect
21195 * Fires when a row is deselected.
21196 * @param {SelectionModel} this
21197 * @param {Number} rowIndex The selected index
21199 "rowdeselect" : true
21201 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21202 this.locked = false;
21205 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21207 * @cfg {Boolean} singleSelect
21208 * True to allow selection of only one row at a time (defaults to false)
21210 singleSelect : false,
21213 initEvents : function(){
21215 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21216 this.grid.on("mousedown", this.handleMouseDown, this);
21217 }else{ // allow click to work like normal
21218 this.grid.on("rowclick", this.handleDragableRowClick, this);
21221 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21222 "up" : function(e){
21224 this.selectPrevious(e.shiftKey);
21225 }else if(this.last !== false && this.lastActive !== false){
21226 var last = this.last;
21227 this.selectRange(this.last, this.lastActive-1);
21228 this.grid.getView().focusRow(this.lastActive);
21229 if(last !== false){
21233 this.selectFirstRow();
21235 this.fireEvent("afterselectionchange", this);
21237 "down" : function(e){
21239 this.selectNext(e.shiftKey);
21240 }else if(this.last !== false && this.lastActive !== false){
21241 var last = this.last;
21242 this.selectRange(this.last, this.lastActive+1);
21243 this.grid.getView().focusRow(this.lastActive);
21244 if(last !== false){
21248 this.selectFirstRow();
21250 this.fireEvent("afterselectionchange", this);
21255 var view = this.grid.view;
21256 view.on("refresh", this.onRefresh, this);
21257 view.on("rowupdated", this.onRowUpdated, this);
21258 view.on("rowremoved", this.onRemove, this);
21262 onRefresh : function(){
21263 var ds = this.grid.dataSource, i, v = this.grid.view;
21264 var s = this.selections;
21265 s.each(function(r){
21266 if((i = ds.indexOfId(r.id)) != -1){
21275 onRemove : function(v, index, r){
21276 this.selections.remove(r);
21280 onRowUpdated : function(v, index, r){
21281 if(this.isSelected(r)){
21282 v.onRowSelect(index);
21288 * @param {Array} records The records to select
21289 * @param {Boolean} keepExisting (optional) True to keep existing selections
21291 selectRecords : function(records, keepExisting){
21293 this.clearSelections();
21295 var ds = this.grid.dataSource;
21296 for(var i = 0, len = records.length; i < len; i++){
21297 this.selectRow(ds.indexOf(records[i]), true);
21302 * Gets the number of selected rows.
21305 getCount : function(){
21306 return this.selections.length;
21310 * Selects the first row in the grid.
21312 selectFirstRow : function(){
21317 * Select the last row.
21318 * @param {Boolean} keepExisting (optional) True to keep existing selections
21320 selectLastRow : function(keepExisting){
21321 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21325 * Selects the row immediately following the last selected row.
21326 * @param {Boolean} keepExisting (optional) True to keep existing selections
21328 selectNext : function(keepExisting){
21329 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21330 this.selectRow(this.last+1, keepExisting);
21331 this.grid.getView().focusRow(this.last);
21336 * Selects the row that precedes the last selected row.
21337 * @param {Boolean} keepExisting (optional) True to keep existing selections
21339 selectPrevious : function(keepExisting){
21341 this.selectRow(this.last-1, keepExisting);
21342 this.grid.getView().focusRow(this.last);
21347 * Returns the selected records
21348 * @return {Array} Array of selected records
21350 getSelections : function(){
21351 return [].concat(this.selections.items);
21355 * Returns the first selected record.
21358 getSelected : function(){
21359 return this.selections.itemAt(0);
21364 * Clears all selections.
21366 clearSelections : function(fast){
21367 if(this.locked) return;
21369 var ds = this.grid.dataSource;
21370 var s = this.selections;
21371 s.each(function(r){
21372 this.deselectRow(ds.indexOfId(r.id));
21376 this.selections.clear();
21383 * Selects all rows.
21385 selectAll : function(){
21386 if(this.locked) return;
21387 this.selections.clear();
21388 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21389 this.selectRow(i, true);
21394 * Returns True if there is a selection.
21395 * @return {Boolean}
21397 hasSelection : function(){
21398 return this.selections.length > 0;
21402 * Returns True if the specified row is selected.
21403 * @param {Number/Record} record The record or index of the record to check
21404 * @return {Boolean}
21406 isSelected : function(index){
21407 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21408 return (r && this.selections.key(r.id) ? true : false);
21412 * Returns True if the specified record id is selected.
21413 * @param {String} id The id of record to check
21414 * @return {Boolean}
21416 isIdSelected : function(id){
21417 return (this.selections.key(id) ? true : false);
21421 handleMouseDown : function(e, t){
21422 var view = this.grid.getView(), rowIndex;
21423 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21426 if(e.shiftKey && this.last !== false){
21427 var last = this.last;
21428 this.selectRange(last, rowIndex, e.ctrlKey);
21429 this.last = last; // reset the last
21430 view.focusRow(rowIndex);
21432 var isSelected = this.isSelected(rowIndex);
21433 if(e.button !== 0 && isSelected){
21434 view.focusRow(rowIndex);
21435 }else if(e.ctrlKey && isSelected){
21436 this.deselectRow(rowIndex);
21437 }else if(!isSelected){
21438 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21439 view.focusRow(rowIndex);
21442 this.fireEvent("afterselectionchange", this);
21445 handleDragableRowClick : function(grid, rowIndex, e)
21447 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21448 this.selectRow(rowIndex, false);
21449 grid.view.focusRow(rowIndex);
21450 this.fireEvent("afterselectionchange", this);
21455 * Selects multiple rows.
21456 * @param {Array} rows Array of the indexes of the row to select
21457 * @param {Boolean} keepExisting (optional) True to keep existing selections
21459 selectRows : function(rows, keepExisting){
21461 this.clearSelections();
21463 for(var i = 0, len = rows.length; i < len; i++){
21464 this.selectRow(rows[i], true);
21469 * Selects a range of rows. All rows in between startRow and endRow are also selected.
21470 * @param {Number} startRow The index of the first row in the range
21471 * @param {Number} endRow The index of the last row in the range
21472 * @param {Boolean} keepExisting (optional) True to retain existing selections
21474 selectRange : function(startRow, endRow, keepExisting){
21475 if(this.locked) return;
21477 this.clearSelections();
21479 if(startRow <= endRow){
21480 for(var i = startRow; i <= endRow; i++){
21481 this.selectRow(i, true);
21484 for(var i = startRow; i >= endRow; i--){
21485 this.selectRow(i, true);
21491 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21492 * @param {Number} startRow The index of the first row in the range
21493 * @param {Number} endRow The index of the last row in the range
21495 deselectRange : function(startRow, endRow, preventViewNotify){
21496 if(this.locked) return;
21497 for(var i = startRow; i <= endRow; i++){
21498 this.deselectRow(i, preventViewNotify);
21504 * @param {Number} row The index of the row to select
21505 * @param {Boolean} keepExisting (optional) True to keep existing selections
21507 selectRow : function(index, keepExisting, preventViewNotify){
21508 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21509 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21510 if(!keepExisting || this.singleSelect){
21511 this.clearSelections();
21513 var r = this.grid.dataSource.getAt(index);
21514 this.selections.add(r);
21515 this.last = this.lastActive = index;
21516 if(!preventViewNotify){
21517 this.grid.getView().onRowSelect(index);
21519 this.fireEvent("rowselect", this, index, r);
21520 this.fireEvent("selectionchange", this);
21526 * @param {Number} row The index of the row to deselect
21528 deselectRow : function(index, preventViewNotify){
21529 if(this.locked) return;
21530 if(this.last == index){
21533 if(this.lastActive == index){
21534 this.lastActive = false;
21536 var r = this.grid.dataSource.getAt(index);
21537 this.selections.remove(r);
21538 if(!preventViewNotify){
21539 this.grid.getView().onRowDeselect(index);
21541 this.fireEvent("rowdeselect", this, index);
21542 this.fireEvent("selectionchange", this);
21546 restoreLast : function(){
21548 this.last = this._last;
21553 acceptsNav : function(row, col, cm){
21554 return !cm.isHidden(col) && cm.isCellEditable(col, row);
21558 onEditorKey : function(field, e){
21559 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21564 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21566 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21568 }else if(k == e.ENTER && !e.ctrlKey){
21572 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21574 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21576 }else if(k == e.ESC){
21580 g.startEditing(newCell[0], newCell[1]);
21585 * Ext JS Library 1.1.1
21586 * Copyright(c) 2006-2007, Ext JS, LLC.
21588 * Originally Released Under LGPL - original licence link has changed is not relivant.
21591 * <script type="text/javascript">
21595 * @class Roo.bootstrap.PagingToolbar
21597 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21599 * Create a new PagingToolbar
21600 * @param {Object} config The config object
21602 Roo.bootstrap.PagingToolbar = function(config)
21604 // old args format still supported... - xtype is prefered..
21605 // created from xtype...
21606 var ds = config.dataSource;
21607 this.toolbarItems = [];
21608 if (config.items) {
21609 this.toolbarItems = config.items;
21610 // config.items = [];
21613 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21620 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21624 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21626 * @cfg {Roo.data.Store} dataSource
21627 * The underlying data store providing the paged data
21630 * @cfg {String/HTMLElement/Element} container
21631 * container The id or element that will contain the toolbar
21634 * @cfg {Boolean} displayInfo
21635 * True to display the displayMsg (defaults to false)
21638 * @cfg {Number} pageSize
21639 * The number of records to display per page (defaults to 20)
21643 * @cfg {String} displayMsg
21644 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21646 displayMsg : 'Displaying {0} - {1} of {2}',
21648 * @cfg {String} emptyMsg
21649 * The message to display when no records are found (defaults to "No data to display")
21651 emptyMsg : 'No data to display',
21653 * Customizable piece of the default paging text (defaults to "Page")
21656 beforePageText : "Page",
21658 * Customizable piece of the default paging text (defaults to "of %0")
21661 afterPageText : "of {0}",
21663 * Customizable piece of the default paging text (defaults to "First Page")
21666 firstText : "First Page",
21668 * Customizable piece of the default paging text (defaults to "Previous Page")
21671 prevText : "Previous Page",
21673 * Customizable piece of the default paging text (defaults to "Next Page")
21676 nextText : "Next Page",
21678 * Customizable piece of the default paging text (defaults to "Last Page")
21681 lastText : "Last Page",
21683 * Customizable piece of the default paging text (defaults to "Refresh")
21686 refreshText : "Refresh",
21690 onRender : function(ct, position)
21692 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21693 this.navgroup.parentId = this.id;
21694 this.navgroup.onRender(this.el, null);
21695 // add the buttons to the navgroup
21697 if(this.displayInfo){
21698 Roo.log(this.el.select('ul.navbar-nav',true).first());
21699 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21700 this.displayEl = this.el.select('.x-paging-info', true).first();
21701 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21702 // this.displayEl = navel.el.select('span',true).first();
21708 Roo.each(_this.buttons, function(e){
21709 Roo.factory(e).onRender(_this.el, null);
21713 Roo.each(_this.toolbarItems, function(e) {
21714 _this.navgroup.addItem(e);
21718 this.first = this.navgroup.addItem({
21719 tooltip: this.firstText,
21721 icon : 'fa fa-backward',
21723 preventDefault: true,
21724 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21727 this.prev = this.navgroup.addItem({
21728 tooltip: this.prevText,
21730 icon : 'fa fa-step-backward',
21732 preventDefault: true,
21733 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
21735 //this.addSeparator();
21738 var field = this.navgroup.addItem( {
21740 cls : 'x-paging-position',
21742 html : this.beforePageText +
21743 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
21744 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
21747 this.field = field.el.select('input', true).first();
21748 this.field.on("keydown", this.onPagingKeydown, this);
21749 this.field.on("focus", function(){this.dom.select();});
21752 this.afterTextEl = field.el.select('.x-paging-after',true).first();
21753 //this.field.setHeight(18);
21754 //this.addSeparator();
21755 this.next = this.navgroup.addItem({
21756 tooltip: this.nextText,
21758 html : ' <i class="fa fa-step-forward">',
21760 preventDefault: true,
21761 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
21763 this.last = this.navgroup.addItem({
21764 tooltip: this.lastText,
21765 icon : 'fa fa-forward',
21768 preventDefault: true,
21769 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
21771 //this.addSeparator();
21772 this.loading = this.navgroup.addItem({
21773 tooltip: this.refreshText,
21774 icon: 'fa fa-refresh',
21775 preventDefault: true,
21776 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
21782 updateInfo : function(){
21783 if(this.displayEl){
21784 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
21785 var msg = count == 0 ?
21789 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
21791 this.displayEl.update(msg);
21796 onLoad : function(ds, r, o){
21797 this.cursor = o.params ? o.params.start : 0;
21798 var d = this.getPageData(),
21802 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
21803 this.field.dom.value = ap;
21804 this.first.setDisabled(ap == 1);
21805 this.prev.setDisabled(ap == 1);
21806 this.next.setDisabled(ap == ps);
21807 this.last.setDisabled(ap == ps);
21808 this.loading.enable();
21813 getPageData : function(){
21814 var total = this.ds.getTotalCount();
21817 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
21818 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
21823 onLoadError : function(){
21824 this.loading.enable();
21828 onPagingKeydown : function(e){
21829 var k = e.getKey();
21830 var d = this.getPageData();
21832 var v = this.field.dom.value, pageNum;
21833 if(!v || isNaN(pageNum = parseInt(v, 10))){
21834 this.field.dom.value = d.activePage;
21837 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
21838 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21841 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))
21843 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
21844 this.field.dom.value = pageNum;
21845 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
21848 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21850 var v = this.field.dom.value, pageNum;
21851 var increment = (e.shiftKey) ? 10 : 1;
21852 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21854 if(!v || isNaN(pageNum = parseInt(v, 10))) {
21855 this.field.dom.value = d.activePage;
21858 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
21860 this.field.dom.value = parseInt(v, 10) + increment;
21861 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
21862 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21869 beforeLoad : function(){
21871 this.loading.disable();
21876 onClick : function(which){
21885 ds.load({params:{start: 0, limit: this.pageSize}});
21888 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
21891 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
21894 var total = ds.getTotalCount();
21895 var extra = total % this.pageSize;
21896 var lastStart = extra ? (total - extra) : total-this.pageSize;
21897 ds.load({params:{start: lastStart, limit: this.pageSize}});
21900 ds.load({params:{start: this.cursor, limit: this.pageSize}});
21906 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
21907 * @param {Roo.data.Store} store The data store to unbind
21909 unbind : function(ds){
21910 ds.un("beforeload", this.beforeLoad, this);
21911 ds.un("load", this.onLoad, this);
21912 ds.un("loadexception", this.onLoadError, this);
21913 ds.un("remove", this.updateInfo, this);
21914 ds.un("add", this.updateInfo, this);
21915 this.ds = undefined;
21919 * Binds the paging toolbar to the specified {@link Roo.data.Store}
21920 * @param {Roo.data.Store} store The data store to bind
21922 bind : function(ds){
21923 ds.on("beforeload", this.beforeLoad, this);
21924 ds.on("load", this.onLoad, this);
21925 ds.on("loadexception", this.onLoadError, this);
21926 ds.on("remove", this.updateInfo, this);
21927 ds.on("add", this.updateInfo, this);
21938 * @class Roo.bootstrap.MessageBar
21939 * @extends Roo.bootstrap.Component
21940 * Bootstrap MessageBar class
21941 * @cfg {String} html contents of the MessageBar
21942 * @cfg {String} weight (info | success | warning | danger) default info
21943 * @cfg {String} beforeClass insert the bar before the given class
21944 * @cfg {Boolean} closable (true | false) default false
21945 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
21948 * Create a new Element
21949 * @param {Object} config The config object
21952 Roo.bootstrap.MessageBar = function(config){
21953 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
21956 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
21962 beforeClass: 'bootstrap-sticky-wrap',
21964 getAutoCreate : function(){
21968 cls: 'alert alert-dismissable alert-' + this.weight,
21973 html: this.html || ''
21979 cfg.cls += ' alert-messages-fixed';
21993 onRender : function(ct, position)
21995 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21998 var cfg = Roo.apply({}, this.getAutoCreate());
22002 cfg.cls += ' ' + this.cls;
22005 cfg.style = this.style;
22007 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22009 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22012 this.el.select('>button.close').on('click', this.hide, this);
22018 if (!this.rendered) {
22024 this.fireEvent('show', this);
22030 if (!this.rendered) {
22036 this.fireEvent('hide', this);
22039 update : function()
22041 // var e = this.el.dom.firstChild;
22043 // if(this.closable){
22044 // e = e.nextSibling;
22047 // e.data = this.html || '';
22049 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22065 * @class Roo.bootstrap.Graph
22066 * @extends Roo.bootstrap.Component
22067 * Bootstrap Graph class
22071 @cfg {String} graphtype bar | vbar | pie
22072 @cfg {number} g_x coodinator | centre x (pie)
22073 @cfg {number} g_y coodinator | centre y (pie)
22074 @cfg {number} g_r radius (pie)
22075 @cfg {number} g_height height of the chart (respected by all elements in the set)
22076 @cfg {number} g_width width of the chart (respected by all elements in the set)
22077 @cfg {Object} title The title of the chart
22080 -opts (object) options for the chart
22082 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22083 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22085 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.
22086 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22088 o stretch (boolean)
22090 -opts (object) options for the pie
22093 o startAngle (number)
22094 o endAngle (number)
22098 * Create a new Input
22099 * @param {Object} config The config object
22102 Roo.bootstrap.Graph = function(config){
22103 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22109 * The img click event for the img.
22110 * @param {Roo.EventObject} e
22116 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22127 //g_colors: this.colors,
22134 getAutoCreate : function(){
22145 onRender : function(ct,position){
22146 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22147 this.raphael = Raphael(this.el.dom);
22149 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22150 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22151 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22152 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22154 r.text(160, 10, "Single Series Chart").attr(txtattr);
22155 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22156 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22157 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22159 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22160 r.barchart(330, 10, 300, 220, data1);
22161 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22162 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22165 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22166 // r.barchart(30, 30, 560, 250, xdata, {
22167 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22168 // axis : "0 0 1 1",
22169 // axisxlabels : xdata
22170 // //yvalues : cols,
22173 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22175 // this.load(null,xdata,{
22176 // axis : "0 0 1 1",
22177 // axisxlabels : xdata
22182 load : function(graphtype,xdata,opts){
22183 this.raphael.clear();
22185 graphtype = this.graphtype;
22190 var r = this.raphael,
22191 fin = function () {
22192 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22194 fout = function () {
22195 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22197 pfin = function() {
22198 this.sector.stop();
22199 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22202 this.label[0].stop();
22203 this.label[0].attr({ r: 7.5 });
22204 this.label[1].attr({ "font-weight": 800 });
22207 pfout = function() {
22208 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22211 this.label[0].animate({ r: 5 }, 500, "bounce");
22212 this.label[1].attr({ "font-weight": 400 });
22218 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22221 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22224 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
22225 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22227 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22234 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22239 setTitle: function(o)
22244 initEvents: function() {
22247 this.el.on('click', this.onClick, this);
22251 onClick : function(e)
22253 Roo.log('img onclick');
22254 this.fireEvent('click', this, e);
22266 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22269 * @class Roo.bootstrap.dash.NumberBox
22270 * @extends Roo.bootstrap.Component
22271 * Bootstrap NumberBox class
22272 * @cfg {String} headline Box headline
22273 * @cfg {String} content Box content
22274 * @cfg {String} icon Box icon
22275 * @cfg {String} footer Footer text
22276 * @cfg {String} fhref Footer href
22279 * Create a new NumberBox
22280 * @param {Object} config The config object
22284 Roo.bootstrap.dash.NumberBox = function(config){
22285 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22289 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
22298 getAutoCreate : function(){
22302 cls : 'small-box ',
22310 cls : 'roo-headline',
22311 html : this.headline
22315 cls : 'roo-content',
22316 html : this.content
22330 cls : 'ion ' + this.icon
22339 cls : 'small-box-footer',
22340 href : this.fhref || '#',
22344 cfg.cn.push(footer);
22351 onRender : function(ct,position){
22352 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22359 setHeadline: function (value)
22361 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22364 setFooter: function (value, href)
22366 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22369 this.el.select('a.small-box-footer',true).first().attr('href', href);
22374 setContent: function (value)
22376 this.el.select('.roo-content',true).first().dom.innerHTML = value;
22379 initEvents: function()
22393 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22396 * @class Roo.bootstrap.dash.TabBox
22397 * @extends Roo.bootstrap.Component
22398 * Bootstrap TabBox class
22399 * @cfg {String} title Title of the TabBox
22400 * @cfg {String} icon Icon of the TabBox
22401 * @cfg {Boolean} showtabs (true|false) show the tabs default true
22402 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22405 * Create a new TabBox
22406 * @param {Object} config The config object
22410 Roo.bootstrap.dash.TabBox = function(config){
22411 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22416 * When a pane is added
22417 * @param {Roo.bootstrap.dash.TabPane} pane
22421 * @event activatepane
22422 * When a pane is activated
22423 * @param {Roo.bootstrap.dash.TabPane} pane
22425 "activatepane" : true
22433 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
22438 tabScrollable : false,
22440 getChildContainer : function()
22442 return this.el.select('.tab-content', true).first();
22445 getAutoCreate : function(){
22449 cls: 'pull-left header',
22457 cls: 'fa ' + this.icon
22463 cls: 'nav nav-tabs pull-right',
22469 if(this.tabScrollable){
22476 cls: 'nav nav-tabs pull-right',
22487 cls: 'nav-tabs-custom',
22492 cls: 'tab-content no-padding',
22500 initEvents : function()
22502 //Roo.log('add add pane handler');
22503 this.on('addpane', this.onAddPane, this);
22506 * Updates the box title
22507 * @param {String} html to set the title to.
22509 setTitle : function(value)
22511 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22513 onAddPane : function(pane)
22515 this.panes.push(pane);
22516 //Roo.log('addpane');
22518 // tabs are rendere left to right..
22519 if(!this.showtabs){
22523 var ctr = this.el.select('.nav-tabs', true).first();
22526 var existing = ctr.select('.nav-tab',true);
22527 var qty = existing.getCount();;
22530 var tab = ctr.createChild({
22532 cls : 'nav-tab' + (qty ? '' : ' active'),
22540 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22543 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22545 pane.el.addClass('active');
22550 onTabClick : function(ev,un,ob,pane)
22552 //Roo.log('tab - prev default');
22553 ev.preventDefault();
22556 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22557 pane.tab.addClass('active');
22558 //Roo.log(pane.title);
22559 this.getChildContainer().select('.tab-pane',true).removeClass('active');
22560 // technically we should have a deactivate event.. but maybe add later.
22561 // and it should not de-activate the selected tab...
22562 this.fireEvent('activatepane', pane);
22563 pane.el.addClass('active');
22564 pane.fireEvent('activate');
22569 getActivePane : function()
22572 Roo.each(this.panes, function(p) {
22573 if(p.el.hasClass('active')){
22594 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22596 * @class Roo.bootstrap.TabPane
22597 * @extends Roo.bootstrap.Component
22598 * Bootstrap TabPane class
22599 * @cfg {Boolean} active (false | true) Default false
22600 * @cfg {String} title title of panel
22604 * Create a new TabPane
22605 * @param {Object} config The config object
22608 Roo.bootstrap.dash.TabPane = function(config){
22609 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22615 * When a pane is activated
22616 * @param {Roo.bootstrap.dash.TabPane} pane
22623 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
22628 // the tabBox that this is attached to.
22631 getAutoCreate : function()
22639 cfg.cls += ' active';
22644 initEvents : function()
22646 //Roo.log('trigger add pane handler');
22647 this.parent().fireEvent('addpane', this)
22651 * Updates the tab title
22652 * @param {String} html to set the title to.
22654 setTitle: function(str)
22660 this.tab.select('a', true).first().dom.innerHTML = str;
22677 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22680 * @class Roo.bootstrap.menu.Menu
22681 * @extends Roo.bootstrap.Component
22682 * Bootstrap Menu class - container for Menu
22683 * @cfg {String} html Text of the menu
22684 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22685 * @cfg {String} icon Font awesome icon
22686 * @cfg {String} pos Menu align to (top | bottom) default bottom
22690 * Create a new Menu
22691 * @param {Object} config The config object
22695 Roo.bootstrap.menu.Menu = function(config){
22696 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22700 * @event beforeshow
22701 * Fires before this menu is displayed
22702 * @param {Roo.bootstrap.menu.Menu} this
22706 * @event beforehide
22707 * Fires before this menu is hidden
22708 * @param {Roo.bootstrap.menu.Menu} this
22713 * Fires after this menu is displayed
22714 * @param {Roo.bootstrap.menu.Menu} this
22719 * Fires after this menu is hidden
22720 * @param {Roo.bootstrap.menu.Menu} this
22725 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22726 * @param {Roo.bootstrap.menu.Menu} this
22727 * @param {Roo.EventObject} e
22734 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
22738 weight : 'default',
22743 getChildContainer : function() {
22744 if(this.isSubMenu){
22748 return this.el.select('ul.dropdown-menu', true).first();
22751 getAutoCreate : function()
22756 cls : 'roo-menu-text',
22764 cls : 'fa ' + this.icon
22775 cls : 'dropdown-button btn btn-' + this.weight,
22780 cls : 'dropdown-toggle btn btn-' + this.weight,
22790 cls : 'dropdown-menu'
22796 if(this.pos == 'top'){
22797 cfg.cls += ' dropup';
22800 if(this.isSubMenu){
22803 cls : 'dropdown-menu'
22810 onRender : function(ct, position)
22812 this.isSubMenu = ct.hasClass('dropdown-submenu');
22814 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
22817 initEvents : function()
22819 if(this.isSubMenu){
22823 this.hidden = true;
22825 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
22826 this.triggerEl.on('click', this.onTriggerPress, this);
22828 this.buttonEl = this.el.select('button.dropdown-button', true).first();
22829 this.buttonEl.on('click', this.onClick, this);
22835 if(this.isSubMenu){
22839 return this.el.select('ul.dropdown-menu', true).first();
22842 onClick : function(e)
22844 this.fireEvent("click", this, e);
22847 onTriggerPress : function(e)
22849 if (this.isVisible()) {
22856 isVisible : function(){
22857 return !this.hidden;
22862 this.fireEvent("beforeshow", this);
22864 this.hidden = false;
22865 this.el.addClass('open');
22867 Roo.get(document).on("mouseup", this.onMouseUp, this);
22869 this.fireEvent("show", this);
22876 this.fireEvent("beforehide", this);
22878 this.hidden = true;
22879 this.el.removeClass('open');
22881 Roo.get(document).un("mouseup", this.onMouseUp);
22883 this.fireEvent("hide", this);
22886 onMouseUp : function()
22900 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22903 * @class Roo.bootstrap.menu.Item
22904 * @extends Roo.bootstrap.Component
22905 * Bootstrap MenuItem class
22906 * @cfg {Boolean} submenu (true | false) default false
22907 * @cfg {String} html text of the item
22908 * @cfg {String} href the link
22909 * @cfg {Boolean} disable (true | false) default false
22910 * @cfg {Boolean} preventDefault (true | false) default true
22911 * @cfg {String} icon Font awesome icon
22912 * @cfg {String} pos Submenu align to (left | right) default right
22916 * Create a new Item
22917 * @param {Object} config The config object
22921 Roo.bootstrap.menu.Item = function(config){
22922 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
22926 * Fires when the mouse is hovering over this menu
22927 * @param {Roo.bootstrap.menu.Item} this
22928 * @param {Roo.EventObject} e
22933 * Fires when the mouse exits this menu
22934 * @param {Roo.bootstrap.menu.Item} this
22935 * @param {Roo.EventObject} e
22941 * The raw click event for the entire grid.
22942 * @param {Roo.EventObject} e
22948 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
22953 preventDefault: true,
22958 getAutoCreate : function()
22963 cls : 'roo-menu-item-text',
22971 cls : 'fa ' + this.icon
22980 href : this.href || '#',
22987 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
22991 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
22993 if(this.pos == 'left'){
22994 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23001 initEvents : function()
23003 this.el.on('mouseover', this.onMouseOver, this);
23004 this.el.on('mouseout', this.onMouseOut, this);
23006 this.el.select('a', true).first().on('click', this.onClick, this);
23010 onClick : function(e)
23012 if(this.preventDefault){
23013 e.preventDefault();
23016 this.fireEvent("click", this, e);
23019 onMouseOver : function(e)
23021 if(this.submenu && this.pos == 'left'){
23022 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23025 this.fireEvent("mouseover", this, e);
23028 onMouseOut : function(e)
23030 this.fireEvent("mouseout", this, e);
23042 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23045 * @class Roo.bootstrap.menu.Separator
23046 * @extends Roo.bootstrap.Component
23047 * Bootstrap Separator class
23050 * Create a new Separator
23051 * @param {Object} config The config object
23055 Roo.bootstrap.menu.Separator = function(config){
23056 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23059 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23061 getAutoCreate : function(){
23082 * @class Roo.bootstrap.Tooltip
23083 * Bootstrap Tooltip class
23084 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23085 * to determine which dom element triggers the tooltip.
23087 * It needs to add support for additional attributes like tooltip-position
23090 * Create a new Toolti
23091 * @param {Object} config The config object
23094 Roo.bootstrap.Tooltip = function(config){
23095 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23098 Roo.apply(Roo.bootstrap.Tooltip, {
23100 * @function init initialize tooltip monitoring.
23104 currentTip : false,
23105 currentRegion : false,
23111 Roo.get(document).on('mouseover', this.enter ,this);
23112 Roo.get(document).on('mouseout', this.leave, this);
23115 this.currentTip = new Roo.bootstrap.Tooltip();
23118 enter : function(ev)
23120 var dom = ev.getTarget();
23122 //Roo.log(['enter',dom]);
23123 var el = Roo.fly(dom);
23124 if (this.currentEl) {
23126 //Roo.log(this.currentEl);
23127 //Roo.log(this.currentEl.contains(dom));
23128 if (this.currentEl == el) {
23131 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23139 if (this.currentTip.el) {
23140 this.currentTip.el.hide(); // force hiding...
23145 // you can not look for children, as if el is the body.. then everythign is the child..
23146 if (!el.attr('tooltip')) { //
23147 if (!el.select("[tooltip]").elements.length) {
23150 // is the mouse over this child...?
23151 bindEl = el.select("[tooltip]").first();
23152 var xy = ev.getXY();
23153 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23154 //Roo.log("not in region.");
23157 //Roo.log("child element over..");
23160 this.currentEl = bindEl;
23161 this.currentTip.bind(bindEl);
23162 this.currentRegion = Roo.lib.Region.getRegion(dom);
23163 this.currentTip.enter();
23166 leave : function(ev)
23168 var dom = ev.getTarget();
23169 //Roo.log(['leave',dom]);
23170 if (!this.currentEl) {
23175 if (dom != this.currentEl.dom) {
23178 var xy = ev.getXY();
23179 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23182 // only activate leave if mouse cursor is outside... bounding box..
23187 if (this.currentTip) {
23188 this.currentTip.leave();
23190 //Roo.log('clear currentEl');
23191 this.currentEl = false;
23196 'left' : ['r-l', [-2,0], 'right'],
23197 'right' : ['l-r', [2,0], 'left'],
23198 'bottom' : ['t-b', [0,2], 'top'],
23199 'top' : [ 'b-t', [0,-2], 'bottom']
23205 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
23210 delay : null, // can be { show : 300 , hide: 500}
23214 hoverState : null, //???
23216 placement : 'bottom',
23218 getAutoCreate : function(){
23225 cls : 'tooltip-arrow'
23228 cls : 'tooltip-inner'
23235 bind : function(el)
23241 enter : function () {
23243 if (this.timeout != null) {
23244 clearTimeout(this.timeout);
23247 this.hoverState = 'in';
23248 //Roo.log("enter - show");
23249 if (!this.delay || !this.delay.show) {
23254 this.timeout = setTimeout(function () {
23255 if (_t.hoverState == 'in') {
23258 }, this.delay.show);
23262 clearTimeout(this.timeout);
23264 this.hoverState = 'out';
23265 if (!this.delay || !this.delay.hide) {
23271 this.timeout = setTimeout(function () {
23272 //Roo.log("leave - timeout");
23274 if (_t.hoverState == 'out') {
23276 Roo.bootstrap.Tooltip.currentEl = false;
23284 this.render(document.body);
23287 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23289 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23291 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23293 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23295 var placement = typeof this.placement == 'function' ?
23296 this.placement.call(this, this.el, on_el) :
23299 var autoToken = /\s?auto?\s?/i;
23300 var autoPlace = autoToken.test(placement);
23302 placement = placement.replace(autoToken, '') || 'top';
23306 //this.el.setXY([0,0]);
23308 //this.el.dom.style.display='block';
23309 this.el.addClass(placement);
23311 //this.el.appendTo(on_el);
23313 var p = this.getPosition();
23314 var box = this.el.getBox();
23319 var align = Roo.bootstrap.Tooltip.alignment[placement];
23320 this.el.alignTo(this.bindEl, align[0],align[1]);
23321 //var arrow = this.el.select('.arrow',true).first();
23322 //arrow.set(align[2],
23324 this.el.addClass('in fade');
23325 this.hoverState = null;
23327 if (this.el.hasClass('fade')) {
23338 //this.el.setXY([0,0]);
23339 this.el.removeClass('in');
23355 * @class Roo.bootstrap.LocationPicker
23356 * @extends Roo.bootstrap.Component
23357 * Bootstrap LocationPicker class
23358 * @cfg {Number} latitude Position when init default 0
23359 * @cfg {Number} longitude Position when init default 0
23360 * @cfg {Number} zoom default 15
23361 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23362 * @cfg {Boolean} mapTypeControl default false
23363 * @cfg {Boolean} disableDoubleClickZoom default false
23364 * @cfg {Boolean} scrollwheel default true
23365 * @cfg {Boolean} streetViewControl default false
23366 * @cfg {Number} radius default 0
23367 * @cfg {String} locationName
23368 * @cfg {Boolean} draggable default true
23369 * @cfg {Boolean} enableAutocomplete default false
23370 * @cfg {Boolean} enableReverseGeocode default true
23371 * @cfg {String} markerTitle
23374 * Create a new LocationPicker
23375 * @param {Object} config The config object
23379 Roo.bootstrap.LocationPicker = function(config){
23381 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23386 * Fires when the picker initialized.
23387 * @param {Roo.bootstrap.LocationPicker} this
23388 * @param {Google Location} location
23392 * @event positionchanged
23393 * Fires when the picker position changed.
23394 * @param {Roo.bootstrap.LocationPicker} this
23395 * @param {Google Location} location
23397 positionchanged : true,
23400 * Fires when the map resize.
23401 * @param {Roo.bootstrap.LocationPicker} this
23406 * Fires when the map show.
23407 * @param {Roo.bootstrap.LocationPicker} this
23412 * Fires when the map hide.
23413 * @param {Roo.bootstrap.LocationPicker} this
23418 * Fires when click the map.
23419 * @param {Roo.bootstrap.LocationPicker} this
23420 * @param {Map event} e
23424 * @event mapRightClick
23425 * Fires when right click the map.
23426 * @param {Roo.bootstrap.LocationPicker} this
23427 * @param {Map event} e
23429 mapRightClick : true,
23431 * @event markerClick
23432 * Fires when click the marker.
23433 * @param {Roo.bootstrap.LocationPicker} this
23434 * @param {Map event} e
23436 markerClick : true,
23438 * @event markerRightClick
23439 * Fires when right click the marker.
23440 * @param {Roo.bootstrap.LocationPicker} this
23441 * @param {Map event} e
23443 markerRightClick : true,
23445 * @event OverlayViewDraw
23446 * Fires when OverlayView Draw
23447 * @param {Roo.bootstrap.LocationPicker} this
23449 OverlayViewDraw : true,
23451 * @event OverlayViewOnAdd
23452 * Fires when OverlayView Draw
23453 * @param {Roo.bootstrap.LocationPicker} this
23455 OverlayViewOnAdd : true,
23457 * @event OverlayViewOnRemove
23458 * Fires when OverlayView Draw
23459 * @param {Roo.bootstrap.LocationPicker} this
23461 OverlayViewOnRemove : true,
23463 * @event OverlayViewShow
23464 * Fires when OverlayView Draw
23465 * @param {Roo.bootstrap.LocationPicker} this
23466 * @param {Pixel} cpx
23468 OverlayViewShow : true,
23470 * @event OverlayViewHide
23471 * Fires when OverlayView Draw
23472 * @param {Roo.bootstrap.LocationPicker} this
23474 OverlayViewHide : true
23479 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
23481 gMapContext: false,
23487 mapTypeControl: false,
23488 disableDoubleClickZoom: false,
23490 streetViewControl: false,
23494 enableAutocomplete: false,
23495 enableReverseGeocode: true,
23498 getAutoCreate: function()
23503 cls: 'roo-location-picker'
23509 initEvents: function(ct, position)
23511 if(!this.el.getWidth() || this.isApplied()){
23515 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23520 initial: function()
23522 if(!this.mapTypeId){
23523 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23526 this.gMapContext = this.GMapContext();
23528 this.initOverlayView();
23530 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23534 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23535 _this.setPosition(_this.gMapContext.marker.position);
23538 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23539 _this.fireEvent('mapClick', this, event);
23543 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23544 _this.fireEvent('mapRightClick', this, event);
23548 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23549 _this.fireEvent('markerClick', this, event);
23553 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23554 _this.fireEvent('markerRightClick', this, event);
23558 this.setPosition(this.gMapContext.location);
23560 this.fireEvent('initial', this, this.gMapContext.location);
23563 initOverlayView: function()
23567 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23571 _this.fireEvent('OverlayViewDraw', _this);
23576 _this.fireEvent('OverlayViewOnAdd', _this);
23579 onRemove: function()
23581 _this.fireEvent('OverlayViewOnRemove', _this);
23584 show: function(cpx)
23586 _this.fireEvent('OverlayViewShow', _this, cpx);
23591 _this.fireEvent('OverlayViewHide', _this);
23597 fromLatLngToContainerPixel: function(event)
23599 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23602 isApplied: function()
23604 return this.getGmapContext() == false ? false : true;
23607 getGmapContext: function()
23609 return this.gMapContext
23612 GMapContext: function()
23614 var position = new google.maps.LatLng(this.latitude, this.longitude);
23616 var _map = new google.maps.Map(this.el.dom, {
23619 mapTypeId: this.mapTypeId,
23620 mapTypeControl: this.mapTypeControl,
23621 disableDoubleClickZoom: this.disableDoubleClickZoom,
23622 scrollwheel: this.scrollwheel,
23623 streetViewControl: this.streetViewControl,
23624 locationName: this.locationName,
23625 draggable: this.draggable,
23626 enableAutocomplete: this.enableAutocomplete,
23627 enableReverseGeocode: this.enableReverseGeocode
23630 var _marker = new google.maps.Marker({
23631 position: position,
23633 title: this.markerTitle,
23634 draggable: this.draggable
23641 location: position,
23642 radius: this.radius,
23643 locationName: this.locationName,
23644 addressComponents: {
23645 formatted_address: null,
23646 addressLine1: null,
23647 addressLine2: null,
23649 streetNumber: null,
23653 stateOrProvince: null
23656 domContainer: this.el.dom,
23657 geodecoder: new google.maps.Geocoder()
23661 drawCircle: function(center, radius, options)
23663 if (this.gMapContext.circle != null) {
23664 this.gMapContext.circle.setMap(null);
23668 options = Roo.apply({}, options, {
23669 strokeColor: "#0000FF",
23670 strokeOpacity: .35,
23672 fillColor: "#0000FF",
23676 options.map = this.gMapContext.map;
23677 options.radius = radius;
23678 options.center = center;
23679 this.gMapContext.circle = new google.maps.Circle(options);
23680 return this.gMapContext.circle;
23686 setPosition: function(location)
23688 this.gMapContext.location = location;
23689 this.gMapContext.marker.setPosition(location);
23690 this.gMapContext.map.panTo(location);
23691 this.drawCircle(location, this.gMapContext.radius, {});
23695 if (this.gMapContext.settings.enableReverseGeocode) {
23696 this.gMapContext.geodecoder.geocode({
23697 latLng: this.gMapContext.location
23698 }, function(results, status) {
23700 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23701 _this.gMapContext.locationName = results[0].formatted_address;
23702 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23704 _this.fireEvent('positionchanged', this, location);
23711 this.fireEvent('positionchanged', this, location);
23716 google.maps.event.trigger(this.gMapContext.map, "resize");
23718 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
23720 this.fireEvent('resize', this);
23723 setPositionByLatLng: function(latitude, longitude)
23725 this.setPosition(new google.maps.LatLng(latitude, longitude));
23728 getCurrentPosition: function()
23731 latitude: this.gMapContext.location.lat(),
23732 longitude: this.gMapContext.location.lng()
23736 getAddressName: function()
23738 return this.gMapContext.locationName;
23741 getAddressComponents: function()
23743 return this.gMapContext.addressComponents;
23746 address_component_from_google_geocode: function(address_components)
23750 for (var i = 0; i < address_components.length; i++) {
23751 var component = address_components[i];
23752 if (component.types.indexOf("postal_code") >= 0) {
23753 result.postalCode = component.short_name;
23754 } else if (component.types.indexOf("street_number") >= 0) {
23755 result.streetNumber = component.short_name;
23756 } else if (component.types.indexOf("route") >= 0) {
23757 result.streetName = component.short_name;
23758 } else if (component.types.indexOf("neighborhood") >= 0) {
23759 result.city = component.short_name;
23760 } else if (component.types.indexOf("locality") >= 0) {
23761 result.city = component.short_name;
23762 } else if (component.types.indexOf("sublocality") >= 0) {
23763 result.district = component.short_name;
23764 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
23765 result.stateOrProvince = component.short_name;
23766 } else if (component.types.indexOf("country") >= 0) {
23767 result.country = component.short_name;
23771 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
23772 result.addressLine2 = "";
23776 setZoomLevel: function(zoom)
23778 this.gMapContext.map.setZoom(zoom);
23791 this.fireEvent('show', this);
23802 this.fireEvent('hide', this);
23807 Roo.apply(Roo.bootstrap.LocationPicker, {
23809 OverlayView : function(map, options)
23811 options = options || {};
23825 * @class Roo.bootstrap.Alert
23826 * @extends Roo.bootstrap.Component
23827 * Bootstrap Alert class
23828 * @cfg {String} title The title of alert
23829 * @cfg {String} html The content of alert
23830 * @cfg {String} weight ( success | info | warning | danger )
23831 * @cfg {String} faicon font-awesomeicon
23834 * Create a new alert
23835 * @param {Object} config The config object
23839 Roo.bootstrap.Alert = function(config){
23840 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
23844 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
23851 getAutoCreate : function()
23860 cls : 'roo-alert-icon'
23865 cls : 'roo-alert-title',
23870 cls : 'roo-alert-text',
23877 cfg.cn[0].cls += ' fa ' + this.faicon;
23881 cfg.cls += ' alert-' + this.weight;
23887 initEvents: function()
23889 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23892 setTitle : function(str)
23894 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
23897 setText : function(str)
23899 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
23902 setWeight : function(weight)
23905 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
23908 this.weight = weight;
23910 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
23913 setIcon : function(icon)
23916 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
23921 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
23942 * @class Roo.bootstrap.UploadCropbox
23943 * @extends Roo.bootstrap.Component
23944 * Bootstrap UploadCropbox class
23945 * @cfg {String} emptyText show when image has been loaded
23946 * @cfg {Number} minWidth default 300
23947 * @cfg {Number} minHeight default 300
23948 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
23951 * Create a new UploadCropbox
23952 * @param {Object} config The config object
23955 Roo.bootstrap.UploadCropbox = function(config){
23956 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
23960 * @event beforeselectfile
23961 * Fire before select file
23962 * @param {Roo.bootstrap.UploadCropbox} this
23964 "beforeselectfile" : true,
23967 * Fire after initEvent
23968 * @param {Roo.bootstrap.UploadCropbox} this
23973 * Fire after initEvent
23974 * @param {Roo.bootstrap.UploadCropbox} this
23975 * @param {String} data
23980 * Fire when preparing the file data
23981 * @param {Roo.bootstrap.UploadCropbox} this
23982 * @param {Object} file
23987 * Fire when get exception
23988 * @param {Roo.bootstrap.UploadCropbox} this
23989 * @param {Object} options
23991 "exception" : true,
23993 * @event beforeloadcanvas
23994 * Fire before load the canvas
23995 * @param {Roo.bootstrap.UploadCropbox} this
23996 * @param {String} src
23998 "beforeloadcanvas" : true,
24001 * Fire when trash image
24002 * @param {Roo.bootstrap.UploadCropbox} this
24007 * Fire when save the image
24008 * @param {Roo.bootstrap.UploadCropbox} this
24014 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24017 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24019 emptyText : 'Click to upload image',
24033 cropType : 'image/jpeg',
24036 getAutoCreate : function()
24040 cls : 'roo-upload-cropbox',
24044 cls : 'roo-upload-cropbox-body',
24048 cls : 'roo-upload-cropbox-preview'
24052 cls : 'roo-upload-cropbox-thumb'
24056 cls : 'roo-upload-cropbox-empty-notify',
24057 html : this.emptyText
24063 cls : 'roo-upload-cropbox-footer',
24066 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24076 onRender : function(ct, position)
24078 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24080 if (this.buttons.length) {
24081 Roo.each(this.buttons, function(bb) {
24083 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24085 btn.on('click', this.onFooterButtonClick.createDelegate(this, [btn, bb.action], true));
24091 initEvents : function()
24093 this.urlAPI = (window.createObjectURL && window) ||
24094 (window.URL && URL.revokeObjectURL && URL) ||
24095 (window.webkitURL && webkitURL);
24097 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24098 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24099 this.bodyHasOnClickEvent = false;
24101 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24102 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24104 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24105 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24106 this.thumbEl.hide();
24108 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24109 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24111 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24112 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24113 this.footerEl.hide();
24115 // this.rotateLeft = this.el.select('.roo-upload-cropbox-rotate-left', true).first();
24116 // this.rotateLeft.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'table-cell';
24117 // this.rotateLeft.hide();
24119 // this.pictureBtn = this.el.select('.roo-upload-cropbox-picture', true).first();
24120 // this.pictureBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'table-cell';
24121 // this.pictureBtn.hide();
24123 // this.trashBtn = this.el.select('.roo-upload-cropbox-trash', true).first();
24124 // this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'table-cell';
24125 // this.trashBtn.hide();
24127 // this.saveBtn = this.el.select('.roo-upload-cropbox-save', true).first();
24128 // this.saveBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'table-cell';
24129 // this.saveBtn.hide();
24131 // this.rotateRight = this.el.select('.roo-upload-cropbox-rotate-right', true).first();
24132 // this.rotateRight.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'table-cell';
24133 // this.rotateRight.hide();
24135 // Roo.each(this.buttons, function(btn){
24136 // this[btn].show();
24139 this.setThumbBoxSize();
24143 this.fireEvent('initial', this);
24150 window.addEventListener("resize", function() { _this.resize(); } );
24152 if(!this.bodyHasOnClickEvent){
24153 this.bodyEl.on('click', this.beforeSelectFile, this);
24154 this.bodyHasOnClickEvent = true;
24158 this.bodyEl.on('touchstart', this.onTouchStart, this);
24159 this.bodyEl.on('touchmove', this.onTouchMove, this);
24160 this.bodyEl.on('touchend', this.onTouchEnd, this);
24164 this.bodyEl.on('mousedown', this.onMouseDown, this);
24165 this.bodyEl.on('mousemove', this.onMouseMove, this);
24166 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24167 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24168 Roo.get(document).on('mouseup', this.onMouseUp, this);
24171 // this.pictureBtn.on('click', this.beforeSelectFile, this);
24173 // this.trashBtn.on('click', this.onTrash, this);
24175 // this.saveBtn.on('click', this.onSave, this);
24177 // this.rotateLeft.on('click', this.onRotateLeft, this);
24179 // this.rotateRight.on('click', this.onRotateRight, this);
24186 this.baseScale = 1;
24188 this.baseRotate = 1;
24189 this.dragable = false;
24190 this.pinching = false;
24193 this.cropData = false;
24197 resize : function()
24199 this.setThumbBoxPosition();
24200 this.setCanvasPosition();
24203 onFooterButtonClick : function(a,b,c,d)
24205 Roo.log([a,b,c,d]);
24208 case 'rotate-left' :
24209 this.onRotateLeft();
24211 case 'rotate-right' :
24212 this.onRotateRight()
24216 beforeSelectFile : function(e)
24218 e.preventDefault();
24220 this.fireEvent('beforeselectfile', this);
24223 onTrash : function(e)
24225 e.preventDefault();
24227 this.fireEvent('trash', this);
24230 onSave : function(e)
24232 e.preventDefault();
24234 this.fireEvent('save', this);
24237 loadCanvas : function(src)
24239 if(this.fireEvent('beforeloadcanvas', this, src) != false){
24243 this.imageEl = document.createElement('img');
24247 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24249 this.imageEl.src = src;
24253 onLoadCanvas : function()
24255 if(this.bodyHasOnClickEvent){
24256 this.bodyEl.un('click', this.beforeSelectFile, this);
24257 this.bodyHasOnClickEvent = false;
24260 this.notifyEl.hide();
24261 this.thumbEl.show();
24262 this.footerEl.show();
24264 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24265 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24267 this.setThumbBoxPosition();
24268 this.baseRotateLevel();
24269 this.baseScaleLevel();
24275 setCanvasPosition : function()
24277 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24278 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24280 this.previewEl.setLeft(pw);
24281 this.previewEl.setTop(ph);
24284 onMouseDown : function(e)
24288 this.dragable = true;
24289 this.pinching = false;
24291 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24292 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24296 onMouseMove : function(e)
24300 if (!this.dragable){
24304 var minX = Math.ceil(this.thumbEl.getLeft(true));
24305 var minY = Math.ceil(this.thumbEl.getTop(true));
24307 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24308 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24310 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24311 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24313 x = x - this.mouseX;
24314 y = y - this.mouseY;
24316 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24317 var bgY = Math.ceil(y + this.previewEl.getTop(true));
24319 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24320 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24322 this.previewEl.setLeft(bgX);
24323 this.previewEl.setTop(bgY);
24325 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24326 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24329 onMouseUp : function(e)
24333 this.dragable = false;
24336 onMouseWheel : function(e)
24340 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24342 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel());
24343 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel());
24346 e.getWheelDelta() == -1 &&
24349 (this.rotate == 0 || this.rotate == 180) && (width < this.thumbEl.getWidth() || height < this.thumbEl.getHeight())
24353 (this.rotate == 90 || this.rotate == 270) && (height < this.thumbEl.getWidth() || width < this.thumbEl.getHeight())
24357 this.scale = (e.getWheelDelta() == 1) ? (this.scale - 1) : (this.scale + 1);
24364 onRotateLeft : function(e)
24370 (this.rotate == 0 || this.rotate == 180)
24372 (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())
24376 (this.rotate == 90 || this.rotate == 270)
24378 (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())
24385 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24391 onRotateRight : function(e)
24397 (this.rotate == 0 || this.rotate == 180)
24399 (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())
24403 (this.rotate == 90 || this.rotate == 270)
24405 (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())
24412 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24420 this.previewEl.dom.innerHTML = '';
24422 var canvasEl = document.createElement("canvas");
24424 var contextEl = canvasEl.getContext("2d");
24426 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24427 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24428 var center = this.imageEl.OriginWidth / 2;
24430 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
24431 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24432 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24433 center = this.imageEl.OriginHeight / 2;
24436 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
24438 contextEl.translate(center, center);
24439 contextEl.rotate(this.rotate * Math.PI / 180);
24441 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24443 this.canvasEl = document.createElement("canvas");
24445 this.contextEl = this.canvasEl.getContext("2d");
24447 switch (this.rotate) {
24450 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24451 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24453 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24458 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24459 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24461 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24462 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);
24466 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24471 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24472 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24474 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24475 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);
24479 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);
24484 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24485 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24487 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24488 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24492 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);
24499 this.previewEl.appendChild(this.canvasEl);
24501 this.setCanvasPosition();
24506 var canvas = document.createElement("canvas");
24508 var context = canvas.getContext("2d");
24510 canvas.width = this.minWidth;
24511 canvas.height = this.minHeight;
24513 var cropWidth = this.thumbEl.getWidth();
24514 var cropHeight = this.thumbEl.getHeight();
24516 var x = this.thumbEl.getLeft(true) - this.previewEl.getLeft(true);
24517 var y = this.thumbEl.getTop(true) - this.previewEl.getTop(true);
24519 if(this.canvasEl.width - cropWidth < x){
24520 x = this.canvasEl.width - cropWidth;
24523 if(this.canvasEl.height - cropHeight < y){
24524 y = this.canvasEl.height - cropHeight;
24530 context.drawImage(this.canvasEl, x, y, cropWidth, cropHeight, 0, 0, canvas.width, canvas.height);
24532 this.cropData = canvas.toDataURL(this.cropType);
24534 this.fireEvent('crop', this, this.cropData);
24538 setThumbBoxSize : function()
24541 var width = Math.ceil(this.minWidth * height / this.minHeight);
24543 if(this.minWidth > this.minHeight){
24545 height = Math.ceil(this.minHeight * width / this.minWidth);
24548 this.thumbEl.setStyle({
24549 width : width + 'px',
24550 height : height + 'px'
24557 setThumbBoxPosition : function()
24559 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
24560 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
24562 this.thumbEl.setLeft(x);
24563 this.thumbEl.setTop(y);
24567 baseRotateLevel : function()
24569 this.baseRotate = 1;
24572 typeof(this.exif) != 'undefined' &&
24573 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
24574 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
24576 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
24579 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
24583 baseScaleLevel : function()
24587 if(this.baseRotate == 6 || this.baseRotate == 8){
24589 width = this.thumbEl.getHeight();
24590 this.baseScale = height / this.imageEl.OriginHeight;
24592 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
24593 height = this.thumbEl.getWidth();
24594 this.baseScale = height / this.imageEl.OriginHeight;
24597 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24598 height = this.thumbEl.getWidth();
24599 this.baseScale = height / this.imageEl.OriginHeight;
24601 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
24602 width = this.thumbEl.getHeight();
24603 this.baseScale = width / this.imageEl.OriginWidth;
24610 width = this.thumbEl.getWidth();
24611 this.baseScale = width / this.imageEl.OriginWidth;
24613 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
24614 height = this.thumbEl.getHeight();
24615 this.baseScale = height / this.imageEl.OriginHeight;
24619 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24621 height = this.thumbEl.getHeight();
24622 this.baseScale = height / this.imageEl.OriginHeight;
24624 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
24625 width = this.thumbEl.getWidth();
24626 this.baseScale = width / this.imageEl.OriginWidth;
24634 getScaleLevel : function()
24636 return this.baseScale * Math.pow(1.1, this.scale);
24639 onTouchStart : function(e)
24643 var touches = e.browserEvent.touches;
24649 if(touches.length == 1){
24650 this.onMouseDown(e);
24654 if(touches.length != 2){
24660 for(var i = 0, finger; finger = touches[i]; i++){
24661 coords.push(finger.pageX, finger.pageY);
24664 var x = Math.pow(coords[0] - coords[2], 2);
24665 var y = Math.pow(coords[1] - coords[3], 2);
24667 this.startDistance = Math.sqrt(x + y);
24669 this.startScale = this.scale;
24671 this.pinching = true;
24672 this.dragable = false;
24676 onTouchMove : function(e)
24680 if(!this.pinching && !this.dragable){
24684 var touches = e.browserEvent.touches;
24691 this.onMouseMove(e);
24697 for(var i = 0, finger; finger = touches[i]; i++){
24698 coords.push(finger.pageX, finger.pageY);
24701 var x = Math.pow(coords[0] - coords[2], 2);
24702 var y = Math.pow(coords[1] - coords[3], 2);
24704 this.endDistance = Math.sqrt(x + y);
24706 var scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
24708 var width = Math.ceil(this.imageEl.OriginWidth * this.baseScale * Math.pow(1.1, scale));
24709 var height = Math.ceil(this.imageEl.OriginHeight * this.baseScale * Math.pow(1.1, scale));
24712 this.endDistance / this.startDistance < 1 &&
24715 (this.rotate == 0 || this.rotate == 180) && (width < this.thumbEl.getWidth() || height < this.thumbEl.getHeight())
24719 (this.rotate == 90 || this.rotate == 270) && (height < this.thumbEl.getWidth() || width < this.thumbEl.getHeight())
24726 this.scale = scale;
24732 onTouchEnd : function(e)
24736 this.pinching = false;
24737 this.dragable = false;
24741 prepare : function(input)
24746 if(typeof(input) === 'string'){
24747 this.loadCanvas(input);
24751 if(!input.files || !input.files[0] || !this.urlAPI){
24755 this.file = input.files[0];
24756 this.cropType = this.file.type;
24760 if(this.fireEvent('prepare', this, this.file) != false){
24762 var reader = new FileReader();
24764 reader.onload = function (e) {
24765 if (e.target.error) {
24766 Roo.log(e.target.error);
24770 var buffer = e.target.result,
24771 dataView = new DataView(buffer),
24773 maxOffset = dataView.byteLength - 4,
24777 if (dataView.getUint16(0) === 0xffd8) {
24778 while (offset < maxOffset) {
24779 markerBytes = dataView.getUint16(offset);
24781 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
24782 markerLength = dataView.getUint16(offset + 2) + 2;
24783 if (offset + markerLength > dataView.byteLength) {
24784 Roo.log('Invalid meta data: Invalid segment size.');
24788 if(markerBytes == 0xffe1){
24789 _this.parseExifData(
24796 offset += markerLength;
24806 var url = _this.urlAPI.createObjectURL(_this.file);
24808 _this.loadCanvas(url);
24813 reader.readAsArrayBuffer(this.file);
24819 parseExifData : function(dataView, offset, length)
24821 var tiffOffset = offset + 10,
24825 if (dataView.getUint32(offset + 4) !== 0x45786966) {
24826 // No Exif data, might be XMP data instead
24830 // Check for the ASCII code for "Exif" (0x45786966):
24831 if (dataView.getUint32(offset + 4) !== 0x45786966) {
24832 // No Exif data, might be XMP data instead
24835 if (tiffOffset + 8 > dataView.byteLength) {
24836 Roo.log('Invalid Exif data: Invalid segment size.');
24839 // Check for the two null bytes:
24840 if (dataView.getUint16(offset + 8) !== 0x0000) {
24841 Roo.log('Invalid Exif data: Missing byte alignment offset.');
24844 // Check the byte alignment:
24845 switch (dataView.getUint16(tiffOffset)) {
24847 littleEndian = true;
24850 littleEndian = false;
24853 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
24856 // Check for the TIFF tag marker (0x002A):
24857 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
24858 Roo.log('Invalid Exif data: Missing TIFF marker.');
24861 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
24862 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
24864 this.parseExifTags(
24867 tiffOffset + dirOffset,
24872 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
24877 if (dirOffset + 6 > dataView.byteLength) {
24878 Roo.log('Invalid Exif data: Invalid directory offset.');
24881 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
24882 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
24883 if (dirEndOffset + 4 > dataView.byteLength) {
24884 Roo.log('Invalid Exif data: Invalid directory size.');
24887 for (i = 0; i < tagsNumber; i += 1) {
24891 dirOffset + 2 + 12 * i, // tag offset
24895 // Return the offset to the next directory:
24896 return dataView.getUint32(dirEndOffset, littleEndian);
24899 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
24901 var tag = dataView.getUint16(offset, littleEndian);
24903 this.exif[tag] = this.getExifValue(
24907 dataView.getUint16(offset + 2, littleEndian), // tag type
24908 dataView.getUint32(offset + 4, littleEndian), // tag length
24913 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
24915 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
24924 Roo.log('Invalid Exif data: Invalid tag type.');
24928 tagSize = tagType.size * length;
24929 // Determine if the value is contained in the dataOffset bytes,
24930 // or if the value at the dataOffset is a pointer to the actual data:
24931 dataOffset = tagSize > 4 ?
24932 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
24933 if (dataOffset + tagSize > dataView.byteLength) {
24934 Roo.log('Invalid Exif data: Invalid data offset.');
24937 if (length === 1) {
24938 return tagType.getValue(dataView, dataOffset, littleEndian);
24941 for (i = 0; i < length; i += 1) {
24942 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
24945 if (tagType.ascii) {
24947 // Concatenate the chars:
24948 for (i = 0; i < values.length; i += 1) {
24950 // Ignore the terminating NULL byte(s):
24951 if (c === '\u0000') {
24963 Roo.apply(Roo.bootstrap.UploadCropbox, {
24965 'Orientation': 0x0112
24969 1: 0, //'top-left',
24971 3: 180, //'bottom-right',
24972 // 4: 'bottom-left',
24974 6: 90, //'right-top',
24975 // 7: 'right-bottom',
24976 8: 270 //'left-bottom'
24980 // byte, 8-bit unsigned int:
24982 getValue: function (dataView, dataOffset) {
24983 return dataView.getUint8(dataOffset);
24987 // ascii, 8-bit byte:
24989 getValue: function (dataView, dataOffset) {
24990 return String.fromCharCode(dataView.getUint8(dataOffset));
24995 // short, 16 bit int:
24997 getValue: function (dataView, dataOffset, littleEndian) {
24998 return dataView.getUint16(dataOffset, littleEndian);
25002 // long, 32 bit int:
25004 getValue: function (dataView, dataOffset, littleEndian) {
25005 return dataView.getUint32(dataOffset, littleEndian);
25009 // rational = two long values, first is numerator, second is denominator:
25011 getValue: function (dataView, dataOffset, littleEndian) {
25012 return dataView.getUint32(dataOffset, littleEndian) /
25013 dataView.getUint32(dataOffset + 4, littleEndian);
25017 // slong, 32 bit signed int:
25019 getValue: function (dataView, dataOffset, littleEndian) {
25020 return dataView.getInt32(dataOffset, littleEndian);
25024 // srational, two slongs, first is numerator, second is denominator:
25026 getValue: function (dataView, dataOffset, littleEndian) {
25027 return dataView.getInt32(dataOffset, littleEndian) /
25028 dataView.getInt32(dataOffset + 4, littleEndian);
25038 cls : 'btn-group roo-upload-cropbox-rotate-left',
25039 action : 'rotate-left',
25043 cls : 'btn btn-default',
25044 html : '<i class="fa fa-undo"></i>'
25050 cls : 'btn-group roo-upload-cropbox-picture',
25051 action : 'picture',
25055 cls : 'btn btn-default',
25056 html : '<i class="fa fa-picture-o"></i>'
25062 cls : 'btn-group roo-upload-cropbox-rotate-right',
25063 action : 'rotate-right',
25067 cls : 'btn btn-default',
25068 html : '<i class="fa fa-repeat"></i>'
25076 cls : 'btn-group roo-upload-cropbox-rotate-left',
25077 action : 'rotate-left',
25081 cls : 'btn btn-default',
25082 html : '<i class="fa fa-undo"></i>'
25088 cls : 'btn-group roo-upload-cropbox-trash',
25093 cls : 'btn btn-default',
25094 html : '<i class="fa fa-trash"></i>'
25100 cls : 'btn-group roo-upload-cropbox-save',
25105 cls : 'btn btn-default',
25106 html : '<i class="fa fa-floppy-o"></i>'
25112 cls : 'btn-group roo-upload-cropbox-rotate-right',
25113 action : 'rotate-right',
25117 cls : 'btn btn-default',
25118 html : '<i class="fa fa-repeat"></i>'
25131 * @class Roo.bootstrap.DocumentManager
25132 * @extends Roo.bootstrap.Component
25133 * Bootstrap DocumentManager class
25134 * @cfg {String} paramName default 'imageUpload'
25135 * @cfg {String} method default POST
25136 * @cfg {String} url action url
25137 * @cfg {Number} boxes number of boxes to show default 12
25138 * @cfg {Boolean} multiple multiple upload default true
25139 * @cfg {Number} minWidth default 300
25140 * @cfg {Number} minHeight default 300
25141 * @cfg {Number} thumbSize default 300
25144 * Create a new DocumentManager
25145 * @param {Object} config The config object
25148 Roo.bootstrap.DocumentManager = function(config){
25149 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
25154 * inspect selected file
25155 * @param {Roo.bootstrap.DocumentManager} this
25156 * @param {File} file
25161 * Fire when xhr load exception
25162 * @param {Roo.bootstrap.DocumentManager} this
25163 * @param {XMLHttpRequest} xhr
25165 "exception" : true,
25168 * prepare the form data
25169 * @param {Roo.bootstrap.DocumentManager} this
25170 * @param {Object} formData
25175 * Fire when remove the file
25176 * @param {Roo.bootstrap.DocumentManager} this
25177 * @param {Object} file
25182 * Fire after refresh the file
25183 * @param {Roo.bootstrap.DocumentManager} this
25188 * Fire after click the image
25189 * @param {Roo.bootstrap.DocumentManager} this
25190 * @param {Object} file
25197 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
25208 paramName : 'imageUpload',
25210 getAutoCreate : function()
25214 cls : 'roo-document-manager',
25218 cls : 'roo-document-manager-selector',
25223 cls : 'roo-document-manager-uploader',
25227 cls : 'roo-document-manager-upload-btn',
25228 html : '<i class="fa fa-plus"></i>'
25240 initEvents : function()
25242 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
25243 this.selectorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25244 this.selectorEl.hide();
25247 this.selectorEl.attr('multiple', 'multiple');
25250 this.selectorEl.on('change', this.onSelect, this);
25252 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
25253 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25255 this.uploader.on('click', this.onUpload, this);
25259 window.addEventListener("resize", function() { _this.refresh(); } );
25263 onUpload : function(e)
25265 e.preventDefault();
25267 this.selectorEl.dom.click();
25271 onSelect : function(e)
25273 e.preventDefault();
25275 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25279 Roo.each(this.selectorEl.dom.files, function(file){
25280 if(this.fireEvent('inspect', this, file) != false){
25281 this.files.push(file);
25289 process : function()
25291 this.selectorEl.dom.value = '';
25293 if(!this.files.length){
25297 if(this.files.length > 12){
25298 this.files = this.files.slice(0, 12);
25301 var xhr = new XMLHttpRequest();
25303 Roo.each(this.files, function(file, index){
25304 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25310 this.el.createChild({
25312 cls : 'roo-document-manager-loading',
25316 tooltip : file.name,
25317 cls : 'roo-document-manager-thumb',
25318 html : '<i class="fa fa-spinner fa-pulse"></i>'
25326 if(this.files.length > 11){
25327 this.uploader.hide();
25331 "Accept": "application/json",
25332 "Cache-Control": "no-cache",
25333 "X-Requested-With": "XMLHttpRequest"
25336 xhr.open(this.method, this.url, true);
25338 for (var headerName in headers) {
25339 var headerValue = headers[headerName];
25341 xhr.setRequestHeader(headerName, headerValue);
25347 xhr.onload = function()
25349 _this.xhrOnLoad(xhr);
25352 xhr.onerror = function()
25354 _this.xhrOnError(xhr);
25357 var formData = new FormData();
25359 formData.append('returnHTML', 'NO');
25361 Roo.each(this.files, function(file, index){
25363 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25367 formData.append(this.getParamName(index), file, file.name);
25371 if(this.fireEvent('prepare', this, formData) != false){
25372 xhr.send(formData);
25377 getParamName : function(i)
25379 if(!this.multiple){
25380 return this.paramName;
25383 return this.paramName + "_" + i;
25386 refresh : function()
25388 Roo.each(this.el.select('.roo-document-manager-loading', true).elements, function(el){
25395 Roo.each(this.files, function(file){
25397 if(typeof(file.id) == 'undefined' || file.id * 1 < 1){
25406 var previewEl = this.el.createChild({
25408 cls : 'roo-document-manager-preview',
25412 tooltip : file.filename,
25413 cls : 'roo-document-manager-thumb',
25414 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
25424 var close = previewEl.select('button.close', true).first();
25426 close.on('click', this.onRemove, this, file);
25428 file.target = previewEl;
25430 var image = previewEl.select('img', true).first();
25432 image.on('click', this.onClick, this, file);
25440 this.files = files;
25442 this.uploader.show();
25444 if(this.files.length > 11){
25445 this.uploader.hide();
25448 Roo.isTouch ? this.closable(false) : this.closable(true);
25450 this.fireEvent('refresh', this);
25453 onRemove : function(e, el, o)
25455 e.preventDefault();
25457 this.fireEvent('remove', this, o);
25461 remove : function(o)
25465 Roo.each(this.files, function(file){
25466 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
25475 this.files = files;
25480 onClick : function(e, el, o)
25482 e.preventDefault();
25484 this.fireEvent('click', this, o);
25488 closable : function(closable)
25490 Roo.each(this.el.select('.roo-document-manager-preview > button.close', true).elements, function(el){
25492 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25504 xhrOnLoad : function(xhr)
25506 if (xhr.readyState !== 4) {
25508 this.fireEvent('exception', this, xhr);
25512 var response = Roo.decode(xhr.responseText);
25514 if(!response.success){
25516 this.fireEvent('exception', this, xhr);
25522 Roo.each(this.files, function(file, index){
25524 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25528 this.files[index] = response.data[i];
25539 xhrOnError : function()
25541 Roo.log('xhr on error');
25543 var response = Roo.decode(xhr.responseText);
25556 * @class Roo.bootstrap.DocumentViewer
25557 * @extends Roo.bootstrap.Component
25558 * Bootstrap DocumentViewer class
25561 * Create a new DocumentViewer
25562 * @param {Object} config The config object
25565 Roo.bootstrap.DocumentViewer = function(config){
25566 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
25571 * Fire after initEvent
25572 * @param {Roo.bootstrap.DocumentViewer} this
25578 * @param {Roo.bootstrap.DocumentViewer} this
25583 * Fire after trash button
25584 * @param {Roo.bootstrap.DocumentViewer} this
25591 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
25593 getAutoCreate : function()
25597 cls : 'roo-document-viewer',
25601 cls : 'roo-document-viewer-body',
25605 cls : 'roo-document-viewer-thumb',
25609 cls : 'roo-document-viewer-image'
25617 cls : 'roo-document-viewer-footer',
25620 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
25628 cls : 'btn btn-default roo-document-viewer-trash',
25629 html : '<i class="fa fa-trash"></i>'
25642 initEvents : function()
25645 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
25646 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25648 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
25649 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25651 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
25652 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25654 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
25655 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25657 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
25658 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25660 this.bodyEl.on('click', this.onClick, this);
25662 this.trashBtn.on('click', this.onTrash, this);
25666 initial : function()
25668 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
25671 this.fireEvent('initial', this);
25675 onClick : function(e)
25677 e.preventDefault();
25679 this.fireEvent('click', this);
25682 onTrash : function(e)
25684 e.preventDefault();
25686 this.fireEvent('trash', this);