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);
2554 window.addEventListener("resize", function() { _this.resize(); } );
2560 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2565 if (!this.rendered) {
2569 this.el.setStyle('display', 'block');
2573 (function(){ _this.el.addClass('in'); }).defer(50);
2575 this.el.addClass('in');
2578 // not sure how we can show data in here..
2580 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2583 Roo.get(document.body).addClass("x-body-masked");
2584 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2586 this.el.setStyle('zIndex', '10001');
2588 this.fireEvent('show', this);
2595 Roo.get(document.body).removeClass("x-body-masked");
2596 this.el.removeClass('in');
2600 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2602 this.el.setStyle('display', 'none');
2605 this.fireEvent('hide', this);
2608 addButton : function(str, cb)
2612 var b = Roo.apply({}, { html : str } );
2613 b.xns = b.xns || Roo.bootstrap;
2614 b.xtype = b.xtype || 'Button';
2615 if (typeof(b.listeners) == 'undefined') {
2616 b.listeners = { click : cb.createDelegate(this) };
2619 var btn = Roo.factory(b);
2621 btn.onRender(this.el.select('.modal-footer div').first());
2627 setDefaultButton : function(btn)
2629 //this.el.select('.modal-footer').()
2631 resizeTo: function(w,h)
2635 setContentSize : function(w, h)
2639 onButtonClick: function(btn,e)
2642 this.fireEvent('btnclick', btn.name, e);
2645 * Set the title of the Dialog
2646 * @param {String} str new Title
2648 setTitle: function(str) {
2649 this.titleEl.dom.innerHTML = str;
2652 * Set the body of the Dialog
2653 * @param {String} str new Title
2655 setBody: function(str) {
2656 this.bodyEl.dom.innerHTML = str;
2659 * Set the body of the Dialog using the template
2660 * @param {Obj} data - apply this data to the template and replace the body contents.
2662 applyBody: function(obj)
2665 Roo.log("Error - using apply Body without a template");
2668 this.tmpl.overwrite(this.bodyEl, obj);
2674 Roo.apply(Roo.bootstrap.Modal, {
2676 * Button config that displays a single OK button
2685 * Button config that displays Yes and No buttons
2701 * Button config that displays OK and Cancel buttons
2716 * Button config that displays Yes, No and Cancel buttons
2739 * messagebox - can be used as a replace
2743 * @class Roo.MessageBox
2744 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2748 Roo.Msg.alert('Status', 'Changes saved successfully.');
2750 // Prompt for user data:
2751 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2753 // process text value...
2757 // Show a dialog using config options:
2759 title:'Save Changes?',
2760 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2761 buttons: Roo.Msg.YESNOCANCEL,
2768 Roo.bootstrap.MessageBox = function(){
2769 var dlg, opt, mask, waitTimer;
2770 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2771 var buttons, activeTextEl, bwidth;
2775 var handleButton = function(button){
2777 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2781 var handleHide = function(){
2783 dlg.el.removeClass(opt.cls);
2786 // Roo.TaskMgr.stop(waitTimer);
2787 // waitTimer = null;
2792 var updateButtons = function(b){
2795 buttons["ok"].hide();
2796 buttons["cancel"].hide();
2797 buttons["yes"].hide();
2798 buttons["no"].hide();
2799 //dlg.footer.dom.style.display = 'none';
2802 dlg.footerEl.dom.style.display = '';
2803 for(var k in buttons){
2804 if(typeof buttons[k] != "function"){
2807 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2808 width += buttons[k].el.getWidth()+15;
2818 var handleEsc = function(d, k, e){
2819 if(opt && opt.closable !== false){
2829 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2830 * @return {Roo.BasicDialog} The BasicDialog element
2832 getDialog : function(){
2834 dlg = new Roo.bootstrap.Modal( {
2837 //constraintoviewport:false,
2839 //collapsible : false,
2844 //buttonAlign:"center",
2845 closeClick : function(){
2846 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2849 handleButton("cancel");
2854 dlg.on("hide", handleHide);
2856 //dlg.addKeyListener(27, handleEsc);
2858 this.buttons = buttons;
2859 var bt = this.buttonText;
2860 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2861 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2862 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2863 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2865 bodyEl = dlg.bodyEl.createChild({
2867 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2868 '<textarea class="roo-mb-textarea"></textarea>' +
2869 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2871 msgEl = bodyEl.dom.firstChild;
2872 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2873 textboxEl.enableDisplayMode();
2874 textboxEl.addKeyListener([10,13], function(){
2875 if(dlg.isVisible() && opt && opt.buttons){
2878 }else if(opt.buttons.yes){
2879 handleButton("yes");
2883 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2884 textareaEl.enableDisplayMode();
2885 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2886 progressEl.enableDisplayMode();
2887 var pf = progressEl.dom.firstChild;
2889 pp = Roo.get(pf.firstChild);
2890 pp.setHeight(pf.offsetHeight);
2898 * Updates the message box body text
2899 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2900 * the XHTML-compliant non-breaking space character '&#160;')
2901 * @return {Roo.MessageBox} This message box
2903 updateText : function(text){
2904 if(!dlg.isVisible() && !opt.width){
2905 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2907 msgEl.innerHTML = text || ' ';
2909 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2910 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2912 Math.min(opt.width || cw , this.maxWidth),
2913 Math.max(opt.minWidth || this.minWidth, bwidth)
2916 activeTextEl.setWidth(w);
2918 if(dlg.isVisible()){
2919 dlg.fixedcenter = false;
2921 // to big, make it scroll. = But as usual stupid IE does not support
2924 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2925 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2926 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2928 bodyEl.dom.style.height = '';
2929 bodyEl.dom.style.overflowY = '';
2932 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2934 bodyEl.dom.style.overflowX = '';
2937 dlg.setContentSize(w, bodyEl.getHeight());
2938 if(dlg.isVisible()){
2939 dlg.fixedcenter = true;
2945 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
2946 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2947 * @param {Number} value Any number between 0 and 1 (e.g., .5)
2948 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2949 * @return {Roo.MessageBox} This message box
2951 updateProgress : function(value, text){
2953 this.updateText(text);
2955 if (pp) { // weird bug on my firefox - for some reason this is not defined
2956 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2962 * Returns true if the message box is currently displayed
2963 * @return {Boolean} True if the message box is visible, else false
2965 isVisible : function(){
2966 return dlg && dlg.isVisible();
2970 * Hides the message box if it is displayed
2973 if(this.isVisible()){
2979 * Displays a new message box, or reinitializes an existing message box, based on the config options
2980 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2981 * The following config object properties are supported:
2983 Property Type Description
2984 ---------- --------------- ------------------------------------------------------------------------------------
2985 animEl String/Element An id or Element from which the message box should animate as it opens and
2986 closes (defaults to undefined)
2987 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2988 cancel:'Bar'}), or false to not show any buttons (defaults to false)
2989 closable Boolean False to hide the top-right close button (defaults to true). Note that
2990 progress and wait dialogs will ignore this property and always hide the
2991 close button as they can only be closed programmatically.
2992 cls String A custom CSS class to apply to the message box element
2993 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
2994 displayed (defaults to 75)
2995 fn Function A callback function to execute after closing the dialog. The arguments to the
2996 function will be btn (the name of the button that was clicked, if applicable,
2997 e.g. "ok"), and text (the value of the active text field, if applicable).
2998 Progress and wait dialogs will ignore this option since they do not respond to
2999 user actions and can only be closed programmatically, so any required function
3000 should be called by the same code after it closes the dialog.
3001 icon String A CSS class that provides a background image to be used as an icon for
3002 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3003 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3004 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3005 modal Boolean False to allow user interaction with the page while the message box is
3006 displayed (defaults to true)
3007 msg String A string that will replace the existing message box body text (defaults
3008 to the XHTML-compliant non-breaking space character ' ')
3009 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3010 progress Boolean True to display a progress bar (defaults to false)
3011 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3012 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3013 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3014 title String The title text
3015 value String The string value to set into the active textbox element if displayed
3016 wait Boolean True to display a progress bar (defaults to false)
3017 width Number The width of the dialog in pixels
3024 msg: 'Please enter your address:',
3026 buttons: Roo.MessageBox.OKCANCEL,
3029 animEl: 'addAddressBtn'
3032 * @param {Object} config Configuration options
3033 * @return {Roo.MessageBox} This message box
3035 show : function(options)
3038 // this causes nightmares if you show one dialog after another
3039 // especially on callbacks..
3041 if(this.isVisible()){
3044 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3045 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3046 Roo.log("New Dialog Message:" + options.msg )
3047 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3048 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3051 var d = this.getDialog();
3053 d.setTitle(opt.title || " ");
3054 d.closeEl.setDisplayed(opt.closable !== false);
3055 activeTextEl = textboxEl;
3056 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3061 textareaEl.setHeight(typeof opt.multiline == "number" ?
3062 opt.multiline : this.defaultTextHeight);
3063 activeTextEl = textareaEl;
3072 progressEl.setDisplayed(opt.progress === true);
3073 this.updateProgress(0);
3074 activeTextEl.dom.value = opt.value || "";
3076 dlg.setDefaultButton(activeTextEl);
3078 var bs = opt.buttons;
3082 }else if(bs && bs.yes){
3083 db = buttons["yes"];
3085 dlg.setDefaultButton(db);
3087 bwidth = updateButtons(opt.buttons);
3088 this.updateText(opt.msg);
3090 d.el.addClass(opt.cls);
3092 d.proxyDrag = opt.proxyDrag === true;
3093 d.modal = opt.modal !== false;
3094 d.mask = opt.modal !== false ? mask : false;
3096 // force it to the end of the z-index stack so it gets a cursor in FF
3097 document.body.appendChild(dlg.el.dom);
3098 d.animateTarget = null;
3099 d.show(options.animEl);
3105 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3106 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3107 * and closing the message box when the process is complete.
3108 * @param {String} title The title bar text
3109 * @param {String} msg The message box body text
3110 * @return {Roo.MessageBox} This message box
3112 progress : function(title, msg){
3119 minWidth: this.minProgressWidth,
3126 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3127 * If a callback function is passed it will be called after the user clicks the button, and the
3128 * id of the button that was clicked will be passed as the only parameter to the callback
3129 * (could also be the top-right close button).
3130 * @param {String} title The title bar text
3131 * @param {String} msg The message box body text
3132 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3133 * @param {Object} scope (optional) The scope of the callback function
3134 * @return {Roo.MessageBox} This message box
3136 alert : function(title, msg, fn, scope){
3149 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3150 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3151 * You are responsible for closing the message box when the process is complete.
3152 * @param {String} msg The message box body text
3153 * @param {String} title (optional) The title bar text
3154 * @return {Roo.MessageBox} This message box
3156 wait : function(msg, title){
3167 waitTimer = Roo.TaskMgr.start({
3169 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3177 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3178 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3179 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3180 * @param {String} title The title bar text
3181 * @param {String} msg The message box body text
3182 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3183 * @param {Object} scope (optional) The scope of the callback function
3184 * @return {Roo.MessageBox} This message box
3186 confirm : function(title, msg, fn, scope){
3190 buttons: this.YESNO,
3199 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3200 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3201 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3202 * (could also be the top-right close button) and the text that was entered will be passed as the two
3203 * parameters to the callback.
3204 * @param {String} title The title bar text
3205 * @param {String} msg The message box body text
3206 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3207 * @param {Object} scope (optional) The scope of the callback function
3208 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3209 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3210 * @return {Roo.MessageBox} This message box
3212 prompt : function(title, msg, fn, scope, multiline){
3216 buttons: this.OKCANCEL,
3221 multiline: multiline,
3228 * Button config that displays a single OK button
3233 * Button config that displays Yes and No buttons
3236 YESNO : {yes:true, no:true},
3238 * Button config that displays OK and Cancel buttons
3241 OKCANCEL : {ok:true, cancel:true},
3243 * Button config that displays Yes, No and Cancel buttons
3246 YESNOCANCEL : {yes:true, no:true, cancel:true},
3249 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3252 defaultTextHeight : 75,
3254 * The maximum width in pixels of the message box (defaults to 600)
3259 * The minimum width in pixels of the message box (defaults to 100)
3264 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3265 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3268 minProgressWidth : 250,
3270 * An object containing the default button text strings that can be overriden for localized language support.
3271 * Supported properties are: ok, cancel, yes and no.
3272 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3285 * Shorthand for {@link Roo.MessageBox}
3287 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3288 Roo.Msg = Roo.Msg || Roo.MessageBox;
3297 * @class Roo.bootstrap.Navbar
3298 * @extends Roo.bootstrap.Component
3299 * Bootstrap Navbar class
3302 * Create a new Navbar
3303 * @param {Object} config The config object
3307 Roo.bootstrap.Navbar = function(config){
3308 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3312 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3321 getAutoCreate : function(){
3324 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3328 initEvents :function ()
3330 //Roo.log(this.el.select('.navbar-toggle',true));
3331 this.el.select('.navbar-toggle',true).on('click', function() {
3332 // Roo.log('click');
3333 this.el.select('.navbar-collapse',true).toggleClass('in');
3341 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3343 var size = this.el.getSize();
3344 this.maskEl.setSize(size.width, size.height);
3345 this.maskEl.enableDisplayMode("block");
3354 getChildContainer : function()
3356 if (this.el.select('.collapse').getCount()) {
3357 return this.el.select('.collapse',true).first();
3390 * @class Roo.bootstrap.NavSimplebar
3391 * @extends Roo.bootstrap.Navbar
3392 * Bootstrap Sidebar class
3394 * @cfg {Boolean} inverse is inverted color
3396 * @cfg {String} type (nav | pills | tabs)
3397 * @cfg {Boolean} arrangement stacked | justified
3398 * @cfg {String} align (left | right) alignment
3400 * @cfg {Boolean} main (true|false) main nav bar? default false
3401 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3403 * @cfg {String} tag (header|footer|nav|div) default is nav
3409 * Create a new Sidebar
3410 * @param {Object} config The config object
3414 Roo.bootstrap.NavSimplebar = function(config){
3415 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3418 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3434 getAutoCreate : function(){
3438 tag : this.tag || 'div',
3451 this.type = this.type || 'nav';
3452 if (['tabs','pills'].indexOf(this.type)!==-1) {
3453 cfg.cn[0].cls += ' nav-' + this.type
3457 if (this.type!=='nav') {
3458 Roo.log('nav type must be nav/tabs/pills')
3460 cfg.cn[0].cls += ' navbar-nav'
3466 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3467 cfg.cn[0].cls += ' nav-' + this.arrangement;
3471 if (this.align === 'right') {
3472 cfg.cn[0].cls += ' navbar-right';
3476 cfg.cls += ' navbar-inverse';
3503 * @class Roo.bootstrap.NavHeaderbar
3504 * @extends Roo.bootstrap.NavSimplebar
3505 * Bootstrap Sidebar class
3507 * @cfg {String} brand what is brand
3508 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3509 * @cfg {String} brand_href href of the brand
3510 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3511 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3512 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3513 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3516 * Create a new Sidebar
3517 * @param {Object} config The config object
3521 Roo.bootstrap.NavHeaderbar = function(config){
3522 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3526 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3533 desktopCenter : false,
3536 getAutoCreate : function(){
3539 tag: this.nav || 'nav',
3546 if (this.desktopCenter) {
3547 cn.push({cls : 'container', cn : []});
3554 cls: 'navbar-header',
3559 cls: 'navbar-toggle',
3560 'data-toggle': 'collapse',
3565 html: 'Toggle navigation'
3587 cls: 'collapse navbar-collapse',
3591 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3593 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3594 cfg.cls += ' navbar-' + this.position;
3596 // tag can override this..
3598 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3601 if (this.brand !== '') {
3604 href: this.brand_href ? this.brand_href : '#',
3605 cls: 'navbar-brand',
3613 cfg.cls += ' main-nav';
3621 getHeaderChildContainer : function()
3623 if (this.el.select('.navbar-header').getCount()) {
3624 return this.el.select('.navbar-header',true).first();
3627 return this.getChildContainer();
3631 initEvents : function()
3633 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3635 if (this.autohide) {
3640 Roo.get(document).on('scroll',function(e) {
3641 var ns = Roo.get(document).getScroll().top;
3642 var os = prevScroll;
3646 ft.removeClass('slideDown');
3647 ft.addClass('slideUp');
3650 ft.removeClass('slideUp');
3651 ft.addClass('slideDown');
3672 * @class Roo.bootstrap.NavSidebar
3673 * @extends Roo.bootstrap.Navbar
3674 * Bootstrap Sidebar class
3677 * Create a new Sidebar
3678 * @param {Object} config The config object
3682 Roo.bootstrap.NavSidebar = function(config){
3683 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3686 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3688 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3690 getAutoCreate : function(){
3695 cls: 'sidebar sidebar-nav'
3717 * @class Roo.bootstrap.NavGroup
3718 * @extends Roo.bootstrap.Component
3719 * Bootstrap NavGroup class
3720 * @cfg {String} align (left|right)
3721 * @cfg {Boolean} inverse
3722 * @cfg {String} type (nav|pills|tab) default nav
3723 * @cfg {String} navId - reference Id for navbar.
3727 * Create a new nav group
3728 * @param {Object} config The config object
3731 Roo.bootstrap.NavGroup = function(config){
3732 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3735 Roo.bootstrap.NavGroup.register(this);
3739 * Fires when the active item changes
3740 * @param {Roo.bootstrap.NavGroup} this
3741 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3742 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3749 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3760 getAutoCreate : function()
3762 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3769 if (['tabs','pills'].indexOf(this.type)!==-1) {
3770 cfg.cls += ' nav-' + this.type
3772 if (this.type!=='nav') {
3773 Roo.log('nav type must be nav/tabs/pills')
3775 cfg.cls += ' navbar-nav'
3778 if (this.parent().sidebar) {
3781 cls: 'dashboard-menu sidebar-menu'
3787 if (this.form === true) {
3793 if (this.align === 'right') {
3794 cfg.cls += ' navbar-right';
3796 cfg.cls += ' navbar-left';
3800 if (this.align === 'right') {
3801 cfg.cls += ' navbar-right';
3805 cfg.cls += ' navbar-inverse';
3813 * sets the active Navigation item
3814 * @param {Roo.bootstrap.NavItem} the new current navitem
3816 setActiveItem : function(item)
3819 Roo.each(this.navItems, function(v){
3824 v.setActive(false, true);
3831 item.setActive(true, true);
3832 this.fireEvent('changed', this, item, prev);
3837 * gets the active Navigation item
3838 * @return {Roo.bootstrap.NavItem} the current navitem
3840 getActive : function()
3844 Roo.each(this.navItems, function(v){
3855 indexOfNav : function()
3859 Roo.each(this.navItems, function(v,i){
3870 * adds a Navigation item
3871 * @param {Roo.bootstrap.NavItem} the navitem to add
3873 addItem : function(cfg)
3875 var cn = new Roo.bootstrap.NavItem(cfg);
3877 cn.parentId = this.id;
3878 cn.onRender(this.el, null);
3882 * register a Navigation item
3883 * @param {Roo.bootstrap.NavItem} the navitem to add
3885 register : function(item)
3887 this.navItems.push( item);
3888 item.navId = this.navId;
3893 * clear all the Navigation item
3896 clearAll : function()
3899 this.el.dom.innerHTML = '';
3902 getNavItem: function(tabId)
3905 Roo.each(this.navItems, function(e) {
3906 if (e.tabId == tabId) {
3916 setActiveNext : function()
3918 var i = this.indexOfNav(this.getActive());
3919 if (i > this.navItems.length) {
3922 this.setActiveItem(this.navItems[i+1]);
3924 setActivePrev : function()
3926 var i = this.indexOfNav(this.getActive());
3930 this.setActiveItem(this.navItems[i-1]);
3932 clearWasActive : function(except) {
3933 Roo.each(this.navItems, function(e) {
3934 if (e.tabId != except.tabId && e.was_active) {
3935 e.was_active = false;
3942 getWasActive : function ()
3945 Roo.each(this.navItems, function(e) {
3960 Roo.apply(Roo.bootstrap.NavGroup, {
3964 * register a Navigation Group
3965 * @param {Roo.bootstrap.NavGroup} the navgroup to add
3967 register : function(navgrp)
3969 this.groups[navgrp.navId] = navgrp;
3973 * fetch a Navigation Group based on the navigation ID
3974 * @param {string} the navgroup to add
3975 * @returns {Roo.bootstrap.NavGroup} the navgroup
3977 get: function(navId) {
3978 if (typeof(this.groups[navId]) == 'undefined') {
3980 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3982 return this.groups[navId] ;
3997 * @class Roo.bootstrap.NavItem
3998 * @extends Roo.bootstrap.Component
3999 * Bootstrap Navbar.NavItem class
4000 * @cfg {String} href link to
4001 * @cfg {String} html content of button
4002 * @cfg {String} badge text inside badge
4003 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4004 * @cfg {String} glyphicon name of glyphicon
4005 * @cfg {String} icon name of font awesome icon
4006 * @cfg {Boolean} active Is item active
4007 * @cfg {Boolean} disabled Is item disabled
4009 * @cfg {Boolean} preventDefault (true | false) default false
4010 * @cfg {String} tabId the tab that this item activates.
4011 * @cfg {String} tagtype (a|span) render as a href or span?
4012 * @cfg {Boolean} animateRef (true|false) link to element default false
4015 * Create a new Navbar Item
4016 * @param {Object} config The config object
4018 Roo.bootstrap.NavItem = function(config){
4019 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4024 * The raw click event for the entire grid.
4025 * @param {Roo.EventObject} e
4030 * Fires when the active item active state changes
4031 * @param {Roo.bootstrap.NavItem} this
4032 * @param {boolean} state the new state
4038 * Fires when scroll to element
4039 * @param {Roo.bootstrap.NavItem} this
4040 * @param {Object} options
4041 * @param {Roo.EventObject} e
4049 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4057 preventDefault : false,
4064 getAutoCreate : function(){
4072 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4074 if (this.disabled) {
4075 cfg.cls += ' disabled';
4078 if (this.href || this.html || this.glyphicon || this.icon) {
4082 href : this.href || "#",
4083 html: this.html || ''
4088 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4091 if(this.glyphicon) {
4092 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4097 cfg.cn[0].html += " <span class='caret'></span>";
4101 if (this.badge !== '') {
4103 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4111 initEvents: function()
4113 if (typeof (this.menu) != 'undefined') {
4114 this.menu.parentType = this.xtype;
4115 this.menu.triggerEl = this.el;
4116 this.menu = this.addxtype(Roo.apply({}, this.menu));
4119 this.el.select('a',true).on('click', this.onClick, this);
4121 if(this.tagtype == 'span'){
4122 this.el.select('span',true).on('click', this.onClick, this);
4125 // at this point parent should be available..
4126 this.parent().register(this);
4129 onClick : function(e)
4132 this.preventDefault ||
4139 if (this.disabled) {
4143 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4144 if (tg && tg.transition) {
4145 Roo.log("waiting for the transitionend");
4151 //Roo.log("fire event clicked");
4152 if(this.fireEvent('click', this, e) === false){
4156 if(this.tagtype == 'span'){
4160 //Roo.log(this.href);
4161 var ael = this.el.select('a',true).first();
4164 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4165 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4166 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4167 return; // ignore... - it's a 'hash' to another page.
4171 this.scrollToElement(e);
4175 var p = this.parent();
4177 if (['tabs','pills'].indexOf(p.type)!==-1) {
4178 if (typeof(p.setActiveItem) !== 'undefined') {
4179 p.setActiveItem(this);
4183 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4184 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4185 // remove the collapsed menu expand...
4186 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4190 isActive: function () {
4193 setActive : function(state, fire, is_was_active)
4195 if (this.active && !state & this.navId) {
4196 this.was_active = true;
4197 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4199 nv.clearWasActive(this);
4203 this.active = state;
4206 this.el.removeClass('active');
4207 } else if (!this.el.hasClass('active')) {
4208 this.el.addClass('active');
4211 this.fireEvent('changed', this, state);
4214 // show a panel if it's registered and related..
4216 if (!this.navId || !this.tabId || !state || is_was_active) {
4220 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4224 var pan = tg.getPanelByName(this.tabId);
4228 // if we can not flip to new panel - go back to old nav highlight..
4229 if (false == tg.showPanel(pan)) {
4230 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4232 var onav = nv.getWasActive();
4234 onav.setActive(true, false, true);
4243 // this should not be here...
4244 setDisabled : function(state)
4246 this.disabled = state;
4248 this.el.removeClass('disabled');
4249 } else if (!this.el.hasClass('disabled')) {
4250 this.el.addClass('disabled');
4256 * Fetch the element to display the tooltip on.
4257 * @return {Roo.Element} defaults to this.el
4259 tooltipEl : function()
4261 return this.el.select('' + this.tagtype + '', true).first();
4264 scrollToElement : function(e)
4266 var c = document.body;
4269 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4271 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4272 c = document.documentElement;
4275 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4281 var o = target.calcOffsetsTo(c);
4288 this.fireEvent('scrollto', this, options, e);
4290 Roo.get(c).scrollTo('top', options.value, true);
4303 * <span> icon </span>
4304 * <span> text </span>
4305 * <span>badge </span>
4309 * @class Roo.bootstrap.NavSidebarItem
4310 * @extends Roo.bootstrap.NavItem
4311 * Bootstrap Navbar.NavSidebarItem class
4313 * Create a new Navbar Button
4314 * @param {Object} config The config object
4316 Roo.bootstrap.NavSidebarItem = function(config){
4317 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4322 * The raw click event for the entire grid.
4323 * @param {Roo.EventObject} e
4328 * Fires when the active item active state changes
4329 * @param {Roo.bootstrap.NavSidebarItem} this
4330 * @param {boolean} state the new state
4338 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4341 getAutoCreate : function(){
4346 href : this.href || '#',
4358 html : this.html || ''
4363 cfg.cls += ' active';
4367 if (this.glyphicon || this.icon) {
4368 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4369 a.cn.push({ tag : 'i', cls : c }) ;
4374 if (this.badge !== '') {
4375 a.cn.push({ tag: 'span', cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge });
4379 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4380 a.cls += 'dropdown-toggle treeview' ;
4404 * @class Roo.bootstrap.Row
4405 * @extends Roo.bootstrap.Component
4406 * Bootstrap Row class (contains columns...)
4410 * @param {Object} config The config object
4413 Roo.bootstrap.Row = function(config){
4414 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4417 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4419 getAutoCreate : function(){
4438 * @class Roo.bootstrap.Element
4439 * @extends Roo.bootstrap.Component
4440 * Bootstrap Element class
4441 * @cfg {String} html contents of the element
4442 * @cfg {String} tag tag of the element
4443 * @cfg {String} cls class of the element
4444 * @cfg {Boolean} preventDefault (true|false) default false
4445 * @cfg {Boolean} clickable (true|false) default false
4448 * Create a new Element
4449 * @param {Object} config The config object
4452 Roo.bootstrap.Element = function(config){
4453 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4459 * When a element is chick
4460 * @param {Roo.bootstrap.Element} this
4461 * @param {Roo.EventObject} e
4467 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4472 preventDefault: false,
4475 getAutoCreate : function(){
4486 initEvents: function()
4488 Roo.bootstrap.Element.superclass.initEvents.call(this);
4491 this.el.on('click', this.onClick, this);
4496 onClick : function(e)
4498 if(this.preventDefault){
4502 this.fireEvent('click', this, e);
4505 getValue : function()
4507 return this.el.dom.innerHTML;
4510 setValue : function(value)
4512 this.el.dom.innerHTML = value;
4527 * @class Roo.bootstrap.Pagination
4528 * @extends Roo.bootstrap.Component
4529 * Bootstrap Pagination class
4530 * @cfg {String} size xs | sm | md | lg
4531 * @cfg {Boolean} inverse false | true
4534 * Create a new Pagination
4535 * @param {Object} config The config object
4538 Roo.bootstrap.Pagination = function(config){
4539 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4542 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4548 getAutoCreate : function(){
4554 cfg.cls += ' inverse';
4560 cfg.cls += " " + this.cls;
4578 * @class Roo.bootstrap.PaginationItem
4579 * @extends Roo.bootstrap.Component
4580 * Bootstrap PaginationItem class
4581 * @cfg {String} html text
4582 * @cfg {String} href the link
4583 * @cfg {Boolean} preventDefault (true | false) default true
4584 * @cfg {Boolean} active (true | false) default false
4585 * @cfg {Boolean} disabled default false
4589 * Create a new PaginationItem
4590 * @param {Object} config The config object
4594 Roo.bootstrap.PaginationItem = function(config){
4595 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4600 * The raw click event for the entire grid.
4601 * @param {Roo.EventObject} e
4607 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4611 preventDefault: true,
4616 getAutoCreate : function(){
4622 href : this.href ? this.href : '#',
4623 html : this.html ? this.html : ''
4633 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4637 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4643 initEvents: function() {
4645 this.el.on('click', this.onClick, this);
4648 onClick : function(e)
4650 Roo.log('PaginationItem on click ');
4651 if(this.preventDefault){
4659 this.fireEvent('click', this, e);
4675 * @class Roo.bootstrap.Slider
4676 * @extends Roo.bootstrap.Component
4677 * Bootstrap Slider class
4680 * Create a new Slider
4681 * @param {Object} config The config object
4684 Roo.bootstrap.Slider = function(config){
4685 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4688 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4690 getAutoCreate : function(){
4694 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4698 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4710 * Ext JS Library 1.1.1
4711 * Copyright(c) 2006-2007, Ext JS, LLC.
4713 * Originally Released Under LGPL - original licence link has changed is not relivant.
4716 * <script type="text/javascript">
4721 * @class Roo.grid.ColumnModel
4722 * @extends Roo.util.Observable
4723 * This is the default implementation of a ColumnModel used by the Grid. It defines
4724 * the columns in the grid.
4727 var colModel = new Roo.grid.ColumnModel([
4728 {header: "Ticker", width: 60, sortable: true, locked: true},
4729 {header: "Company Name", width: 150, sortable: true},
4730 {header: "Market Cap.", width: 100, sortable: true},
4731 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4732 {header: "Employees", width: 100, sortable: true, resizable: false}
4737 * The config options listed for this class are options which may appear in each
4738 * individual column definition.
4739 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4741 * @param {Object} config An Array of column config objects. See this class's
4742 * config objects for details.
4744 Roo.grid.ColumnModel = function(config){
4746 * The config passed into the constructor
4748 this.config = config;
4751 // if no id, create one
4752 // if the column does not have a dataIndex mapping,
4753 // map it to the order it is in the config
4754 for(var i = 0, len = config.length; i < len; i++){
4756 if(typeof c.dataIndex == "undefined"){
4759 if(typeof c.renderer == "string"){
4760 c.renderer = Roo.util.Format[c.renderer];
4762 if(typeof c.id == "undefined"){
4765 if(c.editor && c.editor.xtype){
4766 c.editor = Roo.factory(c.editor, Roo.grid);
4768 if(c.editor && c.editor.isFormField){
4769 c.editor = new Roo.grid.GridEditor(c.editor);
4771 this.lookup[c.id] = c;
4775 * The width of columns which have no width specified (defaults to 100)
4778 this.defaultWidth = 100;
4781 * Default sortable of columns which have no sortable specified (defaults to false)
4784 this.defaultSortable = false;
4788 * @event widthchange
4789 * Fires when the width of a column changes.
4790 * @param {ColumnModel} this
4791 * @param {Number} columnIndex The column index
4792 * @param {Number} newWidth The new width
4794 "widthchange": true,
4796 * @event headerchange
4797 * Fires when the text of a header changes.
4798 * @param {ColumnModel} this
4799 * @param {Number} columnIndex The column index
4800 * @param {Number} newText The new header text
4802 "headerchange": true,
4804 * @event hiddenchange
4805 * Fires when a column is hidden or "unhidden".
4806 * @param {ColumnModel} this
4807 * @param {Number} columnIndex The column index
4808 * @param {Boolean} hidden true if hidden, false otherwise
4810 "hiddenchange": true,
4812 * @event columnmoved
4813 * Fires when a column is moved.
4814 * @param {ColumnModel} this
4815 * @param {Number} oldIndex
4816 * @param {Number} newIndex
4818 "columnmoved" : true,
4820 * @event columlockchange
4821 * Fires when a column's locked state is changed
4822 * @param {ColumnModel} this
4823 * @param {Number} colIndex
4824 * @param {Boolean} locked true if locked
4826 "columnlockchange" : true
4828 Roo.grid.ColumnModel.superclass.constructor.call(this);
4830 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4832 * @cfg {String} header The header text to display in the Grid view.
4835 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4836 * {@link Roo.data.Record} definition from which to draw the column's value. If not
4837 * specified, the column's index is used as an index into the Record's data Array.
4840 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4841 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4844 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4845 * Defaults to the value of the {@link #defaultSortable} property.
4846 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4849 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
4852 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
4855 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4858 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4861 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4862 * given the cell's data value. See {@link #setRenderer}. If not specified, the
4863 * default renderer uses the raw data value. If an object is returned (bootstrap only)
4864 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4867 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
4870 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
4873 * @cfg {String} cursor (Optional)
4876 * @cfg {String} tooltip (Optional)
4879 * Returns the id of the column at the specified index.
4880 * @param {Number} index The column index
4881 * @return {String} the id
4883 getColumnId : function(index){
4884 return this.config[index].id;
4888 * Returns the column for a specified id.
4889 * @param {String} id The column id
4890 * @return {Object} the column
4892 getColumnById : function(id){
4893 return this.lookup[id];
4898 * Returns the column for a specified dataIndex.
4899 * @param {String} dataIndex The column dataIndex
4900 * @return {Object|Boolean} the column or false if not found
4902 getColumnByDataIndex: function(dataIndex){
4903 var index = this.findColumnIndex(dataIndex);
4904 return index > -1 ? this.config[index] : false;
4908 * Returns the index for a specified column id.
4909 * @param {String} id The column id
4910 * @return {Number} the index, or -1 if not found
4912 getIndexById : function(id){
4913 for(var i = 0, len = this.config.length; i < len; i++){
4914 if(this.config[i].id == id){
4922 * Returns the index for a specified column dataIndex.
4923 * @param {String} dataIndex The column dataIndex
4924 * @return {Number} the index, or -1 if not found
4927 findColumnIndex : function(dataIndex){
4928 for(var i = 0, len = this.config.length; i < len; i++){
4929 if(this.config[i].dataIndex == dataIndex){
4937 moveColumn : function(oldIndex, newIndex){
4938 var c = this.config[oldIndex];
4939 this.config.splice(oldIndex, 1);
4940 this.config.splice(newIndex, 0, c);
4941 this.dataMap = null;
4942 this.fireEvent("columnmoved", this, oldIndex, newIndex);
4945 isLocked : function(colIndex){
4946 return this.config[colIndex].locked === true;
4949 setLocked : function(colIndex, value, suppressEvent){
4950 if(this.isLocked(colIndex) == value){
4953 this.config[colIndex].locked = value;
4955 this.fireEvent("columnlockchange", this, colIndex, value);
4959 getTotalLockedWidth : function(){
4961 for(var i = 0; i < this.config.length; i++){
4962 if(this.isLocked(i) && !this.isHidden(i)){
4963 this.totalWidth += this.getColumnWidth(i);
4969 getLockedCount : function(){
4970 for(var i = 0, len = this.config.length; i < len; i++){
4971 if(!this.isLocked(i)){
4978 * Returns the number of columns.
4981 getColumnCount : function(visibleOnly){
4982 if(visibleOnly === true){
4984 for(var i = 0, len = this.config.length; i < len; i++){
4985 if(!this.isHidden(i)){
4991 return this.config.length;
4995 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4996 * @param {Function} fn
4997 * @param {Object} scope (optional)
4998 * @return {Array} result
5000 getColumnsBy : function(fn, scope){
5002 for(var i = 0, len = this.config.length; i < len; i++){
5003 var c = this.config[i];
5004 if(fn.call(scope||this, c, i) === true){
5012 * Returns true if the specified column is sortable.
5013 * @param {Number} col The column index
5016 isSortable : function(col){
5017 if(typeof this.config[col].sortable == "undefined"){
5018 return this.defaultSortable;
5020 return this.config[col].sortable;
5024 * Returns the rendering (formatting) function defined for the column.
5025 * @param {Number} col The column index.
5026 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5028 getRenderer : function(col){
5029 if(!this.config[col].renderer){
5030 return Roo.grid.ColumnModel.defaultRenderer;
5032 return this.config[col].renderer;
5036 * Sets the rendering (formatting) function for a column.
5037 * @param {Number} col The column index
5038 * @param {Function} fn The function to use to process the cell's raw data
5039 * to return HTML markup for the grid view. The render function is called with
5040 * the following parameters:<ul>
5041 * <li>Data value.</li>
5042 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5043 * <li>css A CSS style string to apply to the table cell.</li>
5044 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5045 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5046 * <li>Row index</li>
5047 * <li>Column index</li>
5048 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5050 setRenderer : function(col, fn){
5051 this.config[col].renderer = fn;
5055 * Returns the width for the specified column.
5056 * @param {Number} col The column index
5059 getColumnWidth : function(col){
5060 return this.config[col].width * 1 || this.defaultWidth;
5064 * Sets the width for a column.
5065 * @param {Number} col The column index
5066 * @param {Number} width The new width
5068 setColumnWidth : function(col, width, suppressEvent){
5069 this.config[col].width = width;
5070 this.totalWidth = null;
5072 this.fireEvent("widthchange", this, col, width);
5077 * Returns the total width of all columns.
5078 * @param {Boolean} includeHidden True to include hidden column widths
5081 getTotalWidth : function(includeHidden){
5082 if(!this.totalWidth){
5083 this.totalWidth = 0;
5084 for(var i = 0, len = this.config.length; i < len; i++){
5085 if(includeHidden || !this.isHidden(i)){
5086 this.totalWidth += this.getColumnWidth(i);
5090 return this.totalWidth;
5094 * Returns the header for the specified column.
5095 * @param {Number} col The column index
5098 getColumnHeader : function(col){
5099 return this.config[col].header;
5103 * Sets the header for a column.
5104 * @param {Number} col The column index
5105 * @param {String} header The new header
5107 setColumnHeader : function(col, header){
5108 this.config[col].header = header;
5109 this.fireEvent("headerchange", this, col, header);
5113 * Returns the tooltip for the specified column.
5114 * @param {Number} col The column index
5117 getColumnTooltip : function(col){
5118 return this.config[col].tooltip;
5121 * Sets the tooltip for a column.
5122 * @param {Number} col The column index
5123 * @param {String} tooltip The new tooltip
5125 setColumnTooltip : function(col, tooltip){
5126 this.config[col].tooltip = tooltip;
5130 * Returns the dataIndex for the specified column.
5131 * @param {Number} col The column index
5134 getDataIndex : function(col){
5135 return this.config[col].dataIndex;
5139 * Sets the dataIndex for a column.
5140 * @param {Number} col The column index
5141 * @param {Number} dataIndex The new dataIndex
5143 setDataIndex : function(col, dataIndex){
5144 this.config[col].dataIndex = dataIndex;
5150 * Returns true if the cell is editable.
5151 * @param {Number} colIndex The column index
5152 * @param {Number} rowIndex The row index
5155 isCellEditable : function(colIndex, rowIndex){
5156 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5160 * Returns the editor defined for the cell/column.
5161 * return false or null to disable editing.
5162 * @param {Number} colIndex The column index
5163 * @param {Number} rowIndex The row index
5166 getCellEditor : function(colIndex, rowIndex){
5167 return this.config[colIndex].editor;
5171 * Sets if a column is editable.
5172 * @param {Number} col The column index
5173 * @param {Boolean} editable True if the column is editable
5175 setEditable : function(col, editable){
5176 this.config[col].editable = editable;
5181 * Returns true if the column is hidden.
5182 * @param {Number} colIndex The column index
5185 isHidden : function(colIndex){
5186 return this.config[colIndex].hidden;
5191 * Returns true if the column width cannot be changed
5193 isFixed : function(colIndex){
5194 return this.config[colIndex].fixed;
5198 * Returns true if the column can be resized
5201 isResizable : function(colIndex){
5202 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5205 * Sets if a column is hidden.
5206 * @param {Number} colIndex The column index
5207 * @param {Boolean} hidden True if the column is hidden
5209 setHidden : function(colIndex, hidden){
5210 this.config[colIndex].hidden = hidden;
5211 this.totalWidth = null;
5212 this.fireEvent("hiddenchange", this, colIndex, hidden);
5216 * Sets the editor for a column.
5217 * @param {Number} col The column index
5218 * @param {Object} editor The editor object
5220 setEditor : function(col, editor){
5221 this.config[col].editor = editor;
5225 Roo.grid.ColumnModel.defaultRenderer = function(value){
5226 if(typeof value == "string" && value.length < 1){
5232 // Alias for backwards compatibility
5233 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5236 * Ext JS Library 1.1.1
5237 * Copyright(c) 2006-2007, Ext JS, LLC.
5239 * Originally Released Under LGPL - original licence link has changed is not relivant.
5242 * <script type="text/javascript">
5246 * @class Roo.LoadMask
5247 * A simple utility class for generically masking elements while loading data. If the element being masked has
5248 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5249 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5250 * element's UpdateManager load indicator and will be destroyed after the initial load.
5252 * Create a new LoadMask
5253 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5254 * @param {Object} config The config object
5256 Roo.LoadMask = function(el, config){
5257 this.el = Roo.get(el);
5258 Roo.apply(this, config);
5260 this.store.on('beforeload', this.onBeforeLoad, this);
5261 this.store.on('load', this.onLoad, this);
5262 this.store.on('loadexception', this.onLoadException, this);
5263 this.removeMask = false;
5265 var um = this.el.getUpdateManager();
5266 um.showLoadIndicator = false; // disable the default indicator
5267 um.on('beforeupdate', this.onBeforeLoad, this);
5268 um.on('update', this.onLoad, this);
5269 um.on('failure', this.onLoad, this);
5270 this.removeMask = true;
5274 Roo.LoadMask.prototype = {
5276 * @cfg {Boolean} removeMask
5277 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5278 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5282 * The text to display in a centered loading message box (defaults to 'Loading...')
5286 * @cfg {String} msgCls
5287 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5289 msgCls : 'x-mask-loading',
5292 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5298 * Disables the mask to prevent it from being displayed
5300 disable : function(){
5301 this.disabled = true;
5305 * Enables the mask so that it can be displayed
5307 enable : function(){
5308 this.disabled = false;
5311 onLoadException : function()
5315 if (typeof(arguments[3]) != 'undefined') {
5316 Roo.MessageBox.alert("Error loading",arguments[3]);
5320 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5321 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5330 this.el.unmask(this.removeMask);
5335 this.el.unmask(this.removeMask);
5339 onBeforeLoad : function(){
5341 this.el.mask(this.msg, this.msgCls);
5346 destroy : function(){
5348 this.store.un('beforeload', this.onBeforeLoad, this);
5349 this.store.un('load', this.onLoad, this);
5350 this.store.un('loadexception', this.onLoadException, this);
5352 var um = this.el.getUpdateManager();
5353 um.un('beforeupdate', this.onBeforeLoad, this);
5354 um.un('update', this.onLoad, this);
5355 um.un('failure', this.onLoad, this);
5366 * @class Roo.bootstrap.Table
5367 * @extends Roo.bootstrap.Component
5368 * Bootstrap Table class
5369 * @cfg {String} cls table class
5370 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5371 * @cfg {String} bgcolor Specifies the background color for a table
5372 * @cfg {Number} border Specifies whether the table cells should have borders or not
5373 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5374 * @cfg {Number} cellspacing Specifies the space between cells
5375 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5376 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5377 * @cfg {String} sortable Specifies that the table should be sortable
5378 * @cfg {String} summary Specifies a summary of the content of a table
5379 * @cfg {Number} width Specifies the width of a table
5380 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5382 * @cfg {boolean} striped Should the rows be alternative striped
5383 * @cfg {boolean} bordered Add borders to the table
5384 * @cfg {boolean} hover Add hover highlighting
5385 * @cfg {boolean} condensed Format condensed
5386 * @cfg {boolean} responsive Format condensed
5387 * @cfg {Boolean} loadMask (true|false) default false
5388 * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
5389 * @cfg {Boolean} thead (true|false) generate thead, default true
5390 * @cfg {Boolean} RowSelection (true|false) default false
5391 * @cfg {Boolean} CellSelection (true|false) default false
5392 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5396 * Create a new Table
5397 * @param {Object} config The config object
5400 Roo.bootstrap.Table = function(config){
5401 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5404 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5405 this.sm = this.selModel;
5406 this.sm.xmodule = this.xmodule || false;
5408 if (this.cm && typeof(this.cm.config) == 'undefined') {
5409 this.colModel = new Roo.grid.ColumnModel(this.cm);
5410 this.cm = this.colModel;
5411 this.cm.xmodule = this.xmodule || false;
5414 this.store= Roo.factory(this.store, Roo.data);
5415 this.ds = this.store;
5416 this.ds.xmodule = this.xmodule || false;
5419 if (this.footer && this.store) {
5420 this.footer.dataSource = this.ds;
5421 this.footer = Roo.factory(this.footer);
5428 * Fires when a cell is 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
5437 * @event celldblclick
5438 * Fires when a cell is double clicked
5439 * @param {Roo.bootstrap.Table} this
5440 * @param {Roo.Element} el
5441 * @param {Number} rowIndex
5442 * @param {Number} columnIndex
5443 * @param {Roo.EventObject} e
5445 "celldblclick" : true,
5448 * Fires when a row is clicked
5449 * @param {Roo.bootstrap.Table} this
5450 * @param {Roo.Element} el
5451 * @param {Number} rowIndex
5452 * @param {Roo.EventObject} e
5456 * @event rowdblclick
5457 * Fires when a row is double clicked
5458 * @param {Roo.bootstrap.Table} this
5459 * @param {Roo.Element} el
5460 * @param {Number} rowIndex
5461 * @param {Roo.EventObject} e
5463 "rowdblclick" : true,
5466 * Fires when a mouseover 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 mouseout occur
5477 * @param {Roo.bootstrap.Table} this
5478 * @param {Roo.Element} el
5479 * @param {Number} rowIndex
5480 * @param {Number} columnIndex
5481 * @param {Roo.EventObject} e
5486 * Fires when a row is rendered, so you can change add a style to it.
5487 * @param {Roo.bootstrap.Table} this
5488 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5492 * @event rowsrendered
5493 * Fires when all the rows have been rendered
5494 * @param {Roo.bootstrap.Table} this
5496 'rowsrendered' : true
5501 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5525 RowSelection : false,
5526 CellSelection : false,
5529 // Roo.Element - the tbody
5532 getAutoCreate : function(){
5533 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5542 cfg.cls += ' table-striped';
5546 cfg.cls += ' table-hover';
5548 if (this.bordered) {
5549 cfg.cls += ' table-bordered';
5551 if (this.condensed) {
5552 cfg.cls += ' table-condensed';
5554 if (this.responsive) {
5555 cfg.cls += ' table-responsive';
5559 cfg.cls+= ' ' +this.cls;
5562 // this lot should be simplifed...
5565 cfg.align=this.align;
5568 cfg.bgcolor=this.bgcolor;
5571 cfg.border=this.border;
5573 if (this.cellpadding) {
5574 cfg.cellpadding=this.cellpadding;
5576 if (this.cellspacing) {
5577 cfg.cellspacing=this.cellspacing;
5580 cfg.frame=this.frame;
5583 cfg.rules=this.rules;
5585 if (this.sortable) {
5586 cfg.sortable=this.sortable;
5589 cfg.summary=this.summary;
5592 cfg.width=this.width;
5595 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5598 if(this.store || this.cm){
5600 cfg.cn.push(this.renderHeader());
5603 cfg.cn.push(this.renderBody());
5606 cfg.cn.push(this.renderFooter());
5609 cfg.cls+= ' TableGrid';
5612 return { cn : [ cfg ] };
5615 initEvents : function()
5617 if(!this.store || !this.cm){
5621 //Roo.log('initEvents with ds!!!!');
5623 this.mainBody = this.el.select('tbody', true).first();
5628 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5629 e.on('click', _this.sort, _this);
5632 this.el.on("click", this.onClick, this);
5633 this.el.on("dblclick", this.onDblClick, this);
5635 // why is this done????? = it breaks dialogs??
5636 //this.parent().el.setStyle('position', 'relative');
5640 this.footer.parentId = this.id;
5641 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5644 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5646 this.store.on('load', this.onLoad, this);
5647 this.store.on('beforeload', this.onBeforeLoad, this);
5648 this.store.on('update', this.onUpdate, this);
5649 this.store.on('add', this.onAdd, this);
5653 onMouseover : function(e, el)
5655 var cell = Roo.get(el);
5661 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5662 cell = cell.findParent('td', false, true);
5665 var row = cell.findParent('tr', false, true);
5666 var cellIndex = cell.dom.cellIndex;
5667 var rowIndex = row.dom.rowIndex - 1; // start from 0
5669 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5673 onMouseout : function(e, el)
5675 var cell = Roo.get(el);
5681 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5682 cell = cell.findParent('td', false, true);
5685 var row = cell.findParent('tr', false, true);
5686 var cellIndex = cell.dom.cellIndex;
5687 var rowIndex = row.dom.rowIndex - 1; // start from 0
5689 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5693 onClick : function(e, el)
5695 var cell = Roo.get(el);
5697 if(!cell || (!this.CellSelection && !this.RowSelection)){
5701 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5702 cell = cell.findParent('td', false, true);
5705 if(!cell || typeof(cell) == 'undefined'){
5709 var row = cell.findParent('tr', false, true);
5711 if(!row || typeof(row) == 'undefined'){
5715 var cellIndex = cell.dom.cellIndex;
5716 var rowIndex = this.getRowIndex(row);
5718 if(this.CellSelection){
5719 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5722 if(this.RowSelection){
5723 this.fireEvent('rowclick', this, row, rowIndex, e);
5729 onDblClick : function(e,el)
5731 var cell = Roo.get(el);
5733 if(!cell || (!this.CellSelection && !this.RowSelection)){
5737 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5738 cell = cell.findParent('td', false, true);
5741 if(!cell || typeof(cell) == 'undefined'){
5745 var row = cell.findParent('tr', false, true);
5747 if(!row || typeof(row) == 'undefined'){
5751 var cellIndex = cell.dom.cellIndex;
5752 var rowIndex = this.getRowIndex(row);
5754 if(this.CellSelection){
5755 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5758 if(this.RowSelection){
5759 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5763 sort : function(e,el)
5765 var col = Roo.get(el);
5767 if(!col.hasClass('sortable')){
5771 var sort = col.attr('sort');
5774 if(col.hasClass('glyphicon-arrow-up')){
5778 this.store.sortInfo = {field : sort, direction : dir};
5781 Roo.log("calling footer first");
5782 this.footer.onClick('first');
5785 this.store.load({ params : { start : 0 } });
5789 renderHeader : function()
5798 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5800 var config = cm.config[i];
5805 html: cm.getColumnHeader(i)
5808 if(typeof(config.tooltip) != 'undefined'){
5809 c.tooltip = config.tooltip;
5812 if(typeof(config.colspan) != 'undefined'){
5813 c.colspan = config.colspan;
5816 if(typeof(config.hidden) != 'undefined' && config.hidden){
5817 c.style += ' display:none;';
5820 if(typeof(config.dataIndex) != 'undefined'){
5821 c.sort = config.dataIndex;
5824 if(typeof(config.sortable) != 'undefined' && config.sortable){
5828 if(typeof(config.align) != 'undefined' && config.align.length){
5829 c.style += ' text-align:' + config.align + ';';
5832 if(typeof(config.width) != 'undefined'){
5833 c.style += ' width:' + config.width + 'px;';
5836 if(typeof(config.cls) != 'undefined'){
5837 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5846 renderBody : function()
5856 colspan : this.cm.getColumnCount()
5866 renderFooter : function()
5876 colspan : this.cm.getColumnCount()
5890 Roo.log('ds onload');
5895 var ds = this.store;
5897 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5898 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5900 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5901 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5904 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5905 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5909 var tbody = this.mainBody;
5911 if(ds.getCount() > 0){
5912 ds.data.each(function(d,rowIndex){
5913 var row = this.renderRow(cm, ds, rowIndex);
5915 tbody.createChild(row);
5919 if(row.cellObjects.length){
5920 Roo.each(row.cellObjects, function(r){
5921 _this.renderCellObject(r);
5928 Roo.each(this.el.select('tbody td', true).elements, function(e){
5929 e.on('mouseover', _this.onMouseover, _this);
5932 Roo.each(this.el.select('tbody td', true).elements, function(e){
5933 e.on('mouseout', _this.onMouseout, _this);
5935 this.fireEvent('rowsrendered', this);
5936 //if(this.loadMask){
5937 // this.maskEl.hide();
5942 onUpdate : function(ds,record)
5944 this.refreshRow(record);
5947 onRemove : function(ds, record, index, isUpdate){
5948 if(isUpdate !== true){
5949 this.fireEvent("beforerowremoved", this, index, record);
5951 var bt = this.mainBody.dom;
5953 var rows = this.el.select('tbody > tr', true).elements;
5955 if(typeof(rows[index]) != 'undefined'){
5956 bt.removeChild(rows[index].dom);
5959 // if(bt.rows[index]){
5960 // bt.removeChild(bt.rows[index]);
5963 if(isUpdate !== true){
5964 //this.stripeRows(index);
5965 //this.syncRowHeights(index, index);
5967 this.fireEvent("rowremoved", this, index, record);
5971 onAdd : function(ds, records, rowIndex)
5973 //Roo.log('on Add called');
5974 // - note this does not handle multiple adding very well..
5975 var bt = this.mainBody.dom;
5976 for (var i =0 ; i < records.length;i++) {
5977 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5978 //Roo.log(records[i]);
5979 //Roo.log(this.store.getAt(rowIndex+i));
5980 this.insertRow(this.store, rowIndex + i, false);
5987 refreshRow : function(record){
5988 var ds = this.store, index;
5989 if(typeof record == 'number'){
5991 record = ds.getAt(index);
5993 index = ds.indexOf(record);
5995 this.insertRow(ds, index, true);
5996 this.onRemove(ds, record, index+1, true);
5997 //this.syncRowHeights(index, index);
5999 this.fireEvent("rowupdated", this, index, record);
6002 insertRow : function(dm, rowIndex, isUpdate){
6005 this.fireEvent("beforerowsinserted", this, rowIndex);
6007 //var s = this.getScrollState();
6008 var row = this.renderRow(this.cm, this.store, rowIndex);
6009 // insert before rowIndex..
6010 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6014 if(row.cellObjects.length){
6015 Roo.each(row.cellObjects, function(r){
6016 _this.renderCellObject(r);
6021 this.fireEvent("rowsinserted", this, rowIndex);
6022 //this.syncRowHeights(firstRow, lastRow);
6023 //this.stripeRows(firstRow);
6030 getRowDom : function(rowIndex)
6032 var rows = this.el.select('tbody > tr', true).elements;
6034 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6037 // returns the object tree for a tr..
6040 renderRow : function(cm, ds, rowIndex)
6043 var d = ds.getAt(rowIndex);
6050 var cellObjects = [];
6052 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6053 var config = cm.config[i];
6055 var renderer = cm.getRenderer(i);
6059 if(typeof(renderer) !== 'undefined'){
6060 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6062 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6063 // and are rendered into the cells after the row is rendered - using the id for the element.
6065 if(typeof(value) === 'object'){
6075 rowIndex : rowIndex,
6080 this.fireEvent('rowclass', this, rowcfg);
6084 cls : rowcfg.rowClass,
6086 html: (typeof(value) === 'object') ? '' : value
6093 if(typeof(config.colspan) != 'undefined'){
6094 td.colspan = config.colspan;
6097 if(typeof(config.hidden) != 'undefined' && config.hidden){
6098 td.style += ' display:none;';
6101 if(typeof(config.align) != 'undefined' && config.align.length){
6102 td.style += ' text-align:' + config.align + ';';
6105 if(typeof(config.width) != 'undefined'){
6106 td.style += ' width:' + config.width + 'px;';
6109 if(typeof(config.cursor) != 'undefined'){
6110 td.style += ' cursor:' + config.cursor + ';';
6113 if(typeof(config.cls) != 'undefined'){
6114 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6121 row.cellObjects = cellObjects;
6129 onBeforeLoad : function()
6131 //Roo.log('ds onBeforeLoad');
6135 //if(this.loadMask){
6136 // this.maskEl.show();
6144 this.el.select('tbody', true).first().dom.innerHTML = '';
6147 * Show or hide a row.
6148 * @param {Number} rowIndex to show or hide
6149 * @param {Boolean} state hide
6151 setRowVisibility : function(rowIndex, state)
6153 var bt = this.mainBody.dom;
6155 var rows = this.el.select('tbody > tr', true).elements;
6157 if(typeof(rows[rowIndex]) == 'undefined'){
6160 rows[rowIndex].dom.style.display = state ? '' : 'none';
6164 getSelectionModel : function(){
6166 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6168 return this.selModel;
6171 * Render the Roo.bootstrap object from renderder
6173 renderCellObject : function(r)
6177 var t = r.cfg.render(r.container);
6180 Roo.each(r.cfg.cn, function(c){
6182 container: t.getChildContainer(),
6185 _this.renderCellObject(child);
6190 getRowIndex : function(row)
6194 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6217 * @class Roo.bootstrap.TableCell
6218 * @extends Roo.bootstrap.Component
6219 * Bootstrap TableCell class
6220 * @cfg {String} html cell contain text
6221 * @cfg {String} cls cell class
6222 * @cfg {String} tag cell tag (td|th) default td
6223 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6224 * @cfg {String} align Aligns the content in a cell
6225 * @cfg {String} axis Categorizes cells
6226 * @cfg {String} bgcolor Specifies the background color of a cell
6227 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6228 * @cfg {Number} colspan Specifies the number of columns a cell should span
6229 * @cfg {String} headers Specifies one or more header cells a cell is related to
6230 * @cfg {Number} height Sets the height of a cell
6231 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6232 * @cfg {Number} rowspan Sets the number of rows a cell should span
6233 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6234 * @cfg {String} valign Vertical aligns the content in a cell
6235 * @cfg {Number} width Specifies the width of a cell
6238 * Create a new TableCell
6239 * @param {Object} config The config object
6242 Roo.bootstrap.TableCell = function(config){
6243 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6246 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6266 getAutoCreate : function(){
6267 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6287 cfg.align=this.align
6293 cfg.bgcolor=this.bgcolor
6296 cfg.charoff=this.charoff
6299 cfg.colspan=this.colspan
6302 cfg.headers=this.headers
6305 cfg.height=this.height
6308 cfg.nowrap=this.nowrap
6311 cfg.rowspan=this.rowspan
6314 cfg.scope=this.scope
6317 cfg.valign=this.valign
6320 cfg.width=this.width
6339 * @class Roo.bootstrap.TableRow
6340 * @extends Roo.bootstrap.Component
6341 * Bootstrap TableRow class
6342 * @cfg {String} cls row class
6343 * @cfg {String} align Aligns the content in a table row
6344 * @cfg {String} bgcolor Specifies a background color for a table row
6345 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6346 * @cfg {String} valign Vertical aligns the content in a table row
6349 * Create a new TableRow
6350 * @param {Object} config The config object
6353 Roo.bootstrap.TableRow = function(config){
6354 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6357 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6365 getAutoCreate : function(){
6366 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6376 cfg.align = this.align;
6379 cfg.bgcolor = this.bgcolor;
6382 cfg.charoff = this.charoff;
6385 cfg.valign = this.valign;
6403 * @class Roo.bootstrap.TableBody
6404 * @extends Roo.bootstrap.Component
6405 * Bootstrap TableBody class
6406 * @cfg {String} cls element class
6407 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6408 * @cfg {String} align Aligns the content inside the element
6409 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6410 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6413 * Create a new TableBody
6414 * @param {Object} config The config object
6417 Roo.bootstrap.TableBody = function(config){
6418 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6421 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6429 getAutoCreate : function(){
6430 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6444 cfg.align = this.align;
6447 cfg.charoff = this.charoff;
6450 cfg.valign = this.valign;
6457 // initEvents : function()
6464 // this.store = Roo.factory(this.store, Roo.data);
6465 // this.store.on('load', this.onLoad, this);
6467 // this.store.load();
6471 // onLoad: function ()
6473 // this.fireEvent('load', this);
6483 * Ext JS Library 1.1.1
6484 * Copyright(c) 2006-2007, Ext JS, LLC.
6486 * Originally Released Under LGPL - original licence link has changed is not relivant.
6489 * <script type="text/javascript">
6492 // as we use this in bootstrap.
6493 Roo.namespace('Roo.form');
6495 * @class Roo.form.Action
6496 * Internal Class used to handle form actions
6498 * @param {Roo.form.BasicForm} el The form element or its id
6499 * @param {Object} config Configuration options
6504 // define the action interface
6505 Roo.form.Action = function(form, options){
6507 this.options = options || {};
6510 * Client Validation Failed
6513 Roo.form.Action.CLIENT_INVALID = 'client';
6515 * Server Validation Failed
6518 Roo.form.Action.SERVER_INVALID = 'server';
6520 * Connect to Server Failed
6523 Roo.form.Action.CONNECT_FAILURE = 'connect';
6525 * Reading Data from Server Failed
6528 Roo.form.Action.LOAD_FAILURE = 'load';
6530 Roo.form.Action.prototype = {
6532 failureType : undefined,
6533 response : undefined,
6537 run : function(options){
6542 success : function(response){
6547 handleResponse : function(response){
6551 // default connection failure
6552 failure : function(response){
6554 this.response = response;
6555 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6556 this.form.afterAction(this, false);
6559 processResponse : function(response){
6560 this.response = response;
6561 if(!response.responseText){
6564 this.result = this.handleResponse(response);
6568 // utility functions used internally
6569 getUrl : function(appendParams){
6570 var url = this.options.url || this.form.url || this.form.el.dom.action;
6572 var p = this.getParams();
6574 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6580 getMethod : function(){
6581 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6584 getParams : function(){
6585 var bp = this.form.baseParams;
6586 var p = this.options.params;
6588 if(typeof p == "object"){
6589 p = Roo.urlEncode(Roo.applyIf(p, bp));
6590 }else if(typeof p == 'string' && bp){
6591 p += '&' + Roo.urlEncode(bp);
6594 p = Roo.urlEncode(bp);
6599 createCallback : function(){
6601 success: this.success,
6602 failure: this.failure,
6604 timeout: (this.form.timeout*1000),
6605 upload: this.form.fileUpload ? this.success : undefined
6610 Roo.form.Action.Submit = function(form, options){
6611 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6614 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6617 haveProgress : false,
6618 uploadComplete : false,
6620 // uploadProgress indicator.
6621 uploadProgress : function()
6623 if (!this.form.progressUrl) {
6627 if (!this.haveProgress) {
6628 Roo.MessageBox.progress("Uploading", "Uploading");
6630 if (this.uploadComplete) {
6631 Roo.MessageBox.hide();
6635 this.haveProgress = true;
6637 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6639 var c = new Roo.data.Connection();
6641 url : this.form.progressUrl,
6646 success : function(req){
6647 //console.log(data);
6651 rdata = Roo.decode(req.responseText)
6653 Roo.log("Invalid data from server..");
6657 if (!rdata || !rdata.success) {
6659 Roo.MessageBox.alert(Roo.encode(rdata));
6662 var data = rdata.data;
6664 if (this.uploadComplete) {
6665 Roo.MessageBox.hide();
6670 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6671 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6674 this.uploadProgress.defer(2000,this);
6677 failure: function(data) {
6678 Roo.log('progress url failed ');
6689 // run get Values on the form, so it syncs any secondary forms.
6690 this.form.getValues();
6692 var o = this.options;
6693 var method = this.getMethod();
6694 var isPost = method == 'POST';
6695 if(o.clientValidation === false || this.form.isValid()){
6697 if (this.form.progressUrl) {
6698 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6699 (new Date() * 1) + '' + Math.random());
6704 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6705 form:this.form.el.dom,
6706 url:this.getUrl(!isPost),
6708 params:isPost ? this.getParams() : null,
6709 isUpload: this.form.fileUpload
6712 this.uploadProgress();
6714 }else if (o.clientValidation !== false){ // client validation failed
6715 this.failureType = Roo.form.Action.CLIENT_INVALID;
6716 this.form.afterAction(this, false);
6720 success : function(response)
6722 this.uploadComplete= true;
6723 if (this.haveProgress) {
6724 Roo.MessageBox.hide();
6728 var result = this.processResponse(response);
6729 if(result === true || result.success){
6730 this.form.afterAction(this, true);
6734 this.form.markInvalid(result.errors);
6735 this.failureType = Roo.form.Action.SERVER_INVALID;
6737 this.form.afterAction(this, false);
6739 failure : function(response)
6741 this.uploadComplete= true;
6742 if (this.haveProgress) {
6743 Roo.MessageBox.hide();
6746 this.response = response;
6747 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6748 this.form.afterAction(this, false);
6751 handleResponse : function(response){
6752 if(this.form.errorReader){
6753 var rs = this.form.errorReader.read(response);
6756 for(var i = 0, len = rs.records.length; i < len; i++) {
6757 var r = rs.records[i];
6761 if(errors.length < 1){
6765 success : rs.success,
6771 ret = Roo.decode(response.responseText);
6775 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6785 Roo.form.Action.Load = function(form, options){
6786 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6787 this.reader = this.form.reader;
6790 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6795 Roo.Ajax.request(Roo.apply(
6796 this.createCallback(), {
6797 method:this.getMethod(),
6798 url:this.getUrl(false),
6799 params:this.getParams()
6803 success : function(response){
6805 var result = this.processResponse(response);
6806 if(result === true || !result.success || !result.data){
6807 this.failureType = Roo.form.Action.LOAD_FAILURE;
6808 this.form.afterAction(this, false);
6811 this.form.clearInvalid();
6812 this.form.setValues(result.data);
6813 this.form.afterAction(this, true);
6816 handleResponse : function(response){
6817 if(this.form.reader){
6818 var rs = this.form.reader.read(response);
6819 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6821 success : rs.success,
6825 return Roo.decode(response.responseText);
6829 Roo.form.Action.ACTION_TYPES = {
6830 'load' : Roo.form.Action.Load,
6831 'submit' : Roo.form.Action.Submit
6840 * @class Roo.bootstrap.Form
6841 * @extends Roo.bootstrap.Component
6842 * Bootstrap Form class
6843 * @cfg {String} method GET | POST (default POST)
6844 * @cfg {String} labelAlign top | left (default top)
6845 * @cfg {String} align left | right - for navbars
6846 * @cfg {Boolean} loadMask load mask when submit (default true)
6851 * @param {Object} config The config object
6855 Roo.bootstrap.Form = function(config){
6856 Roo.bootstrap.Form.superclass.constructor.call(this, config);
6859 * @event clientvalidation
6860 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6861 * @param {Form} this
6862 * @param {Boolean} valid true if the form has passed client-side validation
6864 clientvalidation: true,
6866 * @event beforeaction
6867 * Fires before any action is performed. Return false to cancel the action.
6868 * @param {Form} this
6869 * @param {Action} action The action to be performed
6873 * @event actionfailed
6874 * Fires when an action fails.
6875 * @param {Form} this
6876 * @param {Action} action The action that failed
6878 actionfailed : true,
6880 * @event actioncomplete
6881 * Fires when an action is completed.
6882 * @param {Form} this
6883 * @param {Action} action The action that completed
6885 actioncomplete : true
6890 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
6893 * @cfg {String} method
6894 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6899 * The URL to use for form actions if one isn't supplied in the action options.
6902 * @cfg {Boolean} fileUpload
6903 * Set to true if this form is a file upload.
6907 * @cfg {Object} baseParams
6908 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6912 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6916 * @cfg {Sting} align (left|right) for navbar forms
6921 activeAction : null,
6924 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6925 * element by passing it or its id or mask the form itself by passing in true.
6928 waitMsgTarget : false,
6932 getAutoCreate : function(){
6936 method : this.method || 'POST',
6937 id : this.id || Roo.id(),
6940 if (this.parent().xtype.match(/^Nav/)) {
6941 cfg.cls = 'navbar-form navbar-' + this.align;
6945 if (this.labelAlign == 'left' ) {
6946 cfg.cls += ' form-horizontal';
6952 initEvents : function()
6954 this.el.on('submit', this.onSubmit, this);
6955 // this was added as random key presses on the form where triggering form submit.
6956 this.el.on('keypress', function(e) {
6957 if (e.getCharCode() != 13) {
6960 // we might need to allow it for textareas.. and some other items.
6961 // check e.getTarget().
6963 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6967 Roo.log("keypress blocked");
6975 onSubmit : function(e){
6980 * Returns true if client-side validation on the form is successful.
6983 isValid : function(){
6984 var items = this.getItems();
6986 items.each(function(f){
6995 * Returns true if any fields in this form have changed since their original load.
6998 isDirty : function(){
7000 var items = this.getItems();
7001 items.each(function(f){
7011 * Performs a predefined action (submit or load) or custom actions you define on this form.
7012 * @param {String} actionName The name of the action type
7013 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7014 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7015 * accept other config options):
7017 Property Type Description
7018 ---------------- --------------- ----------------------------------------------------------------------------------
7019 url String The url for the action (defaults to the form's url)
7020 method String The form method to use (defaults to the form's method, or POST if not defined)
7021 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7022 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7023 validate the form on the client (defaults to false)
7025 * @return {BasicForm} this
7027 doAction : function(action, options){
7028 if(typeof action == 'string'){
7029 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7031 if(this.fireEvent('beforeaction', this, action) !== false){
7032 this.beforeAction(action);
7033 action.run.defer(100, action);
7039 beforeAction : function(action){
7040 var o = action.options;
7043 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7045 // not really supported yet.. ??
7047 //if(this.waitMsgTarget === true){
7048 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7049 //}else if(this.waitMsgTarget){
7050 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7051 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7053 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7059 afterAction : function(action, success){
7060 this.activeAction = null;
7061 var o = action.options;
7063 //if(this.waitMsgTarget === true){
7065 //}else if(this.waitMsgTarget){
7066 // this.waitMsgTarget.unmask();
7068 // Roo.MessageBox.updateProgress(1);
7069 // Roo.MessageBox.hide();
7076 Roo.callback(o.success, o.scope, [this, action]);
7077 this.fireEvent('actioncomplete', this, action);
7081 // failure condition..
7082 // we have a scenario where updates need confirming.
7083 // eg. if a locking scenario exists..
7084 // we look for { errors : { needs_confirm : true }} in the response.
7086 (typeof(action.result) != 'undefined') &&
7087 (typeof(action.result.errors) != 'undefined') &&
7088 (typeof(action.result.errors.needs_confirm) != 'undefined')
7091 Roo.log("not supported yet");
7094 Roo.MessageBox.confirm(
7095 "Change requires confirmation",
7096 action.result.errorMsg,
7101 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7111 Roo.callback(o.failure, o.scope, [this, action]);
7112 // show an error message if no failed handler is set..
7113 if (!this.hasListener('actionfailed')) {
7114 Roo.log("need to add dialog support");
7116 Roo.MessageBox.alert("Error",
7117 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7118 action.result.errorMsg :
7119 "Saving Failed, please check your entries or try again"
7124 this.fireEvent('actionfailed', this, action);
7129 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7130 * @param {String} id The value to search for
7133 findField : function(id){
7134 var items = this.getItems();
7135 var field = items.get(id);
7137 items.each(function(f){
7138 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7145 return field || null;
7148 * Mark fields in this form invalid in bulk.
7149 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7150 * @return {BasicForm} this
7152 markInvalid : function(errors){
7153 if(errors instanceof Array){
7154 for(var i = 0, len = errors.length; i < len; i++){
7155 var fieldError = errors[i];
7156 var f = this.findField(fieldError.id);
7158 f.markInvalid(fieldError.msg);
7164 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7165 field.markInvalid(errors[id]);
7169 //Roo.each(this.childForms || [], function (f) {
7170 // f.markInvalid(errors);
7177 * Set values for fields in this form in bulk.
7178 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7179 * @return {BasicForm} this
7181 setValues : function(values){
7182 if(values instanceof Array){ // array of objects
7183 for(var i = 0, len = values.length; i < len; i++){
7185 var f = this.findField(v.id);
7187 f.setValue(v.value);
7188 if(this.trackResetOnLoad){
7189 f.originalValue = f.getValue();
7193 }else{ // object hash
7196 if(typeof values[id] != 'function' && (field = this.findField(id))){
7198 if (field.setFromData &&
7200 field.displayField &&
7201 // combos' with local stores can
7202 // be queried via setValue()
7203 // to set their value..
7204 (field.store && !field.store.isLocal)
7208 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7209 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7210 field.setFromData(sd);
7213 field.setValue(values[id]);
7217 if(this.trackResetOnLoad){
7218 field.originalValue = field.getValue();
7224 //Roo.each(this.childForms || [], function (f) {
7225 // f.setValues(values);
7232 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7233 * they are returned as an array.
7234 * @param {Boolean} asString
7237 getValues : function(asString){
7238 //if (this.childForms) {
7239 // copy values from the child forms
7240 // Roo.each(this.childForms, function (f) {
7241 // this.setValues(f.getValues());
7247 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7248 if(asString === true){
7251 return Roo.urlDecode(fs);
7255 * Returns the fields in this form as an object with key/value pairs.
7256 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7259 getFieldValues : function(with_hidden)
7261 var items = this.getItems();
7263 items.each(function(f){
7267 var v = f.getValue();
7268 if (f.inputType =='radio') {
7269 if (typeof(ret[f.getName()]) == 'undefined') {
7270 ret[f.getName()] = ''; // empty..
7273 if (!f.el.dom.checked) {
7281 // not sure if this supported any more..
7282 if ((typeof(v) == 'object') && f.getRawValue) {
7283 v = f.getRawValue() ; // dates..
7285 // combo boxes where name != hiddenName...
7286 if (f.name != f.getName()) {
7287 ret[f.name] = f.getRawValue();
7289 ret[f.getName()] = v;
7296 * Clears all invalid messages in this form.
7297 * @return {BasicForm} this
7299 clearInvalid : function(){
7300 var items = this.getItems();
7302 items.each(function(f){
7313 * @return {BasicForm} this
7316 var items = this.getItems();
7317 items.each(function(f){
7321 Roo.each(this.childForms || [], function (f) {
7328 getItems : function()
7330 var r=new Roo.util.MixedCollection(false, function(o){
7331 return o.id || (o.id = Roo.id());
7333 var iter = function(el) {
7340 Roo.each(el.items,function(e) {
7360 * Ext JS Library 1.1.1
7361 * Copyright(c) 2006-2007, Ext JS, LLC.
7363 * Originally Released Under LGPL - original licence link has changed is not relivant.
7366 * <script type="text/javascript">
7369 * @class Roo.form.VTypes
7370 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7373 Roo.form.VTypes = function(){
7374 // closure these in so they are only created once.
7375 var alpha = /^[a-zA-Z_]+$/;
7376 var alphanum = /^[a-zA-Z0-9_]+$/;
7377 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7378 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7380 // All these messages and functions are configurable
7383 * The function used to validate email addresses
7384 * @param {String} value The email address
7386 'email' : function(v){
7387 return email.test(v);
7390 * The error text to display when the email validation function returns false
7393 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7395 * The keystroke filter mask to be applied on email input
7398 'emailMask' : /[a-z0-9_\.\-@]/i,
7401 * The function used to validate URLs
7402 * @param {String} value The URL
7404 'url' : function(v){
7408 * The error text to display when the url validation function returns false
7411 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7414 * The function used to validate alpha values
7415 * @param {String} value The value
7417 'alpha' : function(v){
7418 return alpha.test(v);
7421 * The error text to display when the alpha validation function returns false
7424 'alphaText' : 'This field should only contain letters and _',
7426 * The keystroke filter mask to be applied on alpha input
7429 'alphaMask' : /[a-z_]/i,
7432 * The function used to validate alphanumeric values
7433 * @param {String} value The value
7435 'alphanum' : function(v){
7436 return alphanum.test(v);
7439 * The error text to display when the alphanumeric validation function returns false
7442 'alphanumText' : 'This field should only contain letters, numbers and _',
7444 * The keystroke filter mask to be applied on alphanumeric input
7447 'alphanumMask' : /[a-z0-9_]/i
7457 * @class Roo.bootstrap.Input
7458 * @extends Roo.bootstrap.Component
7459 * Bootstrap Input class
7460 * @cfg {Boolean} disabled is it disabled
7461 * @cfg {String} fieldLabel - the label associated
7462 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7463 * @cfg {String} name name of the input
7464 * @cfg {string} fieldLabel - the label associated
7465 * @cfg {string} inputType - input / file submit ...
7466 * @cfg {string} placeholder - placeholder to put in text.
7467 * @cfg {string} before - input group add on before
7468 * @cfg {string} after - input group add on after
7469 * @cfg {string} size - (lg|sm) or leave empty..
7470 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7471 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7472 * @cfg {Number} md colspan out of 12 for computer-sized screens
7473 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7474 * @cfg {string} value default value of the input
7475 * @cfg {Number} labelWidth set the width of label (0-12)
7476 * @cfg {String} labelAlign (top|left)
7477 * @cfg {Boolean} readOnly Specifies that the field should be read-only
7478 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7480 * @cfg {String} align (left|center|right) Default left
7485 * Create a new Input
7486 * @param {Object} config The config object
7489 Roo.bootstrap.Input = function(config){
7490 Roo.bootstrap.Input.superclass.constructor.call(this, config);
7495 * Fires when this field receives input focus.
7496 * @param {Roo.form.Field} this
7501 * Fires when this field loses input focus.
7502 * @param {Roo.form.Field} this
7507 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
7508 * {@link Roo.EventObject#getKey} to determine which key was pressed.
7509 * @param {Roo.form.Field} this
7510 * @param {Roo.EventObject} e The event object
7515 * Fires just before the field blurs if the field value has changed.
7516 * @param {Roo.form.Field} this
7517 * @param {Mixed} newValue The new value
7518 * @param {Mixed} oldValue The original value
7523 * Fires after the field has been marked as invalid.
7524 * @param {Roo.form.Field} this
7525 * @param {String} msg The validation message
7530 * Fires after the field has been validated with no errors.
7531 * @param {Roo.form.Field} this
7536 * Fires after the key up
7537 * @param {Roo.form.Field} this
7538 * @param {Roo.EventObject} e The event Object
7544 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
7546 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7547 automatic validation (defaults to "keyup").
7549 validationEvent : "keyup",
7551 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7553 validateOnBlur : true,
7555 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7557 validationDelay : 250,
7559 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7561 focusClass : "x-form-focus", // not needed???
7565 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7567 invalidClass : "has-warning",
7570 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7572 validClass : "has-success",
7575 * @cfg {Boolean} hasFeedback (true|false) default true
7580 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7582 invalidFeedbackClass : "glyphicon-warning-sign",
7585 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7587 validFeedbackClass : "glyphicon-ok",
7590 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7592 selectOnFocus : false,
7595 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7599 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7604 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7606 disableKeyFilter : false,
7609 * @cfg {Boolean} disabled True to disable the field (defaults to false).
7613 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7617 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7619 blankText : "This field is required",
7622 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7626 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7628 maxLength : Number.MAX_VALUE,
7630 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7632 minLengthText : "The minimum length for this field is {0}",
7634 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7636 maxLengthText : "The maximum length for this field is {0}",
7640 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7641 * If available, this function will be called only after the basic validators all return true, and will be passed the
7642 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7646 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7647 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7648 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
7652 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7656 autocomplete: false,
7675 formatedValue : false,
7677 parentLabelAlign : function()
7680 while (parent.parent()) {
7681 parent = parent.parent();
7682 if (typeof(parent.labelAlign) !='undefined') {
7683 return parent.labelAlign;
7690 getAutoCreate : function(){
7692 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7698 if(this.inputType != 'hidden'){
7699 cfg.cls = 'form-group' //input-group
7705 type : this.inputType,
7707 cls : 'form-control',
7708 placeholder : this.placeholder || '',
7709 autocomplete : this.autocomplete || 'new-password'
7714 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7717 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7718 input.maxLength = this.maxLength;
7721 if (this.disabled) {
7722 input.disabled=true;
7725 if (this.readOnly) {
7726 input.readonly=true;
7730 input.name = this.name;
7733 input.cls += ' input-' + this.size;
7736 ['xs','sm','md','lg'].map(function(size){
7737 if (settings[size]) {
7738 cfg.cls += ' col-' + size + '-' + settings[size];
7742 var inputblock = input;
7746 cls: 'glyphicon form-control-feedback'
7749 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7752 cls : 'has-feedback',
7760 if (this.before || this.after) {
7763 cls : 'input-group',
7767 if (this.before && typeof(this.before) == 'string') {
7769 inputblock.cn.push({
7771 cls : 'roo-input-before input-group-addon',
7775 if (this.before && typeof(this.before) == 'object') {
7776 this.before = Roo.factory(this.before);
7777 Roo.log(this.before);
7778 inputblock.cn.push({
7780 cls : 'roo-input-before input-group-' +
7781 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7785 inputblock.cn.push(input);
7787 if (this.after && typeof(this.after) == 'string') {
7788 inputblock.cn.push({
7790 cls : 'roo-input-after input-group-addon',
7794 if (this.after && typeof(this.after) == 'object') {
7795 this.after = Roo.factory(this.after);
7796 Roo.log(this.after);
7797 inputblock.cn.push({
7799 cls : 'roo-input-after input-group-' +
7800 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7804 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7805 inputblock.cls += ' has-feedback';
7806 inputblock.cn.push(feedback);
7810 if (align ==='left' && this.fieldLabel.length) {
7811 Roo.log("left and has label");
7817 cls : 'control-label col-sm-' + this.labelWidth,
7818 html : this.fieldLabel
7822 cls : "col-sm-" + (12 - this.labelWidth),
7829 } else if ( this.fieldLabel.length) {
7835 //cls : 'input-group-addon',
7836 html : this.fieldLabel
7846 Roo.log(" no label && no align");
7855 Roo.log('input-parentType: ' + this.parentType);
7857 if (this.parentType === 'Navbar' && this.parent().bar) {
7858 cfg.cls += ' navbar-form';
7866 * return the real input element.
7868 inputEl: function ()
7870 return this.el.select('input.form-control',true).first();
7873 tooltipEl : function()
7875 return this.inputEl();
7878 setDisabled : function(v)
7880 var i = this.inputEl().dom;
7882 i.removeAttribute('disabled');
7886 i.setAttribute('disabled','true');
7888 initEvents : function()
7891 this.inputEl().on("keydown" , this.fireKey, this);
7892 this.inputEl().on("focus", this.onFocus, this);
7893 this.inputEl().on("blur", this.onBlur, this);
7895 this.inputEl().relayEvent('keyup', this);
7897 // reference to original value for reset
7898 this.originalValue = this.getValue();
7899 //Roo.form.TextField.superclass.initEvents.call(this);
7900 if(this.validationEvent == 'keyup'){
7901 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7902 this.inputEl().on('keyup', this.filterValidation, this);
7904 else if(this.validationEvent !== false){
7905 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7908 if(this.selectOnFocus){
7909 this.on("focus", this.preFocus, this);
7912 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7913 this.inputEl().on("keypress", this.filterKeys, this);
7916 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
7917 this.el.on("click", this.autoSize, this);
7920 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7921 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7924 if (typeof(this.before) == 'object') {
7925 this.before.render(this.el.select('.roo-input-before',true).first());
7927 if (typeof(this.after) == 'object') {
7928 this.after.render(this.el.select('.roo-input-after',true).first());
7933 filterValidation : function(e){
7934 if(!e.isNavKeyPress()){
7935 this.validationTask.delay(this.validationDelay);
7939 * Validates the field value
7940 * @return {Boolean} True if the value is valid, else false
7942 validate : function(){
7943 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7944 if(this.disabled || this.validateValue(this.getRawValue())){
7955 * Validates a value according to the field's validation rules and marks the field as invalid
7956 * if the validation fails
7957 * @param {Mixed} value The value to validate
7958 * @return {Boolean} True if the value is valid, else false
7960 validateValue : function(value){
7961 if(value.length < 1) { // if it's blank
7962 if(this.allowBlank){
7968 if(value.length < this.minLength){
7971 if(value.length > this.maxLength){
7975 var vt = Roo.form.VTypes;
7976 if(!vt[this.vtype](value, this)){
7980 if(typeof this.validator == "function"){
7981 var msg = this.validator(value);
7987 if(this.regex && !this.regex.test(value)){
7997 fireKey : function(e){
7998 //Roo.log('field ' + e.getKey());
7999 if(e.isNavKeyPress()){
8000 this.fireEvent("specialkey", this, e);
8003 focus : function (selectText){
8005 this.inputEl().focus();
8006 if(selectText === true){
8007 this.inputEl().dom.select();
8013 onFocus : function(){
8014 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8015 // this.el.addClass(this.focusClass);
8018 this.hasFocus = true;
8019 this.startValue = this.getValue();
8020 this.fireEvent("focus", this);
8024 beforeBlur : Roo.emptyFn,
8028 onBlur : function(){
8030 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8031 //this.el.removeClass(this.focusClass);
8033 this.hasFocus = false;
8034 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8037 var v = this.getValue();
8038 if(String(v) !== String(this.startValue)){
8039 this.fireEvent('change', this, v, this.startValue);
8041 this.fireEvent("blur", this);
8045 * Resets the current field value to the originally loaded value and clears any validation messages
8048 this.setValue(this.originalValue);
8052 * Returns the name of the field
8053 * @return {Mixed} name The name field
8055 getName: function(){
8059 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8060 * @return {Mixed} value The field value
8062 getValue : function(){
8064 var v = this.inputEl().getValue();
8069 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8070 * @return {Mixed} value The field value
8072 getRawValue : function(){
8073 var v = this.inputEl().getValue();
8079 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8080 * @param {Mixed} value The value to set
8082 setRawValue : function(v){
8083 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8086 selectText : function(start, end){
8087 var v = this.getRawValue();
8089 start = start === undefined ? 0 : start;
8090 end = end === undefined ? v.length : end;
8091 var d = this.inputEl().dom;
8092 if(d.setSelectionRange){
8093 d.setSelectionRange(start, end);
8094 }else if(d.createTextRange){
8095 var range = d.createTextRange();
8096 range.moveStart("character", start);
8097 range.moveEnd("character", v.length-end);
8104 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8105 * @param {Mixed} value The value to set
8107 setValue : function(v){
8110 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8116 processValue : function(value){
8117 if(this.stripCharsRe){
8118 var newValue = value.replace(this.stripCharsRe, '');
8119 if(newValue !== value){
8120 this.setRawValue(newValue);
8127 preFocus : function(){
8129 if(this.selectOnFocus){
8130 this.inputEl().dom.select();
8133 filterKeys : function(e){
8135 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8138 var c = e.getCharCode(), cc = String.fromCharCode(c);
8139 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8142 if(!this.maskRe.test(cc)){
8147 * Clear any invalid styles/messages for this field
8149 clearInvalid : function(){
8151 if(!this.el || this.preventMark){ // not rendered
8154 this.el.removeClass(this.invalidClass);
8156 this.fireEvent('valid', this);
8160 * Mark this field as valid
8162 markValid : function(){
8163 if(!this.el || this.preventMark){ // not rendered
8167 this.el.removeClass([this.invalidClass, this.validClass]);
8169 if(this.disabled || this.allowBlank){
8173 this.el.addClass(this.validClass);
8175 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && this.getValue().length){
8177 var feedback = this.el.select('.form-control-feedback', true).first();
8180 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8181 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8186 this.fireEvent('valid', this);
8190 * Mark this field as invalid
8191 * @param {String} msg The validation message
8193 markInvalid : function(msg){
8194 if(!this.el || this.preventMark){ // not rendered
8198 this.el.removeClass([this.invalidClass, this.validClass]);
8200 if(this.disabled || this.allowBlank){
8204 this.el.addClass(this.invalidClass);
8206 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8208 var feedback = this.el.select('.form-control-feedback', true).first();
8211 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8213 if(this.getValue().length){
8214 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8221 this.fireEvent('invalid', this, msg);
8224 SafariOnKeyDown : function(event)
8226 // this is a workaround for a password hang bug on chrome/ webkit.
8228 var isSelectAll = false;
8230 if(this.inputEl().dom.selectionEnd > 0){
8231 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8233 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8234 event.preventDefault();
8239 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8241 event.preventDefault();
8242 // this is very hacky as keydown always get's upper case.
8244 var cc = String.fromCharCode(event.getCharCode());
8245 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8249 adjustWidth : function(tag, w){
8250 tag = tag.toLowerCase();
8251 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8252 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8256 if(tag == 'textarea'){
8259 }else if(Roo.isOpera){
8263 if(tag == 'textarea'){
8282 * @class Roo.bootstrap.TextArea
8283 * @extends Roo.bootstrap.Input
8284 * Bootstrap TextArea class
8285 * @cfg {Number} cols Specifies the visible width of a text area
8286 * @cfg {Number} rows Specifies the visible number of lines in a text area
8287 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8288 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8289 * @cfg {string} html text
8292 * Create a new TextArea
8293 * @param {Object} config The config object
8296 Roo.bootstrap.TextArea = function(config){
8297 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8301 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8311 getAutoCreate : function(){
8313 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8324 value : this.value || '',
8325 html: this.html || '',
8326 cls : 'form-control',
8327 placeholder : this.placeholder || ''
8331 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8332 input.maxLength = this.maxLength;
8336 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8340 input.cols = this.cols;
8343 if (this.readOnly) {
8344 input.readonly = true;
8348 input.name = this.name;
8352 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8356 ['xs','sm','md','lg'].map(function(size){
8357 if (settings[size]) {
8358 cfg.cls += ' col-' + size + '-' + settings[size];
8362 var inputblock = input;
8364 if(this.hasFeedback && !this.allowBlank){
8368 cls: 'glyphicon form-control-feedback'
8372 cls : 'has-feedback',
8381 if (this.before || this.after) {
8384 cls : 'input-group',
8388 inputblock.cn.push({
8390 cls : 'input-group-addon',
8395 inputblock.cn.push(input);
8397 if(this.hasFeedback && !this.allowBlank){
8398 inputblock.cls += ' has-feedback';
8399 inputblock.cn.push(feedback);
8403 inputblock.cn.push({
8405 cls : 'input-group-addon',
8412 if (align ==='left' && this.fieldLabel.length) {
8413 Roo.log("left and has label");
8419 cls : 'control-label col-sm-' + this.labelWidth,
8420 html : this.fieldLabel
8424 cls : "col-sm-" + (12 - this.labelWidth),
8431 } else if ( this.fieldLabel.length) {
8437 //cls : 'input-group-addon',
8438 html : this.fieldLabel
8448 Roo.log(" no label && no align");
8458 if (this.disabled) {
8459 input.disabled=true;
8466 * return the real textarea element.
8468 inputEl: function ()
8470 return this.el.select('textarea.form-control',true).first();
8478 * trigger field - base class for combo..
8483 * @class Roo.bootstrap.TriggerField
8484 * @extends Roo.bootstrap.Input
8485 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8486 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8487 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8488 * for which you can provide a custom implementation. For example:
8490 var trigger = new Roo.bootstrap.TriggerField();
8491 trigger.onTriggerClick = myTriggerFn;
8492 trigger.applyTo('my-field');
8495 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8496 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8497 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
8498 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8499 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8502 * Create a new TriggerField.
8503 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8504 * to the base TextField)
8506 Roo.bootstrap.TriggerField = function(config){
8507 this.mimicing = false;
8508 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8511 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
8513 * @cfg {String} triggerClass A CSS class to apply to the trigger
8516 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8521 * @cfg {Boolean} removable (true|false) special filter default false
8525 /** @cfg {Boolean} grow @hide */
8526 /** @cfg {Number} growMin @hide */
8527 /** @cfg {Number} growMax @hide */
8533 autoSize: Roo.emptyFn,
8540 actionMode : 'wrap',
8545 getAutoCreate : function(){
8547 var align = this.labelAlign || this.parentLabelAlign();
8552 cls: 'form-group' //input-group
8559 type : this.inputType,
8560 cls : 'form-control',
8561 autocomplete: 'new-password',
8562 placeholder : this.placeholder || ''
8566 input.name = this.name;
8569 input.cls += ' input-' + this.size;
8572 if (this.disabled) {
8573 input.disabled=true;
8576 var inputblock = input;
8578 if(this.hasFeedback && !this.allowBlank){
8582 cls: 'glyphicon form-control-feedback'
8585 if(this.removable && !this.editable && !this.tickable){
8587 cls : 'has-feedback',
8593 cls : 'roo-combo-removable-btn close'
8600 cls : 'has-feedback',
8609 if(this.removable && !this.editable && !this.tickable){
8611 cls : 'roo-removable',
8617 cls : 'roo-combo-removable-btn close'
8624 if (this.before || this.after) {
8627 cls : 'input-group',
8631 inputblock.cn.push({
8633 cls : 'input-group-addon',
8638 inputblock.cn.push(input);
8640 if(this.hasFeedback && !this.allowBlank){
8641 inputblock.cls += ' has-feedback';
8642 inputblock.cn.push(feedback);
8646 inputblock.cn.push({
8648 cls : 'input-group-addon',
8661 cls: 'form-hidden-field'
8669 Roo.log('multiple');
8677 cls: 'form-hidden-field'
8681 cls: 'select2-choices',
8685 cls: 'select2-search-field',
8698 cls: 'select2-container input-group',
8703 // cls: 'typeahead typeahead-long dropdown-menu',
8704 // style: 'display:none'
8709 if(!this.multiple && this.showToggleBtn){
8715 if (this.caret != false) {
8718 cls: 'fa fa-' + this.caret
8725 cls : 'input-group-addon btn dropdown-toggle',
8730 cls: 'combobox-clear',
8744 combobox.cls += ' select2-container-multi';
8747 if (align ==='left' && this.fieldLabel.length) {
8749 Roo.log("left and has label");
8755 cls : 'control-label col-sm-' + this.labelWidth,
8756 html : this.fieldLabel
8760 cls : "col-sm-" + (12 - this.labelWidth),
8767 } else if ( this.fieldLabel.length) {
8773 //cls : 'input-group-addon',
8774 html : this.fieldLabel
8784 Roo.log(" no label && no align");
8791 ['xs','sm','md','lg'].map(function(size){
8792 if (settings[size]) {
8793 cfg.cls += ' col-' + size + '-' + settings[size];
8804 onResize : function(w, h){
8805 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8806 // if(typeof w == 'number'){
8807 // var x = w - this.trigger.getWidth();
8808 // this.inputEl().setWidth(this.adjustWidth('input', x));
8809 // this.trigger.setStyle('left', x+'px');
8814 adjustSize : Roo.BoxComponent.prototype.adjustSize,
8817 getResizeEl : function(){
8818 return this.inputEl();
8822 getPositionEl : function(){
8823 return this.inputEl();
8827 alignErrorIcon : function(){
8828 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8832 initEvents : function(){
8836 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8837 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8838 if(!this.multiple && this.showToggleBtn){
8839 this.trigger = this.el.select('span.dropdown-toggle',true).first();
8840 if(this.hideTrigger){
8841 this.trigger.setDisplayed(false);
8843 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8847 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8850 if(this.removable && !this.editable && !this.tickable){
8851 var close = this.closeTriggerEl();
8854 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
8855 close.on('click', this.removeBtnClick, this, close);
8859 //this.trigger.addClassOnOver('x-form-trigger-over');
8860 //this.trigger.addClassOnClick('x-form-trigger-click');
8863 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8867 closeTriggerEl : function()
8869 var close = this.el.select('.roo-combo-removable-btn', true).first();
8870 return close ? close : false;
8873 removeBtnClick : function(e, h, el)
8877 if(this.fireEvent("remove", this) !== false){
8882 createList : function()
8884 this.list = Roo.get(document.body).createChild({
8886 cls: 'typeahead typeahead-long dropdown-menu',
8887 style: 'display:none'
8890 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8895 initTrigger : function(){
8900 onDestroy : function(){
8902 this.trigger.removeAllListeners();
8903 // this.trigger.remove();
8906 // this.wrap.remove();
8908 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8912 onFocus : function(){
8913 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8916 this.wrap.addClass('x-trigger-wrap-focus');
8917 this.mimicing = true;
8918 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8919 if(this.monitorTab){
8920 this.el.on("keydown", this.checkTab, this);
8927 checkTab : function(e){
8928 if(e.getKey() == e.TAB){
8934 onBlur : function(){
8939 mimicBlur : function(e, t){
8941 if(!this.wrap.contains(t) && this.validateBlur()){
8948 triggerBlur : function(){
8949 this.mimicing = false;
8950 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8951 if(this.monitorTab){
8952 this.el.un("keydown", this.checkTab, this);
8954 //this.wrap.removeClass('x-trigger-wrap-focus');
8955 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8959 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8960 validateBlur : function(e, t){
8965 onDisable : function(){
8966 this.inputEl().dom.disabled = true;
8967 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8969 // this.wrap.addClass('x-item-disabled');
8974 onEnable : function(){
8975 this.inputEl().dom.disabled = false;
8976 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8978 // this.el.removeClass('x-item-disabled');
8983 onShow : function(){
8984 var ae = this.getActionEl();
8987 ae.dom.style.display = '';
8988 ae.dom.style.visibility = 'visible';
8994 onHide : function(){
8995 var ae = this.getActionEl();
8996 ae.dom.style.display = 'none';
9000 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9001 * by an implementing function.
9003 * @param {EventObject} e
9005 onTriggerClick : Roo.emptyFn
9009 * Ext JS Library 1.1.1
9010 * Copyright(c) 2006-2007, Ext JS, LLC.
9012 * Originally Released Under LGPL - original licence link has changed is not relivant.
9015 * <script type="text/javascript">
9020 * @class Roo.data.SortTypes
9022 * Defines the default sorting (casting?) comparison functions used when sorting data.
9024 Roo.data.SortTypes = {
9026 * Default sort that does nothing
9027 * @param {Mixed} s The value being converted
9028 * @return {Mixed} The comparison value
9035 * The regular expression used to strip tags
9039 stripTagsRE : /<\/?[^>]+>/gi,
9042 * Strips all HTML tags to sort on text only
9043 * @param {Mixed} s The value being converted
9044 * @return {String} The comparison value
9046 asText : function(s){
9047 return String(s).replace(this.stripTagsRE, "");
9051 * Strips all HTML tags to sort on text only - Case insensitive
9052 * @param {Mixed} s The value being converted
9053 * @return {String} The comparison value
9055 asUCText : function(s){
9056 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9060 * Case insensitive string
9061 * @param {Mixed} s The value being converted
9062 * @return {String} The comparison value
9064 asUCString : function(s) {
9065 return String(s).toUpperCase();
9070 * @param {Mixed} s The value being converted
9071 * @return {Number} The comparison value
9073 asDate : function(s) {
9077 if(s instanceof Date){
9080 return Date.parse(String(s));
9085 * @param {Mixed} s The value being converted
9086 * @return {Float} The comparison value
9088 asFloat : function(s) {
9089 var val = parseFloat(String(s).replace(/,/g, ""));
9090 if(isNaN(val)) val = 0;
9096 * @param {Mixed} s The value being converted
9097 * @return {Number} The comparison value
9099 asInt : function(s) {
9100 var val = parseInt(String(s).replace(/,/g, ""));
9101 if(isNaN(val)) val = 0;
9106 * Ext JS Library 1.1.1
9107 * Copyright(c) 2006-2007, Ext JS, LLC.
9109 * Originally Released Under LGPL - original licence link has changed is not relivant.
9112 * <script type="text/javascript">
9116 * @class Roo.data.Record
9117 * Instances of this class encapsulate both record <em>definition</em> information, and record
9118 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9119 * to access Records cached in an {@link Roo.data.Store} object.<br>
9121 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9122 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9125 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9127 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9128 * {@link #create}. The parameters are the same.
9129 * @param {Array} data An associative Array of data values keyed by the field name.
9130 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9131 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9132 * not specified an integer id is generated.
9134 Roo.data.Record = function(data, id){
9135 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9140 * Generate a constructor for a specific record layout.
9141 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9142 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9143 * Each field definition object may contain the following properties: <ul>
9144 * <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,
9145 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9146 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9147 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9148 * is being used, then this is a string containing the javascript expression to reference the data relative to
9149 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9150 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9151 * this may be omitted.</p></li>
9152 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9153 * <ul><li>auto (Default, implies no conversion)</li>
9158 * <li>date</li></ul></p></li>
9159 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9160 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9161 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9162 * by the Reader into an object that will be stored in the Record. It is passed the
9163 * following parameters:<ul>
9164 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9166 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9168 * <br>usage:<br><pre><code>
9169 var TopicRecord = Roo.data.Record.create(
9170 {name: 'title', mapping: 'topic_title'},
9171 {name: 'author', mapping: 'username'},
9172 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9173 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9174 {name: 'lastPoster', mapping: 'user2'},
9175 {name: 'excerpt', mapping: 'post_text'}
9178 var myNewRecord = new TopicRecord({
9179 title: 'Do my job please',
9182 lastPost: new Date(),
9183 lastPoster: 'Animal',
9184 excerpt: 'No way dude!'
9186 myStore.add(myNewRecord);
9191 Roo.data.Record.create = function(o){
9193 f.superclass.constructor.apply(this, arguments);
9195 Roo.extend(f, Roo.data.Record);
9196 var p = f.prototype;
9197 p.fields = new Roo.util.MixedCollection(false, function(field){
9200 for(var i = 0, len = o.length; i < len; i++){
9201 p.fields.add(new Roo.data.Field(o[i]));
9203 f.getField = function(name){
9204 return p.fields.get(name);
9209 Roo.data.Record.AUTO_ID = 1000;
9210 Roo.data.Record.EDIT = 'edit';
9211 Roo.data.Record.REJECT = 'reject';
9212 Roo.data.Record.COMMIT = 'commit';
9214 Roo.data.Record.prototype = {
9216 * Readonly flag - true if this record has been modified.
9225 join : function(store){
9230 * Set the named field to the specified value.
9231 * @param {String} name The name of the field to set.
9232 * @param {Object} value The value to set the field to.
9234 set : function(name, value){
9235 if(this.data[name] == value){
9242 if(typeof this.modified[name] == 'undefined'){
9243 this.modified[name] = this.data[name];
9245 this.data[name] = value;
9246 if(!this.editing && this.store){
9247 this.store.afterEdit(this);
9252 * Get the value of the named field.
9253 * @param {String} name The name of the field to get the value of.
9254 * @return {Object} The value of the field.
9256 get : function(name){
9257 return this.data[name];
9261 beginEdit : function(){
9262 this.editing = true;
9267 cancelEdit : function(){
9268 this.editing = false;
9269 delete this.modified;
9273 endEdit : function(){
9274 this.editing = false;
9275 if(this.dirty && this.store){
9276 this.store.afterEdit(this);
9281 * Usually called by the {@link Roo.data.Store} which owns the Record.
9282 * Rejects all changes made to the Record since either creation, or the last commit operation.
9283 * Modified fields are reverted to their original values.
9285 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9286 * of reject operations.
9288 reject : function(){
9289 var m = this.modified;
9291 if(typeof m[n] != "function"){
9292 this.data[n] = m[n];
9296 delete this.modified;
9297 this.editing = false;
9299 this.store.afterReject(this);
9304 * Usually called by the {@link Roo.data.Store} which owns the Record.
9305 * Commits all changes made to the Record since either creation, or the last commit operation.
9307 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9308 * of commit operations.
9310 commit : function(){
9312 delete this.modified;
9313 this.editing = false;
9315 this.store.afterCommit(this);
9320 hasError : function(){
9321 return this.error != null;
9325 clearError : function(){
9330 * Creates a copy of this record.
9331 * @param {String} id (optional) A new record id if you don't want to use this record's id
9334 copy : function(newId) {
9335 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9339 * Ext JS Library 1.1.1
9340 * Copyright(c) 2006-2007, Ext JS, LLC.
9342 * Originally Released Under LGPL - original licence link has changed is not relivant.
9345 * <script type="text/javascript">
9351 * @class Roo.data.Store
9352 * @extends Roo.util.Observable
9353 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9354 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9356 * 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
9357 * has no knowledge of the format of the data returned by the Proxy.<br>
9359 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9360 * instances from the data object. These records are cached and made available through accessor functions.
9362 * Creates a new Store.
9363 * @param {Object} config A config object containing the objects needed for the Store to access data,
9364 * and read the data into Records.
9366 Roo.data.Store = function(config){
9367 this.data = new Roo.util.MixedCollection(false);
9368 this.data.getKey = function(o){
9371 this.baseParams = {};
9378 "multisort" : "_multisort"
9381 if(config && config.data){
9382 this.inlineData = config.data;
9386 Roo.apply(this, config);
9388 if(this.reader){ // reader passed
9389 this.reader = Roo.factory(this.reader, Roo.data);
9390 this.reader.xmodule = this.xmodule || false;
9391 if(!this.recordType){
9392 this.recordType = this.reader.recordType;
9394 if(this.reader.onMetaChange){
9395 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9399 if(this.recordType){
9400 this.fields = this.recordType.prototype.fields;
9406 * @event datachanged
9407 * Fires when the data cache has changed, and a widget which is using this Store
9408 * as a Record cache should refresh its view.
9409 * @param {Store} this
9414 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9415 * @param {Store} this
9416 * @param {Object} meta The JSON metadata
9421 * Fires when Records have been added to the Store
9422 * @param {Store} this
9423 * @param {Roo.data.Record[]} records The array of Records added
9424 * @param {Number} index The index at which the record(s) were added
9429 * Fires when a Record has been removed from the Store
9430 * @param {Store} this
9431 * @param {Roo.data.Record} record The Record that was removed
9432 * @param {Number} index The index at which the record was removed
9437 * Fires when a Record has been updated
9438 * @param {Store} this
9439 * @param {Roo.data.Record} record The Record that was updated
9440 * @param {String} operation The update operation being performed. Value may be one of:
9442 Roo.data.Record.EDIT
9443 Roo.data.Record.REJECT
9444 Roo.data.Record.COMMIT
9450 * Fires when the data cache has been cleared.
9451 * @param {Store} this
9456 * Fires before a request is made for a new data object. If the beforeload handler returns false
9457 * the load action will be canceled.
9458 * @param {Store} this
9459 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9463 * @event beforeloadadd
9464 * Fires after a new set of Records has been loaded.
9465 * @param {Store} this
9466 * @param {Roo.data.Record[]} records The Records that were loaded
9467 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9469 beforeloadadd : true,
9472 * Fires after a new set of Records has been loaded, before they are added to the store.
9473 * @param {Store} this
9474 * @param {Roo.data.Record[]} records The Records that were loaded
9475 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9476 * @params {Object} return from reader
9480 * @event loadexception
9481 * Fires if an exception occurs in the Proxy during loading.
9482 * Called with the signature of the Proxy's "loadexception" event.
9483 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9486 * @param {Object} return from JsonData.reader() - success, totalRecords, records
9487 * @param {Object} load options
9488 * @param {Object} jsonData from your request (normally this contains the Exception)
9490 loadexception : true
9494 this.proxy = Roo.factory(this.proxy, Roo.data);
9495 this.proxy.xmodule = this.xmodule || false;
9496 this.relayEvents(this.proxy, ["loadexception"]);
9498 this.sortToggle = {};
9499 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9501 Roo.data.Store.superclass.constructor.call(this);
9503 if(this.inlineData){
9504 this.loadData(this.inlineData);
9505 delete this.inlineData;
9509 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9511 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
9512 * without a remote query - used by combo/forms at present.
9516 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9519 * @cfg {Array} data Inline data to be loaded when the store is initialized.
9522 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9523 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9526 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9527 * on any HTTP request
9530 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9533 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9537 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9538 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9543 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9544 * loaded or when a record is removed. (defaults to false).
9546 pruneModifiedRecords : false,
9552 * Add Records to the Store and fires the add event.
9553 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9555 add : function(records){
9556 records = [].concat(records);
9557 for(var i = 0, len = records.length; i < len; i++){
9558 records[i].join(this);
9560 var index = this.data.length;
9561 this.data.addAll(records);
9562 this.fireEvent("add", this, records, index);
9566 * Remove a Record from the Store and fires the remove event.
9567 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9569 remove : function(record){
9570 var index = this.data.indexOf(record);
9571 this.data.removeAt(index);
9572 if(this.pruneModifiedRecords){
9573 this.modified.remove(record);
9575 this.fireEvent("remove", this, record, index);
9579 * Remove all Records from the Store and fires the clear event.
9581 removeAll : function(){
9583 if(this.pruneModifiedRecords){
9586 this.fireEvent("clear", this);
9590 * Inserts Records to the Store at the given index and fires the add event.
9591 * @param {Number} index The start index at which to insert the passed Records.
9592 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9594 insert : function(index, records){
9595 records = [].concat(records);
9596 for(var i = 0, len = records.length; i < len; i++){
9597 this.data.insert(index, records[i]);
9598 records[i].join(this);
9600 this.fireEvent("add", this, records, index);
9604 * Get the index within the cache of the passed Record.
9605 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9606 * @return {Number} The index of the passed Record. Returns -1 if not found.
9608 indexOf : function(record){
9609 return this.data.indexOf(record);
9613 * Get the index within the cache of the Record with the passed id.
9614 * @param {String} id The id of the Record to find.
9615 * @return {Number} The index of the Record. Returns -1 if not found.
9617 indexOfId : function(id){
9618 return this.data.indexOfKey(id);
9622 * Get the Record with the specified id.
9623 * @param {String} id The id of the Record to find.
9624 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9626 getById : function(id){
9627 return this.data.key(id);
9631 * Get the Record at the specified index.
9632 * @param {Number} index The index of the Record to find.
9633 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9635 getAt : function(index){
9636 return this.data.itemAt(index);
9640 * Returns a range of Records between specified indices.
9641 * @param {Number} startIndex (optional) The starting index (defaults to 0)
9642 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9643 * @return {Roo.data.Record[]} An array of Records
9645 getRange : function(start, end){
9646 return this.data.getRange(start, end);
9650 storeOptions : function(o){
9651 o = Roo.apply({}, o);
9654 this.lastOptions = o;
9658 * Loads the Record cache from the configured Proxy using the configured Reader.
9660 * If using remote paging, then the first load call must specify the <em>start</em>
9661 * and <em>limit</em> properties in the options.params property to establish the initial
9662 * position within the dataset, and the number of Records to cache on each read from the Proxy.
9664 * <strong>It is important to note that for remote data sources, loading is asynchronous,
9665 * and this call will return before the new data has been loaded. Perform any post-processing
9666 * in a callback function, or in a "load" event handler.</strong>
9668 * @param {Object} options An object containing properties which control loading options:<ul>
9669 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9670 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9671 * passed the following arguments:<ul>
9672 * <li>r : Roo.data.Record[]</li>
9673 * <li>options: Options object from the load call</li>
9674 * <li>success: Boolean success indicator</li></ul></li>
9675 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9676 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9679 load : function(options){
9680 options = options || {};
9681 if(this.fireEvent("beforeload", this, options) !== false){
9682 this.storeOptions(options);
9683 var p = Roo.apply(options.params || {}, this.baseParams);
9684 // if meta was not loaded from remote source.. try requesting it.
9685 if (!this.reader.metaFromRemote) {
9688 if(this.sortInfo && this.remoteSort){
9689 var pn = this.paramNames;
9690 p[pn["sort"]] = this.sortInfo.field;
9691 p[pn["dir"]] = this.sortInfo.direction;
9693 if (this.multiSort) {
9694 var pn = this.paramNames;
9695 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9698 this.proxy.load(p, this.reader, this.loadRecords, this, options);
9703 * Reloads the Record cache from the configured Proxy using the configured Reader and
9704 * the options from the last load operation performed.
9705 * @param {Object} options (optional) An object containing properties which may override the options
9706 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9707 * the most recently used options are reused).
9709 reload : function(options){
9710 this.load(Roo.applyIf(options||{}, this.lastOptions));
9714 // Called as a callback by the Reader during a load operation.
9715 loadRecords : function(o, options, success){
9716 if(!o || success === false){
9717 if(success !== false){
9718 this.fireEvent("load", this, [], options, o);
9720 if(options.callback){
9721 options.callback.call(options.scope || this, [], options, false);
9725 // if data returned failure - throw an exception.
9726 if (o.success === false) {
9727 // show a message if no listener is registered.
9728 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9729 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9731 // loadmask wil be hooked into this..
9732 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9735 var r = o.records, t = o.totalRecords || r.length;
9737 this.fireEvent("beforeloadadd", this, r, options, o);
9739 if(!options || options.add !== true){
9740 if(this.pruneModifiedRecords){
9743 for(var i = 0, len = r.length; i < len; i++){
9747 this.data = this.snapshot;
9748 delete this.snapshot;
9751 this.data.addAll(r);
9752 this.totalLength = t;
9754 this.fireEvent("datachanged", this);
9756 this.totalLength = Math.max(t, this.data.length+r.length);
9759 this.fireEvent("load", this, r, options, o);
9760 if(options.callback){
9761 options.callback.call(options.scope || this, r, options, true);
9767 * Loads data from a passed data block. A Reader which understands the format of the data
9768 * must have been configured in the constructor.
9769 * @param {Object} data The data block from which to read the Records. The format of the data expected
9770 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9771 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9773 loadData : function(o, append){
9774 var r = this.reader.readRecords(o);
9775 this.loadRecords(r, {add: append}, true);
9779 * Gets the number of cached records.
9781 * <em>If using paging, this may not be the total size of the dataset. If the data object
9782 * used by the Reader contains the dataset size, then the getTotalCount() function returns
9783 * the data set size</em>
9785 getCount : function(){
9786 return this.data.length || 0;
9790 * Gets the total number of records in the dataset as returned by the server.
9792 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9793 * the dataset size</em>
9795 getTotalCount : function(){
9796 return this.totalLength || 0;
9800 * Returns the sort state of the Store as an object with two properties:
9802 field {String} The name of the field by which the Records are sorted
9803 direction {String} The sort order, "ASC" or "DESC"
9806 getSortState : function(){
9807 return this.sortInfo;
9811 applySort : function(){
9812 if(this.sortInfo && !this.remoteSort){
9813 var s = this.sortInfo, f = s.field;
9814 var st = this.fields.get(f).sortType;
9815 var fn = function(r1, r2){
9816 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9817 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9819 this.data.sort(s.direction, fn);
9820 if(this.snapshot && this.snapshot != this.data){
9821 this.snapshot.sort(s.direction, fn);
9827 * Sets the default sort column and order to be used by the next load operation.
9828 * @param {String} fieldName The name of the field to sort by.
9829 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9831 setDefaultSort : function(field, dir){
9832 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9837 * If remote sorting is used, the sort is performed on the server, and the cache is
9838 * reloaded. If local sorting is used, the cache is sorted internally.
9839 * @param {String} fieldName The name of the field to sort by.
9840 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9842 sort : function(fieldName, dir){
9843 var f = this.fields.get(fieldName);
9845 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9847 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9848 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9853 this.sortToggle[f.name] = dir;
9854 this.sortInfo = {field: f.name, direction: dir};
9855 if(!this.remoteSort){
9857 this.fireEvent("datachanged", this);
9859 this.load(this.lastOptions);
9864 * Calls the specified function for each of the Records in the cache.
9865 * @param {Function} fn The function to call. The Record is passed as the first parameter.
9866 * Returning <em>false</em> aborts and exits the iteration.
9867 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9869 each : function(fn, scope){
9870 this.data.each(fn, scope);
9874 * Gets all records modified since the last commit. Modified records are persisted across load operations
9875 * (e.g., during paging).
9876 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9878 getModifiedRecords : function(){
9879 return this.modified;
9883 createFilterFn : function(property, value, anyMatch){
9884 if(!value.exec){ // not a regex
9885 value = String(value);
9886 if(value.length == 0){
9889 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9892 return value.test(r.data[property]);
9897 * Sums the value of <i>property</i> for each record between start and end and returns the result.
9898 * @param {String} property A field on your records
9899 * @param {Number} start The record index to start at (defaults to 0)
9900 * @param {Number} end The last record index to include (defaults to length - 1)
9901 * @return {Number} The sum
9903 sum : function(property, start, end){
9904 var rs = this.data.items, v = 0;
9906 end = (end || end === 0) ? end : rs.length-1;
9908 for(var i = start; i <= end; i++){
9909 v += (rs[i].data[property] || 0);
9915 * Filter the records by a specified property.
9916 * @param {String} field A field on your records
9917 * @param {String/RegExp} value Either a string that the field
9918 * should start with or a RegExp to test against the field
9919 * @param {Boolean} anyMatch True to match any part not just the beginning
9921 filter : function(property, value, anyMatch){
9922 var fn = this.createFilterFn(property, value, anyMatch);
9923 return fn ? this.filterBy(fn) : this.clearFilter();
9927 * Filter by a function. The specified function will be called with each
9928 * record in this data source. If the function returns true the record is included,
9929 * otherwise it is filtered.
9930 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9931 * @param {Object} scope (optional) The scope of the function (defaults to this)
9933 filterBy : function(fn, scope){
9934 this.snapshot = this.snapshot || this.data;
9935 this.data = this.queryBy(fn, scope||this);
9936 this.fireEvent("datachanged", this);
9940 * Query the records by a specified property.
9941 * @param {String} field A field on your records
9942 * @param {String/RegExp} value Either a string that the field
9943 * should start with or a RegExp to test against the field
9944 * @param {Boolean} anyMatch True to match any part not just the beginning
9945 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9947 query : function(property, value, anyMatch){
9948 var fn = this.createFilterFn(property, value, anyMatch);
9949 return fn ? this.queryBy(fn) : this.data.clone();
9953 * Query by a function. The specified function will be called with each
9954 * record in this data source. If the function returns true the record is included
9956 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9957 * @param {Object} scope (optional) The scope of the function (defaults to this)
9958 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9960 queryBy : function(fn, scope){
9961 var data = this.snapshot || this.data;
9962 return data.filterBy(fn, scope||this);
9966 * Collects unique values for a particular dataIndex from this store.
9967 * @param {String} dataIndex The property to collect
9968 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9969 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9970 * @return {Array} An array of the unique values
9972 collect : function(dataIndex, allowNull, bypassFilter){
9973 var d = (bypassFilter === true && this.snapshot) ?
9974 this.snapshot.items : this.data.items;
9975 var v, sv, r = [], l = {};
9976 for(var i = 0, len = d.length; i < len; i++){
9977 v = d[i].data[dataIndex];
9979 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9988 * Revert to a view of the Record cache with no filtering applied.
9989 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9991 clearFilter : function(suppressEvent){
9992 if(this.snapshot && this.snapshot != this.data){
9993 this.data = this.snapshot;
9994 delete this.snapshot;
9995 if(suppressEvent !== true){
9996 this.fireEvent("datachanged", this);
10002 afterEdit : function(record){
10003 if(this.modified.indexOf(record) == -1){
10004 this.modified.push(record);
10006 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10010 afterReject : function(record){
10011 this.modified.remove(record);
10012 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10016 afterCommit : function(record){
10017 this.modified.remove(record);
10018 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10022 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10023 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10025 commitChanges : function(){
10026 var m = this.modified.slice(0);
10027 this.modified = [];
10028 for(var i = 0, len = m.length; i < len; i++){
10034 * Cancel outstanding changes on all changed records.
10036 rejectChanges : function(){
10037 var m = this.modified.slice(0);
10038 this.modified = [];
10039 for(var i = 0, len = m.length; i < len; i++){
10044 onMetaChange : function(meta, rtype, o){
10045 this.recordType = rtype;
10046 this.fields = rtype.prototype.fields;
10047 delete this.snapshot;
10048 this.sortInfo = meta.sortInfo || this.sortInfo;
10049 this.modified = [];
10050 this.fireEvent('metachange', this, this.reader.meta);
10053 moveIndex : function(data, type)
10055 var index = this.indexOf(data);
10057 var newIndex = index + type;
10061 this.insert(newIndex, data);
10066 * Ext JS Library 1.1.1
10067 * Copyright(c) 2006-2007, Ext JS, LLC.
10069 * Originally Released Under LGPL - original licence link has changed is not relivant.
10072 * <script type="text/javascript">
10076 * @class Roo.data.SimpleStore
10077 * @extends Roo.data.Store
10078 * Small helper class to make creating Stores from Array data easier.
10079 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10080 * @cfg {Array} fields An array of field definition objects, or field name strings.
10081 * @cfg {Array} data The multi-dimensional array of data
10083 * @param {Object} config
10085 Roo.data.SimpleStore = function(config){
10086 Roo.data.SimpleStore.superclass.constructor.call(this, {
10088 reader: new Roo.data.ArrayReader({
10091 Roo.data.Record.create(config.fields)
10093 proxy : new Roo.data.MemoryProxy(config.data)
10097 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10099 * Ext JS Library 1.1.1
10100 * Copyright(c) 2006-2007, Ext JS, LLC.
10102 * Originally Released Under LGPL - original licence link has changed is not relivant.
10105 * <script type="text/javascript">
10110 * @extends Roo.data.Store
10111 * @class Roo.data.JsonStore
10112 * Small helper class to make creating Stores for JSON data easier. <br/>
10114 var store = new Roo.data.JsonStore({
10115 url: 'get-images.php',
10117 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10120 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10121 * JsonReader and HttpProxy (unless inline data is provided).</b>
10122 * @cfg {Array} fields An array of field definition objects, or field name strings.
10124 * @param {Object} config
10126 Roo.data.JsonStore = function(c){
10127 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10128 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10129 reader: new Roo.data.JsonReader(c, c.fields)
10132 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10134 * Ext JS Library 1.1.1
10135 * Copyright(c) 2006-2007, Ext JS, LLC.
10137 * Originally Released Under LGPL - original licence link has changed is not relivant.
10140 * <script type="text/javascript">
10144 Roo.data.Field = function(config){
10145 if(typeof config == "string"){
10146 config = {name: config};
10148 Roo.apply(this, config);
10151 this.type = "auto";
10154 var st = Roo.data.SortTypes;
10155 // named sortTypes are supported, here we look them up
10156 if(typeof this.sortType == "string"){
10157 this.sortType = st[this.sortType];
10160 // set default sortType for strings and dates
10161 if(!this.sortType){
10164 this.sortType = st.asUCString;
10167 this.sortType = st.asDate;
10170 this.sortType = st.none;
10175 var stripRe = /[\$,%]/g;
10177 // prebuilt conversion function for this field, instead of
10178 // switching every time we're reading a value
10180 var cv, dateFormat = this.dateFormat;
10185 cv = function(v){ return v; };
10188 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10192 return v !== undefined && v !== null && v !== '' ?
10193 parseInt(String(v).replace(stripRe, ""), 10) : '';
10198 return v !== undefined && v !== null && v !== '' ?
10199 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10204 cv = function(v){ return v === true || v === "true" || v == 1; };
10211 if(v instanceof Date){
10215 if(dateFormat == "timestamp"){
10216 return new Date(v*1000);
10218 return Date.parseDate(v, dateFormat);
10220 var parsed = Date.parse(v);
10221 return parsed ? new Date(parsed) : null;
10230 Roo.data.Field.prototype = {
10238 * Ext JS Library 1.1.1
10239 * Copyright(c) 2006-2007, Ext JS, LLC.
10241 * Originally Released Under LGPL - original licence link has changed is not relivant.
10244 * <script type="text/javascript">
10247 // Base class for reading structured data from a data source. This class is intended to be
10248 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10251 * @class Roo.data.DataReader
10252 * Base class for reading structured data from a data source. This class is intended to be
10253 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10256 Roo.data.DataReader = function(meta, recordType){
10260 this.recordType = recordType instanceof Array ?
10261 Roo.data.Record.create(recordType) : recordType;
10264 Roo.data.DataReader.prototype = {
10266 * Create an empty record
10267 * @param {Object} data (optional) - overlay some values
10268 * @return {Roo.data.Record} record created.
10270 newRow : function(d) {
10272 this.recordType.prototype.fields.each(function(c) {
10274 case 'int' : da[c.name] = 0; break;
10275 case 'date' : da[c.name] = new Date(); break;
10276 case 'float' : da[c.name] = 0.0; break;
10277 case 'boolean' : da[c.name] = false; break;
10278 default : da[c.name] = ""; break;
10282 return new this.recordType(Roo.apply(da, d));
10287 * Ext JS Library 1.1.1
10288 * Copyright(c) 2006-2007, Ext JS, LLC.
10290 * Originally Released Under LGPL - original licence link has changed is not relivant.
10293 * <script type="text/javascript">
10297 * @class Roo.data.DataProxy
10298 * @extends Roo.data.Observable
10299 * This class is an abstract base class for implementations which provide retrieval of
10300 * unformatted data objects.<br>
10302 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10303 * (of the appropriate type which knows how to parse the data object) to provide a block of
10304 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10306 * Custom implementations must implement the load method as described in
10307 * {@link Roo.data.HttpProxy#load}.
10309 Roo.data.DataProxy = function(){
10312 * @event beforeload
10313 * Fires before a network request is made to retrieve a data object.
10314 * @param {Object} This DataProxy object.
10315 * @param {Object} params The params parameter to the load function.
10320 * Fires before the load method's callback is called.
10321 * @param {Object} This DataProxy object.
10322 * @param {Object} o The data object.
10323 * @param {Object} arg The callback argument object passed to the load function.
10327 * @event loadexception
10328 * Fires if an Exception occurs during data retrieval.
10329 * @param {Object} This DataProxy object.
10330 * @param {Object} o The data object.
10331 * @param {Object} arg The callback argument object passed to the load function.
10332 * @param {Object} e The Exception.
10334 loadexception : true
10336 Roo.data.DataProxy.superclass.constructor.call(this);
10339 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10342 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10346 * Ext JS Library 1.1.1
10347 * Copyright(c) 2006-2007, Ext JS, LLC.
10349 * Originally Released Under LGPL - original licence link has changed is not relivant.
10352 * <script type="text/javascript">
10355 * @class Roo.data.MemoryProxy
10356 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10357 * to the Reader when its load method is called.
10359 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10361 Roo.data.MemoryProxy = function(data){
10365 Roo.data.MemoryProxy.superclass.constructor.call(this);
10369 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10371 * Load data from the requested source (in this case an in-memory
10372 * data object passed to the constructor), read the data object into
10373 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10374 * process that block using the passed callback.
10375 * @param {Object} params This parameter is not used by the MemoryProxy class.
10376 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10377 * object into a block of Roo.data.Records.
10378 * @param {Function} callback The function into which to pass the block of Roo.data.records.
10379 * The function must be passed <ul>
10380 * <li>The Record block object</li>
10381 * <li>The "arg" argument from the load function</li>
10382 * <li>A boolean success indicator</li>
10384 * @param {Object} scope The scope in which to call the callback
10385 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10387 load : function(params, reader, callback, scope, arg){
10388 params = params || {};
10391 result = reader.readRecords(this.data);
10393 this.fireEvent("loadexception", this, arg, null, e);
10394 callback.call(scope, null, arg, false);
10397 callback.call(scope, result, arg, true);
10401 update : function(params, records){
10406 * Ext JS Library 1.1.1
10407 * Copyright(c) 2006-2007, Ext JS, LLC.
10409 * Originally Released Under LGPL - original licence link has changed is not relivant.
10412 * <script type="text/javascript">
10415 * @class Roo.data.HttpProxy
10416 * @extends Roo.data.DataProxy
10417 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10418 * configured to reference a certain URL.<br><br>
10420 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10421 * from which the running page was served.<br><br>
10423 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10425 * Be aware that to enable the browser to parse an XML document, the server must set
10426 * the Content-Type header in the HTTP response to "text/xml".
10428 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10429 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
10430 * will be used to make the request.
10432 Roo.data.HttpProxy = function(conn){
10433 Roo.data.HttpProxy.superclass.constructor.call(this);
10434 // is conn a conn config or a real conn?
10436 this.useAjax = !conn || !conn.events;
10440 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10441 // thse are take from connection...
10444 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10447 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10448 * extra parameters to each request made by this object. (defaults to undefined)
10451 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10452 * to each request made by this object. (defaults to undefined)
10455 * @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)
10458 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10461 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10467 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10471 * Return the {@link Roo.data.Connection} object being used by this Proxy.
10472 * @return {Connection} The Connection object. This object may be used to subscribe to events on
10473 * a finer-grained basis than the DataProxy events.
10475 getConnection : function(){
10476 return this.useAjax ? Roo.Ajax : this.conn;
10480 * Load data from the configured {@link Roo.data.Connection}, read the data object into
10481 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10482 * process that block using the passed callback.
10483 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10484 * for the request to the remote server.
10485 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10486 * object into a block of Roo.data.Records.
10487 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10488 * The function must be passed <ul>
10489 * <li>The Record block object</li>
10490 * <li>The "arg" argument from the load function</li>
10491 * <li>A boolean success indicator</li>
10493 * @param {Object} scope The scope in which to call the callback
10494 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10496 load : function(params, reader, callback, scope, arg){
10497 if(this.fireEvent("beforeload", this, params) !== false){
10499 params : params || {},
10501 callback : callback,
10506 callback : this.loadResponse,
10510 Roo.applyIf(o, this.conn);
10511 if(this.activeRequest){
10512 Roo.Ajax.abort(this.activeRequest);
10514 this.activeRequest = Roo.Ajax.request(o);
10516 this.conn.request(o);
10519 callback.call(scope||this, null, arg, false);
10524 loadResponse : function(o, success, response){
10525 delete this.activeRequest;
10527 this.fireEvent("loadexception", this, o, response);
10528 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10533 result = o.reader.read(response);
10535 this.fireEvent("loadexception", this, o, response, e);
10536 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10540 this.fireEvent("load", this, o, o.request.arg);
10541 o.request.callback.call(o.request.scope, result, o.request.arg, true);
10545 update : function(dataSet){
10550 updateResponse : function(dataSet){
10555 * Ext JS Library 1.1.1
10556 * Copyright(c) 2006-2007, Ext JS, LLC.
10558 * Originally Released Under LGPL - original licence link has changed is not relivant.
10561 * <script type="text/javascript">
10565 * @class Roo.data.ScriptTagProxy
10566 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10567 * other than the originating domain of the running page.<br><br>
10569 * <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
10570 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10572 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10573 * source code that is used as the source inside a <script> tag.<br><br>
10575 * In order for the browser to process the returned data, the server must wrap the data object
10576 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10577 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10578 * depending on whether the callback name was passed:
10581 boolean scriptTag = false;
10582 String cb = request.getParameter("callback");
10585 response.setContentType("text/javascript");
10587 response.setContentType("application/x-json");
10589 Writer out = response.getWriter();
10591 out.write(cb + "(");
10593 out.print(dataBlock.toJsonString());
10600 * @param {Object} config A configuration object.
10602 Roo.data.ScriptTagProxy = function(config){
10603 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10604 Roo.apply(this, config);
10605 this.head = document.getElementsByTagName("head")[0];
10608 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10610 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10612 * @cfg {String} url The URL from which to request the data object.
10615 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10619 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10620 * the server the name of the callback function set up by the load call to process the returned data object.
10621 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10622 * javascript output which calls this named function passing the data object as its only parameter.
10624 callbackParam : "callback",
10626 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10627 * name to the request.
10632 * Load data from the configured URL, read the data object into
10633 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10634 * process that block using the passed callback.
10635 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10636 * for the request to the remote server.
10637 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10638 * object into a block of Roo.data.Records.
10639 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10640 * The function must be passed <ul>
10641 * <li>The Record block object</li>
10642 * <li>The "arg" argument from the load function</li>
10643 * <li>A boolean success indicator</li>
10645 * @param {Object} scope The scope in which to call the callback
10646 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10648 load : function(params, reader, callback, scope, arg){
10649 if(this.fireEvent("beforeload", this, params) !== false){
10651 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10653 var url = this.url;
10654 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10656 url += "&_dc=" + (new Date().getTime());
10658 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10661 cb : "stcCallback"+transId,
10662 scriptId : "stcScript"+transId,
10666 callback : callback,
10672 window[trans.cb] = function(o){
10673 conn.handleResponse(o, trans);
10676 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10678 if(this.autoAbort !== false){
10682 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10684 var script = document.createElement("script");
10685 script.setAttribute("src", url);
10686 script.setAttribute("type", "text/javascript");
10687 script.setAttribute("id", trans.scriptId);
10688 this.head.appendChild(script);
10690 this.trans = trans;
10692 callback.call(scope||this, null, arg, false);
10697 isLoading : function(){
10698 return this.trans ? true : false;
10702 * Abort the current server request.
10704 abort : function(){
10705 if(this.isLoading()){
10706 this.destroyTrans(this.trans);
10711 destroyTrans : function(trans, isLoaded){
10712 this.head.removeChild(document.getElementById(trans.scriptId));
10713 clearTimeout(trans.timeoutId);
10715 window[trans.cb] = undefined;
10717 delete window[trans.cb];
10720 // if hasn't been loaded, wait for load to remove it to prevent script error
10721 window[trans.cb] = function(){
10722 window[trans.cb] = undefined;
10724 delete window[trans.cb];
10731 handleResponse : function(o, trans){
10732 this.trans = false;
10733 this.destroyTrans(trans, true);
10736 result = trans.reader.readRecords(o);
10738 this.fireEvent("loadexception", this, o, trans.arg, e);
10739 trans.callback.call(trans.scope||window, null, trans.arg, false);
10742 this.fireEvent("load", this, o, trans.arg);
10743 trans.callback.call(trans.scope||window, result, trans.arg, true);
10747 handleFailure : function(trans){
10748 this.trans = false;
10749 this.destroyTrans(trans, false);
10750 this.fireEvent("loadexception", this, null, trans.arg);
10751 trans.callback.call(trans.scope||window, null, trans.arg, false);
10755 * Ext JS Library 1.1.1
10756 * Copyright(c) 2006-2007, Ext JS, LLC.
10758 * Originally Released Under LGPL - original licence link has changed is not relivant.
10761 * <script type="text/javascript">
10765 * @class Roo.data.JsonReader
10766 * @extends Roo.data.DataReader
10767 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10768 * based on mappings in a provided Roo.data.Record constructor.
10770 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10771 * in the reply previously.
10776 var RecordDef = Roo.data.Record.create([
10777 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
10778 {name: 'occupation'} // This field will use "occupation" as the mapping.
10780 var myReader = new Roo.data.JsonReader({
10781 totalProperty: "results", // The property which contains the total dataset size (optional)
10782 root: "rows", // The property which contains an Array of row objects
10783 id: "id" // The property within each row object that provides an ID for the record (optional)
10787 * This would consume a JSON file like this:
10789 { 'results': 2, 'rows': [
10790 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10791 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10794 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10795 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10796 * paged from the remote server.
10797 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10798 * @cfg {String} root name of the property which contains the Array of row objects.
10799 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10800 * @cfg {Array} fields Array of field definition objects
10802 * Create a new JsonReader
10803 * @param {Object} meta Metadata configuration options
10804 * @param {Object} recordType Either an Array of field definition objects,
10805 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10807 Roo.data.JsonReader = function(meta, recordType){
10810 // set some defaults:
10811 Roo.applyIf(meta, {
10812 totalProperty: 'total',
10813 successProperty : 'success',
10818 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10820 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10823 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
10824 * Used by Store query builder to append _requestMeta to params.
10827 metaFromRemote : false,
10829 * This method is only used by a DataProxy which has retrieved data from a remote server.
10830 * @param {Object} response The XHR object which contains the JSON data in its responseText.
10831 * @return {Object} data A data block which is used by an Roo.data.Store object as
10832 * a cache of Roo.data.Records.
10834 read : function(response){
10835 var json = response.responseText;
10837 var o = /* eval:var:o */ eval("("+json+")");
10839 throw {message: "JsonReader.read: Json object not found"};
10845 this.metaFromRemote = true;
10846 this.meta = o.metaData;
10847 this.recordType = Roo.data.Record.create(o.metaData.fields);
10848 this.onMetaChange(this.meta, this.recordType, o);
10850 return this.readRecords(o);
10853 // private function a store will implement
10854 onMetaChange : function(meta, recordType, o){
10861 simpleAccess: function(obj, subsc) {
10868 getJsonAccessor: function(){
10870 return function(expr) {
10872 return(re.test(expr))
10873 ? new Function("obj", "return obj." + expr)
10878 return Roo.emptyFn;
10883 * Create a data block containing Roo.data.Records from an XML document.
10884 * @param {Object} o An object which contains an Array of row objects in the property specified
10885 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10886 * which contains the total size of the dataset.
10887 * @return {Object} data A data block which is used by an Roo.data.Store object as
10888 * a cache of Roo.data.Records.
10890 readRecords : function(o){
10892 * After any data loads, the raw JSON data is available for further custom processing.
10896 var s = this.meta, Record = this.recordType,
10897 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10899 // Generate extraction functions for the totalProperty, the root, the id, and for each field
10901 if(s.totalProperty) {
10902 this.getTotal = this.getJsonAccessor(s.totalProperty);
10904 if(s.successProperty) {
10905 this.getSuccess = this.getJsonAccessor(s.successProperty);
10907 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10909 var g = this.getJsonAccessor(s.id);
10910 this.getId = function(rec) {
10912 return (r === undefined || r === "") ? null : r;
10915 this.getId = function(){return null;};
10918 for(var jj = 0; jj < fl; jj++){
10920 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10921 this.ef[jj] = this.getJsonAccessor(map);
10925 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10926 if(s.totalProperty){
10927 var vt = parseInt(this.getTotal(o), 10);
10932 if(s.successProperty){
10933 var vs = this.getSuccess(o);
10934 if(vs === false || vs === 'false'){
10939 for(var i = 0; i < c; i++){
10942 var id = this.getId(n);
10943 for(var j = 0; j < fl; j++){
10945 var v = this.ef[j](n);
10947 Roo.log('missing convert for ' + f.name);
10951 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10953 var record = new Record(values, id);
10955 records[i] = record;
10961 totalRecords : totalRecords
10966 * Ext JS Library 1.1.1
10967 * Copyright(c) 2006-2007, Ext JS, LLC.
10969 * Originally Released Under LGPL - original licence link has changed is not relivant.
10972 * <script type="text/javascript">
10976 * @class Roo.data.ArrayReader
10977 * @extends Roo.data.DataReader
10978 * Data reader class to create an Array of Roo.data.Record objects from an Array.
10979 * Each element of that Array represents a row of data fields. The
10980 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10981 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10985 var RecordDef = Roo.data.Record.create([
10986 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
10987 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
10989 var myReader = new Roo.data.ArrayReader({
10990 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
10994 * This would consume an Array like this:
10996 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10998 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11000 * Create a new JsonReader
11001 * @param {Object} meta Metadata configuration options.
11002 * @param {Object} recordType Either an Array of field definition objects
11003 * as specified to {@link Roo.data.Record#create},
11004 * or an {@link Roo.data.Record} object
11005 * created using {@link Roo.data.Record#create}.
11007 Roo.data.ArrayReader = function(meta, recordType){
11008 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11011 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11013 * Create a data block containing Roo.data.Records from an XML document.
11014 * @param {Object} o An Array of row objects which represents the dataset.
11015 * @return {Object} data A data block which is used by an Roo.data.Store object as
11016 * a cache of Roo.data.Records.
11018 readRecords : function(o){
11019 var sid = this.meta ? this.meta.id : null;
11020 var recordType = this.recordType, fields = recordType.prototype.fields;
11023 for(var i = 0; i < root.length; i++){
11026 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11027 for(var j = 0, jlen = fields.length; j < jlen; j++){
11028 var f = fields.items[j];
11029 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11030 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11032 values[f.name] = v;
11034 var record = new recordType(values, id);
11036 records[records.length] = record;
11040 totalRecords : records.length
11049 * @class Roo.bootstrap.ComboBox
11050 * @extends Roo.bootstrap.TriggerField
11051 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11052 * @cfg {Boolean} append (true|false) default false
11053 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11054 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11055 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11056 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11057 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11058 * @cfg {Boolean} animate default true
11059 * @cfg {Boolean} emptyResultText only for touch device
11061 * Create a new ComboBox.
11062 * @param {Object} config Configuration options
11064 Roo.bootstrap.ComboBox = function(config){
11065 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11069 * Fires when the dropdown list is expanded
11070 * @param {Roo.bootstrap.ComboBox} combo This combo box
11075 * Fires when the dropdown list is collapsed
11076 * @param {Roo.bootstrap.ComboBox} combo This combo box
11080 * @event beforeselect
11081 * Fires before a list item is selected. Return false to cancel the selection.
11082 * @param {Roo.bootstrap.ComboBox} combo This combo box
11083 * @param {Roo.data.Record} record The data record returned from the underlying store
11084 * @param {Number} index The index of the selected item in the dropdown list
11086 'beforeselect' : true,
11089 * Fires when a list item is selected
11090 * @param {Roo.bootstrap.ComboBox} combo This combo box
11091 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11092 * @param {Number} index The index of the selected item in the dropdown list
11096 * @event beforequery
11097 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11098 * The event object passed has these properties:
11099 * @param {Roo.bootstrap.ComboBox} combo This combo box
11100 * @param {String} query The query
11101 * @param {Boolean} forceAll true to force "all" query
11102 * @param {Boolean} cancel true to cancel the query
11103 * @param {Object} e The query event object
11105 'beforequery': true,
11108 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11109 * @param {Roo.bootstrap.ComboBox} combo This combo box
11114 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11115 * @param {Roo.bootstrap.ComboBox} combo This combo box
11116 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11121 * Fires when the remove value from the combobox array
11122 * @param {Roo.bootstrap.ComboBox} combo This combo box
11126 * @event specialfilter
11127 * Fires when specialfilter
11128 * @param {Roo.bootstrap.ComboBox} combo This combo box
11130 'specialfilter' : true
11135 this.tickItems = [];
11137 this.selectedIndex = -1;
11138 if(this.mode == 'local'){
11139 if(config.queryDelay === undefined){
11140 this.queryDelay = 10;
11142 if(config.minChars === undefined){
11148 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11151 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11152 * rendering into an Roo.Editor, defaults to false)
11155 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11156 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11159 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11162 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11163 * the dropdown list (defaults to undefined, with no header element)
11167 * @cfg {String/Roo.Template} tpl The template to use to render the output
11171 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11173 listWidth: undefined,
11175 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11176 * mode = 'remote' or 'text' if mode = 'local')
11178 displayField: undefined,
11181 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11182 * mode = 'remote' or 'value' if mode = 'local').
11183 * Note: use of a valueField requires the user make a selection
11184 * in order for a value to be mapped.
11186 valueField: undefined,
11190 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11191 * field's data value (defaults to the underlying DOM element's name)
11193 hiddenName: undefined,
11195 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11199 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11201 selectedClass: 'active',
11204 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11208 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11209 * anchor positions (defaults to 'tl-bl')
11211 listAlign: 'tl-bl?',
11213 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11217 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11218 * query specified by the allQuery config option (defaults to 'query')
11220 triggerAction: 'query',
11222 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11223 * (defaults to 4, does not apply if editable = false)
11227 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11228 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11232 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11233 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11237 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11238 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11242 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11243 * when editable = true (defaults to false)
11245 selectOnFocus:false,
11247 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11249 queryParam: 'query',
11251 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
11252 * when mode = 'remote' (defaults to 'Loading...')
11254 loadingText: 'Loading...',
11256 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11260 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11264 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11265 * traditional select (defaults to true)
11269 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11273 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11277 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11278 * listWidth has a higher value)
11282 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11283 * allow the user to set arbitrary text into the field (defaults to false)
11285 forceSelection:false,
11287 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11288 * if typeAhead = true (defaults to 250)
11290 typeAheadDelay : 250,
11292 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11293 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11295 valueNotFoundText : undefined,
11297 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11299 blockFocus : false,
11302 * @cfg {Boolean} disableClear Disable showing of clear button.
11304 disableClear : false,
11306 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
11308 alwaysQuery : false,
11311 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
11316 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11318 invalidClass : "has-warning",
11321 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11323 validClass : "has-success",
11326 * @cfg {Boolean} specialFilter (true|false) special filter default false
11328 specialFilter : false,
11331 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11333 mobileTouchView : true,
11345 btnPosition : 'right',
11346 triggerList : true,
11347 showToggleBtn : true,
11349 emptyResultText: 'Empty',
11350 // element that contains real text value.. (when hidden is used..)
11352 getAutoCreate : function()
11360 if(Roo.isTouch && this.mobileTouchView){
11361 cfg = this.getAutoCreateTouchView();
11368 if(!this.tickable){
11369 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11374 * ComboBox with tickable selections
11377 var align = this.labelAlign || this.parentLabelAlign();
11380 cls : 'form-group roo-combobox-tickable' //input-group
11385 cls : 'tickable-buttons',
11390 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11397 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11404 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11411 buttons.cn.unshift({
11413 cls: 'select2-search-field-input'
11419 Roo.each(buttons.cn, function(c){
11421 c.cls += ' btn-' + _this.size;
11424 if (_this.disabled) {
11435 cls: 'form-hidden-field'
11439 cls: 'select2-choices',
11443 cls: 'select2-search-field',
11455 cls: 'select2-container input-group select2-container-multi',
11460 // cls: 'typeahead typeahead-long dropdown-menu',
11461 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
11466 if(this.hasFeedback && !this.allowBlank){
11470 cls: 'glyphicon form-control-feedback'
11473 combobox.cn.push(feedback);
11476 if (align ==='left' && this.fieldLabel.length) {
11478 Roo.log("left and has label");
11484 cls : 'control-label col-sm-' + this.labelWidth,
11485 html : this.fieldLabel
11489 cls : "col-sm-" + (12 - this.labelWidth),
11496 } else if ( this.fieldLabel.length) {
11502 //cls : 'input-group-addon',
11503 html : this.fieldLabel
11513 Roo.log(" no label && no align");
11520 ['xs','sm','md','lg'].map(function(size){
11521 if (settings[size]) {
11522 cfg.cls += ' col-' + size + '-' + settings[size];
11531 initEvents: function()
11535 throw "can not find store for combo";
11538 this.store = Roo.factory(this.store, Roo.data);
11544 if(Roo.isTouch && this.mobileTouchView){
11545 this.initTouchView();
11550 this.initTickableEvents();
11554 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11556 if(this.hiddenName){
11558 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11560 this.hiddenField.dom.value =
11561 this.hiddenValue !== undefined ? this.hiddenValue :
11562 this.value !== undefined ? this.value : '';
11564 // prevent input submission
11565 this.el.dom.removeAttribute('name');
11566 this.hiddenField.dom.setAttribute('name', this.hiddenName);
11571 // this.el.dom.setAttribute('autocomplete', 'off');
11574 var cls = 'x-combo-list';
11576 //this.list = new Roo.Layer({
11577 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11583 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11584 _this.list.setWidth(lw);
11587 this.list.on('mouseover', this.onViewOver, this);
11588 this.list.on('mousemove', this.onViewMove, this);
11590 this.list.on('scroll', this.onViewScroll, this);
11593 this.list.swallowEvent('mousewheel');
11594 this.assetHeight = 0;
11597 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11598 this.assetHeight += this.header.getHeight();
11601 this.innerList = this.list.createChild({cls:cls+'-inner'});
11602 this.innerList.on('mouseover', this.onViewOver, this);
11603 this.innerList.on('mousemove', this.onViewMove, this);
11604 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11606 if(this.allowBlank && !this.pageSize && !this.disableClear){
11607 this.footer = this.list.createChild({cls:cls+'-ft'});
11608 this.pageTb = new Roo.Toolbar(this.footer);
11612 this.footer = this.list.createChild({cls:cls+'-ft'});
11613 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11614 {pageSize: this.pageSize});
11618 if (this.pageTb && this.allowBlank && !this.disableClear) {
11620 this.pageTb.add(new Roo.Toolbar.Fill(), {
11621 cls: 'x-btn-icon x-btn-clear',
11623 handler: function()
11626 _this.clearValue();
11627 _this.onSelect(false, -1);
11632 this.assetHeight += this.footer.getHeight();
11637 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11640 this.view = new Roo.View(this.list, this.tpl, {
11641 singleSelect:true, store: this.store, selectedClass: this.selectedClass
11643 //this.view.wrapEl.setDisplayed(false);
11644 this.view.on('click', this.onViewClick, this);
11648 this.store.on('beforeload', this.onBeforeLoad, this);
11649 this.store.on('load', this.onLoad, this);
11650 this.store.on('loadexception', this.onLoadException, this);
11652 if(this.resizable){
11653 this.resizer = new Roo.Resizable(this.list, {
11654 pinned:true, handles:'se'
11656 this.resizer.on('resize', function(r, w, h){
11657 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11658 this.listWidth = w;
11659 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11660 this.restrictHeight();
11662 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11665 if(!this.editable){
11666 this.editable = true;
11667 this.setEditable(false);
11672 if (typeof(this.events.add.listeners) != 'undefined') {
11674 this.addicon = this.wrap.createChild(
11675 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
11677 this.addicon.on('click', function(e) {
11678 this.fireEvent('add', this);
11681 if (typeof(this.events.edit.listeners) != 'undefined') {
11683 this.editicon = this.wrap.createChild(
11684 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
11685 if (this.addicon) {
11686 this.editicon.setStyle('margin-left', '40px');
11688 this.editicon.on('click', function(e) {
11690 // we fire even if inothing is selected..
11691 this.fireEvent('edit', this, this.lastData );
11697 this.keyNav = new Roo.KeyNav(this.inputEl(), {
11698 "up" : function(e){
11699 this.inKeyMode = true;
11703 "down" : function(e){
11704 if(!this.isExpanded()){
11705 this.onTriggerClick();
11707 this.inKeyMode = true;
11712 "enter" : function(e){
11713 // this.onViewClick();
11717 if(this.fireEvent("specialkey", this, e)){
11718 this.onViewClick(false);
11724 "esc" : function(e){
11728 "tab" : function(e){
11731 if(this.fireEvent("specialkey", this, e)){
11732 this.onViewClick(false);
11740 doRelay : function(foo, bar, hname){
11741 if(hname == 'down' || this.scope.isExpanded()){
11742 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11751 this.queryDelay = Math.max(this.queryDelay || 10,
11752 this.mode == 'local' ? 10 : 250);
11755 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11757 if(this.typeAhead){
11758 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11760 if(this.editable !== false){
11761 this.inputEl().on("keyup", this.onKeyUp, this);
11763 if(this.forceSelection){
11764 this.inputEl().on('blur', this.doForce, this);
11768 this.choices = this.el.select('ul.select2-choices', true).first();
11769 this.searchField = this.el.select('ul li.select2-search-field', true).first();
11773 initTickableEvents: function()
11777 if(this.hiddenName){
11779 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11781 this.hiddenField.dom.value =
11782 this.hiddenValue !== undefined ? this.hiddenValue :
11783 this.value !== undefined ? this.value : '';
11785 // prevent input submission
11786 this.el.dom.removeAttribute('name');
11787 this.hiddenField.dom.setAttribute('name', this.hiddenName);
11792 // this.list = this.el.select('ul.dropdown-menu',true).first();
11794 this.choices = this.el.select('ul.select2-choices', true).first();
11795 this.searchField = this.el.select('ul li.select2-search-field', true).first();
11796 if(this.triggerList){
11797 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11800 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11801 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11803 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11804 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11806 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11807 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11809 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11810 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11811 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11814 this.cancelBtn.hide();
11819 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11820 _this.list.setWidth(lw);
11823 this.list.on('mouseover', this.onViewOver, this);
11824 this.list.on('mousemove', this.onViewMove, this);
11826 this.list.on('scroll', this.onViewScroll, this);
11829 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>';
11832 this.view = new Roo.View(this.list, this.tpl, {
11833 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11836 //this.view.wrapEl.setDisplayed(false);
11837 this.view.on('click', this.onViewClick, this);
11841 this.store.on('beforeload', this.onBeforeLoad, this);
11842 this.store.on('load', this.onLoad, this);
11843 this.store.on('loadexception', this.onLoadException, this);
11846 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11847 "up" : function(e){
11848 this.inKeyMode = true;
11852 "down" : function(e){
11853 this.inKeyMode = true;
11857 "enter" : function(e){
11858 if(this.fireEvent("specialkey", this, e)){
11859 this.onViewClick(false);
11865 "esc" : function(e){
11866 this.onTickableFooterButtonClick(e, false, false);
11869 "tab" : function(e){
11870 this.fireEvent("specialkey", this, e);
11872 this.onTickableFooterButtonClick(e, false, false);
11879 doRelay : function(e, fn, key){
11880 if(this.scope.isExpanded()){
11881 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11890 this.queryDelay = Math.max(this.queryDelay || 10,
11891 this.mode == 'local' ? 10 : 250);
11894 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11896 if(this.typeAhead){
11897 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11900 if(this.editable !== false){
11901 this.tickableInputEl().on("keyup", this.onKeyUp, this);
11906 onDestroy : function(){
11908 this.view.setStore(null);
11909 this.view.el.removeAllListeners();
11910 this.view.el.remove();
11911 this.view.purgeListeners();
11914 this.list.dom.innerHTML = '';
11918 this.store.un('beforeload', this.onBeforeLoad, this);
11919 this.store.un('load', this.onLoad, this);
11920 this.store.un('loadexception', this.onLoadException, this);
11922 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11926 fireKey : function(e){
11927 if(e.isNavKeyPress() && !this.list.isVisible()){
11928 this.fireEvent("specialkey", this, e);
11933 onResize: function(w, h){
11934 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11936 // if(typeof w != 'number'){
11937 // // we do not handle it!?!?
11940 // var tw = this.trigger.getWidth();
11941 // // tw += this.addicon ? this.addicon.getWidth() : 0;
11942 // // tw += this.editicon ? this.editicon.getWidth() : 0;
11944 // this.inputEl().setWidth( this.adjustWidth('input', x));
11946 // //this.trigger.setStyle('left', x+'px');
11948 // if(this.list && this.listWidth === undefined){
11949 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11950 // this.list.setWidth(lw);
11951 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11959 * Allow or prevent the user from directly editing the field text. If false is passed,
11960 * the user will only be able to select from the items defined in the dropdown list. This method
11961 * is the runtime equivalent of setting the 'editable' config option at config time.
11962 * @param {Boolean} value True to allow the user to directly edit the field text
11964 setEditable : function(value){
11965 if(value == this.editable){
11968 this.editable = value;
11970 this.inputEl().dom.setAttribute('readOnly', true);
11971 this.inputEl().on('mousedown', this.onTriggerClick, this);
11972 this.inputEl().addClass('x-combo-noedit');
11974 this.inputEl().dom.setAttribute('readOnly', false);
11975 this.inputEl().un('mousedown', this.onTriggerClick, this);
11976 this.inputEl().removeClass('x-combo-noedit');
11982 onBeforeLoad : function(combo,opts){
11983 if(!this.hasFocus){
11987 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11989 this.restrictHeight();
11990 this.selectedIndex = -1;
11994 onLoad : function(){
11996 this.hasQuery = false;
11998 if(!this.hasFocus){
12002 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12003 this.loading.hide();
12006 if(this.store.getCount() > 0){
12008 this.restrictHeight();
12009 if(this.lastQuery == this.allQuery){
12010 if(this.editable && !this.tickable){
12011 this.inputEl().dom.select();
12015 !this.selectByValue(this.value, true) &&
12018 !this.store.lastOptions ||
12019 typeof(this.store.lastOptions.add) == 'undefined' ||
12020 this.store.lastOptions.add != true
12023 this.select(0, true);
12026 if(this.autoFocus){
12029 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12030 this.taTask.delay(this.typeAheadDelay);
12034 this.onEmptyResults();
12040 onLoadException : function()
12042 this.hasQuery = false;
12044 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12045 this.loading.hide();
12048 if(this.tickable && this.editable){
12054 Roo.log(this.store.reader.jsonData);
12055 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12057 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12063 onTypeAhead : function(){
12064 if(this.store.getCount() > 0){
12065 var r = this.store.getAt(0);
12066 var newValue = r.data[this.displayField];
12067 var len = newValue.length;
12068 var selStart = this.getRawValue().length;
12070 if(selStart != len){
12071 this.setRawValue(newValue);
12072 this.selectText(selStart, newValue.length);
12078 onSelect : function(record, index){
12080 if(this.fireEvent('beforeselect', this, record, index) !== false){
12082 this.setFromData(index > -1 ? record.data : false);
12085 this.fireEvent('select', this, record, index);
12090 * Returns the currently selected field value or empty string if no value is set.
12091 * @return {String} value The selected value
12093 getValue : function(){
12096 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12099 if(this.valueField){
12100 return typeof this.value != 'undefined' ? this.value : '';
12102 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12107 * Clears any text/value currently set in the field
12109 clearValue : function(){
12110 if(this.hiddenField){
12111 this.hiddenField.dom.value = '';
12114 this.setRawValue('');
12115 this.lastSelectionText = '';
12116 this.lastData = false;
12118 var close = this.closeTriggerEl();
12127 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12128 * will be displayed in the field. If the value does not match the data value of an existing item,
12129 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12130 * Otherwise the field will be blank (although the value will still be set).
12131 * @param {String} value The value to match
12133 setValue : function(v){
12140 if(this.valueField){
12141 var r = this.findRecord(this.valueField, v);
12143 text = r.data[this.displayField];
12144 }else if(this.valueNotFoundText !== undefined){
12145 text = this.valueNotFoundText;
12148 this.lastSelectionText = text;
12149 if(this.hiddenField){
12150 this.hiddenField.dom.value = v;
12152 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12155 var close = this.closeTriggerEl();
12158 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12162 * @property {Object} the last set data for the element
12167 * Sets the value of the field based on a object which is related to the record format for the store.
12168 * @param {Object} value the value to set as. or false on reset?
12170 setFromData : function(o){
12177 var dv = ''; // display value
12178 var vv = ''; // value value..
12180 if (this.displayField) {
12181 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12183 // this is an error condition!!!
12184 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12187 if(this.valueField){
12188 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12191 var close = this.closeTriggerEl();
12194 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12197 if(this.hiddenField){
12198 this.hiddenField.dom.value = vv;
12200 this.lastSelectionText = dv;
12201 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12205 // no hidden field.. - we store the value in 'value', but still display
12206 // display field!!!!
12207 this.lastSelectionText = dv;
12208 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12215 reset : function(){
12216 // overridden so that last data is reset..
12223 this.setValue(this.originalValue);
12224 this.clearInvalid();
12225 this.lastData = false;
12227 this.view.clearSelections();
12231 findRecord : function(prop, value){
12233 if(this.store.getCount() > 0){
12234 this.store.each(function(r){
12235 if(r.data[prop] == value){
12245 getName: function()
12247 // returns hidden if it's set..
12248 if (!this.rendered) {return ''};
12249 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
12253 onViewMove : function(e, t){
12254 this.inKeyMode = false;
12258 onViewOver : function(e, t){
12259 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12262 var item = this.view.findItemFromChild(t);
12265 var index = this.view.indexOf(item);
12266 this.select(index, false);
12271 onViewClick : function(view, doFocus, el, e)
12273 var index = this.view.getSelectedIndexes()[0];
12275 var r = this.store.getAt(index);
12279 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12286 Roo.each(this.tickItems, function(v,k){
12288 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12289 _this.tickItems.splice(k, 1);
12291 if(typeof(e) == 'undefined' && view == false){
12292 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12304 this.tickItems.push(r.data);
12306 if(typeof(e) == 'undefined' && view == false){
12307 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12314 this.onSelect(r, index);
12316 if(doFocus !== false && !this.blockFocus){
12317 this.inputEl().focus();
12322 restrictHeight : function(){
12323 //this.innerList.dom.style.height = '';
12324 //var inner = this.innerList.dom;
12325 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12326 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12327 //this.list.beginUpdate();
12328 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12329 this.list.alignTo(this.inputEl(), this.listAlign);
12330 this.list.alignTo(this.inputEl(), this.listAlign);
12331 //this.list.endUpdate();
12335 onEmptyResults : function(){
12337 if(this.tickable && this.editable){
12338 this.restrictHeight();
12346 * Returns true if the dropdown list is expanded, else false.
12348 isExpanded : function(){
12349 return this.list.isVisible();
12353 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12354 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12355 * @param {String} value The data value of the item to select
12356 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12357 * selected item if it is not currently in view (defaults to true)
12358 * @return {Boolean} True if the value matched an item in the list, else false
12360 selectByValue : function(v, scrollIntoView){
12361 if(v !== undefined && v !== null){
12362 var r = this.findRecord(this.valueField || this.displayField, v);
12364 this.select(this.store.indexOf(r), scrollIntoView);
12372 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12373 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12374 * @param {Number} index The zero-based index of the list item to select
12375 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12376 * selected item if it is not currently in view (defaults to true)
12378 select : function(index, scrollIntoView){
12379 this.selectedIndex = index;
12380 this.view.select(index);
12381 if(scrollIntoView !== false){
12382 var el = this.view.getNode(index);
12384 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12387 this.list.scrollChildIntoView(el, false);
12393 selectNext : function(){
12394 var ct = this.store.getCount();
12396 if(this.selectedIndex == -1){
12398 }else if(this.selectedIndex < ct-1){
12399 this.select(this.selectedIndex+1);
12405 selectPrev : function(){
12406 var ct = this.store.getCount();
12408 if(this.selectedIndex == -1){
12410 }else if(this.selectedIndex != 0){
12411 this.select(this.selectedIndex-1);
12417 onKeyUp : function(e){
12418 if(this.editable !== false && !e.isSpecialKey()){
12419 this.lastKey = e.getKey();
12420 this.dqTask.delay(this.queryDelay);
12425 validateBlur : function(){
12426 return !this.list || !this.list.isVisible();
12430 initQuery : function(){
12432 var v = this.getRawValue();
12434 if(this.tickable && this.editable){
12435 v = this.tickableInputEl().getValue();
12442 doForce : function(){
12443 if(this.inputEl().dom.value.length > 0){
12444 this.inputEl().dom.value =
12445 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12451 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
12452 * query allowing the query action to be canceled if needed.
12453 * @param {String} query The SQL query to execute
12454 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12455 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
12456 * saved in the current store (defaults to false)
12458 doQuery : function(q, forceAll){
12460 if(q === undefined || q === null){
12465 forceAll: forceAll,
12469 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12474 forceAll = qe.forceAll;
12475 if(forceAll === true || (q.length >= this.minChars)){
12477 this.hasQuery = true;
12479 if(this.lastQuery != q || this.alwaysQuery){
12480 this.lastQuery = q;
12481 if(this.mode == 'local'){
12482 this.selectedIndex = -1;
12484 this.store.clearFilter();
12487 if(this.specialFilter){
12488 this.fireEvent('specialfilter', this);
12493 this.store.filter(this.displayField, q);
12496 this.store.fireEvent("datachanged", this.store);
12503 this.store.baseParams[this.queryParam] = q;
12505 var options = {params : this.getParams(q)};
12508 options.add = true;
12509 options.params.start = this.page * this.pageSize;
12512 this.store.load(options);
12515 * this code will make the page width larger, at the beginning, the list not align correctly,
12516 * we should expand the list on onLoad
12517 * so command out it
12522 this.selectedIndex = -1;
12527 this.loadNext = false;
12531 getParams : function(q){
12533 //p[this.queryParam] = q;
12537 p.limit = this.pageSize;
12543 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12545 collapse : function(){
12546 if(!this.isExpanded()){
12553 this.hasFocus = false;
12555 this.cancelBtn.hide();
12556 this.trigger.show();
12559 this.tickableInputEl().dom.value = '';
12560 this.tickableInputEl().blur();
12565 Roo.get(document).un('mousedown', this.collapseIf, this);
12566 Roo.get(document).un('mousewheel', this.collapseIf, this);
12567 if (!this.editable) {
12568 Roo.get(document).un('keydown', this.listKeyPress, this);
12570 this.fireEvent('collapse', this);
12574 collapseIf : function(e){
12575 var in_combo = e.within(this.el);
12576 var in_list = e.within(this.list);
12577 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12579 if (in_combo || in_list || is_list) {
12580 //e.stopPropagation();
12585 this.onTickableFooterButtonClick(e, false, false);
12593 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12595 expand : function(){
12597 if(this.isExpanded() || !this.hasFocus){
12601 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12602 this.list.setWidth(lw);
12609 this.restrictHeight();
12613 this.tickItems = Roo.apply([], this.item);
12616 this.cancelBtn.show();
12617 this.trigger.hide();
12620 this.tickableInputEl().focus();
12625 Roo.get(document).on('mousedown', this.collapseIf, this);
12626 Roo.get(document).on('mousewheel', this.collapseIf, this);
12627 if (!this.editable) {
12628 Roo.get(document).on('keydown', this.listKeyPress, this);
12631 this.fireEvent('expand', this);
12635 // Implements the default empty TriggerField.onTriggerClick function
12636 onTriggerClick : function(e)
12638 Roo.log('trigger click');
12640 if(this.disabled || !this.triggerList){
12645 this.loadNext = false;
12647 if(this.isExpanded()){
12649 if (!this.blockFocus) {
12650 this.inputEl().focus();
12654 this.hasFocus = true;
12655 if(this.triggerAction == 'all') {
12656 this.doQuery(this.allQuery, true);
12658 this.doQuery(this.getRawValue());
12660 if (!this.blockFocus) {
12661 this.inputEl().focus();
12666 onTickableTriggerClick : function(e)
12673 this.loadNext = false;
12674 this.hasFocus = true;
12676 if(this.triggerAction == 'all') {
12677 this.doQuery(this.allQuery, true);
12679 this.doQuery(this.getRawValue());
12683 onSearchFieldClick : function(e)
12685 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12686 this.onTickableFooterButtonClick(e, false, false);
12690 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12695 this.loadNext = false;
12696 this.hasFocus = true;
12698 if(this.triggerAction == 'all') {
12699 this.doQuery(this.allQuery, true);
12701 this.doQuery(this.getRawValue());
12705 listKeyPress : function(e)
12707 //Roo.log('listkeypress');
12708 // scroll to first matching element based on key pres..
12709 if (e.isSpecialKey()) {
12712 var k = String.fromCharCode(e.getKey()).toUpperCase();
12715 var csel = this.view.getSelectedNodes();
12716 var cselitem = false;
12718 var ix = this.view.indexOf(csel[0]);
12719 cselitem = this.store.getAt(ix);
12720 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12726 this.store.each(function(v) {
12728 // start at existing selection.
12729 if (cselitem.id == v.id) {
12735 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12736 match = this.store.indexOf(v);
12742 if (match === false) {
12743 return true; // no more action?
12746 this.view.select(match);
12747 var sn = Roo.get(this.view.getSelectedNodes()[0])
12748 sn.scrollIntoView(sn.dom.parentNode, false);
12751 onViewScroll : function(e, t){
12753 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){
12757 this.hasQuery = true;
12759 this.loading = this.list.select('.loading', true).first();
12761 if(this.loading === null){
12762 this.list.createChild({
12764 cls: 'loading select2-more-results select2-active',
12765 html: 'Loading more results...'
12768 this.loading = this.list.select('.loading', true).first();
12770 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12772 this.loading.hide();
12775 this.loading.show();
12780 this.loadNext = true;
12782 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12787 addItem : function(o)
12789 var dv = ''; // display value
12791 if (this.displayField) {
12792 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12794 // this is an error condition!!!
12795 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12802 var choice = this.choices.createChild({
12804 cls: 'select2-search-choice',
12813 cls: 'select2-search-choice-close',
12818 }, this.searchField);
12820 var close = choice.select('a.select2-search-choice-close', true).first()
12822 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12830 this.inputEl().dom.value = '';
12835 onRemoveItem : function(e, _self, o)
12837 e.preventDefault();
12839 this.lastItem = Roo.apply([], this.item);
12841 var index = this.item.indexOf(o.data) * 1;
12844 Roo.log('not this item?!');
12848 this.item.splice(index, 1);
12853 this.fireEvent('remove', this, e);
12859 syncValue : function()
12861 if(!this.item.length){
12868 Roo.each(this.item, function(i){
12869 if(_this.valueField){
12870 value.push(i[_this.valueField]);
12877 this.value = value.join(',');
12879 if(this.hiddenField){
12880 this.hiddenField.dom.value = this.value;
12883 this.store.fireEvent("datachanged", this.store);
12886 clearItem : function()
12888 if(!this.multiple){
12894 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12903 inputEl: function ()
12905 if(Roo.isTouch && this.mobileTouchView){
12906 return this.el.select('input.form-control',true).first();
12910 return this.searchField;
12913 return this.el.select('input.form-control',true).first();
12917 onTickableFooterButtonClick : function(e, btn, el)
12919 e.preventDefault();
12921 this.lastItem = Roo.apply([], this.item);
12923 if(btn && btn.name == 'cancel'){
12924 this.tickItems = Roo.apply([], this.item);
12933 Roo.each(this.tickItems, function(o){
12941 validate : function()
12943 var v = this.getRawValue();
12946 v = this.getValue();
12949 if(this.disabled || this.allowBlank || v.length){
12954 this.markInvalid();
12958 tickableInputEl : function()
12960 if(!this.tickable || !this.editable){
12961 return this.inputEl();
12964 return this.inputEl().select('.select2-search-field-input', true).first();
12968 getAutoCreateTouchView : function()
12973 cls: 'form-group' //input-group
12979 type : this.inputType,
12980 cls : 'form-control x-combo-noedit',
12981 autocomplete: 'new-password',
12982 placeholder : this.placeholder || '',
12987 input.name = this.name;
12991 input.cls += ' input-' + this.size;
12994 if (this.disabled) {
12995 input.disabled = true;
13006 inputblock.cls += ' input-group';
13008 inputblock.cn.unshift({
13010 cls : 'input-group-addon',
13015 if(this.removable && !this.multiple){
13016 inputblock.cls += ' roo-removable';
13018 inputblock.cn.push({
13021 cls : 'roo-combo-removable-btn close'
13025 if(this.hasFeedback && !this.allowBlank){
13027 inputblock.cls += ' has-feedback';
13029 inputblock.cn.push({
13031 cls: 'glyphicon form-control-feedback'
13038 inputblock.cls += (this.before) ? '' : ' input-group';
13040 inputblock.cn.push({
13042 cls : 'input-group-addon',
13053 cls: 'form-hidden-field'
13067 cls: 'form-hidden-field'
13071 cls: 'select2-choices',
13075 cls: 'select2-search-field',
13088 cls: 'select2-container input-group',
13095 combobox.cls += ' select2-container-multi';
13098 var align = this.labelAlign || this.parentLabelAlign();
13102 if(this.fieldLabel.length){
13104 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13105 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13110 cls : 'control-label ' + lw,
13111 html : this.fieldLabel
13123 var settings = this;
13125 ['xs','sm','md','lg'].map(function(size){
13126 if (settings[size]) {
13127 cfg.cls += ' col-' + size + '-' + settings[size];
13134 initTouchView : function()
13136 this.renderTouchView();
13138 this.touchViewEl.on('scroll', function(){
13139 this.el.dom.scrollTop = 0;
13142 this.inputEl().on("click", this.showTouchView, this);
13143 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13144 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13146 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13148 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13149 this.store.on('load', this.onTouchViewLoad, this);
13150 this.store.on('loadexception', this.onTouchViewLoadException, this);
13152 if(this.hiddenName){
13154 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13156 this.hiddenField.dom.value =
13157 this.hiddenValue !== undefined ? this.hiddenValue :
13158 this.value !== undefined ? this.value : '';
13160 this.el.dom.removeAttribute('name');
13161 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13165 this.choices = this.el.select('ul.select2-choices', true).first();
13166 this.searchField = this.el.select('ul li.select2-search-field', true).first();
13169 if(this.removable && !this.multiple){
13170 var close = this.closeTriggerEl();
13172 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13173 close.on('click', this.removeBtnClick, this, close);
13182 renderTouchView : function()
13184 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13185 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13187 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13188 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13190 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13191 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13192 this.touchViewBodyEl.setStyle('overflow', 'auto');
13194 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13195 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13197 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13198 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13202 showTouchView : function()
13204 this.touchViewHeaderEl.hide();
13206 if(this.fieldLabel.length){
13207 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13208 this.touchViewHeaderEl.show();
13211 this.touchViewEl.show();
13213 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13214 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13216 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13218 if(this.fieldLabel.length){
13219 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13222 this.touchViewBodyEl.setHeight(bodyHeight);
13226 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13228 this.touchViewEl.addClass('in');
13231 this.doTouchViewQuery();
13235 hideTouchView : function()
13237 this.touchViewEl.removeClass('in');
13241 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13243 this.touchViewEl.setStyle('display', 'none');
13248 setTouchViewValue : function()
13255 Roo.each(this.tickItems, function(o){
13260 this.hideTouchView();
13263 doTouchViewQuery : function()
13272 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13276 if(!this.alwaysQuery || this.mode == 'local'){
13277 this.onTouchViewLoad();
13284 onTouchViewBeforeLoad : function(combo,opts)
13290 onTouchViewLoad : function()
13292 if(this.store.getCount() < 1){
13293 this.onTouchViewEmptyResults();
13297 this.clearTouchView();
13299 var rawValue = this.getRawValue();
13301 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13303 this.tickItems = [];
13305 this.store.data.each(function(d, rowIndex){
13306 var row = this.touchViewListGroup.createChild(template);
13308 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13309 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13312 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13313 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13316 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13317 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13318 this.tickItems.push(d.data);
13321 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13325 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13327 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13329 if(this.fieldLabel.length){
13330 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13333 var listHeight = this.touchViewListGroup.getHeight();
13335 if(firstChecked && listHeight > bodyHeight){
13336 (function() { firstChecked.findParent('li').scrollIntoView(this.touchViewListGroup.dom); }).defer(500);
13341 onTouchViewLoadException : function()
13343 this.hideTouchView();
13346 onTouchViewEmptyResults : function()
13348 this.clearTouchView();
13350 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13352 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13356 clearTouchView : function()
13358 this.touchViewListGroup.dom.innerHTML = '';
13361 onTouchViewClick : function(e, el, o)
13363 e.preventDefault();
13366 var rowIndex = o.rowIndex;
13368 var r = this.store.getAt(rowIndex);
13370 if(!this.multiple){
13371 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13372 c.dom.removeAttribute('checked');
13375 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13377 this.setFromData(r.data);
13379 var close = this.closeTriggerEl();
13385 this.hideTouchView();
13387 this.fireEvent('select', this, r, rowIndex);
13392 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13393 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13394 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13398 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13399 this.addItem(r.data);
13400 this.tickItems.push(r.data);
13406 * @cfg {Boolean} grow
13410 * @cfg {Number} growMin
13414 * @cfg {Number} growMax
13423 Roo.apply(Roo.bootstrap.ComboBox, {
13427 cls: 'modal-header',
13449 cls: 'list-group-item',
13453 cls: 'roo-combobox-list-group-item-value'
13457 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13471 listItemCheckbox : {
13473 cls: 'list-group-item',
13477 cls: 'roo-combobox-list-group-item-value'
13481 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13497 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13502 cls: 'modal-footer',
13510 cls: 'col-xs-6 text-left',
13513 cls: 'btn btn-danger roo-touch-view-cancel',
13519 cls: 'col-xs-6 text-right',
13522 cls: 'btn btn-success roo-touch-view-ok',
13533 Roo.apply(Roo.bootstrap.ComboBox, {
13535 touchViewTemplate : {
13537 cls: 'modal fade roo-combobox-touch-view',
13541 cls: 'modal-dialog',
13545 cls: 'modal-content',
13547 Roo.bootstrap.ComboBox.header,
13548 Roo.bootstrap.ComboBox.body,
13549 Roo.bootstrap.ComboBox.footer
13558 * Ext JS Library 1.1.1
13559 * Copyright(c) 2006-2007, Ext JS, LLC.
13561 * Originally Released Under LGPL - original licence link has changed is not relivant.
13564 * <script type="text/javascript">
13569 * @extends Roo.util.Observable
13570 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
13571 * This class also supports single and multi selection modes. <br>
13572 * Create a data model bound view:
13574 var store = new Roo.data.Store(...);
13576 var view = new Roo.View({
13578 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
13580 singleSelect: true,
13581 selectedClass: "ydataview-selected",
13585 // listen for node click?
13586 view.on("click", function(vw, index, node, e){
13587 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13591 dataModel.load("foobar.xml");
13593 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13595 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13596 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13598 * Note: old style constructor is still suported (container, template, config)
13601 * Create a new View
13602 * @param {Object} config The config object
13605 Roo.View = function(config, depreciated_tpl, depreciated_config){
13607 this.parent = false;
13609 if (typeof(depreciated_tpl) == 'undefined') {
13610 // new way.. - universal constructor.
13611 Roo.apply(this, config);
13612 this.el = Roo.get(this.el);
13615 this.el = Roo.get(config);
13616 this.tpl = depreciated_tpl;
13617 Roo.apply(this, depreciated_config);
13619 this.wrapEl = this.el.wrap().wrap();
13620 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13623 if(typeof(this.tpl) == "string"){
13624 this.tpl = new Roo.Template(this.tpl);
13626 // support xtype ctors..
13627 this.tpl = new Roo.factory(this.tpl, Roo);
13631 this.tpl.compile();
13636 * @event beforeclick
13637 * Fires before a click is processed. Returns false to cancel the default action.
13638 * @param {Roo.View} this
13639 * @param {Number} index The index of the target node
13640 * @param {HTMLElement} node The target node
13641 * @param {Roo.EventObject} e The raw event object
13643 "beforeclick" : true,
13646 * Fires when a template node is clicked.
13647 * @param {Roo.View} this
13648 * @param {Number} index The index of the target node
13649 * @param {HTMLElement} node The target node
13650 * @param {Roo.EventObject} e The raw event object
13655 * Fires when a template node is double clicked.
13656 * @param {Roo.View} this
13657 * @param {Number} index The index of the target node
13658 * @param {HTMLElement} node The target node
13659 * @param {Roo.EventObject} e The raw event object
13663 * @event contextmenu
13664 * Fires when a template node is right clicked.
13665 * @param {Roo.View} this
13666 * @param {Number} index The index of the target node
13667 * @param {HTMLElement} node The target node
13668 * @param {Roo.EventObject} e The raw event object
13670 "contextmenu" : true,
13672 * @event selectionchange
13673 * Fires when the selected nodes change.
13674 * @param {Roo.View} this
13675 * @param {Array} selections Array of the selected nodes
13677 "selectionchange" : true,
13680 * @event beforeselect
13681 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13682 * @param {Roo.View} this
13683 * @param {HTMLElement} node The node to be selected
13684 * @param {Array} selections Array of currently selected nodes
13686 "beforeselect" : true,
13688 * @event preparedata
13689 * Fires on every row to render, to allow you to change the data.
13690 * @param {Roo.View} this
13691 * @param {Object} data to be rendered (change this)
13693 "preparedata" : true
13701 "click": this.onClick,
13702 "dblclick": this.onDblClick,
13703 "contextmenu": this.onContextMenu,
13707 this.selections = [];
13709 this.cmp = new Roo.CompositeElementLite([]);
13711 this.store = Roo.factory(this.store, Roo.data);
13712 this.setStore(this.store, true);
13715 if ( this.footer && this.footer.xtype) {
13717 var fctr = this.wrapEl.appendChild(document.createElement("div"));
13719 this.footer.dataSource = this.store
13720 this.footer.container = fctr;
13721 this.footer = Roo.factory(this.footer, Roo);
13722 fctr.insertFirst(this.el);
13724 // this is a bit insane - as the paging toolbar seems to detach the el..
13725 // dom.parentNode.parentNode.parentNode
13726 // they get detached?
13730 Roo.View.superclass.constructor.call(this);
13735 Roo.extend(Roo.View, Roo.util.Observable, {
13738 * @cfg {Roo.data.Store} store Data store to load data from.
13743 * @cfg {String|Roo.Element} el The container element.
13748 * @cfg {String|Roo.Template} tpl The template used by this View
13752 * @cfg {String} dataName the named area of the template to use as the data area
13753 * Works with domtemplates roo-name="name"
13757 * @cfg {String} selectedClass The css class to add to selected nodes
13759 selectedClass : "x-view-selected",
13761 * @cfg {String} emptyText The empty text to show when nothing is loaded.
13766 * @cfg {String} text to display on mask (default Loading)
13770 * @cfg {Boolean} multiSelect Allow multiple selection
13772 multiSelect : false,
13774 * @cfg {Boolean} singleSelect Allow single selection
13776 singleSelect: false,
13779 * @cfg {Boolean} toggleSelect - selecting
13781 toggleSelect : false,
13784 * @cfg {Boolean} tickable - selecting
13789 * Returns the element this view is bound to.
13790 * @return {Roo.Element}
13792 getEl : function(){
13793 return this.wrapEl;
13799 * Refreshes the view. - called by datachanged on the store. - do not call directly.
13801 refresh : function(){
13802 //Roo.log('refresh');
13805 // if we are using something like 'domtemplate', then
13806 // the what gets used is:
13807 // t.applySubtemplate(NAME, data, wrapping data..)
13808 // the outer template then get' applied with
13809 // the store 'extra data'
13810 // and the body get's added to the
13811 // roo-name="data" node?
13812 // <span class='roo-tpl-{name}'></span> ?????
13816 this.clearSelections();
13817 this.el.update("");
13819 var records = this.store.getRange();
13820 if(records.length < 1) {
13822 // is this valid?? = should it render a template??
13824 this.el.update(this.emptyText);
13828 if (this.dataName) {
13829 this.el.update(t.apply(this.store.meta)); //????
13830 el = this.el.child('.roo-tpl-' + this.dataName);
13833 for(var i = 0, len = records.length; i < len; i++){
13834 var data = this.prepareData(records[i].data, i, records[i]);
13835 this.fireEvent("preparedata", this, data, i, records[i]);
13837 var d = Roo.apply({}, data);
13840 Roo.apply(d, {'roo-id' : Roo.id()});
13844 Roo.each(this.parent.item, function(item){
13845 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
13848 Roo.apply(d, {'roo-data-checked' : 'checked'});
13852 html[html.length] = Roo.util.Format.trim(
13854 t.applySubtemplate(this.dataName, d, this.store.meta) :
13861 el.update(html.join(""));
13862 this.nodes = el.dom.childNodes;
13863 this.updateIndexes(0);
13868 * Function to override to reformat the data that is sent to
13869 * the template for each node.
13870 * DEPRICATED - use the preparedata event handler.
13871 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
13872 * a JSON object for an UpdateManager bound view).
13874 prepareData : function(data, index, record)
13876 this.fireEvent("preparedata", this, data, index, record);
13880 onUpdate : function(ds, record){
13881 // Roo.log('on update');
13882 this.clearSelections();
13883 var index = this.store.indexOf(record);
13884 var n = this.nodes[index];
13885 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
13886 n.parentNode.removeChild(n);
13887 this.updateIndexes(index, index);
13893 onAdd : function(ds, records, index)
13895 //Roo.log(['on Add', ds, records, index] );
13896 this.clearSelections();
13897 if(this.nodes.length == 0){
13901 var n = this.nodes[index];
13902 for(var i = 0, len = records.length; i < len; i++){
13903 var d = this.prepareData(records[i].data, i, records[i]);
13905 this.tpl.insertBefore(n, d);
13908 this.tpl.append(this.el, d);
13911 this.updateIndexes(index);
13914 onRemove : function(ds, record, index){
13915 // Roo.log('onRemove');
13916 this.clearSelections();
13917 var el = this.dataName ?
13918 this.el.child('.roo-tpl-' + this.dataName) :
13921 el.dom.removeChild(this.nodes[index]);
13922 this.updateIndexes(index);
13926 * Refresh an individual node.
13927 * @param {Number} index
13929 refreshNode : function(index){
13930 this.onUpdate(this.store, this.store.getAt(index));
13933 updateIndexes : function(startIndex, endIndex){
13934 var ns = this.nodes;
13935 startIndex = startIndex || 0;
13936 endIndex = endIndex || ns.length - 1;
13937 for(var i = startIndex; i <= endIndex; i++){
13938 ns[i].nodeIndex = i;
13943 * Changes the data store this view uses and refresh the view.
13944 * @param {Store} store
13946 setStore : function(store, initial){
13947 if(!initial && this.store){
13948 this.store.un("datachanged", this.refresh);
13949 this.store.un("add", this.onAdd);
13950 this.store.un("remove", this.onRemove);
13951 this.store.un("update", this.onUpdate);
13952 this.store.un("clear", this.refresh);
13953 this.store.un("beforeload", this.onBeforeLoad);
13954 this.store.un("load", this.onLoad);
13955 this.store.un("loadexception", this.onLoad);
13959 store.on("datachanged", this.refresh, this);
13960 store.on("add", this.onAdd, this);
13961 store.on("remove", this.onRemove, this);
13962 store.on("update", this.onUpdate, this);
13963 store.on("clear", this.refresh, this);
13964 store.on("beforeload", this.onBeforeLoad, this);
13965 store.on("load", this.onLoad, this);
13966 store.on("loadexception", this.onLoad, this);
13974 * onbeforeLoad - masks the loading area.
13977 onBeforeLoad : function(store,opts)
13979 //Roo.log('onBeforeLoad');
13981 this.el.update("");
13983 this.el.mask(this.mask ? this.mask : "Loading" );
13985 onLoad : function ()
13992 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
13993 * @param {HTMLElement} node
13994 * @return {HTMLElement} The template node
13996 findItemFromChild : function(node){
13997 var el = this.dataName ?
13998 this.el.child('.roo-tpl-' + this.dataName,true) :
14001 if(!node || node.parentNode == el){
14004 var p = node.parentNode;
14005 while(p && p != el){
14006 if(p.parentNode == el){
14015 onClick : function(e){
14016 var item = this.findItemFromChild(e.getTarget());
14018 var index = this.indexOf(item);
14019 if(this.onItemClick(item, index, e) !== false){
14020 this.fireEvent("click", this, index, item, e);
14023 this.clearSelections();
14028 onContextMenu : function(e){
14029 var item = this.findItemFromChild(e.getTarget());
14031 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14036 onDblClick : function(e){
14037 var item = this.findItemFromChild(e.getTarget());
14039 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14043 onItemClick : function(item, index, e)
14045 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14048 if (this.toggleSelect) {
14049 var m = this.isSelected(item) ? 'unselect' : 'select';
14052 _t[m](item, true, false);
14055 if(this.multiSelect || this.singleSelect){
14056 if(this.multiSelect && e.shiftKey && this.lastSelection){
14057 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14059 this.select(item, this.multiSelect && e.ctrlKey);
14060 this.lastSelection = item;
14063 if(!this.tickable){
14064 e.preventDefault();
14072 * Get the number of selected nodes.
14075 getSelectionCount : function(){
14076 return this.selections.length;
14080 * Get the currently selected nodes.
14081 * @return {Array} An array of HTMLElements
14083 getSelectedNodes : function(){
14084 return this.selections;
14088 * Get the indexes of the selected nodes.
14091 getSelectedIndexes : function(){
14092 var indexes = [], s = this.selections;
14093 for(var i = 0, len = s.length; i < len; i++){
14094 indexes.push(s[i].nodeIndex);
14100 * Clear all selections
14101 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14103 clearSelections : function(suppressEvent){
14104 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14105 this.cmp.elements = this.selections;
14106 this.cmp.removeClass(this.selectedClass);
14107 this.selections = [];
14108 if(!suppressEvent){
14109 this.fireEvent("selectionchange", this, this.selections);
14115 * Returns true if the passed node is selected
14116 * @param {HTMLElement/Number} node The node or node index
14117 * @return {Boolean}
14119 isSelected : function(node){
14120 var s = this.selections;
14124 node = this.getNode(node);
14125 return s.indexOf(node) !== -1;
14130 * @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
14131 * @param {Boolean} keepExisting (optional) true to keep existing selections
14132 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14134 select : function(nodeInfo, keepExisting, suppressEvent){
14135 if(nodeInfo instanceof Array){
14137 this.clearSelections(true);
14139 for(var i = 0, len = nodeInfo.length; i < len; i++){
14140 this.select(nodeInfo[i], true, true);
14144 var node = this.getNode(nodeInfo);
14145 if(!node || this.isSelected(node)){
14146 return; // already selected.
14149 this.clearSelections(true);
14152 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14153 Roo.fly(node).addClass(this.selectedClass);
14154 this.selections.push(node);
14155 if(!suppressEvent){
14156 this.fireEvent("selectionchange", this, this.selections);
14164 * @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
14165 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14166 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14168 unselect : function(nodeInfo, keepExisting, suppressEvent)
14170 if(nodeInfo instanceof Array){
14171 Roo.each(this.selections, function(s) {
14172 this.unselect(s, nodeInfo);
14176 var node = this.getNode(nodeInfo);
14177 if(!node || !this.isSelected(node)){
14178 //Roo.log("not selected");
14179 return; // not selected.
14183 Roo.each(this.selections, function(s) {
14185 Roo.fly(node).removeClass(this.selectedClass);
14192 this.selections= ns;
14193 this.fireEvent("selectionchange", this, this.selections);
14197 * Gets a template node.
14198 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14199 * @return {HTMLElement} The node or null if it wasn't found
14201 getNode : function(nodeInfo){
14202 if(typeof nodeInfo == "string"){
14203 return document.getElementById(nodeInfo);
14204 }else if(typeof nodeInfo == "number"){
14205 return this.nodes[nodeInfo];
14211 * Gets a range template nodes.
14212 * @param {Number} startIndex
14213 * @param {Number} endIndex
14214 * @return {Array} An array of nodes
14216 getNodes : function(start, end){
14217 var ns = this.nodes;
14218 start = start || 0;
14219 end = typeof end == "undefined" ? ns.length - 1 : end;
14222 for(var i = start; i <= end; i++){
14226 for(var i = start; i >= end; i--){
14234 * Finds the index of the passed node
14235 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14236 * @return {Number} The index of the node or -1
14238 indexOf : function(node){
14239 node = this.getNode(node);
14240 if(typeof node.nodeIndex == "number"){
14241 return node.nodeIndex;
14243 var ns = this.nodes;
14244 for(var i = 0, len = ns.length; i < len; i++){
14255 * based on jquery fullcalendar
14259 Roo.bootstrap = Roo.bootstrap || {};
14261 * @class Roo.bootstrap.Calendar
14262 * @extends Roo.bootstrap.Component
14263 * Bootstrap Calendar class
14264 * @cfg {Boolean} loadMask (true|false) default false
14265 * @cfg {Object} header generate the user specific header of the calendar, default false
14268 * Create a new Container
14269 * @param {Object} config The config object
14274 Roo.bootstrap.Calendar = function(config){
14275 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14279 * Fires when a date is selected
14280 * @param {DatePicker} this
14281 * @param {Date} date The selected date
14285 * @event monthchange
14286 * Fires when the displayed month changes
14287 * @param {DatePicker} this
14288 * @param {Date} date The selected month
14290 'monthchange': true,
14292 * @event evententer
14293 * Fires when mouse over an event
14294 * @param {Calendar} this
14295 * @param {event} Event
14297 'evententer': true,
14299 * @event eventleave
14300 * Fires when the mouse leaves an
14301 * @param {Calendar} this
14304 'eventleave': true,
14306 * @event eventclick
14307 * Fires when the mouse click an
14308 * @param {Calendar} this
14317 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
14320 * @cfg {Number} startDay
14321 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14329 getAutoCreate : function(){
14332 var fc_button = function(name, corner, style, content ) {
14333 return Roo.apply({},{
14335 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
14337 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14340 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14351 style : 'width:100%',
14358 cls : 'fc-header-left',
14360 fc_button('prev', 'left', 'arrow', '‹' ),
14361 fc_button('next', 'right', 'arrow', '›' ),
14362 { tag: 'span', cls: 'fc-header-space' },
14363 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
14371 cls : 'fc-header-center',
14375 cls: 'fc-header-title',
14378 html : 'month / year'
14386 cls : 'fc-header-right',
14388 /* fc_button('month', 'left', '', 'month' ),
14389 fc_button('week', '', '', 'week' ),
14390 fc_button('day', 'right', '', 'day' )
14402 header = this.header;
14405 var cal_heads = function() {
14407 // fixme - handle this.
14409 for (var i =0; i < Date.dayNames.length; i++) {
14410 var d = Date.dayNames[i];
14413 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14414 html : d.substring(0,3)
14418 ret[0].cls += ' fc-first';
14419 ret[6].cls += ' fc-last';
14422 var cal_cell = function(n) {
14425 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14430 cls: 'fc-day-number',
14434 cls: 'fc-day-content',
14438 style: 'position: relative;' // height: 17px;
14450 var cal_rows = function() {
14453 for (var r = 0; r < 6; r++) {
14460 for (var i =0; i < Date.dayNames.length; i++) {
14461 var d = Date.dayNames[i];
14462 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14465 row.cn[0].cls+=' fc-first';
14466 row.cn[0].cn[0].style = 'min-height:90px';
14467 row.cn[6].cls+=' fc-last';
14471 ret[0].cls += ' fc-first';
14472 ret[4].cls += ' fc-prev-last';
14473 ret[5].cls += ' fc-last';
14480 cls: 'fc-border-separate',
14481 style : 'width:100%',
14489 cls : 'fc-first fc-last',
14507 cls : 'fc-content',
14508 style : "position: relative;",
14511 cls : 'fc-view fc-view-month fc-grid',
14512 style : 'position: relative',
14513 unselectable : 'on',
14516 cls : 'fc-event-container',
14517 style : 'position:absolute;z-index:8;top:0;left:0;'
14535 initEvents : function()
14538 throw "can not find store for calendar";
14544 style: "text-align:center",
14548 style: "background-color:white;width:50%;margin:250 auto",
14552 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
14563 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14565 var size = this.el.select('.fc-content', true).first().getSize();
14566 this.maskEl.setSize(size.width, size.height);
14567 this.maskEl.enableDisplayMode("block");
14568 if(!this.loadMask){
14569 this.maskEl.hide();
14572 this.store = Roo.factory(this.store, Roo.data);
14573 this.store.on('load', this.onLoad, this);
14574 this.store.on('beforeload', this.onBeforeLoad, this);
14578 this.cells = this.el.select('.fc-day',true);
14579 //Roo.log(this.cells);
14580 this.textNodes = this.el.query('.fc-day-number');
14581 this.cells.addClassOnOver('fc-state-hover');
14583 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14584 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14585 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14586 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14588 this.on('monthchange', this.onMonthChange, this);
14590 this.update(new Date().clearTime());
14593 resize : function() {
14594 var sz = this.el.getSize();
14596 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14597 this.el.select('.fc-day-content div',true).setHeight(34);
14602 showPrevMonth : function(e){
14603 this.update(this.activeDate.add("mo", -1));
14605 showToday : function(e){
14606 this.update(new Date().clearTime());
14609 showNextMonth : function(e){
14610 this.update(this.activeDate.add("mo", 1));
14614 showPrevYear : function(){
14615 this.update(this.activeDate.add("y", -1));
14619 showNextYear : function(){
14620 this.update(this.activeDate.add("y", 1));
14625 update : function(date)
14627 var vd = this.activeDate;
14628 this.activeDate = date;
14629 // if(vd && this.el){
14630 // var t = date.getTime();
14631 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14632 // Roo.log('using add remove');
14634 // this.fireEvent('monthchange', this, date);
14636 // this.cells.removeClass("fc-state-highlight");
14637 // this.cells.each(function(c){
14638 // if(c.dateValue == t){
14639 // c.addClass("fc-state-highlight");
14640 // setTimeout(function(){
14641 // try{c.dom.firstChild.focus();}catch(e){}
14651 var days = date.getDaysInMonth();
14653 var firstOfMonth = date.getFirstDateOfMonth();
14654 var startingPos = firstOfMonth.getDay()-this.startDay;
14656 if(startingPos < this.startDay){
14660 var pm = date.add(Date.MONTH, -1);
14661 var prevStart = pm.getDaysInMonth()-startingPos;
14663 this.cells = this.el.select('.fc-day',true);
14664 this.textNodes = this.el.query('.fc-day-number');
14665 this.cells.addClassOnOver('fc-state-hover');
14667 var cells = this.cells.elements;
14668 var textEls = this.textNodes;
14670 Roo.each(cells, function(cell){
14671 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14674 days += startingPos;
14676 // convert everything to numbers so it's fast
14677 var day = 86400000;
14678 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14681 //Roo.log(prevStart);
14683 var today = new Date().clearTime().getTime();
14684 var sel = date.clearTime().getTime();
14685 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14686 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14687 var ddMatch = this.disabledDatesRE;
14688 var ddText = this.disabledDatesText;
14689 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14690 var ddaysText = this.disabledDaysText;
14691 var format = this.format;
14693 var setCellClass = function(cal, cell){
14697 //Roo.log('set Cell Class');
14699 var t = d.getTime();
14703 cell.dateValue = t;
14705 cell.className += " fc-today";
14706 cell.className += " fc-state-highlight";
14707 cell.title = cal.todayText;
14710 // disable highlight in other month..
14711 //cell.className += " fc-state-highlight";
14716 cell.className = " fc-state-disabled";
14717 cell.title = cal.minText;
14721 cell.className = " fc-state-disabled";
14722 cell.title = cal.maxText;
14726 if(ddays.indexOf(d.getDay()) != -1){
14727 cell.title = ddaysText;
14728 cell.className = " fc-state-disabled";
14731 if(ddMatch && format){
14732 var fvalue = d.dateFormat(format);
14733 if(ddMatch.test(fvalue)){
14734 cell.title = ddText.replace("%0", fvalue);
14735 cell.className = " fc-state-disabled";
14739 if (!cell.initialClassName) {
14740 cell.initialClassName = cell.dom.className;
14743 cell.dom.className = cell.initialClassName + ' ' + cell.className;
14748 for(; i < startingPos; i++) {
14749 textEls[i].innerHTML = (++prevStart);
14750 d.setDate(d.getDate()+1);
14752 cells[i].className = "fc-past fc-other-month";
14753 setCellClass(this, cells[i]);
14758 for(; i < days; i++){
14759 intDay = i - startingPos + 1;
14760 textEls[i].innerHTML = (intDay);
14761 d.setDate(d.getDate()+1);
14763 cells[i].className = ''; // "x-date-active";
14764 setCellClass(this, cells[i]);
14768 for(; i < 42; i++) {
14769 textEls[i].innerHTML = (++extraDays);
14770 d.setDate(d.getDate()+1);
14772 cells[i].className = "fc-future fc-other-month";
14773 setCellClass(this, cells[i]);
14776 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14778 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14780 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14781 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14783 if(totalRows != 6){
14784 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14785 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14788 this.fireEvent('monthchange', this, date);
14792 if(!this.internalRender){
14793 var main = this.el.dom.firstChild;
14794 var w = main.offsetWidth;
14795 this.el.setWidth(w + this.el.getBorderWidth("lr"));
14796 Roo.fly(main).setWidth(w);
14797 this.internalRender = true;
14798 // opera does not respect the auto grow header center column
14799 // then, after it gets a width opera refuses to recalculate
14800 // without a second pass
14801 if(Roo.isOpera && !this.secondPass){
14802 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14803 this.secondPass = true;
14804 this.update.defer(10, this, [date]);
14811 findCell : function(dt) {
14812 dt = dt.clearTime().getTime();
14814 this.cells.each(function(c){
14815 //Roo.log("check " +c.dateValue + '?=' + dt);
14816 if(c.dateValue == dt){
14826 findCells : function(ev) {
14827 var s = ev.start.clone().clearTime().getTime();
14829 var e= ev.end.clone().clearTime().getTime();
14832 this.cells.each(function(c){
14833 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
14835 if(c.dateValue > e){
14838 if(c.dateValue < s){
14847 // findBestRow: function(cells)
14851 // for (var i =0 ; i < cells.length;i++) {
14852 // ret = Math.max(cells[i].rows || 0,ret);
14859 addItem : function(ev)
14861 // look for vertical location slot in
14862 var cells = this.findCells(ev);
14864 // ev.row = this.findBestRow(cells);
14866 // work out the location.
14870 for(var i =0; i < cells.length; i++) {
14872 cells[i].row = cells[0].row;
14875 cells[i].row = cells[i].row + 1;
14885 if (crow.start.getY() == cells[i].getY()) {
14887 crow.end = cells[i];
14904 cells[0].events.push(ev);
14906 this.calevents.push(ev);
14909 clearEvents: function() {
14911 if(!this.calevents){
14915 Roo.each(this.cells.elements, function(c){
14921 Roo.each(this.calevents, function(e) {
14922 Roo.each(e.els, function(el) {
14923 el.un('mouseenter' ,this.onEventEnter, this);
14924 el.un('mouseleave' ,this.onEventLeave, this);
14929 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
14935 renderEvents: function()
14939 this.cells.each(function(c) {
14948 if(c.row != c.events.length){
14949 r = 4 - (4 - (c.row - c.events.length));
14952 c.events = ev.slice(0, r);
14953 c.more = ev.slice(r);
14955 if(c.more.length && c.more.length == 1){
14956 c.events.push(c.more.pop());
14959 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
14963 this.cells.each(function(c) {
14965 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
14968 for (var e = 0; e < c.events.length; e++){
14969 var ev = c.events[e];
14970 var rows = ev.rows;
14972 for(var i = 0; i < rows.length; i++) {
14974 // how many rows should it span..
14977 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
14978 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
14980 unselectable : "on",
14983 cls: 'fc-event-inner',
14987 // cls: 'fc-event-time',
14988 // html : cells.length > 1 ? '' : ev.time
14992 cls: 'fc-event-title',
14993 html : String.format('{0}', ev.title)
15000 cls: 'ui-resizable-handle ui-resizable-e',
15001 html : '  '
15008 cfg.cls += ' fc-event-start';
15010 if ((i+1) == rows.length) {
15011 cfg.cls += ' fc-event-end';
15014 var ctr = _this.el.select('.fc-event-container',true).first();
15015 var cg = ctr.createChild(cfg);
15017 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15018 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15020 var r = (c.more.length) ? 1 : 0;
15021 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15022 cg.setWidth(ebox.right - sbox.x -2);
15024 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15025 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15026 cg.on('click', _this.onEventClick, _this, ev);
15037 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15038 style : 'position: absolute',
15039 unselectable : "on",
15042 cls: 'fc-event-inner',
15046 cls: 'fc-event-title',
15054 cls: 'ui-resizable-handle ui-resizable-e',
15055 html : '  '
15061 var ctr = _this.el.select('.fc-event-container',true).first();
15062 var cg = ctr.createChild(cfg);
15064 var sbox = c.select('.fc-day-content',true).first().getBox();
15065 var ebox = c.select('.fc-day-content',true).first().getBox();
15067 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15068 cg.setWidth(ebox.right - sbox.x -2);
15070 cg.on('click', _this.onMoreEventClick, _this, c.more);
15080 onEventEnter: function (e, el,event,d) {
15081 this.fireEvent('evententer', this, el, event);
15084 onEventLeave: function (e, el,event,d) {
15085 this.fireEvent('eventleave', this, el, event);
15088 onEventClick: function (e, el,event,d) {
15089 this.fireEvent('eventclick', this, el, event);
15092 onMonthChange: function () {
15096 onMoreEventClick: function(e, el, more)
15100 this.calpopover.placement = 'right';
15101 this.calpopover.setTitle('More');
15103 this.calpopover.setContent('');
15105 var ctr = this.calpopover.el.select('.popover-content', true).first();
15107 Roo.each(more, function(m){
15109 cls : 'fc-event-hori fc-event-draggable',
15112 var cg = ctr.createChild(cfg);
15114 cg.on('click', _this.onEventClick, _this, m);
15117 this.calpopover.show(el);
15122 onLoad: function ()
15124 this.calevents = [];
15127 if(this.store.getCount() > 0){
15128 this.store.data.each(function(d){
15131 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15132 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15133 time : d.data.start_time,
15134 title : d.data.title,
15135 description : d.data.description,
15136 venue : d.data.venue
15141 this.renderEvents();
15143 if(this.calevents.length && this.loadMask){
15144 this.maskEl.hide();
15148 onBeforeLoad: function()
15150 this.clearEvents();
15152 this.maskEl.show();
15166 * @class Roo.bootstrap.Popover
15167 * @extends Roo.bootstrap.Component
15168 * Bootstrap Popover class
15169 * @cfg {String} html contents of the popover (or false to use children..)
15170 * @cfg {String} title of popover (or false to hide)
15171 * @cfg {String} placement how it is placed
15172 * @cfg {String} trigger click || hover (or false to trigger manually)
15173 * @cfg {String} over what (parent or false to trigger manually.)
15174 * @cfg {Number} delay - delay before showing
15177 * Create a new Popover
15178 * @param {Object} config The config object
15181 Roo.bootstrap.Popover = function(config){
15182 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15185 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
15187 title: 'Fill in a title',
15190 placement : 'right',
15191 trigger : 'hover', // hover
15197 can_build_overlaid : false,
15199 getChildContainer : function()
15201 return this.el.select('.popover-content',true).first();
15204 getAutoCreate : function(){
15205 Roo.log('make popover?');
15207 cls : 'popover roo-dynamic',
15208 style: 'display:block',
15214 cls : 'popover-inner',
15218 cls: 'popover-title',
15222 cls : 'popover-content',
15233 setTitle: function(str)
15236 this.el.select('.popover-title',true).first().dom.innerHTML = str;
15238 setContent: function(str)
15241 this.el.select('.popover-content',true).first().dom.innerHTML = str;
15243 // as it get's added to the bottom of the page.
15244 onRender : function(ct, position)
15246 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15248 var cfg = Roo.apply({}, this.getAutoCreate());
15252 cfg.cls += ' ' + this.cls;
15255 cfg.style = this.style;
15257 Roo.log("adding to ")
15258 this.el = Roo.get(document.body).createChild(cfg, position);
15264 initEvents : function()
15266 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15267 this.el.enableDisplayMode('block');
15269 if (this.over === false) {
15272 if (this.triggers === false) {
15275 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15276 var triggers = this.trigger ? this.trigger.split(' ') : [];
15277 Roo.each(triggers, function(trigger) {
15279 if (trigger == 'click') {
15280 on_el.on('click', this.toggle, this);
15281 } else if (trigger != 'manual') {
15282 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
15283 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15285 on_el.on(eventIn ,this.enter, this);
15286 on_el.on(eventOut, this.leave, this);
15297 toggle : function () {
15298 this.hoverState == 'in' ? this.leave() : this.enter();
15301 enter : function () {
15304 clearTimeout(this.timeout);
15306 this.hoverState = 'in';
15308 if (!this.delay || !this.delay.show) {
15313 this.timeout = setTimeout(function () {
15314 if (_t.hoverState == 'in') {
15317 }, this.delay.show)
15319 leave : function() {
15320 clearTimeout(this.timeout);
15322 this.hoverState = 'out';
15324 if (!this.delay || !this.delay.hide) {
15329 this.timeout = setTimeout(function () {
15330 if (_t.hoverState == 'out') {
15333 }, this.delay.hide)
15336 show : function (on_el)
15339 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15342 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15343 if (this.html !== false) {
15344 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15346 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15347 if (!this.title.length) {
15348 this.el.select('.popover-title',true).hide();
15351 var placement = typeof this.placement == 'function' ?
15352 this.placement.call(this, this.el, on_el) :
15355 var autoToken = /\s?auto?\s?/i;
15356 var autoPlace = autoToken.test(placement);
15358 placement = placement.replace(autoToken, '') || 'top';
15362 //this.el.setXY([0,0]);
15364 this.el.dom.style.display='block';
15365 this.el.addClass(placement);
15367 //this.el.appendTo(on_el);
15369 var p = this.getPosition();
15370 var box = this.el.getBox();
15375 var align = Roo.bootstrap.Popover.alignment[placement];
15376 this.el.alignTo(on_el, align[0],align[1]);
15377 //var arrow = this.el.select('.arrow',true).first();
15378 //arrow.set(align[2],
15380 this.el.addClass('in');
15383 if (this.el.hasClass('fade')) {
15390 this.el.setXY([0,0]);
15391 this.el.removeClass('in');
15393 this.hoverState = null;
15399 Roo.bootstrap.Popover.alignment = {
15400 'left' : ['r-l', [-10,0], 'right'],
15401 'right' : ['l-r', [10,0], 'left'],
15402 'bottom' : ['t-b', [0,10], 'top'],
15403 'top' : [ 'b-t', [0,-10], 'bottom']
15414 * @class Roo.bootstrap.Progress
15415 * @extends Roo.bootstrap.Component
15416 * Bootstrap Progress class
15417 * @cfg {Boolean} striped striped of the progress bar
15418 * @cfg {Boolean} active animated of the progress bar
15422 * Create a new Progress
15423 * @param {Object} config The config object
15426 Roo.bootstrap.Progress = function(config){
15427 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15430 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
15435 getAutoCreate : function(){
15443 cfg.cls += ' progress-striped';
15447 cfg.cls += ' active';
15466 * @class Roo.bootstrap.ProgressBar
15467 * @extends Roo.bootstrap.Component
15468 * Bootstrap ProgressBar class
15469 * @cfg {Number} aria_valuenow aria-value now
15470 * @cfg {Number} aria_valuemin aria-value min
15471 * @cfg {Number} aria_valuemax aria-value max
15472 * @cfg {String} label label for the progress bar
15473 * @cfg {String} panel (success | info | warning | danger )
15474 * @cfg {String} role role of the progress bar
15475 * @cfg {String} sr_only text
15479 * Create a new ProgressBar
15480 * @param {Object} config The config object
15483 Roo.bootstrap.ProgressBar = function(config){
15484 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15487 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
15491 aria_valuemax : 100,
15497 getAutoCreate : function()
15502 cls: 'progress-bar',
15503 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15515 cfg.role = this.role;
15518 if(this.aria_valuenow){
15519 cfg['aria-valuenow'] = this.aria_valuenow;
15522 if(this.aria_valuemin){
15523 cfg['aria-valuemin'] = this.aria_valuemin;
15526 if(this.aria_valuemax){
15527 cfg['aria-valuemax'] = this.aria_valuemax;
15530 if(this.label && !this.sr_only){
15531 cfg.html = this.label;
15535 cfg.cls += ' progress-bar-' + this.panel;
15541 update : function(aria_valuenow)
15543 this.aria_valuenow = aria_valuenow;
15545 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15560 * @class Roo.bootstrap.TabGroup
15561 * @extends Roo.bootstrap.Column
15562 * Bootstrap Column class
15563 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15564 * @cfg {Boolean} carousel true to make the group behave like a carousel
15565 * @cfg {Number} bullets show the panel pointer.. default 0
15566 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15567 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15568 * @cfg {Number} timer auto slide timer .. default 0 millisecond
15571 * Create a new TabGroup
15572 * @param {Object} config The config object
15575 Roo.bootstrap.TabGroup = function(config){
15576 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15578 this.navId = Roo.id();
15581 Roo.bootstrap.TabGroup.register(this);
15585 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
15588 transition : false,
15593 slideOnTouch : false,
15595 getAutoCreate : function()
15597 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15599 cfg.cls += ' tab-content';
15601 Roo.log('get auto create...............');
15603 if (this.carousel) {
15604 cfg.cls += ' carousel slide';
15607 cls : 'carousel-inner'
15610 if(this.bullets > 0 && !Roo.isTouch){
15613 cls : 'carousel-bullets',
15617 if(this.bullets_cls){
15618 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15621 for (var i = 0; i < this.bullets; i++){
15623 cls : 'bullet bullet-' + i
15631 cfg.cn[0].cn = bullets;
15638 initEvents: function()
15640 Roo.log('-------- init events on tab group ---------');
15642 if(this.bullets > 0 && !Roo.isTouch){
15648 if(Roo.isTouch && this.slideOnTouch){
15649 this.el.on("touchstart", this.onTouchStart, this);
15652 if(this.autoslide){
15655 this.slideFn = window.setInterval(function() {
15656 _this.showPanelNext();
15662 onTouchStart : function(e, el, o)
15664 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15668 this.showPanelNext();
15671 getChildContainer : function()
15673 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15677 * register a Navigation item
15678 * @param {Roo.bootstrap.NavItem} the navitem to add
15680 register : function(item)
15682 this.tabs.push( item);
15683 item.navId = this.navId; // not really needed..
15687 getActivePanel : function()
15690 Roo.each(this.tabs, function(t) {
15700 getPanelByName : function(n)
15703 Roo.each(this.tabs, function(t) {
15704 if (t.tabId == n) {
15712 indexOfPanel : function(p)
15715 Roo.each(this.tabs, function(t,i) {
15716 if (t.tabId == p.tabId) {
15725 * show a specific panel
15726 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15727 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15729 showPanel : function (pan)
15731 if(this.transition){
15732 Roo.log("waiting for the transitionend");
15736 if (typeof(pan) == 'number') {
15737 pan = this.tabs[pan];
15739 if (typeof(pan) == 'string') {
15740 pan = this.getPanelByName(pan);
15742 if (pan.tabId == this.getActivePanel().tabId) {
15745 var cur = this.getActivePanel();
15747 if (false === cur.fireEvent('beforedeactivate')) {
15751 if(this.bullets > 0 && !Roo.isTouch){
15752 this.setActiveBullet(this.indexOfPanel(pan));
15755 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15757 this.transition = true;
15758 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
15759 var lr = dir == 'next' ? 'left' : 'right';
15760 pan.el.addClass(dir); // or prev
15761 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
15762 cur.el.addClass(lr); // or right
15763 pan.el.addClass(lr);
15766 cur.el.on('transitionend', function() {
15767 Roo.log("trans end?");
15769 pan.el.removeClass([lr,dir]);
15770 pan.setActive(true);
15772 cur.el.removeClass([lr]);
15773 cur.setActive(false);
15775 _this.transition = false;
15777 }, this, { single: true } );
15782 cur.setActive(false);
15783 pan.setActive(true);
15788 showPanelNext : function()
15790 var i = this.indexOfPanel(this.getActivePanel());
15792 if (i >= this.tabs.length - 1 && !this.autoslide) {
15796 if (i >= this.tabs.length - 1 && this.autoslide) {
15800 this.showPanel(this.tabs[i+1]);
15803 showPanelPrev : function()
15805 var i = this.indexOfPanel(this.getActivePanel());
15807 if (i < 1 && !this.autoslide) {
15811 if (i < 1 && this.autoslide) {
15812 i = this.tabs.length;
15815 this.showPanel(this.tabs[i-1]);
15818 initBullet : function()
15826 for (var i = 0; i < this.bullets; i++){
15827 var bullet = this.el.select('.bullet-' + i, true).first();
15833 bullet.on('click', (function(e, el, o, ii, t){
15835 e.preventDefault();
15837 _this.showPanel(ii);
15839 if(_this.autoslide && _this.slideFn){
15840 clearInterval(_this.slideFn);
15841 _this.slideFn = window.setInterval(function() {
15842 _this.showPanelNext();
15846 }).createDelegate(this, [i, bullet], true));
15850 setActiveBullet : function(i)
15856 Roo.each(this.el.select('.bullet', true).elements, function(el){
15857 el.removeClass('selected');
15860 var bullet = this.el.select('.bullet-' + i, true).first();
15866 bullet.addClass('selected');
15877 Roo.apply(Roo.bootstrap.TabGroup, {
15881 * register a Navigation Group
15882 * @param {Roo.bootstrap.NavGroup} the navgroup to add
15884 register : function(navgrp)
15886 this.groups[navgrp.navId] = navgrp;
15890 * fetch a Navigation Group based on the navigation ID
15891 * if one does not exist , it will get created.
15892 * @param {string} the navgroup to add
15893 * @returns {Roo.bootstrap.NavGroup} the navgroup
15895 get: function(navId) {
15896 if (typeof(this.groups[navId]) == 'undefined') {
15897 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
15899 return this.groups[navId] ;
15914 * @class Roo.bootstrap.TabPanel
15915 * @extends Roo.bootstrap.Component
15916 * Bootstrap TabPanel class
15917 * @cfg {Boolean} active panel active
15918 * @cfg {String} html panel content
15919 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
15920 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
15924 * Create a new TabPanel
15925 * @param {Object} config The config object
15928 Roo.bootstrap.TabPanel = function(config){
15929 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
15933 * Fires when the active status changes
15934 * @param {Roo.bootstrap.TabPanel} this
15935 * @param {Boolean} state the new state
15940 * @event beforedeactivate
15941 * Fires before a tab is de-activated - can be used to do validation on a form.
15942 * @param {Roo.bootstrap.TabPanel} this
15943 * @return {Boolean} false if there is an error
15946 'beforedeactivate': true
15949 this.tabId = this.tabId || Roo.id();
15953 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
15960 getAutoCreate : function(){
15963 // item is needed for carousel - not sure if it has any effect otherwise
15964 cls: 'tab-pane item',
15965 html: this.html || ''
15969 cfg.cls += ' active';
15973 cfg.tabId = this.tabId;
15980 initEvents: function()
15982 Roo.log('-------- init events on tab panel ---------');
15984 var p = this.parent();
15985 this.navId = this.navId || p.navId;
15987 if (typeof(this.navId) != 'undefined') {
15988 // not really needed.. but just in case.. parent should be a NavGroup.
15989 var tg = Roo.bootstrap.TabGroup.get(this.navId);
15990 Roo.log(['register', tg, this]);
15993 var i = tg.tabs.length - 1;
15995 if(this.active && tg.bullets > 0 && i < tg.bullets){
15996 tg.setActiveBullet(i);
16003 onRender : function(ct, position)
16005 // Roo.log("Call onRender: " + this.xtype);
16007 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16015 setActive: function(state)
16017 Roo.log("panel - set active " + this.tabId + "=" + state);
16019 this.active = state;
16021 this.el.removeClass('active');
16023 } else if (!this.el.hasClass('active')) {
16024 this.el.addClass('active');
16027 this.fireEvent('changed', this, state);
16044 * @class Roo.bootstrap.DateField
16045 * @extends Roo.bootstrap.Input
16046 * Bootstrap DateField class
16047 * @cfg {Number} weekStart default 0
16048 * @cfg {String} viewMode default empty, (months|years)
16049 * @cfg {String} minViewMode default empty, (months|years)
16050 * @cfg {Number} startDate default -Infinity
16051 * @cfg {Number} endDate default Infinity
16052 * @cfg {Boolean} todayHighlight default false
16053 * @cfg {Boolean} todayBtn default false
16054 * @cfg {Boolean} calendarWeeks default false
16055 * @cfg {Object} daysOfWeekDisabled default empty
16056 * @cfg {Boolean} singleMode default false (true | false)
16058 * @cfg {Boolean} keyboardNavigation default true
16059 * @cfg {String} language default en
16062 * Create a new DateField
16063 * @param {Object} config The config object
16066 Roo.bootstrap.DateField = function(config){
16067 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16071 * Fires when this field show.
16072 * @param {Roo.bootstrap.DateField} this
16073 * @param {Mixed} date The date value
16078 * Fires when this field hide.
16079 * @param {Roo.bootstrap.DateField} this
16080 * @param {Mixed} date The date value
16085 * Fires when select a date.
16086 * @param {Roo.bootstrap.DateField} this
16087 * @param {Mixed} date The date value
16093 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16096 * @cfg {String} format
16097 * The default date format string which can be overriden for localization support. The format must be
16098 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16102 * @cfg {String} altFormats
16103 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16104 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16106 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16114 todayHighlight : false,
16120 keyboardNavigation: true,
16122 calendarWeeks: false,
16124 startDate: -Infinity,
16128 daysOfWeekDisabled: [],
16132 singleMode : false,
16134 UTCDate: function()
16136 return new Date(Date.UTC.apply(Date, arguments));
16139 UTCToday: function()
16141 var today = new Date();
16142 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16145 getDate: function() {
16146 var d = this.getUTCDate();
16147 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16150 getUTCDate: function() {
16154 setDate: function(d) {
16155 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16158 setUTCDate: function(d) {
16160 this.setValue(this.formatDate(this.date));
16163 onRender: function(ct, position)
16166 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16168 this.language = this.language || 'en';
16169 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16170 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16172 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16173 this.format = this.format || 'm/d/y';
16174 this.isInline = false;
16175 this.isInput = true;
16176 this.component = this.el.select('.add-on', true).first() || false;
16177 this.component = (this.component && this.component.length === 0) ? false : this.component;
16178 this.hasInput = this.component && this.inputEL().length;
16180 if (typeof(this.minViewMode === 'string')) {
16181 switch (this.minViewMode) {
16183 this.minViewMode = 1;
16186 this.minViewMode = 2;
16189 this.minViewMode = 0;
16194 if (typeof(this.viewMode === 'string')) {
16195 switch (this.viewMode) {
16208 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16210 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16212 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16214 this.picker().on('mousedown', this.onMousedown, this);
16215 this.picker().on('click', this.onClick, this);
16217 this.picker().addClass('datepicker-dropdown');
16219 this.startViewMode = this.viewMode;
16221 if(this.singleMode){
16222 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16223 v.setVisibilityMode(Roo.Element.DISPLAY)
16227 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16228 v.setStyle('width', '189px');
16232 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16233 if(!this.calendarWeeks){
16238 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16239 v.attr('colspan', function(i, val){
16240 return parseInt(val) + 1;
16245 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16247 this.setStartDate(this.startDate);
16248 this.setEndDate(this.endDate);
16250 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16257 if(this.isInline) {
16262 picker : function()
16264 return this.pickerEl;
16265 // return this.el.select('.datepicker', true).first();
16268 fillDow: function()
16270 var dowCnt = this.weekStart;
16279 if(this.calendarWeeks){
16287 while (dowCnt < this.weekStart + 7) {
16291 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16295 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16298 fillMonths: function()
16301 var months = this.picker().select('>.datepicker-months td', true).first();
16303 months.dom.innerHTML = '';
16309 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16312 months.createChild(month);
16319 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;
16321 if (this.date < this.startDate) {
16322 this.viewDate = new Date(this.startDate);
16323 } else if (this.date > this.endDate) {
16324 this.viewDate = new Date(this.endDate);
16326 this.viewDate = new Date(this.date);
16334 var d = new Date(this.viewDate),
16335 year = d.getUTCFullYear(),
16336 month = d.getUTCMonth(),
16337 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16338 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16339 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16340 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16341 currentDate = this.date && this.date.valueOf(),
16342 today = this.UTCToday();
16344 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16346 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16348 // this.picker.select('>tfoot th.today').
16349 // .text(dates[this.language].today)
16350 // .toggle(this.todayBtn !== false);
16352 this.updateNavArrows();
16355 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16357 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16359 prevMonth.setUTCDate(day);
16361 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16363 var nextMonth = new Date(prevMonth);
16365 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16367 nextMonth = nextMonth.valueOf();
16369 var fillMonths = false;
16371 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16373 while(prevMonth.valueOf() < nextMonth) {
16376 if (prevMonth.getUTCDay() === this.weekStart) {
16378 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16386 if(this.calendarWeeks){
16387 // ISO 8601: First week contains first thursday.
16388 // ISO also states week starts on Monday, but we can be more abstract here.
16390 // Start of current week: based on weekstart/current date
16391 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16392 // Thursday of this week
16393 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16394 // First Thursday of year, year from thursday
16395 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16396 // Calendar week: ms between thursdays, div ms per day, div 7 days
16397 calWeek = (th - yth) / 864e5 / 7 + 1;
16399 fillMonths.cn.push({
16407 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16409 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16412 if (this.todayHighlight &&
16413 prevMonth.getUTCFullYear() == today.getFullYear() &&
16414 prevMonth.getUTCMonth() == today.getMonth() &&
16415 prevMonth.getUTCDate() == today.getDate()) {
16416 clsName += ' today';
16419 if (currentDate && prevMonth.valueOf() === currentDate) {
16420 clsName += ' active';
16423 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16424 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16425 clsName += ' disabled';
16428 fillMonths.cn.push({
16430 cls: 'day ' + clsName,
16431 html: prevMonth.getDate()
16434 prevMonth.setDate(prevMonth.getDate()+1);
16437 var currentYear = this.date && this.date.getUTCFullYear();
16438 var currentMonth = this.date && this.date.getUTCMonth();
16440 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16442 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16443 v.removeClass('active');
16445 if(currentYear === year && k === currentMonth){
16446 v.addClass('active');
16449 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16450 v.addClass('disabled');
16456 year = parseInt(year/10, 10) * 10;
16458 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16460 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16463 for (var i = -1; i < 11; i++) {
16464 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16466 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16474 showMode: function(dir)
16477 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16480 Roo.each(this.picker().select('>div',true).elements, function(v){
16481 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16484 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16489 if(this.isInline) return;
16491 this.picker().removeClass(['bottom', 'top']);
16493 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16495 * place to the top of element!
16499 this.picker().addClass('top');
16500 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16505 this.picker().addClass('bottom');
16507 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16510 parseDate : function(value)
16512 if(!value || value instanceof Date){
16515 var v = Date.parseDate(value, this.format);
16516 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16517 v = Date.parseDate(value, 'Y-m-d');
16519 if(!v && this.altFormats){
16520 if(!this.altFormatsArray){
16521 this.altFormatsArray = this.altFormats.split("|");
16523 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16524 v = Date.parseDate(value, this.altFormatsArray[i]);
16530 formatDate : function(date, fmt)
16532 return (!date || !(date instanceof Date)) ?
16533 date : date.dateFormat(fmt || this.format);
16536 onFocus : function()
16538 Roo.bootstrap.DateField.superclass.onFocus.call(this);
16542 onBlur : function()
16544 Roo.bootstrap.DateField.superclass.onBlur.call(this);
16546 var d = this.inputEl().getValue();
16555 this.picker().show();
16559 this.fireEvent('show', this, this.date);
16564 if(this.isInline) return;
16565 this.picker().hide();
16566 this.viewMode = this.startViewMode;
16569 this.fireEvent('hide', this, this.date);
16573 onMousedown: function(e)
16575 e.stopPropagation();
16576 e.preventDefault();
16581 Roo.bootstrap.DateField.superclass.keyup.call(this);
16585 setValue: function(v)
16588 // v can be a string or a date..
16591 var d = new Date(this.parseDate(v) ).clearTime();
16593 if(isNaN(d.getTime())){
16594 this.date = this.viewDate = '';
16595 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16599 v = this.formatDate(d);
16601 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16603 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16607 this.fireEvent('select', this, this.date);
16611 getValue: function()
16613 return this.formatDate(this.date);
16616 fireKey: function(e)
16618 if (!this.picker().isVisible()){
16619 if (e.keyCode == 27) // allow escape to hide and re-show picker
16624 var dateChanged = false,
16626 newDate, newViewDate;
16631 e.preventDefault();
16635 if (!this.keyboardNavigation) break;
16636 dir = e.keyCode == 37 ? -1 : 1;
16639 newDate = this.moveYear(this.date, dir);
16640 newViewDate = this.moveYear(this.viewDate, dir);
16641 } else if (e.shiftKey){
16642 newDate = this.moveMonth(this.date, dir);
16643 newViewDate = this.moveMonth(this.viewDate, dir);
16645 newDate = new Date(this.date);
16646 newDate.setUTCDate(this.date.getUTCDate() + dir);
16647 newViewDate = new Date(this.viewDate);
16648 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16650 if (this.dateWithinRange(newDate)){
16651 this.date = newDate;
16652 this.viewDate = newViewDate;
16653 this.setValue(this.formatDate(this.date));
16655 e.preventDefault();
16656 dateChanged = true;
16661 if (!this.keyboardNavigation) break;
16662 dir = e.keyCode == 38 ? -1 : 1;
16664 newDate = this.moveYear(this.date, dir);
16665 newViewDate = this.moveYear(this.viewDate, dir);
16666 } else if (e.shiftKey){
16667 newDate = this.moveMonth(this.date, dir);
16668 newViewDate = this.moveMonth(this.viewDate, dir);
16670 newDate = new Date(this.date);
16671 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16672 newViewDate = new Date(this.viewDate);
16673 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16675 if (this.dateWithinRange(newDate)){
16676 this.date = newDate;
16677 this.viewDate = newViewDate;
16678 this.setValue(this.formatDate(this.date));
16680 e.preventDefault();
16681 dateChanged = true;
16685 this.setValue(this.formatDate(this.date));
16687 e.preventDefault();
16690 this.setValue(this.formatDate(this.date));
16704 onClick: function(e)
16706 e.stopPropagation();
16707 e.preventDefault();
16709 var target = e.getTarget();
16711 if(target.nodeName.toLowerCase() === 'i'){
16712 target = Roo.get(target).dom.parentNode;
16715 var nodeName = target.nodeName;
16716 var className = target.className;
16717 var html = target.innerHTML;
16718 //Roo.log(nodeName);
16720 switch(nodeName.toLowerCase()) {
16722 switch(className) {
16728 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16729 switch(this.viewMode){
16731 this.viewDate = this.moveMonth(this.viewDate, dir);
16735 this.viewDate = this.moveYear(this.viewDate, dir);
16741 var date = new Date();
16742 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16744 this.setValue(this.formatDate(this.date));
16751 if (className.indexOf('disabled') < 0) {
16752 this.viewDate.setUTCDate(1);
16753 if (className.indexOf('month') > -1) {
16754 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16756 var year = parseInt(html, 10) || 0;
16757 this.viewDate.setUTCFullYear(year);
16761 if(this.singleMode){
16762 this.setValue(this.formatDate(this.viewDate));
16773 //Roo.log(className);
16774 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
16775 var day = parseInt(html, 10) || 1;
16776 var year = this.viewDate.getUTCFullYear(),
16777 month = this.viewDate.getUTCMonth();
16779 if (className.indexOf('old') > -1) {
16786 } else if (className.indexOf('new') > -1) {
16794 //Roo.log([year,month,day]);
16795 this.date = this.UTCDate(year, month, day,0,0,0,0);
16796 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16798 //Roo.log(this.formatDate(this.date));
16799 this.setValue(this.formatDate(this.date));
16806 setStartDate: function(startDate)
16808 this.startDate = startDate || -Infinity;
16809 if (this.startDate !== -Infinity) {
16810 this.startDate = this.parseDate(this.startDate);
16813 this.updateNavArrows();
16816 setEndDate: function(endDate)
16818 this.endDate = endDate || Infinity;
16819 if (this.endDate !== Infinity) {
16820 this.endDate = this.parseDate(this.endDate);
16823 this.updateNavArrows();
16826 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
16828 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
16829 if (typeof(this.daysOfWeekDisabled) !== 'object') {
16830 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
16832 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
16833 return parseInt(d, 10);
16836 this.updateNavArrows();
16839 updateNavArrows: function()
16841 if(this.singleMode){
16845 var d = new Date(this.viewDate),
16846 year = d.getUTCFullYear(),
16847 month = d.getUTCMonth();
16849 Roo.each(this.picker().select('.prev', true).elements, function(v){
16851 switch (this.viewMode) {
16854 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
16860 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
16867 Roo.each(this.picker().select('.next', true).elements, function(v){
16869 switch (this.viewMode) {
16872 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
16878 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
16886 moveMonth: function(date, dir)
16888 if (!dir) return date;
16889 var new_date = new Date(date.valueOf()),
16890 day = new_date.getUTCDate(),
16891 month = new_date.getUTCMonth(),
16892 mag = Math.abs(dir),
16894 dir = dir > 0 ? 1 : -1;
16897 // If going back one month, make sure month is not current month
16898 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
16900 return new_date.getUTCMonth() == month;
16902 // If going forward one month, make sure month is as expected
16903 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
16905 return new_date.getUTCMonth() != new_month;
16907 new_month = month + dir;
16908 new_date.setUTCMonth(new_month);
16909 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
16910 if (new_month < 0 || new_month > 11)
16911 new_month = (new_month + 12) % 12;
16913 // For magnitudes >1, move one month at a time...
16914 for (var i=0; i<mag; i++)
16915 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
16916 new_date = this.moveMonth(new_date, dir);
16917 // ...then reset the day, keeping it in the new month
16918 new_month = new_date.getUTCMonth();
16919 new_date.setUTCDate(day);
16921 return new_month != new_date.getUTCMonth();
16924 // Common date-resetting loop -- if date is beyond end of month, make it
16927 new_date.setUTCDate(--day);
16928 new_date.setUTCMonth(new_month);
16933 moveYear: function(date, dir)
16935 return this.moveMonth(date, dir*12);
16938 dateWithinRange: function(date)
16940 return date >= this.startDate && date <= this.endDate;
16946 this.picker().remove();
16951 Roo.apply(Roo.bootstrap.DateField, {
16962 html: '<i class="fa fa-arrow-left"/>'
16972 html: '<i class="fa fa-arrow-right"/>'
17014 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17015 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17016 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17017 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17018 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17031 navFnc: 'FullYear',
17036 navFnc: 'FullYear',
17041 Roo.apply(Roo.bootstrap.DateField, {
17045 cls: 'datepicker dropdown-menu roo-dynamic',
17049 cls: 'datepicker-days',
17053 cls: 'table-condensed',
17055 Roo.bootstrap.DateField.head,
17059 Roo.bootstrap.DateField.footer
17066 cls: 'datepicker-months',
17070 cls: 'table-condensed',
17072 Roo.bootstrap.DateField.head,
17073 Roo.bootstrap.DateField.content,
17074 Roo.bootstrap.DateField.footer
17081 cls: 'datepicker-years',
17085 cls: 'table-condensed',
17087 Roo.bootstrap.DateField.head,
17088 Roo.bootstrap.DateField.content,
17089 Roo.bootstrap.DateField.footer
17108 * @class Roo.bootstrap.TimeField
17109 * @extends Roo.bootstrap.Input
17110 * Bootstrap DateField class
17114 * Create a new TimeField
17115 * @param {Object} config The config object
17118 Roo.bootstrap.TimeField = function(config){
17119 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17123 * Fires when this field show.
17124 * @param {Roo.bootstrap.DateField} thisthis
17125 * @param {Mixed} date The date value
17130 * Fires when this field hide.
17131 * @param {Roo.bootstrap.DateField} this
17132 * @param {Mixed} date The date value
17137 * Fires when select a date.
17138 * @param {Roo.bootstrap.DateField} this
17139 * @param {Mixed} date The date value
17145 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17148 * @cfg {String} format
17149 * The default time format string which can be overriden for localization support. The format must be
17150 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17154 onRender: function(ct, position)
17157 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17159 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17161 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17163 this.pop = this.picker().select('>.datepicker-time',true).first();
17164 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17166 this.picker().on('mousedown', this.onMousedown, this);
17167 this.picker().on('click', this.onClick, this);
17169 this.picker().addClass('datepicker-dropdown');
17174 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17175 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17176 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17177 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17178 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17179 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17183 fireKey: function(e){
17184 if (!this.picker().isVisible()){
17185 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17191 e.preventDefault();
17199 this.onTogglePeriod();
17202 this.onIncrementMinutes();
17205 this.onDecrementMinutes();
17214 onClick: function(e) {
17215 e.stopPropagation();
17216 e.preventDefault();
17219 picker : function()
17221 return this.el.select('.datepicker', true).first();
17224 fillTime: function()
17226 var time = this.pop.select('tbody', true).first();
17228 time.dom.innerHTML = '';
17243 cls: 'hours-up glyphicon glyphicon-chevron-up'
17263 cls: 'minutes-up glyphicon glyphicon-chevron-up'
17284 cls: 'timepicker-hour',
17299 cls: 'timepicker-minute',
17314 cls: 'btn btn-primary period',
17336 cls: 'hours-down glyphicon glyphicon-chevron-down'
17356 cls: 'minutes-down glyphicon glyphicon-chevron-down'
17374 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17381 var hours = this.time.getHours();
17382 var minutes = this.time.getMinutes();
17395 hours = hours - 12;
17399 hours = '0' + hours;
17403 minutes = '0' + minutes;
17406 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17407 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17408 this.pop.select('button', true).first().dom.innerHTML = period;
17414 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17416 var cls = ['bottom'];
17418 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17425 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17430 this.picker().addClass(cls.join('-'));
17434 Roo.each(cls, function(c){
17436 _this.picker().setTop(_this.inputEl().getHeight());
17440 _this.picker().setTop(0 - _this.picker().getHeight());
17445 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17449 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17456 onFocus : function()
17458 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17462 onBlur : function()
17464 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17470 this.picker().show();
17475 this.fireEvent('show', this, this.date);
17480 this.picker().hide();
17483 this.fireEvent('hide', this, this.date);
17486 setTime : function()
17489 this.setValue(this.time.format(this.format));
17491 this.fireEvent('select', this, this.date);
17496 onMousedown: function(e){
17497 e.stopPropagation();
17498 e.preventDefault();
17501 onIncrementHours: function()
17503 Roo.log('onIncrementHours');
17504 this.time = this.time.add(Date.HOUR, 1);
17509 onDecrementHours: function()
17511 Roo.log('onDecrementHours');
17512 this.time = this.time.add(Date.HOUR, -1);
17516 onIncrementMinutes: function()
17518 Roo.log('onIncrementMinutes');
17519 this.time = this.time.add(Date.MINUTE, 1);
17523 onDecrementMinutes: function()
17525 Roo.log('onDecrementMinutes');
17526 this.time = this.time.add(Date.MINUTE, -1);
17530 onTogglePeriod: function()
17532 Roo.log('onTogglePeriod');
17533 this.time = this.time.add(Date.HOUR, 12);
17540 Roo.apply(Roo.bootstrap.TimeField, {
17570 cls: 'btn btn-info ok',
17582 Roo.apply(Roo.bootstrap.TimeField, {
17586 cls: 'datepicker dropdown-menu',
17590 cls: 'datepicker-time',
17594 cls: 'table-condensed',
17596 Roo.bootstrap.TimeField.content,
17597 Roo.bootstrap.TimeField.footer
17616 * @class Roo.bootstrap.MonthField
17617 * @extends Roo.bootstrap.Input
17618 * Bootstrap MonthField class
17620 * @cfg {String} language default en
17623 * Create a new MonthField
17624 * @param {Object} config The config object
17627 Roo.bootstrap.MonthField = function(config){
17628 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17633 * Fires when this field show.
17634 * @param {Roo.bootstrap.MonthField} this
17635 * @param {Mixed} date The date value
17640 * Fires when this field hide.
17641 * @param {Roo.bootstrap.MonthField} this
17642 * @param {Mixed} date The date value
17647 * Fires when select a date.
17648 * @param {Roo.bootstrap.MonthField} this
17649 * @param {String} oldvalue The old value
17650 * @param {String} newvalue The new value
17656 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
17658 onRender: function(ct, position)
17661 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17663 this.language = this.language || 'en';
17664 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17665 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17667 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17668 this.isInline = false;
17669 this.isInput = true;
17670 this.component = this.el.select('.add-on', true).first() || false;
17671 this.component = (this.component && this.component.length === 0) ? false : this.component;
17672 this.hasInput = this.component && this.inputEL().length;
17674 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17676 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17678 this.picker().on('mousedown', this.onMousedown, this);
17679 this.picker().on('click', this.onClick, this);
17681 this.picker().addClass('datepicker-dropdown');
17683 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17684 v.setStyle('width', '189px');
17691 if(this.isInline) {
17697 setValue: function(v, suppressEvent)
17699 var o = this.getValue();
17701 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17705 if(suppressEvent !== true){
17706 this.fireEvent('select', this, o, v);
17711 getValue: function()
17716 onClick: function(e)
17718 e.stopPropagation();
17719 e.preventDefault();
17721 var target = e.getTarget();
17723 if(target.nodeName.toLowerCase() === 'i'){
17724 target = Roo.get(target).dom.parentNode;
17727 var nodeName = target.nodeName;
17728 var className = target.className;
17729 var html = target.innerHTML;
17731 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17735 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17737 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17743 picker : function()
17745 return this.pickerEl;
17748 fillMonths: function()
17751 var months = this.picker().select('>.datepicker-months td', true).first();
17753 months.dom.innerHTML = '';
17759 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
17762 months.createChild(month);
17771 if(typeof(this.vIndex) == 'undefined' && this.value.length){
17772 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
17775 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
17776 e.removeClass('active');
17778 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
17779 e.addClass('active');
17786 if(this.isInline) return;
17788 this.picker().removeClass(['bottom', 'top']);
17790 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17792 * place to the top of element!
17796 this.picker().addClass('top');
17797 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17802 this.picker().addClass('bottom');
17804 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17807 onFocus : function()
17809 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
17813 onBlur : function()
17815 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
17817 var d = this.inputEl().getValue();
17826 this.picker().show();
17827 this.picker().select('>.datepicker-months', true).first().show();
17831 this.fireEvent('show', this, this.date);
17836 if(this.isInline) return;
17837 this.picker().hide();
17838 this.fireEvent('hide', this, this.date);
17842 onMousedown: function(e)
17844 e.stopPropagation();
17845 e.preventDefault();
17850 Roo.bootstrap.MonthField.superclass.keyup.call(this);
17854 fireKey: function(e)
17856 if (!this.picker().isVisible()){
17857 if (e.keyCode == 27) // allow escape to hide and re-show picker
17867 e.preventDefault();
17871 dir = e.keyCode == 37 ? -1 : 1;
17873 this.vIndex = this.vIndex + dir;
17875 if(this.vIndex < 0){
17879 if(this.vIndex > 11){
17883 if(isNaN(this.vIndex)){
17887 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17893 dir = e.keyCode == 38 ? -1 : 1;
17895 this.vIndex = this.vIndex + dir * 4;
17897 if(this.vIndex < 0){
17901 if(this.vIndex > 11){
17905 if(isNaN(this.vIndex)){
17909 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17914 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17915 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17919 e.preventDefault();
17922 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17923 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17939 this.picker().remove();
17944 Roo.apply(Roo.bootstrap.MonthField, {
17963 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17964 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
17969 Roo.apply(Roo.bootstrap.MonthField, {
17973 cls: 'datepicker dropdown-menu roo-dynamic',
17977 cls: 'datepicker-months',
17981 cls: 'table-condensed',
17983 Roo.bootstrap.DateField.content
18003 * @class Roo.bootstrap.CheckBox
18004 * @extends Roo.bootstrap.Input
18005 * Bootstrap CheckBox class
18007 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18008 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18009 * @cfg {String} boxLabel The text that appears beside the checkbox
18010 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18011 * @cfg {Boolean} checked initnal the element
18012 * @cfg {Boolean} inline inline the element (default false)
18013 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18016 * Create a new CheckBox
18017 * @param {Object} config The config object
18020 Roo.bootstrap.CheckBox = function(config){
18021 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18026 * Fires when the element is checked or unchecked.
18027 * @param {Roo.bootstrap.CheckBox} this This input
18028 * @param {Boolean} checked The new checked value
18035 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18037 inputType: 'checkbox',
18045 getAutoCreate : function()
18047 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18053 cfg.cls = 'form-group ' + this.inputType; //input-group
18056 cfg.cls += ' ' + this.inputType + '-inline';
18062 type : this.inputType,
18063 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18064 cls : 'roo-' + this.inputType, //'form-box',
18065 placeholder : this.placeholder || ''
18069 if (this.weight) { // Validity check?
18070 cfg.cls += " " + this.inputType + "-" + this.weight;
18073 if (this.disabled) {
18074 input.disabled=true;
18078 input.checked = this.checked;
18082 input.name = this.name;
18086 input.cls += ' input-' + this.size;
18091 ['xs','sm','md','lg'].map(function(size){
18092 if (settings[size]) {
18093 cfg.cls += ' col-' + size + '-' + settings[size];
18097 var inputblock = input;
18099 if (this.before || this.after) {
18102 cls : 'input-group',
18107 inputblock.cn.push({
18109 cls : 'input-group-addon',
18114 inputblock.cn.push(input);
18117 inputblock.cn.push({
18119 cls : 'input-group-addon',
18126 if (align ==='left' && this.fieldLabel.length) {
18127 Roo.log("left and has label");
18133 cls : 'control-label col-md-' + this.labelWidth,
18134 html : this.fieldLabel
18138 cls : "col-md-" + (12 - this.labelWidth),
18145 } else if ( this.fieldLabel.length) {
18150 tag: this.boxLabel ? 'span' : 'label',
18152 cls: 'control-label box-input-label',
18153 //cls : 'input-group-addon',
18154 html : this.fieldLabel
18164 Roo.log(" no label && no align");
18165 cfg.cn = [ inputblock ] ;
18170 var boxLabelCfg = {
18172 //'for': id, // box label is handled by onclick - so no for...
18174 html: this.boxLabel
18178 boxLabelCfg.tooltip = this.tooltip;
18181 cfg.cn.push(boxLabelCfg);
18191 * return the real input element.
18193 inputEl: function ()
18195 return this.el.select('input.roo-' + this.inputType,true).first();
18198 labelEl: function()
18200 return this.el.select('label.control-label',true).first();
18202 /* depricated... */
18206 return this.labelEl();
18209 initEvents : function()
18211 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18213 this.inputEl().on('click', this.onClick, this);
18215 if (this.boxLabel) {
18216 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18219 this.startValue = this.getValue();
18222 Roo.bootstrap.CheckBox.register(this);
18226 onClick : function()
18228 this.setChecked(!this.checked);
18231 setChecked : function(state,suppressEvent)
18233 this.startValue = this.getValue();
18235 if(this.inputType == 'radio'){
18237 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18238 e.dom.checked = false;
18241 this.inputEl().dom.checked = true;
18243 this.inputEl().dom.value = this.inputValue;
18245 if(suppressEvent !== true){
18246 this.fireEvent('check', this, true);
18254 this.checked = state;
18256 this.inputEl().dom.checked = state;
18258 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18260 if(suppressEvent !== true){
18261 this.fireEvent('check', this, state);
18267 getValue : function()
18269 if(this.inputType == 'radio'){
18270 return this.getGroupValue();
18273 return this.inputEl().getValue();
18277 getGroupValue : function()
18279 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18283 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18286 setValue : function(v,suppressEvent)
18288 if(this.inputType == 'radio'){
18289 this.setGroupValue(v, suppressEvent);
18293 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18298 setGroupValue : function(v, suppressEvent)
18300 this.startValue = this.getValue();
18302 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18303 e.dom.checked = false;
18305 if(e.dom.value == v){
18306 e.dom.checked = true;
18310 if(suppressEvent !== true){
18311 this.fireEvent('check', this, true);
18319 validate : function()
18323 (this.inputType == 'radio' && this.validateRadio()) ||
18324 (this.inputType == 'checkbox' && this.validateCheckbox())
18330 this.markInvalid();
18334 validateRadio : function()
18338 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18339 if(!e.dom.checked){
18351 validateCheckbox : function()
18354 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18357 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18365 for(var i in group){
18370 r = (group[i].getValue() == group[i].inputValue) ? true : false;
18377 * Mark this field as valid
18379 markValid : function()
18381 if(this.allowBlank){
18387 this.fireEvent('valid', this);
18389 if(this.inputType == 'radio'){
18390 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18391 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18392 e.findParent('.form-group', false, true).addClass(_this.validClass);
18399 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18400 this.el.findParent('.form-group', false, true).addClass(this.validClass);
18404 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18410 for(var i in group){
18411 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18412 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18417 * Mark this field as invalid
18418 * @param {String} msg The validation message
18420 markInvalid : function(msg)
18422 if(this.allowBlank){
18428 this.fireEvent('invalid', this, msg);
18430 if(this.inputType == 'radio'){
18431 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18432 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18433 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18440 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18441 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18445 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18451 for(var i in group){
18452 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18453 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18460 Roo.apply(Roo.bootstrap.CheckBox, {
18465 * register a CheckBox Group
18466 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18468 register : function(checkbox)
18470 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18471 this.groups[checkbox.groupId] = {};
18474 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18478 this.groups[checkbox.groupId][checkbox.name] = checkbox;
18482 * fetch a CheckBox Group based on the group ID
18483 * @param {string} the group ID
18484 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18486 get: function(groupId) {
18487 if (typeof(this.groups[groupId]) == 'undefined') {
18491 return this.groups[groupId] ;
18503 *<div class="radio">
18505 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18506 Option one is this and that—be sure to include why it's great
18513 *<label class="radio-inline">fieldLabel</label>
18514 *<label class="radio-inline">
18515 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18523 * @class Roo.bootstrap.Radio
18524 * @extends Roo.bootstrap.CheckBox
18525 * Bootstrap Radio class
18528 * Create a new Radio
18529 * @param {Object} config The config object
18532 Roo.bootstrap.Radio = function(config){
18533 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18537 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
18539 inputType: 'radio',
18543 getAutoCreate : function()
18545 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18546 align = align || 'left'; // default...
18553 tag : this.inline ? 'span' : 'div',
18558 var inline = this.inline ? ' radio-inline' : '';
18562 // does not need for, as we wrap the input with it..
18564 cls : 'control-label box-label' + inline,
18567 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18571 //cls : 'control-label' + inline,
18572 html : this.fieldLabel,
18573 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18582 type : this.inputType,
18583 //value : (!this.checked) ? this.valueOff : this.inputValue,
18584 value : this.inputValue,
18586 placeholder : this.placeholder || '' // ?? needed????
18589 if (this.weight) { // Validity check?
18590 input.cls += " radio-" + this.weight;
18592 if (this.disabled) {
18593 input.disabled=true;
18597 input.checked = this.checked;
18601 input.name = this.name;
18605 input.cls += ' input-' + this.size;
18608 //?? can span's inline have a width??
18611 ['xs','sm','md','lg'].map(function(size){
18612 if (settings[size]) {
18613 cfg.cls += ' col-' + size + '-' + settings[size];
18617 var inputblock = input;
18619 if (this.before || this.after) {
18622 cls : 'input-group',
18627 inputblock.cn.push({
18629 cls : 'input-group-addon',
18633 inputblock.cn.push(input);
18635 inputblock.cn.push({
18637 cls : 'input-group-addon',
18645 if (this.fieldLabel && this.fieldLabel.length) {
18646 cfg.cn.push(fieldLabel);
18649 // normal bootstrap puts the input inside the label.
18650 // however with our styled version - it has to go after the input.
18652 //lbl.cn.push(inputblock);
18656 cls: 'radio' + inline,
18663 cfg.cn.push( lblwrap);
18668 html: this.boxLabel
18677 initEvents : function()
18679 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18681 this.inputEl().on('click', this.onClick, this);
18682 if (this.boxLabel) {
18683 Roo.log('find label')
18684 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
18689 inputEl: function ()
18691 return this.el.select('input.roo-radio',true).first();
18693 onClick : function()
18696 this.setChecked(true);
18699 setChecked : function(state,suppressEvent)
18702 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18703 v.dom.checked = false;
18706 Roo.log(this.inputEl().dom);
18707 this.checked = state;
18708 this.inputEl().dom.checked = state;
18710 if(suppressEvent !== true){
18711 this.fireEvent('check', this, state);
18714 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18718 getGroupValue : function()
18721 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18722 if(v.dom.checked == true){
18723 value = v.dom.value;
18731 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
18732 * @return {Mixed} value The field value
18734 getValue : function(){
18735 return this.getGroupValue();
18741 //<script type="text/javascript">
18744 * Based Ext JS Library 1.1.1
18745 * Copyright(c) 2006-2007, Ext JS, LLC.
18751 * @class Roo.HtmlEditorCore
18752 * @extends Roo.Component
18753 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18755 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18758 Roo.HtmlEditorCore = function(config){
18761 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
18766 * @event initialize
18767 * Fires when the editor is fully initialized (including the iframe)
18768 * @param {Roo.HtmlEditorCore} this
18773 * Fires when the editor is first receives the focus. Any insertion must wait
18774 * until after this event.
18775 * @param {Roo.HtmlEditorCore} this
18779 * @event beforesync
18780 * Fires before the textarea is updated with content from the editor iframe. Return false
18781 * to cancel the sync.
18782 * @param {Roo.HtmlEditorCore} this
18783 * @param {String} html
18787 * @event beforepush
18788 * Fires before the iframe editor is updated with content from the textarea. Return false
18789 * to cancel the push.
18790 * @param {Roo.HtmlEditorCore} this
18791 * @param {String} html
18796 * Fires when the textarea is updated with content from the editor iframe.
18797 * @param {Roo.HtmlEditorCore} this
18798 * @param {String} html
18803 * Fires when the iframe editor is updated with content from the textarea.
18804 * @param {Roo.HtmlEditorCore} this
18805 * @param {String} html
18810 * @event editorevent
18811 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18812 * @param {Roo.HtmlEditorCore} this
18818 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
18820 // defaults : white / black...
18821 this.applyBlacklists();
18828 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
18832 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
18838 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
18843 * @cfg {Number} height (in pixels)
18847 * @cfg {Number} width (in pixels)
18852 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18855 stylesheets: false,
18860 // private properties
18861 validationEvent : false,
18863 initialized : false,
18865 sourceEditMode : false,
18866 onFocus : Roo.emptyFn,
18868 hideMode:'offsets',
18872 // blacklist + whitelisted elements..
18879 * Protected method that will not generally be called directly. It
18880 * is called when the editor initializes the iframe with HTML contents. Override this method if you
18881 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
18883 getDocMarkup : function(){
18887 // inherit styels from page...??
18888 if (this.stylesheets === false) {
18890 Roo.get(document.head).select('style').each(function(node) {
18891 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18894 Roo.get(document.head).select('link').each(function(node) {
18895 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18898 } else if (!this.stylesheets.length) {
18900 st = '<style type="text/css">' +
18901 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18907 st += '<style type="text/css">' +
18908 'IMG { cursor: pointer } ' +
18912 return '<html><head>' + st +
18913 //<style type="text/css">' +
18914 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18916 ' </head><body class="roo-htmleditor-body"></body></html>';
18920 onRender : function(ct, position)
18923 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
18924 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
18927 this.el.dom.style.border = '0 none';
18928 this.el.dom.setAttribute('tabIndex', -1);
18929 this.el.addClass('x-hidden hide');
18933 if(Roo.isIE){ // fix IE 1px bogus margin
18934 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
18938 this.frameId = Roo.id();
18942 var iframe = this.owner.wrap.createChild({
18944 cls: 'form-control', // bootstrap..
18946 name: this.frameId,
18947 frameBorder : 'no',
18948 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
18953 this.iframe = iframe.dom;
18955 this.assignDocWin();
18957 this.doc.designMode = 'on';
18960 this.doc.write(this.getDocMarkup());
18964 var task = { // must defer to wait for browser to be ready
18966 //console.log("run task?" + this.doc.readyState);
18967 this.assignDocWin();
18968 if(this.doc.body || this.doc.readyState == 'complete'){
18970 this.doc.designMode="on";
18974 Roo.TaskMgr.stop(task);
18975 this.initEditor.defer(10, this);
18982 Roo.TaskMgr.start(task);
18987 onResize : function(w, h)
18989 Roo.log('resize: ' +w + ',' + h );
18990 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
18994 if(typeof w == 'number'){
18996 this.iframe.style.width = w + 'px';
18998 if(typeof h == 'number'){
19000 this.iframe.style.height = h + 'px';
19002 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19009 * Toggles the editor between standard and source edit mode.
19010 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19012 toggleSourceEdit : function(sourceEditMode){
19014 this.sourceEditMode = sourceEditMode === true;
19016 if(this.sourceEditMode){
19018 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19021 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19022 //this.iframe.className = '';
19025 //this.setSize(this.owner.wrap.getSize());
19026 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19033 * Protected method that will not generally be called directly. If you need/want
19034 * custom HTML cleanup, this is the method you should override.
19035 * @param {String} html The HTML to be cleaned
19036 * return {String} The cleaned HTML
19038 cleanHtml : function(html){
19039 html = String(html);
19040 if(html.length > 5){
19041 if(Roo.isSafari){ // strip safari nonsense
19042 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19045 if(html == ' '){
19052 * HTML Editor -> Textarea
19053 * Protected method that will not generally be called directly. Syncs the contents
19054 * of the editor iframe with the textarea.
19056 syncValue : function(){
19057 if(this.initialized){
19058 var bd = (this.doc.body || this.doc.documentElement);
19059 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19060 var html = bd.innerHTML;
19062 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19063 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19065 html = '<div style="'+m[0]+'">' + html + '</div>';
19068 html = this.cleanHtml(html);
19069 // fix up the special chars.. normaly like back quotes in word...
19070 // however we do not want to do this with chinese..
19071 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19072 var cc = b.charCodeAt();
19074 (cc >= 0x4E00 && cc < 0xA000 ) ||
19075 (cc >= 0x3400 && cc < 0x4E00 ) ||
19076 (cc >= 0xf900 && cc < 0xfb00 )
19082 if(this.owner.fireEvent('beforesync', this, html) !== false){
19083 this.el.dom.value = html;
19084 this.owner.fireEvent('sync', this, html);
19090 * Protected method that will not generally be called directly. Pushes the value of the textarea
19091 * into the iframe editor.
19093 pushValue : function(){
19094 if(this.initialized){
19095 var v = this.el.dom.value.trim();
19097 // if(v.length < 1){
19101 if(this.owner.fireEvent('beforepush', this, v) !== false){
19102 var d = (this.doc.body || this.doc.documentElement);
19104 this.cleanUpPaste();
19105 this.el.dom.value = d.innerHTML;
19106 this.owner.fireEvent('push', this, v);
19112 deferFocus : function(){
19113 this.focus.defer(10, this);
19117 focus : function(){
19118 if(this.win && !this.sourceEditMode){
19125 assignDocWin: function()
19127 var iframe = this.iframe;
19130 this.doc = iframe.contentWindow.document;
19131 this.win = iframe.contentWindow;
19133 // if (!Roo.get(this.frameId)) {
19136 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19137 // this.win = Roo.get(this.frameId).dom.contentWindow;
19139 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19143 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19144 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19149 initEditor : function(){
19150 //console.log("INIT EDITOR");
19151 this.assignDocWin();
19155 this.doc.designMode="on";
19157 this.doc.write(this.getDocMarkup());
19160 var dbody = (this.doc.body || this.doc.documentElement);
19161 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19162 // this copies styles from the containing element into thsi one..
19163 // not sure why we need all of this..
19164 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19166 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19167 //ss['background-attachment'] = 'fixed'; // w3c
19168 dbody.bgProperties = 'fixed'; // ie
19169 //Roo.DomHelper.applyStyles(dbody, ss);
19170 Roo.EventManager.on(this.doc, {
19171 //'mousedown': this.onEditorEvent,
19172 'mouseup': this.onEditorEvent,
19173 'dblclick': this.onEditorEvent,
19174 'click': this.onEditorEvent,
19175 'keyup': this.onEditorEvent,
19180 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19182 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19183 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19185 this.initialized = true;
19187 this.owner.fireEvent('initialize', this);
19192 onDestroy : function(){
19198 //for (var i =0; i < this.toolbars.length;i++) {
19199 // // fixme - ask toolbars for heights?
19200 // this.toolbars[i].onDestroy();
19203 //this.wrap.dom.innerHTML = '';
19204 //this.wrap.remove();
19209 onFirstFocus : function(){
19211 this.assignDocWin();
19214 this.activated = true;
19217 if(Roo.isGecko){ // prevent silly gecko errors
19219 var s = this.win.getSelection();
19220 if(!s.focusNode || s.focusNode.nodeType != 3){
19221 var r = s.getRangeAt(0);
19222 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19227 this.execCmd('useCSS', true);
19228 this.execCmd('styleWithCSS', false);
19231 this.owner.fireEvent('activate', this);
19235 adjustFont: function(btn){
19236 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19237 //if(Roo.isSafari){ // safari
19240 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19241 if(Roo.isSafari){ // safari
19242 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19243 v = (v < 10) ? 10 : v;
19244 v = (v > 48) ? 48 : v;
19245 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19250 v = Math.max(1, v+adjust);
19252 this.execCmd('FontSize', v );
19255 onEditorEvent : function(e)
19257 this.owner.fireEvent('editorevent', this, e);
19258 // this.updateToolbar();
19259 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19262 insertTag : function(tg)
19264 // could be a bit smarter... -> wrap the current selected tRoo..
19265 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19267 range = this.createRange(this.getSelection());
19268 var wrappingNode = this.doc.createElement(tg.toLowerCase());
19269 wrappingNode.appendChild(range.extractContents());
19270 range.insertNode(wrappingNode);
19277 this.execCmd("formatblock", tg);
19281 insertText : function(txt)
19285 var range = this.createRange();
19286 range.deleteContents();
19287 //alert(Sender.getAttribute('label'));
19289 range.insertNode(this.doc.createTextNode(txt));
19295 * Executes a Midas editor command on the editor document and performs necessary focus and
19296 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19297 * @param {String} cmd The Midas command
19298 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19300 relayCmd : function(cmd, value){
19302 this.execCmd(cmd, value);
19303 this.owner.fireEvent('editorevent', this);
19304 //this.updateToolbar();
19305 this.owner.deferFocus();
19309 * Executes a Midas editor command directly on the editor document.
19310 * For visual commands, you should use {@link #relayCmd} instead.
19311 * <b>This should only be called after the editor is initialized.</b>
19312 * @param {String} cmd The Midas command
19313 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19315 execCmd : function(cmd, value){
19316 this.doc.execCommand(cmd, false, value === undefined ? null : value);
19323 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19325 * @param {String} text | dom node..
19327 insertAtCursor : function(text)
19332 if(!this.activated){
19338 var r = this.doc.selection.createRange();
19349 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19353 // from jquery ui (MIT licenced)
19355 var win = this.win;
19357 if (win.getSelection && win.getSelection().getRangeAt) {
19358 range = win.getSelection().getRangeAt(0);
19359 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19360 range.insertNode(node);
19361 } else if (win.document.selection && win.document.selection.createRange) {
19362 // no firefox support
19363 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19364 win.document.selection.createRange().pasteHTML(txt);
19366 // no firefox support
19367 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19368 this.execCmd('InsertHTML', txt);
19377 mozKeyPress : function(e){
19379 var c = e.getCharCode(), cmd;
19382 c = String.fromCharCode(c).toLowerCase();
19396 this.cleanUpPaste.defer(100, this);
19404 e.preventDefault();
19412 fixKeys : function(){ // load time branching for fastest keydown performance
19414 return function(e){
19415 var k = e.getKey(), r;
19418 r = this.doc.selection.createRange();
19421 r.pasteHTML('    ');
19428 r = this.doc.selection.createRange();
19430 var target = r.parentElement();
19431 if(!target || target.tagName.toLowerCase() != 'li'){
19433 r.pasteHTML('<br />');
19439 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19440 this.cleanUpPaste.defer(100, this);
19446 }else if(Roo.isOpera){
19447 return function(e){
19448 var k = e.getKey();
19452 this.execCmd('InsertHTML','    ');
19455 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19456 this.cleanUpPaste.defer(100, this);
19461 }else if(Roo.isSafari){
19462 return function(e){
19463 var k = e.getKey();
19467 this.execCmd('InsertText','\t');
19471 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19472 this.cleanUpPaste.defer(100, this);
19480 getAllAncestors: function()
19482 var p = this.getSelectedNode();
19485 a.push(p); // push blank onto stack..
19486 p = this.getParentElement();
19490 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19494 a.push(this.doc.body);
19498 lastSelNode : false,
19501 getSelection : function()
19503 this.assignDocWin();
19504 return Roo.isIE ? this.doc.selection : this.win.getSelection();
19507 getSelectedNode: function()
19509 // this may only work on Gecko!!!
19511 // should we cache this!!!!
19516 var range = this.createRange(this.getSelection()).cloneRange();
19519 var parent = range.parentElement();
19521 var testRange = range.duplicate();
19522 testRange.moveToElementText(parent);
19523 if (testRange.inRange(range)) {
19526 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19529 parent = parent.parentElement;
19534 // is ancestor a text element.
19535 var ac = range.commonAncestorContainer;
19536 if (ac.nodeType == 3) {
19537 ac = ac.parentNode;
19540 var ar = ac.childNodes;
19543 var other_nodes = [];
19544 var has_other_nodes = false;
19545 for (var i=0;i<ar.length;i++) {
19546 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
19549 // fullly contained node.
19551 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19556 // probably selected..
19557 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19558 other_nodes.push(ar[i]);
19562 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
19567 has_other_nodes = true;
19569 if (!nodes.length && other_nodes.length) {
19570 nodes= other_nodes;
19572 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19578 createRange: function(sel)
19580 // this has strange effects when using with
19581 // top toolbar - not sure if it's a great idea.
19582 //this.editor.contentWindow.focus();
19583 if (typeof sel != "undefined") {
19585 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19587 return this.doc.createRange();
19590 return this.doc.createRange();
19593 getParentElement: function()
19596 this.assignDocWin();
19597 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19599 var range = this.createRange(sel);
19602 var p = range.commonAncestorContainer;
19603 while (p.nodeType == 3) { // text node
19614 * Range intersection.. the hard stuff...
19618 * [ -- selected range --- ]
19622 * if end is before start or hits it. fail.
19623 * if start is after end or hits it fail.
19625 * if either hits (but other is outside. - then it's not
19631 // @see http://www.thismuchiknow.co.uk/?p=64.
19632 rangeIntersectsNode : function(range, node)
19634 var nodeRange = node.ownerDocument.createRange();
19636 nodeRange.selectNode(node);
19638 nodeRange.selectNodeContents(node);
19641 var rangeStartRange = range.cloneRange();
19642 rangeStartRange.collapse(true);
19644 var rangeEndRange = range.cloneRange();
19645 rangeEndRange.collapse(false);
19647 var nodeStartRange = nodeRange.cloneRange();
19648 nodeStartRange.collapse(true);
19650 var nodeEndRange = nodeRange.cloneRange();
19651 nodeEndRange.collapse(false);
19653 return rangeStartRange.compareBoundaryPoints(
19654 Range.START_TO_START, nodeEndRange) == -1 &&
19655 rangeEndRange.compareBoundaryPoints(
19656 Range.START_TO_START, nodeStartRange) == 1;
19660 rangeCompareNode : function(range, node)
19662 var nodeRange = node.ownerDocument.createRange();
19664 nodeRange.selectNode(node);
19666 nodeRange.selectNodeContents(node);
19670 range.collapse(true);
19672 nodeRange.collapse(true);
19674 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19675 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
19677 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19679 var nodeIsBefore = ss == 1;
19680 var nodeIsAfter = ee == -1;
19682 if (nodeIsBefore && nodeIsAfter)
19684 if (!nodeIsBefore && nodeIsAfter)
19685 return 1; //right trailed.
19687 if (nodeIsBefore && !nodeIsAfter)
19688 return 2; // left trailed.
19693 // private? - in a new class?
19694 cleanUpPaste : function()
19696 // cleans up the whole document..
19697 Roo.log('cleanuppaste');
19699 this.cleanUpChildren(this.doc.body);
19700 var clean = this.cleanWordChars(this.doc.body.innerHTML);
19701 if (clean != this.doc.body.innerHTML) {
19702 this.doc.body.innerHTML = clean;
19707 cleanWordChars : function(input) {// change the chars to hex code
19708 var he = Roo.HtmlEditorCore;
19710 var output = input;
19711 Roo.each(he.swapCodes, function(sw) {
19712 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19714 output = output.replace(swapper, sw[1]);
19721 cleanUpChildren : function (n)
19723 if (!n.childNodes.length) {
19726 for (var i = n.childNodes.length-1; i > -1 ; i--) {
19727 this.cleanUpChild(n.childNodes[i]);
19734 cleanUpChild : function (node)
19737 //console.log(node);
19738 if (node.nodeName == "#text") {
19739 // clean up silly Windows -- stuff?
19742 if (node.nodeName == "#comment") {
19743 node.parentNode.removeChild(node);
19744 // clean up silly Windows -- stuff?
19747 var lcname = node.tagName.toLowerCase();
19748 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19749 // whitelist of tags..
19751 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19753 node.parentNode.removeChild(node);
19758 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
19760 // remove <a name=....> as rendering on yahoo mailer is borked with this.
19761 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
19763 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
19764 // remove_keep_children = true;
19767 if (remove_keep_children) {
19768 this.cleanUpChildren(node);
19769 // inserts everything just before this node...
19770 while (node.childNodes.length) {
19771 var cn = node.childNodes[0];
19772 node.removeChild(cn);
19773 node.parentNode.insertBefore(cn, node);
19775 node.parentNode.removeChild(node);
19779 if (!node.attributes || !node.attributes.length) {
19780 this.cleanUpChildren(node);
19784 function cleanAttr(n,v)
19787 if (v.match(/^\./) || v.match(/^\//)) {
19790 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19793 if (v.match(/^#/)) {
19796 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19797 node.removeAttribute(n);
19801 var cwhite = this.cwhite;
19802 var cblack = this.cblack;
19804 function cleanStyle(n,v)
19806 if (v.match(/expression/)) { //XSS?? should we even bother..
19807 node.removeAttribute(n);
19811 var parts = v.split(/;/);
19814 Roo.each(parts, function(p) {
19815 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
19819 var l = p.split(':').shift().replace(/\s+/g,'');
19820 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
19822 if ( cwhite.length && cblack.indexOf(l) > -1) {
19823 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19824 //node.removeAttribute(n);
19828 // only allow 'c whitelisted system attributes'
19829 if ( cwhite.length && cwhite.indexOf(l) < 0) {
19830 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19831 //node.removeAttribute(n);
19841 if (clean.length) {
19842 node.setAttribute(n, clean.join(';'));
19844 node.removeAttribute(n);
19850 for (var i = node.attributes.length-1; i > -1 ; i--) {
19851 var a = node.attributes[i];
19854 if (a.name.toLowerCase().substr(0,2)=='on') {
19855 node.removeAttribute(a.name);
19858 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
19859 node.removeAttribute(a.name);
19862 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
19863 cleanAttr(a.name,a.value); // fixme..
19866 if (a.name == 'style') {
19867 cleanStyle(a.name,a.value);
19870 /// clean up MS crap..
19871 // tecnically this should be a list of valid class'es..
19874 if (a.name == 'class') {
19875 if (a.value.match(/^Mso/)) {
19876 node.className = '';
19879 if (a.value.match(/body/)) {
19880 node.className = '';
19891 this.cleanUpChildren(node);
19897 * Clean up MS wordisms...
19899 cleanWord : function(node)
19904 this.cleanWord(this.doc.body);
19907 if (node.nodeName == "#text") {
19908 // clean up silly Windows -- stuff?
19911 if (node.nodeName == "#comment") {
19912 node.parentNode.removeChild(node);
19913 // clean up silly Windows -- stuff?
19917 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
19918 node.parentNode.removeChild(node);
19922 // remove - but keep children..
19923 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
19924 while (node.childNodes.length) {
19925 var cn = node.childNodes[0];
19926 node.removeChild(cn);
19927 node.parentNode.insertBefore(cn, node);
19929 node.parentNode.removeChild(node);
19930 this.iterateChildren(node, this.cleanWord);
19934 if (node.className.length) {
19936 var cn = node.className.split(/\W+/);
19938 Roo.each(cn, function(cls) {
19939 if (cls.match(/Mso[a-zA-Z]+/)) {
19944 node.className = cna.length ? cna.join(' ') : '';
19946 node.removeAttribute("class");
19950 if (node.hasAttribute("lang")) {
19951 node.removeAttribute("lang");
19954 if (node.hasAttribute("style")) {
19956 var styles = node.getAttribute("style").split(";");
19958 Roo.each(styles, function(s) {
19959 if (!s.match(/:/)) {
19962 var kv = s.split(":");
19963 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
19966 // what ever is left... we allow.
19969 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19970 if (!nstyle.length) {
19971 node.removeAttribute('style');
19974 this.iterateChildren(node, this.cleanWord);
19980 * iterateChildren of a Node, calling fn each time, using this as the scole..
19981 * @param {DomNode} node node to iterate children of.
19982 * @param {Function} fn method of this class to call on each item.
19984 iterateChildren : function(node, fn)
19986 if (!node.childNodes.length) {
19989 for (var i = node.childNodes.length-1; i > -1 ; i--) {
19990 fn.call(this, node.childNodes[i])
19996 * cleanTableWidths.
19998 * Quite often pasting from word etc.. results in tables with column and widths.
19999 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20002 cleanTableWidths : function(node)
20007 this.cleanTableWidths(this.doc.body);
20012 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20015 Roo.log(node.tagName);
20016 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20017 this.iterateChildren(node, this.cleanTableWidths);
20020 if (node.hasAttribute('width')) {
20021 node.removeAttribute('width');
20025 if (node.hasAttribute("style")) {
20028 var styles = node.getAttribute("style").split(";");
20030 Roo.each(styles, function(s) {
20031 if (!s.match(/:/)) {
20034 var kv = s.split(":");
20035 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20038 // what ever is left... we allow.
20041 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20042 if (!nstyle.length) {
20043 node.removeAttribute('style');
20047 this.iterateChildren(node, this.cleanTableWidths);
20055 domToHTML : function(currentElement, depth, nopadtext) {
20057 depth = depth || 0;
20058 nopadtext = nopadtext || false;
20060 if (!currentElement) {
20061 return this.domToHTML(this.doc.body);
20064 //Roo.log(currentElement);
20066 var allText = false;
20067 var nodeName = currentElement.nodeName;
20068 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20070 if (nodeName == '#text') {
20072 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20077 if (nodeName != 'BODY') {
20080 // Prints the node tagName, such as <A>, <IMG>, etc
20083 for(i = 0; i < currentElement.attributes.length;i++) {
20085 var aname = currentElement.attributes.item(i).name;
20086 if (!currentElement.attributes.item(i).value.length) {
20089 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20092 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20101 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20104 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20109 // Traverse the tree
20111 var currentElementChild = currentElement.childNodes.item(i);
20112 var allText = true;
20113 var innerHTML = '';
20115 while (currentElementChild) {
20116 // Formatting code (indent the tree so it looks nice on the screen)
20117 var nopad = nopadtext;
20118 if (lastnode == 'SPAN') {
20122 if (currentElementChild.nodeName == '#text') {
20123 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20124 toadd = nopadtext ? toadd : toadd.trim();
20125 if (!nopad && toadd.length > 80) {
20126 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20128 innerHTML += toadd;
20131 currentElementChild = currentElement.childNodes.item(i);
20137 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20139 // Recursively traverse the tree structure of the child node
20140 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20141 lastnode = currentElementChild.nodeName;
20143 currentElementChild=currentElement.childNodes.item(i);
20149 // The remaining code is mostly for formatting the tree
20150 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20155 ret+= "</"+tagName+">";
20161 applyBlacklists : function()
20163 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20164 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20168 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20169 if (b.indexOf(tag) > -1) {
20172 this.white.push(tag);
20176 Roo.each(w, function(tag) {
20177 if (b.indexOf(tag) > -1) {
20180 if (this.white.indexOf(tag) > -1) {
20183 this.white.push(tag);
20188 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20189 if (w.indexOf(tag) > -1) {
20192 this.black.push(tag);
20196 Roo.each(b, function(tag) {
20197 if (w.indexOf(tag) > -1) {
20200 if (this.black.indexOf(tag) > -1) {
20203 this.black.push(tag);
20208 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20209 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20213 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20214 if (b.indexOf(tag) > -1) {
20217 this.cwhite.push(tag);
20221 Roo.each(w, function(tag) {
20222 if (b.indexOf(tag) > -1) {
20225 if (this.cwhite.indexOf(tag) > -1) {
20228 this.cwhite.push(tag);
20233 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20234 if (w.indexOf(tag) > -1) {
20237 this.cblack.push(tag);
20241 Roo.each(b, function(tag) {
20242 if (w.indexOf(tag) > -1) {
20245 if (this.cblack.indexOf(tag) > -1) {
20248 this.cblack.push(tag);
20253 setStylesheets : function(stylesheets)
20255 if(typeof(stylesheets) == 'string'){
20256 Roo.get(this.iframe.contentDocument.head).createChild({
20258 rel : 'stylesheet',
20267 Roo.each(stylesheets, function(s) {
20272 Roo.get(_this.iframe.contentDocument.head).createChild({
20274 rel : 'stylesheet',
20283 removeStylesheets : function()
20287 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20292 // hide stuff that is not compatible
20306 * @event specialkey
20310 * @cfg {String} fieldClass @hide
20313 * @cfg {String} focusClass @hide
20316 * @cfg {String} autoCreate @hide
20319 * @cfg {String} inputType @hide
20322 * @cfg {String} invalidClass @hide
20325 * @cfg {String} invalidText @hide
20328 * @cfg {String} msgFx @hide
20331 * @cfg {String} validateOnBlur @hide
20335 Roo.HtmlEditorCore.white = [
20336 'area', 'br', 'img', 'input', 'hr', 'wbr',
20338 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
20339 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
20340 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
20341 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
20342 'table', 'ul', 'xmp',
20344 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
20347 'dir', 'menu', 'ol', 'ul', 'dl',
20353 Roo.HtmlEditorCore.black = [
20354 // 'embed', 'object', // enable - backend responsiblity to clean thiese
20356 'base', 'basefont', 'bgsound', 'blink', 'body',
20357 'frame', 'frameset', 'head', 'html', 'ilayer',
20358 'iframe', 'layer', 'link', 'meta', 'object',
20359 'script', 'style' ,'title', 'xml' // clean later..
20361 Roo.HtmlEditorCore.clean = [
20362 'script', 'style', 'title', 'xml'
20364 Roo.HtmlEditorCore.remove = [
20369 Roo.HtmlEditorCore.ablack = [
20373 Roo.HtmlEditorCore.aclean = [
20374 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
20378 Roo.HtmlEditorCore.pwhite= [
20379 'http', 'https', 'mailto'
20382 // white listed style attributes.
20383 Roo.HtmlEditorCore.cwhite= [
20384 // 'text-align', /// default is to allow most things..
20390 // black listed style attributes.
20391 Roo.HtmlEditorCore.cblack= [
20392 // 'font-size' -- this can be set by the project
20396 Roo.HtmlEditorCore.swapCodes =[
20415 * @class Roo.bootstrap.HtmlEditor
20416 * @extends Roo.bootstrap.TextArea
20417 * Bootstrap HtmlEditor class
20420 * Create a new HtmlEditor
20421 * @param {Object} config The config object
20424 Roo.bootstrap.HtmlEditor = function(config){
20425 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20426 if (!this.toolbars) {
20427 this.toolbars = [];
20429 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20432 * @event initialize
20433 * Fires when the editor is fully initialized (including the iframe)
20434 * @param {HtmlEditor} this
20439 * Fires when the editor is first receives the focus. Any insertion must wait
20440 * until after this event.
20441 * @param {HtmlEditor} this
20445 * @event beforesync
20446 * Fires before the textarea is updated with content from the editor iframe. Return false
20447 * to cancel the sync.
20448 * @param {HtmlEditor} this
20449 * @param {String} html
20453 * @event beforepush
20454 * Fires before the iframe editor is updated with content from the textarea. Return false
20455 * to cancel the push.
20456 * @param {HtmlEditor} this
20457 * @param {String} html
20462 * Fires when the textarea is updated with content from the editor iframe.
20463 * @param {HtmlEditor} this
20464 * @param {String} html
20469 * Fires when the iframe editor is updated with content from the textarea.
20470 * @param {HtmlEditor} this
20471 * @param {String} html
20475 * @event editmodechange
20476 * Fires when the editor switches edit modes
20477 * @param {HtmlEditor} this
20478 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20480 editmodechange: true,
20482 * @event editorevent
20483 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20484 * @param {HtmlEditor} this
20488 * @event firstfocus
20489 * Fires when on first focus - needed by toolbars..
20490 * @param {HtmlEditor} this
20495 * Auto save the htmlEditor value as a file into Events
20496 * @param {HtmlEditor} this
20500 * @event savedpreview
20501 * preview the saved version of htmlEditor
20502 * @param {HtmlEditor} this
20509 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
20513 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20518 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20523 * @cfg {Number} height (in pixels)
20527 * @cfg {Number} width (in pixels)
20532 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20535 stylesheets: false,
20540 // private properties
20541 validationEvent : false,
20543 initialized : false,
20546 onFocus : Roo.emptyFn,
20548 hideMode:'offsets',
20551 tbContainer : false,
20553 toolbarContainer :function() {
20554 return this.wrap.select('.x-html-editor-tb',true).first();
20558 * Protected method that will not generally be called directly. It
20559 * is called when the editor creates its toolbar. Override this method if you need to
20560 * add custom toolbar buttons.
20561 * @param {HtmlEditor} editor
20563 createToolbar : function(){
20565 Roo.log("create toolbars");
20567 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20568 this.toolbars[0].render(this.toolbarContainer());
20572 // if (!editor.toolbars || !editor.toolbars.length) {
20573 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20576 // for (var i =0 ; i < editor.toolbars.length;i++) {
20577 // editor.toolbars[i] = Roo.factory(
20578 // typeof(editor.toolbars[i]) == 'string' ?
20579 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
20580 // Roo.bootstrap.HtmlEditor);
20581 // editor.toolbars[i].init(editor);
20587 onRender : function(ct, position)
20589 // Roo.log("Call onRender: " + this.xtype);
20591 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20593 this.wrap = this.inputEl().wrap({
20594 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20597 this.editorcore.onRender(ct, position);
20599 if (this.resizable) {
20600 this.resizeEl = new Roo.Resizable(this.wrap, {
20604 minHeight : this.height,
20605 height: this.height,
20606 handles : this.resizable,
20609 resize : function(r, w, h) {
20610 _t.onResize(w,h); // -something
20616 this.createToolbar(this);
20619 if(!this.width && this.resizable){
20620 this.setSize(this.wrap.getSize());
20622 if (this.resizeEl) {
20623 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20624 // should trigger onReize..
20630 onResize : function(w, h)
20632 Roo.log('resize: ' +w + ',' + h );
20633 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20637 if(this.inputEl() ){
20638 if(typeof w == 'number'){
20639 var aw = w - this.wrap.getFrameWidth('lr');
20640 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20643 if(typeof h == 'number'){
20644 var tbh = -11; // fixme it needs to tool bar size!
20645 for (var i =0; i < this.toolbars.length;i++) {
20646 // fixme - ask toolbars for heights?
20647 tbh += this.toolbars[i].el.getHeight();
20648 //if (this.toolbars[i].footer) {
20649 // tbh += this.toolbars[i].footer.el.getHeight();
20657 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20658 ah -= 5; // knock a few pixes off for look..
20659 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20663 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20664 this.editorcore.onResize(ew,eh);
20669 * Toggles the editor between standard and source edit mode.
20670 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20672 toggleSourceEdit : function(sourceEditMode)
20674 this.editorcore.toggleSourceEdit(sourceEditMode);
20676 if(this.editorcore.sourceEditMode){
20677 Roo.log('editor - showing textarea');
20680 // Roo.log(this.syncValue());
20682 this.inputEl().removeClass(['hide', 'x-hidden']);
20683 this.inputEl().dom.removeAttribute('tabIndex');
20684 this.inputEl().focus();
20686 Roo.log('editor - hiding textarea');
20688 // Roo.log(this.pushValue());
20691 this.inputEl().addClass(['hide', 'x-hidden']);
20692 this.inputEl().dom.setAttribute('tabIndex', -1);
20693 //this.deferFocus();
20696 if(this.resizable){
20697 this.setSize(this.wrap.getSize());
20700 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20703 // private (for BoxComponent)
20704 adjustSize : Roo.BoxComponent.prototype.adjustSize,
20706 // private (for BoxComponent)
20707 getResizeEl : function(){
20711 // private (for BoxComponent)
20712 getPositionEl : function(){
20717 initEvents : function(){
20718 this.originalValue = this.getValue();
20722 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20725 // markInvalid : Roo.emptyFn,
20727 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20730 // clearInvalid : Roo.emptyFn,
20732 setValue : function(v){
20733 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20734 this.editorcore.pushValue();
20739 deferFocus : function(){
20740 this.focus.defer(10, this);
20744 focus : function(){
20745 this.editorcore.focus();
20751 onDestroy : function(){
20757 for (var i =0; i < this.toolbars.length;i++) {
20758 // fixme - ask toolbars for heights?
20759 this.toolbars[i].onDestroy();
20762 this.wrap.dom.innerHTML = '';
20763 this.wrap.remove();
20768 onFirstFocus : function(){
20769 //Roo.log("onFirstFocus");
20770 this.editorcore.onFirstFocus();
20771 for (var i =0; i < this.toolbars.length;i++) {
20772 this.toolbars[i].onFirstFocus();
20778 syncValue : function()
20780 this.editorcore.syncValue();
20783 pushValue : function()
20785 this.editorcore.pushValue();
20789 // hide stuff that is not compatible
20803 * @event specialkey
20807 * @cfg {String} fieldClass @hide
20810 * @cfg {String} focusClass @hide
20813 * @cfg {String} autoCreate @hide
20816 * @cfg {String} inputType @hide
20819 * @cfg {String} invalidClass @hide
20822 * @cfg {String} invalidText @hide
20825 * @cfg {String} msgFx @hide
20828 * @cfg {String} validateOnBlur @hide
20837 Roo.namespace('Roo.bootstrap.htmleditor');
20839 * @class Roo.bootstrap.HtmlEditorToolbar1
20844 new Roo.bootstrap.HtmlEditor({
20847 new Roo.bootstrap.HtmlEditorToolbar1({
20848 disable : { fonts: 1 , format: 1, ..., ... , ...],
20854 * @cfg {Object} disable List of elements to disable..
20855 * @cfg {Array} btns List of additional buttons.
20859 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
20862 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
20865 Roo.apply(this, config);
20867 // default disabled, based on 'good practice'..
20868 this.disable = this.disable || {};
20869 Roo.applyIf(this.disable, {
20872 specialElements : true
20874 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
20876 this.editor = config.editor;
20877 this.editorcore = config.editor.editorcore;
20879 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
20881 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
20882 // dont call parent... till later.
20884 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
20889 editorcore : false,
20894 "h1","h2","h3","h4","h5","h6",
20896 "abbr", "acronym", "address", "cite", "samp", "var",
20900 onRender : function(ct, position)
20902 // Roo.log("Call onRender: " + this.xtype);
20904 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
20906 this.el.dom.style.marginBottom = '0';
20908 var editorcore = this.editorcore;
20909 var editor= this.editor;
20912 var btn = function(id,cmd , toggle, handler){
20914 var event = toggle ? 'toggle' : 'click';
20919 xns: Roo.bootstrap,
20922 enableToggle:toggle !== false,
20924 pressed : toggle ? false : null,
20927 a.listeners[toggle ? 'toggle' : 'click'] = function() {
20928 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
20937 xns: Roo.bootstrap,
20938 glyphicon : 'font',
20942 xns: Roo.bootstrap,
20946 Roo.each(this.formats, function(f) {
20947 style.menu.items.push({
20949 xns: Roo.bootstrap,
20950 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
20955 editorcore.insertTag(this.tagname);
20962 children.push(style);
20965 btn('bold',false,true);
20966 btn('italic',false,true);
20967 btn('align-left', 'justifyleft',true);
20968 btn('align-center', 'justifycenter',true);
20969 btn('align-right' , 'justifyright',true);
20970 btn('link', false, false, function(btn) {
20971 //Roo.log("create link?");
20972 var url = prompt(this.createLinkText, this.defaultLinkValue);
20973 if(url && url != 'http:/'+'/'){
20974 this.editorcore.relayCmd('createlink', url);
20977 btn('list','insertunorderedlist',true);
20978 btn('pencil', false,true, function(btn){
20981 this.toggleSourceEdit(btn.pressed);
20987 xns: Roo.bootstrap,
20992 xns: Roo.bootstrap,
20997 cog.menu.items.push({
20999 xns: Roo.bootstrap,
21000 html : Clean styles,
21005 editorcore.insertTag(this.tagname);
21014 this.xtype = 'NavSimplebar';
21016 for(var i=0;i< children.length;i++) {
21018 this.buttons.add(this.addxtypeChild(children[i]));
21022 editor.on('editorevent', this.updateToolbar, this);
21024 onBtnClick : function(id)
21026 this.editorcore.relayCmd(id);
21027 this.editorcore.focus();
21031 * Protected method that will not generally be called directly. It triggers
21032 * a toolbar update by reading the markup state of the current selection in the editor.
21034 updateToolbar: function(){
21036 if(!this.editorcore.activated){
21037 this.editor.onFirstFocus(); // is this neeed?
21041 var btns = this.buttons;
21042 var doc = this.editorcore.doc;
21043 btns.get('bold').setActive(doc.queryCommandState('bold'));
21044 btns.get('italic').setActive(doc.queryCommandState('italic'));
21045 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21047 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21048 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21049 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21051 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21052 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21055 var ans = this.editorcore.getAllAncestors();
21056 if (this.formatCombo) {
21059 var store = this.formatCombo.store;
21060 this.formatCombo.setValue("");
21061 for (var i =0; i < ans.length;i++) {
21062 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21064 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21072 // hides menus... - so this cant be on a menu...
21073 Roo.bootstrap.MenuMgr.hideAll();
21075 Roo.bootstrap.MenuMgr.hideAll();
21076 //this.editorsyncValue();
21078 onFirstFocus: function() {
21079 this.buttons.each(function(item){
21083 toggleSourceEdit : function(sourceEditMode){
21086 if(sourceEditMode){
21087 Roo.log("disabling buttons");
21088 this.buttons.each( function(item){
21089 if(item.cmd != 'pencil'){
21095 Roo.log("enabling buttons");
21096 if(this.editorcore.initialized){
21097 this.buttons.each( function(item){
21103 Roo.log("calling toggole on editor");
21104 // tell the editor that it's been pressed..
21105 this.editor.toggleSourceEdit(sourceEditMode);
21115 * @class Roo.bootstrap.Table.AbstractSelectionModel
21116 * @extends Roo.util.Observable
21117 * Abstract base class for grid SelectionModels. It provides the interface that should be
21118 * implemented by descendant classes. This class should not be directly instantiated.
21121 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21122 this.locked = false;
21123 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21127 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21128 /** @ignore Called by the grid automatically. Do not call directly. */
21129 init : function(grid){
21135 * Locks the selections.
21138 this.locked = true;
21142 * Unlocks the selections.
21144 unlock : function(){
21145 this.locked = false;
21149 * Returns true if the selections are locked.
21150 * @return {Boolean}
21152 isLocked : function(){
21153 return this.locked;
21157 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21158 * @class Roo.bootstrap.Table.RowSelectionModel
21159 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21160 * It supports multiple selections and keyboard selection/navigation.
21162 * @param {Object} config
21165 Roo.bootstrap.Table.RowSelectionModel = function(config){
21166 Roo.apply(this, config);
21167 this.selections = new Roo.util.MixedCollection(false, function(o){
21172 this.lastActive = false;
21176 * @event selectionchange
21177 * Fires when the selection changes
21178 * @param {SelectionModel} this
21180 "selectionchange" : true,
21182 * @event afterselectionchange
21183 * Fires after the selection changes (eg. by key press or clicking)
21184 * @param {SelectionModel} this
21186 "afterselectionchange" : true,
21188 * @event beforerowselect
21189 * Fires when a row is selected being selected, return false to cancel.
21190 * @param {SelectionModel} this
21191 * @param {Number} rowIndex The selected index
21192 * @param {Boolean} keepExisting False if other selections will be cleared
21194 "beforerowselect" : true,
21197 * Fires when a row is selected.
21198 * @param {SelectionModel} this
21199 * @param {Number} rowIndex The selected index
21200 * @param {Roo.data.Record} r The record
21202 "rowselect" : true,
21204 * @event rowdeselect
21205 * Fires when a row is deselected.
21206 * @param {SelectionModel} this
21207 * @param {Number} rowIndex The selected index
21209 "rowdeselect" : true
21211 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21212 this.locked = false;
21215 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21217 * @cfg {Boolean} singleSelect
21218 * True to allow selection of only one row at a time (defaults to false)
21220 singleSelect : false,
21223 initEvents : function(){
21225 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21226 this.grid.on("mousedown", this.handleMouseDown, this);
21227 }else{ // allow click to work like normal
21228 this.grid.on("rowclick", this.handleDragableRowClick, this);
21231 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21232 "up" : function(e){
21234 this.selectPrevious(e.shiftKey);
21235 }else if(this.last !== false && this.lastActive !== false){
21236 var last = this.last;
21237 this.selectRange(this.last, this.lastActive-1);
21238 this.grid.getView().focusRow(this.lastActive);
21239 if(last !== false){
21243 this.selectFirstRow();
21245 this.fireEvent("afterselectionchange", this);
21247 "down" : function(e){
21249 this.selectNext(e.shiftKey);
21250 }else if(this.last !== false && this.lastActive !== false){
21251 var last = this.last;
21252 this.selectRange(this.last, this.lastActive+1);
21253 this.grid.getView().focusRow(this.lastActive);
21254 if(last !== false){
21258 this.selectFirstRow();
21260 this.fireEvent("afterselectionchange", this);
21265 var view = this.grid.view;
21266 view.on("refresh", this.onRefresh, this);
21267 view.on("rowupdated", this.onRowUpdated, this);
21268 view.on("rowremoved", this.onRemove, this);
21272 onRefresh : function(){
21273 var ds = this.grid.dataSource, i, v = this.grid.view;
21274 var s = this.selections;
21275 s.each(function(r){
21276 if((i = ds.indexOfId(r.id)) != -1){
21285 onRemove : function(v, index, r){
21286 this.selections.remove(r);
21290 onRowUpdated : function(v, index, r){
21291 if(this.isSelected(r)){
21292 v.onRowSelect(index);
21298 * @param {Array} records The records to select
21299 * @param {Boolean} keepExisting (optional) True to keep existing selections
21301 selectRecords : function(records, keepExisting){
21303 this.clearSelections();
21305 var ds = this.grid.dataSource;
21306 for(var i = 0, len = records.length; i < len; i++){
21307 this.selectRow(ds.indexOf(records[i]), true);
21312 * Gets the number of selected rows.
21315 getCount : function(){
21316 return this.selections.length;
21320 * Selects the first row in the grid.
21322 selectFirstRow : function(){
21327 * Select the last row.
21328 * @param {Boolean} keepExisting (optional) True to keep existing selections
21330 selectLastRow : function(keepExisting){
21331 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21335 * Selects the row immediately following the last selected row.
21336 * @param {Boolean} keepExisting (optional) True to keep existing selections
21338 selectNext : function(keepExisting){
21339 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21340 this.selectRow(this.last+1, keepExisting);
21341 this.grid.getView().focusRow(this.last);
21346 * Selects the row that precedes the last selected row.
21347 * @param {Boolean} keepExisting (optional) True to keep existing selections
21349 selectPrevious : function(keepExisting){
21351 this.selectRow(this.last-1, keepExisting);
21352 this.grid.getView().focusRow(this.last);
21357 * Returns the selected records
21358 * @return {Array} Array of selected records
21360 getSelections : function(){
21361 return [].concat(this.selections.items);
21365 * Returns the first selected record.
21368 getSelected : function(){
21369 return this.selections.itemAt(0);
21374 * Clears all selections.
21376 clearSelections : function(fast){
21377 if(this.locked) return;
21379 var ds = this.grid.dataSource;
21380 var s = this.selections;
21381 s.each(function(r){
21382 this.deselectRow(ds.indexOfId(r.id));
21386 this.selections.clear();
21393 * Selects all rows.
21395 selectAll : function(){
21396 if(this.locked) return;
21397 this.selections.clear();
21398 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21399 this.selectRow(i, true);
21404 * Returns True if there is a selection.
21405 * @return {Boolean}
21407 hasSelection : function(){
21408 return this.selections.length > 0;
21412 * Returns True if the specified row is selected.
21413 * @param {Number/Record} record The record or index of the record to check
21414 * @return {Boolean}
21416 isSelected : function(index){
21417 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21418 return (r && this.selections.key(r.id) ? true : false);
21422 * Returns True if the specified record id is selected.
21423 * @param {String} id The id of record to check
21424 * @return {Boolean}
21426 isIdSelected : function(id){
21427 return (this.selections.key(id) ? true : false);
21431 handleMouseDown : function(e, t){
21432 var view = this.grid.getView(), rowIndex;
21433 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21436 if(e.shiftKey && this.last !== false){
21437 var last = this.last;
21438 this.selectRange(last, rowIndex, e.ctrlKey);
21439 this.last = last; // reset the last
21440 view.focusRow(rowIndex);
21442 var isSelected = this.isSelected(rowIndex);
21443 if(e.button !== 0 && isSelected){
21444 view.focusRow(rowIndex);
21445 }else if(e.ctrlKey && isSelected){
21446 this.deselectRow(rowIndex);
21447 }else if(!isSelected){
21448 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21449 view.focusRow(rowIndex);
21452 this.fireEvent("afterselectionchange", this);
21455 handleDragableRowClick : function(grid, rowIndex, e)
21457 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21458 this.selectRow(rowIndex, false);
21459 grid.view.focusRow(rowIndex);
21460 this.fireEvent("afterselectionchange", this);
21465 * Selects multiple rows.
21466 * @param {Array} rows Array of the indexes of the row to select
21467 * @param {Boolean} keepExisting (optional) True to keep existing selections
21469 selectRows : function(rows, keepExisting){
21471 this.clearSelections();
21473 for(var i = 0, len = rows.length; i < len; i++){
21474 this.selectRow(rows[i], true);
21479 * Selects a range of rows. All rows in between startRow and endRow are also selected.
21480 * @param {Number} startRow The index of the first row in the range
21481 * @param {Number} endRow The index of the last row in the range
21482 * @param {Boolean} keepExisting (optional) True to retain existing selections
21484 selectRange : function(startRow, endRow, keepExisting){
21485 if(this.locked) return;
21487 this.clearSelections();
21489 if(startRow <= endRow){
21490 for(var i = startRow; i <= endRow; i++){
21491 this.selectRow(i, true);
21494 for(var i = startRow; i >= endRow; i--){
21495 this.selectRow(i, true);
21501 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21502 * @param {Number} startRow The index of the first row in the range
21503 * @param {Number} endRow The index of the last row in the range
21505 deselectRange : function(startRow, endRow, preventViewNotify){
21506 if(this.locked) return;
21507 for(var i = startRow; i <= endRow; i++){
21508 this.deselectRow(i, preventViewNotify);
21514 * @param {Number} row The index of the row to select
21515 * @param {Boolean} keepExisting (optional) True to keep existing selections
21517 selectRow : function(index, keepExisting, preventViewNotify){
21518 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21519 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21520 if(!keepExisting || this.singleSelect){
21521 this.clearSelections();
21523 var r = this.grid.dataSource.getAt(index);
21524 this.selections.add(r);
21525 this.last = this.lastActive = index;
21526 if(!preventViewNotify){
21527 this.grid.getView().onRowSelect(index);
21529 this.fireEvent("rowselect", this, index, r);
21530 this.fireEvent("selectionchange", this);
21536 * @param {Number} row The index of the row to deselect
21538 deselectRow : function(index, preventViewNotify){
21539 if(this.locked) return;
21540 if(this.last == index){
21543 if(this.lastActive == index){
21544 this.lastActive = false;
21546 var r = this.grid.dataSource.getAt(index);
21547 this.selections.remove(r);
21548 if(!preventViewNotify){
21549 this.grid.getView().onRowDeselect(index);
21551 this.fireEvent("rowdeselect", this, index);
21552 this.fireEvent("selectionchange", this);
21556 restoreLast : function(){
21558 this.last = this._last;
21563 acceptsNav : function(row, col, cm){
21564 return !cm.isHidden(col) && cm.isCellEditable(col, row);
21568 onEditorKey : function(field, e){
21569 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21574 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21576 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21578 }else if(k == e.ENTER && !e.ctrlKey){
21582 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21584 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21586 }else if(k == e.ESC){
21590 g.startEditing(newCell[0], newCell[1]);
21595 * Ext JS Library 1.1.1
21596 * Copyright(c) 2006-2007, Ext JS, LLC.
21598 * Originally Released Under LGPL - original licence link has changed is not relivant.
21601 * <script type="text/javascript">
21605 * @class Roo.bootstrap.PagingToolbar
21607 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21609 * Create a new PagingToolbar
21610 * @param {Object} config The config object
21612 Roo.bootstrap.PagingToolbar = function(config)
21614 // old args format still supported... - xtype is prefered..
21615 // created from xtype...
21616 var ds = config.dataSource;
21617 this.toolbarItems = [];
21618 if (config.items) {
21619 this.toolbarItems = config.items;
21620 // config.items = [];
21623 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21630 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21634 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21636 * @cfg {Roo.data.Store} dataSource
21637 * The underlying data store providing the paged data
21640 * @cfg {String/HTMLElement/Element} container
21641 * container The id or element that will contain the toolbar
21644 * @cfg {Boolean} displayInfo
21645 * True to display the displayMsg (defaults to false)
21648 * @cfg {Number} pageSize
21649 * The number of records to display per page (defaults to 20)
21653 * @cfg {String} displayMsg
21654 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21656 displayMsg : 'Displaying {0} - {1} of {2}',
21658 * @cfg {String} emptyMsg
21659 * The message to display when no records are found (defaults to "No data to display")
21661 emptyMsg : 'No data to display',
21663 * Customizable piece of the default paging text (defaults to "Page")
21666 beforePageText : "Page",
21668 * Customizable piece of the default paging text (defaults to "of %0")
21671 afterPageText : "of {0}",
21673 * Customizable piece of the default paging text (defaults to "First Page")
21676 firstText : "First Page",
21678 * Customizable piece of the default paging text (defaults to "Previous Page")
21681 prevText : "Previous Page",
21683 * Customizable piece of the default paging text (defaults to "Next Page")
21686 nextText : "Next Page",
21688 * Customizable piece of the default paging text (defaults to "Last Page")
21691 lastText : "Last Page",
21693 * Customizable piece of the default paging text (defaults to "Refresh")
21696 refreshText : "Refresh",
21700 onRender : function(ct, position)
21702 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21703 this.navgroup.parentId = this.id;
21704 this.navgroup.onRender(this.el, null);
21705 // add the buttons to the navgroup
21707 if(this.displayInfo){
21708 Roo.log(this.el.select('ul.navbar-nav',true).first());
21709 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21710 this.displayEl = this.el.select('.x-paging-info', true).first();
21711 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21712 // this.displayEl = navel.el.select('span',true).first();
21718 Roo.each(_this.buttons, function(e){
21719 Roo.factory(e).onRender(_this.el, null);
21723 Roo.each(_this.toolbarItems, function(e) {
21724 _this.navgroup.addItem(e);
21728 this.first = this.navgroup.addItem({
21729 tooltip: this.firstText,
21731 icon : 'fa fa-backward',
21733 preventDefault: true,
21734 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21737 this.prev = this.navgroup.addItem({
21738 tooltip: this.prevText,
21740 icon : 'fa fa-step-backward',
21742 preventDefault: true,
21743 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
21745 //this.addSeparator();
21748 var field = this.navgroup.addItem( {
21750 cls : 'x-paging-position',
21752 html : this.beforePageText +
21753 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
21754 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
21757 this.field = field.el.select('input', true).first();
21758 this.field.on("keydown", this.onPagingKeydown, this);
21759 this.field.on("focus", function(){this.dom.select();});
21762 this.afterTextEl = field.el.select('.x-paging-after',true).first();
21763 //this.field.setHeight(18);
21764 //this.addSeparator();
21765 this.next = this.navgroup.addItem({
21766 tooltip: this.nextText,
21768 html : ' <i class="fa fa-step-forward">',
21770 preventDefault: true,
21771 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
21773 this.last = this.navgroup.addItem({
21774 tooltip: this.lastText,
21775 icon : 'fa fa-forward',
21778 preventDefault: true,
21779 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
21781 //this.addSeparator();
21782 this.loading = this.navgroup.addItem({
21783 tooltip: this.refreshText,
21784 icon: 'fa fa-refresh',
21785 preventDefault: true,
21786 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
21792 updateInfo : function(){
21793 if(this.displayEl){
21794 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
21795 var msg = count == 0 ?
21799 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
21801 this.displayEl.update(msg);
21806 onLoad : function(ds, r, o){
21807 this.cursor = o.params ? o.params.start : 0;
21808 var d = this.getPageData(),
21812 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
21813 this.field.dom.value = ap;
21814 this.first.setDisabled(ap == 1);
21815 this.prev.setDisabled(ap == 1);
21816 this.next.setDisabled(ap == ps);
21817 this.last.setDisabled(ap == ps);
21818 this.loading.enable();
21823 getPageData : function(){
21824 var total = this.ds.getTotalCount();
21827 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
21828 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
21833 onLoadError : function(){
21834 this.loading.enable();
21838 onPagingKeydown : function(e){
21839 var k = e.getKey();
21840 var d = this.getPageData();
21842 var v = this.field.dom.value, pageNum;
21843 if(!v || isNaN(pageNum = parseInt(v, 10))){
21844 this.field.dom.value = d.activePage;
21847 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
21848 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21851 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))
21853 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
21854 this.field.dom.value = pageNum;
21855 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
21858 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21860 var v = this.field.dom.value, pageNum;
21861 var increment = (e.shiftKey) ? 10 : 1;
21862 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21864 if(!v || isNaN(pageNum = parseInt(v, 10))) {
21865 this.field.dom.value = d.activePage;
21868 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
21870 this.field.dom.value = parseInt(v, 10) + increment;
21871 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
21872 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21879 beforeLoad : function(){
21881 this.loading.disable();
21886 onClick : function(which){
21895 ds.load({params:{start: 0, limit: this.pageSize}});
21898 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
21901 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
21904 var total = ds.getTotalCount();
21905 var extra = total % this.pageSize;
21906 var lastStart = extra ? (total - extra) : total-this.pageSize;
21907 ds.load({params:{start: lastStart, limit: this.pageSize}});
21910 ds.load({params:{start: this.cursor, limit: this.pageSize}});
21916 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
21917 * @param {Roo.data.Store} store The data store to unbind
21919 unbind : function(ds){
21920 ds.un("beforeload", this.beforeLoad, this);
21921 ds.un("load", this.onLoad, this);
21922 ds.un("loadexception", this.onLoadError, this);
21923 ds.un("remove", this.updateInfo, this);
21924 ds.un("add", this.updateInfo, this);
21925 this.ds = undefined;
21929 * Binds the paging toolbar to the specified {@link Roo.data.Store}
21930 * @param {Roo.data.Store} store The data store to bind
21932 bind : function(ds){
21933 ds.on("beforeload", this.beforeLoad, this);
21934 ds.on("load", this.onLoad, this);
21935 ds.on("loadexception", this.onLoadError, this);
21936 ds.on("remove", this.updateInfo, this);
21937 ds.on("add", this.updateInfo, this);
21948 * @class Roo.bootstrap.MessageBar
21949 * @extends Roo.bootstrap.Component
21950 * Bootstrap MessageBar class
21951 * @cfg {String} html contents of the MessageBar
21952 * @cfg {String} weight (info | success | warning | danger) default info
21953 * @cfg {String} beforeClass insert the bar before the given class
21954 * @cfg {Boolean} closable (true | false) default false
21955 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
21958 * Create a new Element
21959 * @param {Object} config The config object
21962 Roo.bootstrap.MessageBar = function(config){
21963 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
21966 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
21972 beforeClass: 'bootstrap-sticky-wrap',
21974 getAutoCreate : function(){
21978 cls: 'alert alert-dismissable alert-' + this.weight,
21983 html: this.html || ''
21989 cfg.cls += ' alert-messages-fixed';
22003 onRender : function(ct, position)
22005 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22008 var cfg = Roo.apply({}, this.getAutoCreate());
22012 cfg.cls += ' ' + this.cls;
22015 cfg.style = this.style;
22017 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22019 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22022 this.el.select('>button.close').on('click', this.hide, this);
22028 if (!this.rendered) {
22034 this.fireEvent('show', this);
22040 if (!this.rendered) {
22046 this.fireEvent('hide', this);
22049 update : function()
22051 // var e = this.el.dom.firstChild;
22053 // if(this.closable){
22054 // e = e.nextSibling;
22057 // e.data = this.html || '';
22059 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22075 * @class Roo.bootstrap.Graph
22076 * @extends Roo.bootstrap.Component
22077 * Bootstrap Graph class
22081 @cfg {String} graphtype bar | vbar | pie
22082 @cfg {number} g_x coodinator | centre x (pie)
22083 @cfg {number} g_y coodinator | centre y (pie)
22084 @cfg {number} g_r radius (pie)
22085 @cfg {number} g_height height of the chart (respected by all elements in the set)
22086 @cfg {number} g_width width of the chart (respected by all elements in the set)
22087 @cfg {Object} title The title of the chart
22090 -opts (object) options for the chart
22092 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22093 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22095 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.
22096 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22098 o stretch (boolean)
22100 -opts (object) options for the pie
22103 o startAngle (number)
22104 o endAngle (number)
22108 * Create a new Input
22109 * @param {Object} config The config object
22112 Roo.bootstrap.Graph = function(config){
22113 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22119 * The img click event for the img.
22120 * @param {Roo.EventObject} e
22126 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22137 //g_colors: this.colors,
22144 getAutoCreate : function(){
22155 onRender : function(ct,position){
22156 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22157 this.raphael = Raphael(this.el.dom);
22159 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22160 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22161 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22162 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22164 r.text(160, 10, "Single Series Chart").attr(txtattr);
22165 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22166 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22167 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22169 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22170 r.barchart(330, 10, 300, 220, data1);
22171 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22172 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22175 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22176 // r.barchart(30, 30, 560, 250, xdata, {
22177 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22178 // axis : "0 0 1 1",
22179 // axisxlabels : xdata
22180 // //yvalues : cols,
22183 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22185 // this.load(null,xdata,{
22186 // axis : "0 0 1 1",
22187 // axisxlabels : xdata
22192 load : function(graphtype,xdata,opts){
22193 this.raphael.clear();
22195 graphtype = this.graphtype;
22200 var r = this.raphael,
22201 fin = function () {
22202 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22204 fout = function () {
22205 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22207 pfin = function() {
22208 this.sector.stop();
22209 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22212 this.label[0].stop();
22213 this.label[0].attr({ r: 7.5 });
22214 this.label[1].attr({ "font-weight": 800 });
22217 pfout = function() {
22218 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22221 this.label[0].animate({ r: 5 }, 500, "bounce");
22222 this.label[1].attr({ "font-weight": 400 });
22228 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22231 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22234 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
22235 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22237 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22244 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22249 setTitle: function(o)
22254 initEvents: function() {
22257 this.el.on('click', this.onClick, this);
22261 onClick : function(e)
22263 Roo.log('img onclick');
22264 this.fireEvent('click', this, e);
22276 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22279 * @class Roo.bootstrap.dash.NumberBox
22280 * @extends Roo.bootstrap.Component
22281 * Bootstrap NumberBox class
22282 * @cfg {String} headline Box headline
22283 * @cfg {String} content Box content
22284 * @cfg {String} icon Box icon
22285 * @cfg {String} footer Footer text
22286 * @cfg {String} fhref Footer href
22289 * Create a new NumberBox
22290 * @param {Object} config The config object
22294 Roo.bootstrap.dash.NumberBox = function(config){
22295 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22299 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
22308 getAutoCreate : function(){
22312 cls : 'small-box ',
22320 cls : 'roo-headline',
22321 html : this.headline
22325 cls : 'roo-content',
22326 html : this.content
22340 cls : 'ion ' + this.icon
22349 cls : 'small-box-footer',
22350 href : this.fhref || '#',
22354 cfg.cn.push(footer);
22361 onRender : function(ct,position){
22362 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22369 setHeadline: function (value)
22371 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22374 setFooter: function (value, href)
22376 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22379 this.el.select('a.small-box-footer',true).first().attr('href', href);
22384 setContent: function (value)
22386 this.el.select('.roo-content',true).first().dom.innerHTML = value;
22389 initEvents: function()
22403 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22406 * @class Roo.bootstrap.dash.TabBox
22407 * @extends Roo.bootstrap.Component
22408 * Bootstrap TabBox class
22409 * @cfg {String} title Title of the TabBox
22410 * @cfg {String} icon Icon of the TabBox
22411 * @cfg {Boolean} showtabs (true|false) show the tabs default true
22412 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22415 * Create a new TabBox
22416 * @param {Object} config The config object
22420 Roo.bootstrap.dash.TabBox = function(config){
22421 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22426 * When a pane is added
22427 * @param {Roo.bootstrap.dash.TabPane} pane
22431 * @event activatepane
22432 * When a pane is activated
22433 * @param {Roo.bootstrap.dash.TabPane} pane
22435 "activatepane" : true
22443 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
22448 tabScrollable : false,
22450 getChildContainer : function()
22452 return this.el.select('.tab-content', true).first();
22455 getAutoCreate : function(){
22459 cls: 'pull-left header',
22467 cls: 'fa ' + this.icon
22473 cls: 'nav nav-tabs pull-right',
22479 if(this.tabScrollable){
22486 cls: 'nav nav-tabs pull-right',
22497 cls: 'nav-tabs-custom',
22502 cls: 'tab-content no-padding',
22510 initEvents : function()
22512 //Roo.log('add add pane handler');
22513 this.on('addpane', this.onAddPane, this);
22516 * Updates the box title
22517 * @param {String} html to set the title to.
22519 setTitle : function(value)
22521 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22523 onAddPane : function(pane)
22525 this.panes.push(pane);
22526 //Roo.log('addpane');
22528 // tabs are rendere left to right..
22529 if(!this.showtabs){
22533 var ctr = this.el.select('.nav-tabs', true).first();
22536 var existing = ctr.select('.nav-tab',true);
22537 var qty = existing.getCount();;
22540 var tab = ctr.createChild({
22542 cls : 'nav-tab' + (qty ? '' : ' active'),
22550 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22553 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22555 pane.el.addClass('active');
22560 onTabClick : function(ev,un,ob,pane)
22562 //Roo.log('tab - prev default');
22563 ev.preventDefault();
22566 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22567 pane.tab.addClass('active');
22568 //Roo.log(pane.title);
22569 this.getChildContainer().select('.tab-pane',true).removeClass('active');
22570 // technically we should have a deactivate event.. but maybe add later.
22571 // and it should not de-activate the selected tab...
22572 this.fireEvent('activatepane', pane);
22573 pane.el.addClass('active');
22574 pane.fireEvent('activate');
22579 getActivePane : function()
22582 Roo.each(this.panes, function(p) {
22583 if(p.el.hasClass('active')){
22604 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22606 * @class Roo.bootstrap.TabPane
22607 * @extends Roo.bootstrap.Component
22608 * Bootstrap TabPane class
22609 * @cfg {Boolean} active (false | true) Default false
22610 * @cfg {String} title title of panel
22614 * Create a new TabPane
22615 * @param {Object} config The config object
22618 Roo.bootstrap.dash.TabPane = function(config){
22619 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22625 * When a pane is activated
22626 * @param {Roo.bootstrap.dash.TabPane} pane
22633 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
22638 // the tabBox that this is attached to.
22641 getAutoCreate : function()
22649 cfg.cls += ' active';
22654 initEvents : function()
22656 //Roo.log('trigger add pane handler');
22657 this.parent().fireEvent('addpane', this)
22661 * Updates the tab title
22662 * @param {String} html to set the title to.
22664 setTitle: function(str)
22670 this.tab.select('a', true).first().dom.innerHTML = str;
22687 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22690 * @class Roo.bootstrap.menu.Menu
22691 * @extends Roo.bootstrap.Component
22692 * Bootstrap Menu class - container for Menu
22693 * @cfg {String} html Text of the menu
22694 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22695 * @cfg {String} icon Font awesome icon
22696 * @cfg {String} pos Menu align to (top | bottom) default bottom
22700 * Create a new Menu
22701 * @param {Object} config The config object
22705 Roo.bootstrap.menu.Menu = function(config){
22706 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22710 * @event beforeshow
22711 * Fires before this menu is displayed
22712 * @param {Roo.bootstrap.menu.Menu} this
22716 * @event beforehide
22717 * Fires before this menu is hidden
22718 * @param {Roo.bootstrap.menu.Menu} this
22723 * Fires after this menu is displayed
22724 * @param {Roo.bootstrap.menu.Menu} this
22729 * Fires after this menu is hidden
22730 * @param {Roo.bootstrap.menu.Menu} this
22735 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22736 * @param {Roo.bootstrap.menu.Menu} this
22737 * @param {Roo.EventObject} e
22744 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
22748 weight : 'default',
22753 getChildContainer : function() {
22754 if(this.isSubMenu){
22758 return this.el.select('ul.dropdown-menu', true).first();
22761 getAutoCreate : function()
22766 cls : 'roo-menu-text',
22774 cls : 'fa ' + this.icon
22785 cls : 'dropdown-button btn btn-' + this.weight,
22790 cls : 'dropdown-toggle btn btn-' + this.weight,
22800 cls : 'dropdown-menu'
22806 if(this.pos == 'top'){
22807 cfg.cls += ' dropup';
22810 if(this.isSubMenu){
22813 cls : 'dropdown-menu'
22820 onRender : function(ct, position)
22822 this.isSubMenu = ct.hasClass('dropdown-submenu');
22824 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
22827 initEvents : function()
22829 if(this.isSubMenu){
22833 this.hidden = true;
22835 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
22836 this.triggerEl.on('click', this.onTriggerPress, this);
22838 this.buttonEl = this.el.select('button.dropdown-button', true).first();
22839 this.buttonEl.on('click', this.onClick, this);
22845 if(this.isSubMenu){
22849 return this.el.select('ul.dropdown-menu', true).first();
22852 onClick : function(e)
22854 this.fireEvent("click", this, e);
22857 onTriggerPress : function(e)
22859 if (this.isVisible()) {
22866 isVisible : function(){
22867 return !this.hidden;
22872 this.fireEvent("beforeshow", this);
22874 this.hidden = false;
22875 this.el.addClass('open');
22877 Roo.get(document).on("mouseup", this.onMouseUp, this);
22879 this.fireEvent("show", this);
22886 this.fireEvent("beforehide", this);
22888 this.hidden = true;
22889 this.el.removeClass('open');
22891 Roo.get(document).un("mouseup", this.onMouseUp);
22893 this.fireEvent("hide", this);
22896 onMouseUp : function()
22910 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22913 * @class Roo.bootstrap.menu.Item
22914 * @extends Roo.bootstrap.Component
22915 * Bootstrap MenuItem class
22916 * @cfg {Boolean} submenu (true | false) default false
22917 * @cfg {String} html text of the item
22918 * @cfg {String} href the link
22919 * @cfg {Boolean} disable (true | false) default false
22920 * @cfg {Boolean} preventDefault (true | false) default true
22921 * @cfg {String} icon Font awesome icon
22922 * @cfg {String} pos Submenu align to (left | right) default right
22926 * Create a new Item
22927 * @param {Object} config The config object
22931 Roo.bootstrap.menu.Item = function(config){
22932 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
22936 * Fires when the mouse is hovering over this menu
22937 * @param {Roo.bootstrap.menu.Item} this
22938 * @param {Roo.EventObject} e
22943 * Fires when the mouse exits this menu
22944 * @param {Roo.bootstrap.menu.Item} this
22945 * @param {Roo.EventObject} e
22951 * The raw click event for the entire grid.
22952 * @param {Roo.EventObject} e
22958 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
22963 preventDefault: true,
22968 getAutoCreate : function()
22973 cls : 'roo-menu-item-text',
22981 cls : 'fa ' + this.icon
22990 href : this.href || '#',
22997 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23001 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23003 if(this.pos == 'left'){
23004 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23011 initEvents : function()
23013 this.el.on('mouseover', this.onMouseOver, this);
23014 this.el.on('mouseout', this.onMouseOut, this);
23016 this.el.select('a', true).first().on('click', this.onClick, this);
23020 onClick : function(e)
23022 if(this.preventDefault){
23023 e.preventDefault();
23026 this.fireEvent("click", this, e);
23029 onMouseOver : function(e)
23031 if(this.submenu && this.pos == 'left'){
23032 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23035 this.fireEvent("mouseover", this, e);
23038 onMouseOut : function(e)
23040 this.fireEvent("mouseout", this, e);
23052 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23055 * @class Roo.bootstrap.menu.Separator
23056 * @extends Roo.bootstrap.Component
23057 * Bootstrap Separator class
23060 * Create a new Separator
23061 * @param {Object} config The config object
23065 Roo.bootstrap.menu.Separator = function(config){
23066 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23069 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23071 getAutoCreate : function(){
23092 * @class Roo.bootstrap.Tooltip
23093 * Bootstrap Tooltip class
23094 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23095 * to determine which dom element triggers the tooltip.
23097 * It needs to add support for additional attributes like tooltip-position
23100 * Create a new Toolti
23101 * @param {Object} config The config object
23104 Roo.bootstrap.Tooltip = function(config){
23105 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23108 Roo.apply(Roo.bootstrap.Tooltip, {
23110 * @function init initialize tooltip monitoring.
23114 currentTip : false,
23115 currentRegion : false,
23121 Roo.get(document).on('mouseover', this.enter ,this);
23122 Roo.get(document).on('mouseout', this.leave, this);
23125 this.currentTip = new Roo.bootstrap.Tooltip();
23128 enter : function(ev)
23130 var dom = ev.getTarget();
23132 //Roo.log(['enter',dom]);
23133 var el = Roo.fly(dom);
23134 if (this.currentEl) {
23136 //Roo.log(this.currentEl);
23137 //Roo.log(this.currentEl.contains(dom));
23138 if (this.currentEl == el) {
23141 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23149 if (this.currentTip.el) {
23150 this.currentTip.el.hide(); // force hiding...
23155 // you can not look for children, as if el is the body.. then everythign is the child..
23156 if (!el.attr('tooltip')) { //
23157 if (!el.select("[tooltip]").elements.length) {
23160 // is the mouse over this child...?
23161 bindEl = el.select("[tooltip]").first();
23162 var xy = ev.getXY();
23163 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23164 //Roo.log("not in region.");
23167 //Roo.log("child element over..");
23170 this.currentEl = bindEl;
23171 this.currentTip.bind(bindEl);
23172 this.currentRegion = Roo.lib.Region.getRegion(dom);
23173 this.currentTip.enter();
23176 leave : function(ev)
23178 var dom = ev.getTarget();
23179 //Roo.log(['leave',dom]);
23180 if (!this.currentEl) {
23185 if (dom != this.currentEl.dom) {
23188 var xy = ev.getXY();
23189 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23192 // only activate leave if mouse cursor is outside... bounding box..
23197 if (this.currentTip) {
23198 this.currentTip.leave();
23200 //Roo.log('clear currentEl');
23201 this.currentEl = false;
23206 'left' : ['r-l', [-2,0], 'right'],
23207 'right' : ['l-r', [2,0], 'left'],
23208 'bottom' : ['t-b', [0,2], 'top'],
23209 'top' : [ 'b-t', [0,-2], 'bottom']
23215 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
23220 delay : null, // can be { show : 300 , hide: 500}
23224 hoverState : null, //???
23226 placement : 'bottom',
23228 getAutoCreate : function(){
23235 cls : 'tooltip-arrow'
23238 cls : 'tooltip-inner'
23245 bind : function(el)
23251 enter : function () {
23253 if (this.timeout != null) {
23254 clearTimeout(this.timeout);
23257 this.hoverState = 'in';
23258 //Roo.log("enter - show");
23259 if (!this.delay || !this.delay.show) {
23264 this.timeout = setTimeout(function () {
23265 if (_t.hoverState == 'in') {
23268 }, this.delay.show);
23272 clearTimeout(this.timeout);
23274 this.hoverState = 'out';
23275 if (!this.delay || !this.delay.hide) {
23281 this.timeout = setTimeout(function () {
23282 //Roo.log("leave - timeout");
23284 if (_t.hoverState == 'out') {
23286 Roo.bootstrap.Tooltip.currentEl = false;
23294 this.render(document.body);
23297 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23299 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23301 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23303 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23305 var placement = typeof this.placement == 'function' ?
23306 this.placement.call(this, this.el, on_el) :
23309 var autoToken = /\s?auto?\s?/i;
23310 var autoPlace = autoToken.test(placement);
23312 placement = placement.replace(autoToken, '') || 'top';
23316 //this.el.setXY([0,0]);
23318 //this.el.dom.style.display='block';
23319 this.el.addClass(placement);
23321 //this.el.appendTo(on_el);
23323 var p = this.getPosition();
23324 var box = this.el.getBox();
23329 var align = Roo.bootstrap.Tooltip.alignment[placement];
23330 this.el.alignTo(this.bindEl, align[0],align[1]);
23331 //var arrow = this.el.select('.arrow',true).first();
23332 //arrow.set(align[2],
23334 this.el.addClass('in fade');
23335 this.hoverState = null;
23337 if (this.el.hasClass('fade')) {
23348 //this.el.setXY([0,0]);
23349 this.el.removeClass('in');
23365 * @class Roo.bootstrap.LocationPicker
23366 * @extends Roo.bootstrap.Component
23367 * Bootstrap LocationPicker class
23368 * @cfg {Number} latitude Position when init default 0
23369 * @cfg {Number} longitude Position when init default 0
23370 * @cfg {Number} zoom default 15
23371 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23372 * @cfg {Boolean} mapTypeControl default false
23373 * @cfg {Boolean} disableDoubleClickZoom default false
23374 * @cfg {Boolean} scrollwheel default true
23375 * @cfg {Boolean} streetViewControl default false
23376 * @cfg {Number} radius default 0
23377 * @cfg {String} locationName
23378 * @cfg {Boolean} draggable default true
23379 * @cfg {Boolean} enableAutocomplete default false
23380 * @cfg {Boolean} enableReverseGeocode default true
23381 * @cfg {String} markerTitle
23384 * Create a new LocationPicker
23385 * @param {Object} config The config object
23389 Roo.bootstrap.LocationPicker = function(config){
23391 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23396 * Fires when the picker initialized.
23397 * @param {Roo.bootstrap.LocationPicker} this
23398 * @param {Google Location} location
23402 * @event positionchanged
23403 * Fires when the picker position changed.
23404 * @param {Roo.bootstrap.LocationPicker} this
23405 * @param {Google Location} location
23407 positionchanged : true,
23410 * Fires when the map resize.
23411 * @param {Roo.bootstrap.LocationPicker} this
23416 * Fires when the map show.
23417 * @param {Roo.bootstrap.LocationPicker} this
23422 * Fires when the map hide.
23423 * @param {Roo.bootstrap.LocationPicker} this
23428 * Fires when click the map.
23429 * @param {Roo.bootstrap.LocationPicker} this
23430 * @param {Map event} e
23434 * @event mapRightClick
23435 * Fires when right click the map.
23436 * @param {Roo.bootstrap.LocationPicker} this
23437 * @param {Map event} e
23439 mapRightClick : true,
23441 * @event markerClick
23442 * Fires when click the marker.
23443 * @param {Roo.bootstrap.LocationPicker} this
23444 * @param {Map event} e
23446 markerClick : true,
23448 * @event markerRightClick
23449 * Fires when right click the marker.
23450 * @param {Roo.bootstrap.LocationPicker} this
23451 * @param {Map event} e
23453 markerRightClick : true,
23455 * @event OverlayViewDraw
23456 * Fires when OverlayView Draw
23457 * @param {Roo.bootstrap.LocationPicker} this
23459 OverlayViewDraw : true,
23461 * @event OverlayViewOnAdd
23462 * Fires when OverlayView Draw
23463 * @param {Roo.bootstrap.LocationPicker} this
23465 OverlayViewOnAdd : true,
23467 * @event OverlayViewOnRemove
23468 * Fires when OverlayView Draw
23469 * @param {Roo.bootstrap.LocationPicker} this
23471 OverlayViewOnRemove : true,
23473 * @event OverlayViewShow
23474 * Fires when OverlayView Draw
23475 * @param {Roo.bootstrap.LocationPicker} this
23476 * @param {Pixel} cpx
23478 OverlayViewShow : true,
23480 * @event OverlayViewHide
23481 * Fires when OverlayView Draw
23482 * @param {Roo.bootstrap.LocationPicker} this
23484 OverlayViewHide : true
23489 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
23491 gMapContext: false,
23497 mapTypeControl: false,
23498 disableDoubleClickZoom: false,
23500 streetViewControl: false,
23504 enableAutocomplete: false,
23505 enableReverseGeocode: true,
23508 getAutoCreate: function()
23513 cls: 'roo-location-picker'
23519 initEvents: function(ct, position)
23521 if(!this.el.getWidth() || this.isApplied()){
23525 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23530 initial: function()
23532 if(!this.mapTypeId){
23533 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23536 this.gMapContext = this.GMapContext();
23538 this.initOverlayView();
23540 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23544 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23545 _this.setPosition(_this.gMapContext.marker.position);
23548 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23549 _this.fireEvent('mapClick', this, event);
23553 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23554 _this.fireEvent('mapRightClick', this, event);
23558 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23559 _this.fireEvent('markerClick', this, event);
23563 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23564 _this.fireEvent('markerRightClick', this, event);
23568 this.setPosition(this.gMapContext.location);
23570 this.fireEvent('initial', this, this.gMapContext.location);
23573 initOverlayView: function()
23577 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23581 _this.fireEvent('OverlayViewDraw', _this);
23586 _this.fireEvent('OverlayViewOnAdd', _this);
23589 onRemove: function()
23591 _this.fireEvent('OverlayViewOnRemove', _this);
23594 show: function(cpx)
23596 _this.fireEvent('OverlayViewShow', _this, cpx);
23601 _this.fireEvent('OverlayViewHide', _this);
23607 fromLatLngToContainerPixel: function(event)
23609 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23612 isApplied: function()
23614 return this.getGmapContext() == false ? false : true;
23617 getGmapContext: function()
23619 return this.gMapContext
23622 GMapContext: function()
23624 var position = new google.maps.LatLng(this.latitude, this.longitude);
23626 var _map = new google.maps.Map(this.el.dom, {
23629 mapTypeId: this.mapTypeId,
23630 mapTypeControl: this.mapTypeControl,
23631 disableDoubleClickZoom: this.disableDoubleClickZoom,
23632 scrollwheel: this.scrollwheel,
23633 streetViewControl: this.streetViewControl,
23634 locationName: this.locationName,
23635 draggable: this.draggable,
23636 enableAutocomplete: this.enableAutocomplete,
23637 enableReverseGeocode: this.enableReverseGeocode
23640 var _marker = new google.maps.Marker({
23641 position: position,
23643 title: this.markerTitle,
23644 draggable: this.draggable
23651 location: position,
23652 radius: this.radius,
23653 locationName: this.locationName,
23654 addressComponents: {
23655 formatted_address: null,
23656 addressLine1: null,
23657 addressLine2: null,
23659 streetNumber: null,
23663 stateOrProvince: null
23666 domContainer: this.el.dom,
23667 geodecoder: new google.maps.Geocoder()
23671 drawCircle: function(center, radius, options)
23673 if (this.gMapContext.circle != null) {
23674 this.gMapContext.circle.setMap(null);
23678 options = Roo.apply({}, options, {
23679 strokeColor: "#0000FF",
23680 strokeOpacity: .35,
23682 fillColor: "#0000FF",
23686 options.map = this.gMapContext.map;
23687 options.radius = radius;
23688 options.center = center;
23689 this.gMapContext.circle = new google.maps.Circle(options);
23690 return this.gMapContext.circle;
23696 setPosition: function(location)
23698 this.gMapContext.location = location;
23699 this.gMapContext.marker.setPosition(location);
23700 this.gMapContext.map.panTo(location);
23701 this.drawCircle(location, this.gMapContext.radius, {});
23705 if (this.gMapContext.settings.enableReverseGeocode) {
23706 this.gMapContext.geodecoder.geocode({
23707 latLng: this.gMapContext.location
23708 }, function(results, status) {
23710 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23711 _this.gMapContext.locationName = results[0].formatted_address;
23712 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23714 _this.fireEvent('positionchanged', this, location);
23721 this.fireEvent('positionchanged', this, location);
23726 google.maps.event.trigger(this.gMapContext.map, "resize");
23728 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
23730 this.fireEvent('resize', this);
23733 setPositionByLatLng: function(latitude, longitude)
23735 this.setPosition(new google.maps.LatLng(latitude, longitude));
23738 getCurrentPosition: function()
23741 latitude: this.gMapContext.location.lat(),
23742 longitude: this.gMapContext.location.lng()
23746 getAddressName: function()
23748 return this.gMapContext.locationName;
23751 getAddressComponents: function()
23753 return this.gMapContext.addressComponents;
23756 address_component_from_google_geocode: function(address_components)
23760 for (var i = 0; i < address_components.length; i++) {
23761 var component = address_components[i];
23762 if (component.types.indexOf("postal_code") >= 0) {
23763 result.postalCode = component.short_name;
23764 } else if (component.types.indexOf("street_number") >= 0) {
23765 result.streetNumber = component.short_name;
23766 } else if (component.types.indexOf("route") >= 0) {
23767 result.streetName = component.short_name;
23768 } else if (component.types.indexOf("neighborhood") >= 0) {
23769 result.city = component.short_name;
23770 } else if (component.types.indexOf("locality") >= 0) {
23771 result.city = component.short_name;
23772 } else if (component.types.indexOf("sublocality") >= 0) {
23773 result.district = component.short_name;
23774 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
23775 result.stateOrProvince = component.short_name;
23776 } else if (component.types.indexOf("country") >= 0) {
23777 result.country = component.short_name;
23781 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
23782 result.addressLine2 = "";
23786 setZoomLevel: function(zoom)
23788 this.gMapContext.map.setZoom(zoom);
23801 this.fireEvent('show', this);
23812 this.fireEvent('hide', this);
23817 Roo.apply(Roo.bootstrap.LocationPicker, {
23819 OverlayView : function(map, options)
23821 options = options || {};
23835 * @class Roo.bootstrap.Alert
23836 * @extends Roo.bootstrap.Component
23837 * Bootstrap Alert class
23838 * @cfg {String} title The title of alert
23839 * @cfg {String} html The content of alert
23840 * @cfg {String} weight ( success | info | warning | danger )
23841 * @cfg {String} faicon font-awesomeicon
23844 * Create a new alert
23845 * @param {Object} config The config object
23849 Roo.bootstrap.Alert = function(config){
23850 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
23854 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
23861 getAutoCreate : function()
23870 cls : 'roo-alert-icon'
23875 cls : 'roo-alert-title',
23880 cls : 'roo-alert-text',
23887 cfg.cn[0].cls += ' fa ' + this.faicon;
23891 cfg.cls += ' alert-' + this.weight;
23897 initEvents: function()
23899 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23902 setTitle : function(str)
23904 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
23907 setText : function(str)
23909 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
23912 setWeight : function(weight)
23915 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
23918 this.weight = weight;
23920 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
23923 setIcon : function(icon)
23926 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
23931 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
23952 * @class Roo.bootstrap.UploadCropbox
23953 * @extends Roo.bootstrap.Component
23954 * Bootstrap UploadCropbox class
23955 * @cfg {String} emptyText show when image has been loaded
23956 * @cfg {Number} minWidth default 300
23957 * @cfg {Number} minHeight default 300
23958 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
23961 * Create a new UploadCropbox
23962 * @param {Object} config The config object
23965 Roo.bootstrap.UploadCropbox = function(config){
23966 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
23970 * @event beforeselectfile
23971 * Fire before select file
23972 * @param {Roo.bootstrap.UploadCropbox} this
23974 "beforeselectfile" : true,
23977 * Fire after initEvent
23978 * @param {Roo.bootstrap.UploadCropbox} this
23983 * Fire after initEvent
23984 * @param {Roo.bootstrap.UploadCropbox} this
23985 * @param {String} data
23990 * Fire when preparing the file data
23991 * @param {Roo.bootstrap.UploadCropbox} this
23992 * @param {Object} file
23997 * Fire when get exception
23998 * @param {Roo.bootstrap.UploadCropbox} this
23999 * @param {Object} options
24001 "exception" : true,
24003 * @event beforeloadcanvas
24004 * Fire before load the canvas
24005 * @param {Roo.bootstrap.UploadCropbox} this
24006 * @param {String} src
24008 "beforeloadcanvas" : true,
24011 * Fire when trash image
24012 * @param {Roo.bootstrap.UploadCropbox} this
24017 * Fire when download the image
24018 * @param {Roo.bootstrap.UploadCropbox} this
24022 * @event footerbuttonclick
24023 * Fire when footerbuttonclick
24024 * @param {Roo.bootstrap.UploadCropbox} this
24025 * @param {String} type
24027 "footerbuttonclick" : true,
24031 * @param {Roo.bootstrap.UploadCropbox} this
24037 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24040 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24042 emptyText : 'Click to upload image',
24056 cropType : 'image/jpeg',
24059 getAutoCreate : function()
24063 cls : 'roo-upload-cropbox',
24067 cls : 'roo-upload-cropbox-body',
24071 cls : 'roo-upload-cropbox-preview'
24075 cls : 'roo-upload-cropbox-thumb'
24079 cls : 'roo-upload-cropbox-empty-notify',
24080 html : this.emptyText
24086 cls : 'roo-upload-cropbox-footer',
24089 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24099 onRender : function(ct, position)
24101 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24103 if (this.buttons.length) {
24105 Roo.each(this.buttons, function(bb) {
24107 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24109 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24115 initEvents : function()
24117 this.urlAPI = (window.createObjectURL && window) ||
24118 (window.URL && URL.revokeObjectURL && URL) ||
24119 (window.webkitURL && webkitURL);
24121 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24122 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24123 this.bodyHasOnClickEvent = false;
24125 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24126 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24128 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24129 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24130 this.thumbEl.hide();
24132 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24133 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24135 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24136 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24137 this.footerEl.hide();
24139 this.setThumbBoxSize();
24145 this.fireEvent('initial', this);
24152 window.addEventListener("resize", function() { _this.resize(); } );
24154 if(!this.bodyHasOnClickEvent){
24155 this.bodyEl.on('click', this.beforeSelectFile, this);
24156 this.bodyHasOnClickEvent = true;
24160 this.bodyEl.on('touchstart', this.onTouchStart, this);
24161 this.bodyEl.on('touchmove', this.onTouchMove, this);
24162 this.bodyEl.on('touchend', this.onTouchEnd, this);
24166 this.bodyEl.on('mousedown', this.onMouseDown, this);
24167 this.bodyEl.on('mousemove', this.onMouseMove, this);
24168 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24169 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24170 Roo.get(document).on('mouseup', this.onMouseUp, this);
24177 this.baseScale = 1;
24179 this.baseRotate = 1;
24180 this.dragable = false;
24181 this.pinching = false;
24184 this.cropData = false;
24185 this.notifyEl.dom.innerHTML = this.emptyText;
24189 resize : function()
24191 if(this.fireEvent('resize', this) != false){
24192 this.setThumbBoxPosition();
24193 this.setCanvasPosition();
24197 onFooterButtonClick : function(e, el, o, type)
24200 case 'rotate-left' :
24201 this.onRotateLeft(e);
24203 case 'rotate-right' :
24204 this.onRotateRight(e);
24207 this.beforeSelectFile(e);
24222 this.fireEvent('footerbuttonclick', this, type);
24225 beforeSelectFile : function(e)
24228 e.preventDefault();
24231 this.fireEvent('beforeselectfile', this);
24234 trash : function(e)
24237 e.preventDefault();
24240 this.fireEvent('trash', this);
24243 download : function(e)
24246 e.preventDefault();
24249 this.fireEvent('download', this);
24252 loadCanvas : function(src)
24254 if(this.fireEvent('beforeloadcanvas', this, src) != false){
24258 this.imageEl = document.createElement('img');
24262 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24264 this.imageEl.src = src;
24268 onLoadCanvas : function()
24270 if(this.bodyHasOnClickEvent){
24271 this.bodyEl.un('click', this.beforeSelectFile, this);
24272 this.bodyHasOnClickEvent = false;
24275 this.notifyEl.hide();
24276 this.thumbEl.show();
24277 this.footerEl.show();
24279 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24280 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24282 this.setThumbBoxPosition();
24283 this.baseRotateLevel();
24284 this.baseScaleLevel();
24292 setCanvasPosition : function()
24294 if(!this.canvasEl){
24298 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24299 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24301 this.previewEl.setLeft(pw);
24302 this.previewEl.setTop(ph);
24305 onMouseDown : function(e)
24309 this.dragable = true;
24310 this.pinching = false;
24312 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24313 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24317 onMouseMove : function(e)
24321 if(typeof(this.canvasEl) == 'undefined'){
24325 if (!this.dragable){
24329 var minX = Math.ceil(this.thumbEl.getLeft(true));
24330 var minY = Math.ceil(this.thumbEl.getTop(true));
24332 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24333 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24335 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24336 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24338 x = x - this.mouseX;
24339 y = y - this.mouseY;
24341 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24342 var bgY = Math.ceil(y + this.previewEl.getTop(true));
24344 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24345 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24347 this.previewEl.setLeft(bgX);
24348 this.previewEl.setTop(bgY);
24350 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24351 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24354 onMouseUp : function(e)
24358 this.dragable = false;
24361 onMouseWheel : function(e)
24365 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24367 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel());
24368 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel());
24371 e.getWheelDelta() == -1 &&
24374 (this.rotate == 0 || this.rotate == 180) && (width < this.thumbEl.getWidth() || height < this.thumbEl.getHeight())
24378 (this.rotate == 90 || this.rotate == 270) && (height < this.thumbEl.getWidth() || width < this.thumbEl.getHeight())
24382 this.scale = (e.getWheelDelta() == 1) ? (this.scale - 1) : (this.scale + 1);
24389 onRotateLeft : function(e)
24392 e.preventDefault();
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 < 90) ? 270 : this.rotate - 90;
24418 onRotateRight : function(e)
24421 e.preventDefault();
24426 (this.rotate == 0 || this.rotate == 180)
24428 (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())
24432 (this.rotate == 90 || this.rotate == 270)
24434 (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())
24441 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24449 this.previewEl.dom.innerHTML = '';
24451 var canvasEl = document.createElement("canvas");
24453 var contextEl = canvasEl.getContext("2d");
24455 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24456 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24457 var center = this.imageEl.OriginWidth / 2;
24459 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
24460 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24461 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24462 center = this.imageEl.OriginHeight / 2;
24465 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
24467 contextEl.translate(center, center);
24468 contextEl.rotate(this.rotate * Math.PI / 180);
24470 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24472 this.canvasEl = document.createElement("canvas");
24474 this.contextEl = this.canvasEl.getContext("2d");
24476 switch (this.rotate) {
24479 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24480 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24482 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24487 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24488 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24490 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24491 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);
24495 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24500 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24501 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24503 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24504 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);
24508 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);
24513 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24514 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24516 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24517 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24521 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);
24528 this.previewEl.appendChild(this.canvasEl);
24530 this.setCanvasPosition();
24535 if(typeof(this.canvasEl) == 'undefined'){
24538 var canvas = document.createElement("canvas");
24540 var context = canvas.getContext("2d");
24542 canvas.width = this.minWidth;
24543 canvas.height = this.minHeight;
24545 var cropWidth = this.thumbEl.getWidth();
24546 var cropHeight = this.thumbEl.getHeight();
24548 var x = this.thumbEl.getLeft(true) - this.previewEl.getLeft(true);
24549 var y = this.thumbEl.getTop(true) - this.previewEl.getTop(true);
24551 if(this.canvasEl.width - cropWidth < x){
24552 x = this.canvasEl.width - cropWidth;
24555 if(this.canvasEl.height - cropHeight < y){
24556 y = this.canvasEl.height - cropHeight;
24562 context.drawImage(this.canvasEl, x, y, cropWidth, cropHeight, 0, 0, canvas.width, canvas.height);
24564 this.cropData = canvas.toDataURL(this.cropType);
24566 this.fireEvent('crop', this, this.cropData);
24570 setThumbBoxSize : function()
24573 var width = Math.ceil(this.minWidth * height / this.minHeight);
24575 if(this.minWidth > this.minHeight){
24577 height = Math.ceil(this.minHeight * width / this.minWidth);
24580 this.thumbEl.setStyle({
24581 width : width + 'px',
24582 height : height + 'px'
24589 setThumbBoxPosition : function()
24591 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
24592 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
24594 this.thumbEl.setLeft(x);
24595 this.thumbEl.setTop(y);
24599 baseRotateLevel : function()
24601 this.baseRotate = 1;
24604 typeof(this.exif) != 'undefined' &&
24605 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
24606 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
24608 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
24611 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
24615 baseScaleLevel : function()
24619 if(this.baseRotate == 6 || this.baseRotate == 8){
24621 width = this.thumbEl.getHeight();
24622 this.baseScale = height / this.imageEl.OriginHeight;
24624 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
24625 height = this.thumbEl.getWidth();
24626 this.baseScale = height / this.imageEl.OriginHeight;
24629 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24630 height = this.thumbEl.getWidth();
24631 this.baseScale = height / this.imageEl.OriginHeight;
24633 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
24634 width = this.thumbEl.getHeight();
24635 this.baseScale = width / this.imageEl.OriginWidth;
24642 width = this.thumbEl.getWidth();
24643 this.baseScale = width / this.imageEl.OriginWidth;
24645 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
24646 height = this.thumbEl.getHeight();
24647 this.baseScale = height / this.imageEl.OriginHeight;
24651 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24653 height = this.thumbEl.getHeight();
24654 this.baseScale = height / this.imageEl.OriginHeight;
24656 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
24657 width = this.thumbEl.getWidth();
24658 this.baseScale = width / this.imageEl.OriginWidth;
24666 getScaleLevel : function()
24668 return this.baseScale * Math.pow(1.1, this.scale);
24671 onTouchStart : function(e)
24675 var touches = e.browserEvent.touches;
24681 if(touches.length == 1){
24682 this.onMouseDown(e);
24686 if(touches.length != 2){
24692 for(var i = 0, finger; finger = touches[i]; i++){
24693 coords.push(finger.pageX, finger.pageY);
24696 var x = Math.pow(coords[0] - coords[2], 2);
24697 var y = Math.pow(coords[1] - coords[3], 2);
24699 this.startDistance = Math.sqrt(x + y);
24701 this.startScale = this.scale;
24703 this.pinching = true;
24704 this.dragable = false;
24708 onTouchMove : function(e)
24712 if(!this.pinching && !this.dragable){
24716 var touches = e.browserEvent.touches;
24723 this.onMouseMove(e);
24729 for(var i = 0, finger; finger = touches[i]; i++){
24730 coords.push(finger.pageX, finger.pageY);
24733 var x = Math.pow(coords[0] - coords[2], 2);
24734 var y = Math.pow(coords[1] - coords[3], 2);
24736 this.endDistance = Math.sqrt(x + y);
24738 var scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
24740 var width = Math.ceil(this.imageEl.OriginWidth * this.baseScale * Math.pow(1.1, scale));
24741 var height = Math.ceil(this.imageEl.OriginHeight * this.baseScale * Math.pow(1.1, scale));
24744 this.endDistance / this.startDistance < 1 &&
24747 (this.rotate == 0 || this.rotate == 180) && (width < this.thumbEl.getWidth() || height < this.thumbEl.getHeight())
24751 (this.rotate == 90 || this.rotate == 270) && (height < this.thumbEl.getWidth() || width < this.thumbEl.getHeight())
24758 this.scale = scale;
24764 onTouchEnd : function(e)
24768 this.pinching = false;
24769 this.dragable = false;
24773 prepare : function(input)
24778 if(typeof(input) === 'string'){
24779 this.loadCanvas(input);
24783 if(!input.files || !input.files[0] || !this.urlAPI){
24787 this.file = input.files[0];
24788 this.cropType = this.file.type;
24792 if(this.fireEvent('prepare', this, this.file) != false){
24794 var reader = new FileReader();
24796 reader.onload = function (e) {
24797 if (e.target.error) {
24798 Roo.log(e.target.error);
24802 var buffer = e.target.result,
24803 dataView = new DataView(buffer),
24805 maxOffset = dataView.byteLength - 4,
24809 if (dataView.getUint16(0) === 0xffd8) {
24810 while (offset < maxOffset) {
24811 markerBytes = dataView.getUint16(offset);
24813 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
24814 markerLength = dataView.getUint16(offset + 2) + 2;
24815 if (offset + markerLength > dataView.byteLength) {
24816 Roo.log('Invalid meta data: Invalid segment size.');
24820 if(markerBytes == 0xffe1){
24821 _this.parseExifData(
24828 offset += markerLength;
24838 var url = _this.urlAPI.createObjectURL(_this.file);
24840 _this.loadCanvas(url);
24845 reader.readAsArrayBuffer(this.file);
24851 parseExifData : function(dataView, offset, length)
24853 var tiffOffset = offset + 10,
24857 if (dataView.getUint32(offset + 4) !== 0x45786966) {
24858 // No Exif data, might be XMP data instead
24862 // Check for the ASCII code for "Exif" (0x45786966):
24863 if (dataView.getUint32(offset + 4) !== 0x45786966) {
24864 // No Exif data, might be XMP data instead
24867 if (tiffOffset + 8 > dataView.byteLength) {
24868 Roo.log('Invalid Exif data: Invalid segment size.');
24871 // Check for the two null bytes:
24872 if (dataView.getUint16(offset + 8) !== 0x0000) {
24873 Roo.log('Invalid Exif data: Missing byte alignment offset.');
24876 // Check the byte alignment:
24877 switch (dataView.getUint16(tiffOffset)) {
24879 littleEndian = true;
24882 littleEndian = false;
24885 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
24888 // Check for the TIFF tag marker (0x002A):
24889 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
24890 Roo.log('Invalid Exif data: Missing TIFF marker.');
24893 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
24894 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
24896 this.parseExifTags(
24899 tiffOffset + dirOffset,
24904 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
24909 if (dirOffset + 6 > dataView.byteLength) {
24910 Roo.log('Invalid Exif data: Invalid directory offset.');
24913 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
24914 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
24915 if (dirEndOffset + 4 > dataView.byteLength) {
24916 Roo.log('Invalid Exif data: Invalid directory size.');
24919 for (i = 0; i < tagsNumber; i += 1) {
24923 dirOffset + 2 + 12 * i, // tag offset
24927 // Return the offset to the next directory:
24928 return dataView.getUint32(dirEndOffset, littleEndian);
24931 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
24933 var tag = dataView.getUint16(offset, littleEndian);
24935 this.exif[tag] = this.getExifValue(
24939 dataView.getUint16(offset + 2, littleEndian), // tag type
24940 dataView.getUint32(offset + 4, littleEndian), // tag length
24945 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
24947 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
24956 Roo.log('Invalid Exif data: Invalid tag type.');
24960 tagSize = tagType.size * length;
24961 // Determine if the value is contained in the dataOffset bytes,
24962 // or if the value at the dataOffset is a pointer to the actual data:
24963 dataOffset = tagSize > 4 ?
24964 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
24965 if (dataOffset + tagSize > dataView.byteLength) {
24966 Roo.log('Invalid Exif data: Invalid data offset.');
24969 if (length === 1) {
24970 return tagType.getValue(dataView, dataOffset, littleEndian);
24973 for (i = 0; i < length; i += 1) {
24974 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
24977 if (tagType.ascii) {
24979 // Concatenate the chars:
24980 for (i = 0; i < values.length; i += 1) {
24982 // Ignore the terminating NULL byte(s):
24983 if (c === '\u0000') {
24995 Roo.apply(Roo.bootstrap.UploadCropbox, {
24997 'Orientation': 0x0112
25001 1: 0, //'top-left',
25003 3: 180, //'bottom-right',
25004 // 4: 'bottom-left',
25006 6: 90, //'right-top',
25007 // 7: 'right-bottom',
25008 8: 270 //'left-bottom'
25012 // byte, 8-bit unsigned int:
25014 getValue: function (dataView, dataOffset) {
25015 return dataView.getUint8(dataOffset);
25019 // ascii, 8-bit byte:
25021 getValue: function (dataView, dataOffset) {
25022 return String.fromCharCode(dataView.getUint8(dataOffset));
25027 // short, 16 bit int:
25029 getValue: function (dataView, dataOffset, littleEndian) {
25030 return dataView.getUint16(dataOffset, littleEndian);
25034 // long, 32 bit int:
25036 getValue: function (dataView, dataOffset, littleEndian) {
25037 return dataView.getUint32(dataOffset, littleEndian);
25041 // rational = two long values, first is numerator, second is denominator:
25043 getValue: function (dataView, dataOffset, littleEndian) {
25044 return dataView.getUint32(dataOffset, littleEndian) /
25045 dataView.getUint32(dataOffset + 4, littleEndian);
25049 // slong, 32 bit signed int:
25051 getValue: function (dataView, dataOffset, littleEndian) {
25052 return dataView.getInt32(dataOffset, littleEndian);
25056 // srational, two slongs, first is numerator, second is denominator:
25058 getValue: function (dataView, dataOffset, littleEndian) {
25059 return dataView.getInt32(dataOffset, littleEndian) /
25060 dataView.getInt32(dataOffset + 4, littleEndian);
25070 cls : 'btn-group roo-upload-cropbox-rotate-left',
25071 action : 'rotate-left',
25075 cls : 'btn btn-default',
25076 html : '<i class="fa fa-undo"></i>'
25082 cls : 'btn-group roo-upload-cropbox-picture',
25083 action : 'picture',
25087 cls : 'btn btn-default',
25088 html : '<i class="fa fa-picture-o"></i>'
25094 cls : 'btn-group roo-upload-cropbox-rotate-right',
25095 action : 'rotate-right',
25099 cls : 'btn btn-default',
25100 html : '<i class="fa fa-repeat"></i>'
25108 cls : 'btn-group roo-upload-cropbox-rotate-left',
25109 action : 'rotate-left',
25113 cls : 'btn btn-default',
25114 html : '<i class="fa fa-undo"></i>'
25120 cls : 'btn-group roo-upload-cropbox-download',
25121 action : 'download',
25125 cls : 'btn btn-default',
25126 html : '<i class="fa fa-download"></i>'
25132 cls : 'btn-group roo-upload-cropbox-crop',
25137 cls : 'btn btn-default',
25138 html : '<i class="fa fa-crop"></i>'
25144 cls : 'btn-group roo-upload-cropbox-trash',
25149 cls : 'btn btn-default',
25150 html : '<i class="fa fa-trash"></i>'
25156 cls : 'btn-group roo-upload-cropbox-rotate-right',
25157 action : 'rotate-right',
25161 cls : 'btn btn-default',
25162 html : '<i class="fa fa-repeat"></i>'
25175 * @class Roo.bootstrap.DocumentManager
25176 * @extends Roo.bootstrap.Component
25177 * Bootstrap DocumentManager class
25178 * @cfg {String} paramName default 'imageUpload'
25179 * @cfg {String} method default POST
25180 * @cfg {String} url action url
25181 * @cfg {Number} boxes number of boxes default 12
25182 * @cfg {Boolean} multiple multiple upload default true
25183 * @cfg {Number} minWidth default 300
25184 * @cfg {Number} minHeight default 300
25185 * @cfg {Number} thumbSize default 300
25186 * @cfg {String} fieldLabel
25187 * @cfg {Number} labelWidth default 4
25188 * @cfg {String} labelAlign (left|top) default left
25191 * Create a new DocumentManager
25192 * @param {Object} config The config object
25195 Roo.bootstrap.DocumentManager = function(config){
25196 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
25201 * Fire when initial the DocumentManager
25202 * @param {Roo.bootstrap.DocumentManager} this
25207 * inspect selected file
25208 * @param {Roo.bootstrap.DocumentManager} this
25209 * @param {File} file
25214 * Fire when xhr load exception
25215 * @param {Roo.bootstrap.DocumentManager} this
25216 * @param {XMLHttpRequest} xhr
25218 "exception" : true,
25221 * prepare the form data
25222 * @param {Roo.bootstrap.DocumentManager} this
25223 * @param {Object} formData
25228 * Fire when remove the file
25229 * @param {Roo.bootstrap.DocumentManager} this
25230 * @param {Object} file
25235 * Fire after refresh the file
25236 * @param {Roo.bootstrap.DocumentManager} this
25241 * Fire after click the image
25242 * @param {Roo.bootstrap.DocumentManager} this
25243 * @param {Object} file
25250 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
25261 paramName : 'imageUpload',
25264 labelAlign : 'left',
25266 getAutoCreate : function()
25268 var managerWidget = {
25270 cls : 'roo-document-manager',
25274 cls : 'roo-document-manager-selector',
25279 cls : 'roo-document-manager-uploader',
25283 cls : 'roo-document-manager-upload-btn',
25284 html : '<i class="fa fa-plus"></i>'
25295 cls : 'column col-md-12',
25300 if(this.fieldLabel.length){
25305 cls : 'column col-md-12',
25306 html : this.fieldLabel
25310 cls : 'column col-md-12',
25315 if(this.labelAlign == 'left'){
25319 cls : 'column col-md-' + this.labelWidth,
25320 html : this.fieldLabel
25324 cls : 'column col-md-' + (12 - this.labelWidth),
25334 cls : 'row clearfix',
25342 initEvents : function()
25344 this.managerEl = this.el.select('.roo-document-manager', true).first();
25345 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25347 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
25348 this.selectorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25349 this.selectorEl.hide();
25352 this.selectorEl.attr('multiple', 'multiple');
25355 this.selectorEl.on('change', this.onSelect, this);
25357 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
25358 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25360 this.uploader.on('click', this.onUpload, this);
25364 window.addEventListener("resize", function() { _this.refresh(); } );
25366 this.fireEvent('initial', this);
25369 onUpload : function(e)
25371 e.preventDefault();
25373 this.selectorEl.dom.click();
25377 onSelect : function(e)
25379 e.preventDefault();
25381 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25385 Roo.each(this.selectorEl.dom.files, function(file){
25386 if(this.fireEvent('inspect', this, file) != false){
25387 this.files.push(file);
25395 process : function()
25397 this.selectorEl.dom.value = '';
25399 if(!this.files.length){
25403 if(this.files.length > this.boxes){
25404 this.files = this.files.slice(0, this.boxes);
25407 var xhr = new XMLHttpRequest();
25409 Roo.each(this.files, function(file, index){
25410 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25416 this.managerEl.createChild({
25418 cls : 'roo-document-manager-loading',
25422 tooltip : file.name,
25423 cls : 'roo-document-manager-thumb',
25424 html : '<i class="fa fa-spinner fa-pulse"></i>'
25432 if(this.files.length > this.boxes - 1 ){
25433 this.uploader.hide();
25437 "Accept": "application/json",
25438 "Cache-Control": "no-cache",
25439 "X-Requested-With": "XMLHttpRequest"
25442 xhr.open(this.method, this.url, true);
25444 for (var headerName in headers) {
25445 var headerValue = headers[headerName];
25447 xhr.setRequestHeader(headerName, headerValue);
25453 xhr.onload = function()
25455 _this.xhrOnLoad(xhr);
25458 xhr.onerror = function()
25460 _this.xhrOnError(xhr);
25463 var formData = new FormData();
25465 formData.append('returnHTML', 'NO');
25467 Roo.each(this.files, function(file, index){
25469 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25473 formData.append(this.getParamName(index), file, file.name);
25477 if(this.fireEvent('prepare', this, formData) != false){
25478 xhr.send(formData);
25483 getParamName : function(i)
25485 if(!this.multiple){
25486 return this.paramName;
25489 return this.paramName + "_" + i;
25492 refresh : function()
25494 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
25501 Roo.each(this.files, function(file){
25503 if(typeof(file.id) == 'undefined' || file.id * 1 < 1){
25512 var previewEl = this.managerEl.createChild({
25514 cls : 'roo-document-manager-preview',
25518 tooltip : file.filename,
25519 cls : 'roo-document-manager-thumb',
25520 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
25530 var close = previewEl.select('button.close', true).first();
25532 close.on('click', this.onRemove, this, file);
25534 file.target = previewEl;
25536 var image = previewEl.select('img', true).first();
25538 image.on('click', this.onClick, this, file);
25546 this.files = files;
25548 this.uploader.show();
25550 if(this.files.length > this.boxes - 1){
25551 this.uploader.hide();
25554 Roo.isTouch ? this.closable(false) : this.closable(true);
25556 this.fireEvent('refresh', this);
25559 onRemove : function(e, el, o)
25561 e.preventDefault();
25563 this.fireEvent('remove', this, o);
25567 remove : function(o)
25571 Roo.each(this.files, function(file){
25572 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
25581 this.files = files;
25586 onClick : function(e, el, o)
25588 e.preventDefault();
25590 this.fireEvent('click', this, o);
25594 closable : function(closable)
25596 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
25598 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25610 xhrOnLoad : function(xhr)
25612 if (xhr.readyState !== 4) {
25614 this.fireEvent('exception', this, xhr);
25618 var response = Roo.decode(xhr.responseText);
25620 if(!response.success){
25622 this.fireEvent('exception', this, xhr);
25628 Roo.each(this.files, function(file, index){
25630 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25634 this.files[index] = response.data[i];
25645 xhrOnError : function()
25647 Roo.log('xhr on error');
25649 var response = Roo.decode(xhr.responseText);
25662 * @class Roo.bootstrap.DocumentViewer
25663 * @extends Roo.bootstrap.Component
25664 * Bootstrap DocumentViewer class
25667 * Create a new DocumentViewer
25668 * @param {Object} config The config object
25671 Roo.bootstrap.DocumentViewer = function(config){
25672 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
25677 * Fire after initEvent
25678 * @param {Roo.bootstrap.DocumentViewer} this
25684 * @param {Roo.bootstrap.DocumentViewer} this
25689 * Fire after trash button
25690 * @param {Roo.bootstrap.DocumentViewer} this
25697 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
25699 getAutoCreate : function()
25703 cls : 'roo-document-viewer',
25707 cls : 'roo-document-viewer-body',
25711 cls : 'roo-document-viewer-thumb',
25715 cls : 'roo-document-viewer-image'
25723 cls : 'roo-document-viewer-footer',
25726 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
25734 cls : 'btn btn-default roo-document-viewer-trash',
25735 html : '<i class="fa fa-trash"></i>'
25748 initEvents : function()
25751 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
25752 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25754 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
25755 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25757 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
25758 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25760 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
25761 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25763 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
25764 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25766 this.bodyEl.on('click', this.onClick, this);
25768 this.trashBtn.on('click', this.onTrash, this);
25772 initial : function()
25774 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
25777 this.fireEvent('initial', this);
25781 onClick : function(e)
25783 e.preventDefault();
25785 this.fireEvent('click', this);
25788 onTrash : function(e)
25790 e.preventDefault();
25792 this.fireEvent('trash', this);