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());
105 cfg.id = this.id || Roo.id();
107 // fill in the extra attributes
108 if (this.xattr && typeof(this.xattr) =='object') {
109 for (var i in this.xattr) {
110 cfg[i] = this.xattr[i];
115 cfg.dataId = this.dataId;
119 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
122 if (this.style) { // fixme needs to support more complex style data.
123 cfg.style = this.style;
127 cfg.name = this.name;
130 this.el = ct.createChild(cfg, position);
133 this.tooltipEl().attr('tooltip', this.tooltip);
136 if(this.tabIndex !== undefined){
137 this.el.dom.setAttribute('tabIndex', this.tabIndex);
144 * Fetch the element to add children to
145 * @return {Roo.Element} defaults to this.el
147 getChildContainer : function()
152 * Fetch the element to display the tooltip on.
153 * @return {Roo.Element} defaults to this.el
155 tooltipEl : function()
160 addxtype : function(tree,cntr)
164 cn = Roo.factory(tree);
165 //Roo.log(['addxtype', cn]);
167 cn.parentType = this.xtype; //??
168 cn.parentId = this.id;
170 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171 if (typeof(cn.container_method) == 'string') {
172 cntr = cn.container_method;
176 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
178 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
180 var build_from_html = Roo.XComponent.build_from_html;
182 var is_body = (tree.xtype == 'Body') ;
184 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186 var self_cntr_el = Roo.get(this[cntr](false));
188 // do not try and build conditional elements
189 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
193 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195 return this.addxtypeChild(tree,cntr, is_body);
198 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
201 return this.addxtypeChild(Roo.apply({}, tree),cntr);
204 Roo.log('skipping render');
210 if (!build_from_html) {
214 // this i think handles overlaying multiple children of the same type
215 // with the sam eelement.. - which might be buggy..
217 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
223 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
227 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
233 addxtypeChild : function (tree, cntr, is_body)
235 Roo.debug && Roo.log('addxtypeChild:' + cntr);
237 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
240 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
241 (typeof(tree['flexy:foreach']) != 'undefined');
245 skip_children = false;
246 // render the element if it's not BODY.
249 cn = Roo.factory(tree);
251 cn.parentType = this.xtype; //??
252 cn.parentId = this.id;
254 var build_from_html = Roo.XComponent.build_from_html;
257 // does the container contain child eleemnts with 'xtype' attributes.
258 // that match this xtype..
259 // note - when we render we create these as well..
260 // so we should check to see if body has xtype set.
261 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263 var self_cntr_el = Roo.get(this[cntr](false));
264 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266 //Roo.log(Roo.XComponent.build_from_html);
267 //Roo.log("got echild:");
270 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
271 // and are not displayed -this causes this to use up the wrong element when matching.
272 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
275 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
276 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
282 //echild.dom.removeAttribute('xtype');
284 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
285 Roo.debug && Roo.log(self_cntr_el);
286 Roo.debug && Roo.log(echild);
287 Roo.debug && Roo.log(cn);
293 // if object has flexy:if - then it may or may not be rendered.
294 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
295 // skip a flexy if element.
296 Roo.debug && Roo.log('skipping render');
297 Roo.debug && Roo.log(tree);
299 Roo.debug && Roo.log('skipping all children');
300 skip_children = true;
305 // actually if flexy:foreach is found, we really want to create
306 // multiple copies here...
308 //Roo.log(this[cntr]());
309 cn.render(this[cntr](true));
311 // then add the element..
319 if (typeof (tree.menu) != 'undefined') {
320 tree.menu.parentType = cn.xtype;
321 tree.menu.triggerEl = cn.el;
322 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
326 if (!tree.items || !tree.items.length) {
328 //Roo.log(["no children", this]);
333 var items = tree.items;
336 //Roo.log(items.length);
338 if (!skip_children) {
339 for(var i =0;i < items.length;i++) {
340 // Roo.log(['add child', items[i]]);
341 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
347 //Roo.log("fire childrenrendered");
349 cn.fireEvent('childrenrendered', this);
354 * Show a component - removes 'hidden' class
359 this.el.removeClass('hidden');
363 * Hide a component - adds 'hidden' class
367 if (this.el && !this.el.hasClass('hidden')) {
368 this.el.addClass('hidden');
382 * @class Roo.bootstrap.Body
383 * @extends Roo.bootstrap.Component
384 * Bootstrap Body class
388 * @param {Object} config The config object
391 Roo.bootstrap.Body = function(config){
392 Roo.bootstrap.Body.superclass.constructor.call(this, config);
393 this.el = Roo.get(document.body);
394 if (this.cls && this.cls.length) {
395 Roo.get(document.body).addClass(this.cls);
399 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
401 is_body : true,// just to make sure it's constructed?
406 onRender : function(ct, position)
408 /* Roo.log("Roo.bootstrap.Body - onRender");
409 if (this.cls && this.cls.length) {
410 Roo.get(document.body).addClass(this.cls);
430 * @class Roo.bootstrap.ButtonGroup
431 * @extends Roo.bootstrap.Component
432 * Bootstrap ButtonGroup class
433 * @cfg {String} size lg | sm | xs (default empty normal)
434 * @cfg {String} align vertical | justified (default none)
435 * @cfg {String} direction up | down (default down)
436 * @cfg {Boolean} toolbar false | true
437 * @cfg {Boolean} btn true | false
442 * @param {Object} config The config object
445 Roo.bootstrap.ButtonGroup = function(config){
446 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
449 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
457 getAutoCreate : function(){
463 cfg.html = this.html || cfg.html;
474 if (['vertical','justified'].indexOf(this.align)!==-1) {
475 cfg.cls = 'btn-group-' + this.align;
477 if (this.align == 'justified') {
478 console.log(this.items);
482 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
483 cfg.cls += ' btn-group-' + this.size;
486 if (this.direction == 'up') {
487 cfg.cls += ' dropup' ;
503 * @class Roo.bootstrap.Button
504 * @extends Roo.bootstrap.Component
505 * Bootstrap Button class
506 * @cfg {String} html The button content
507 * @cfg {String} weight ( primary | success | info | warning | danger | link ) default
508 * @cfg {String} size ( lg | sm | xs)
509 * @cfg {String} tag ( a | input | submit)
510 * @cfg {String} href empty or href
511 * @cfg {Boolean} disabled default false;
512 * @cfg {Boolean} isClose default false;
513 * @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)
514 * @cfg {String} badge text for badge
515 * @cfg {String} theme default
516 * @cfg {Boolean} inverse
517 * @cfg {Boolean} toggle
518 * @cfg {String} ontext text for on toggle state
519 * @cfg {String} offtext text for off toggle state
520 * @cfg {Boolean} defaulton
521 * @cfg {Boolean} preventDefault default true
522 * @cfg {Boolean} removeClass remove the standard class..
523 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
526 * Create a new button
527 * @param {Object} config The config object
531 Roo.bootstrap.Button = function(config){
532 Roo.bootstrap.Button.superclass.constructor.call(this, config);
537 * When a butotn is pressed
538 * @param {Roo.bootstrap.Button} this
539 * @param {Roo.EventObject} e
544 * After the button has been toggles
545 * @param {Roo.EventObject} e
546 * @param {boolean} pressed (also available as button.pressed)
552 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
570 preventDefault: true,
579 getAutoCreate : function(){
587 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
588 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
593 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
595 if (this.toggle == true) {
598 cls: 'slider-frame roo-button',
603 'data-off-text':'OFF',
604 cls: 'slider-button',
610 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
611 cfg.cls += ' '+this.weight;
620 cfg["aria-hidden"] = true;
622 cfg.html = "×";
628 if (this.theme==='default') {
629 cfg.cls = 'btn roo-button';
631 //if (this.parentType != 'Navbar') {
632 this.weight = this.weight.length ? this.weight : 'default';
634 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
636 cfg.cls += ' btn-' + this.weight;
638 } else if (this.theme==='glow') {
641 cfg.cls = 'btn-glow roo-button';
643 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
645 cfg.cls += ' ' + this.weight;
651 this.cls += ' inverse';
656 cfg.cls += ' active';
660 cfg.disabled = 'disabled';
664 Roo.log('changing to ul' );
666 this.glyphicon = 'caret';
669 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
671 //gsRoo.log(this.parentType);
672 if (this.parentType === 'Navbar' && !this.parent().bar) {
673 Roo.log('changing to li?');
682 href : this.href || '#'
685 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
686 cfg.cls += ' dropdown';
693 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
695 if (this.glyphicon) {
696 cfg.html = ' ' + cfg.html;
701 cls: 'glyphicon glyphicon-' + this.glyphicon
711 // cfg.cls='btn roo-button';
715 var value = cfg.html;
720 cls: 'glyphicon glyphicon-' + this.glyphicon,
739 cfg.cls += ' dropdown';
740 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
743 if (cfg.tag !== 'a' && this.href !== '') {
744 throw "Tag must be a to set href.";
745 } else if (this.href.length > 0) {
746 cfg.href = this.href;
749 if(this.removeClass){
754 cfg.target = this.target;
759 initEvents: function() {
760 // Roo.log('init events?');
761 // Roo.log(this.el.dom);
764 if (typeof (this.menu) != 'undefined') {
765 this.menu.parentType = this.xtype;
766 this.menu.triggerEl = this.el;
767 this.addxtype(Roo.apply({}, this.menu));
771 if (this.el.hasClass('roo-button')) {
772 this.el.on('click', this.onClick, this);
774 this.el.select('.roo-button').on('click', this.onClick, this);
777 if(this.removeClass){
778 this.el.on('click', this.onClick, this);
781 this.el.enableDisplayMode();
784 onClick : function(e)
791 Roo.log('button on click ');
792 if(this.preventDefault){
795 if (this.pressed === true || this.pressed === false) {
796 this.pressed = !this.pressed;
797 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
798 this.fireEvent('toggle', this, e, this.pressed);
802 this.fireEvent('click', this, e);
806 * Enables this button
810 this.disabled = false;
811 this.el.removeClass('disabled');
815 * Disable this button
819 this.disabled = true;
820 this.el.addClass('disabled');
823 * sets the active state on/off,
824 * @param {Boolean} state (optional) Force a particular state
826 setActive : function(v) {
828 this.el[v ? 'addClass' : 'removeClass']('active');
831 * toggles the current active state
833 toggleActive : function()
835 var active = this.el.hasClass('active');
836 this.setActive(!active);
840 setText : function(str)
842 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
846 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
869 * @class Roo.bootstrap.Column
870 * @extends Roo.bootstrap.Component
871 * Bootstrap Column class
872 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
873 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
874 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
875 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
876 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
877 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
878 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
879 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
882 * @cfg {Boolean} hidden (true|false) hide the element
883 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
884 * @cfg {String} fa (ban|check|...) font awesome icon
885 * @cfg {Number} fasize (1|2|....) font awsome size
887 * @cfg {String} icon (info-sign|check|...) glyphicon name
889 * @cfg {String} html content of column.
892 * Create a new Column
893 * @param {Object} config The config object
896 Roo.bootstrap.Column = function(config){
897 Roo.bootstrap.Column.superclass.constructor.call(this, config);
900 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
918 getAutoCreate : function(){
919 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
927 ['xs','sm','md','lg'].map(function(size){
928 //Roo.log( size + ':' + settings[size]);
930 if (settings[size+'off'] !== false) {
931 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
934 if (settings[size] === false) {
938 if (!settings[size]) { // 0 = hidden
939 cfg.cls += ' hidden-' + size;
942 cfg.cls += ' col-' + size + '-' + settings[size];
947 cfg.cls += ' hidden';
950 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
951 cfg.cls +=' alert alert-' + this.alert;
955 if (this.html.length) {
956 cfg.html = this.html;
960 if (this.fasize > 1) {
961 fasize = ' fa-' + this.fasize + 'x';
963 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
968 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
987 * @class Roo.bootstrap.Container
988 * @extends Roo.bootstrap.Component
989 * Bootstrap Container class
990 * @cfg {Boolean} jumbotron is it a jumbotron element
991 * @cfg {String} html content of element
992 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
993 * @cfg {String} panel (primary|success|info|warning|danger) render as panel - type - primary/success.....
994 * @cfg {String} header content of header (for panel)
995 * @cfg {String} footer content of footer (for panel)
996 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
997 * @cfg {String} tag (header|aside|section) type of HTML tag.
998 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
999 * @cfg {String} fa font awesome icon
1000 * @cfg {String} icon (info-sign|check|...) glyphicon name
1001 * @cfg {Boolean} hidden (true|false) hide the element
1002 * @cfg {Boolean} expandable (true|false) default false
1003 * @cfg {Boolean} expanded (true|false) default true
1004 * @cfg {String} rheader contet on the right of header
1005 * @cfg {Boolean} clickable (true|false) default false
1009 * Create a new Container
1010 * @param {Object} config The config object
1013 Roo.bootstrap.Container = function(config){
1014 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1020 * After the panel has been expand
1022 * @param {Roo.bootstrap.Container} this
1027 * After the panel has been collapsed
1029 * @param {Roo.bootstrap.Container} this
1034 * When a element is chick
1035 * @param {Roo.bootstrap.Container} this
1036 * @param {Roo.EventObject} e
1042 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1060 getChildContainer : function() {
1066 if (this.panel.length) {
1067 return this.el.select('.panel-body',true).first();
1074 getAutoCreate : function(){
1077 tag : this.tag || 'div',
1081 if (this.jumbotron) {
1082 cfg.cls = 'jumbotron';
1087 // - this is applied by the parent..
1089 // cfg.cls = this.cls + '';
1092 if (this.sticky.length) {
1094 var bd = Roo.get(document.body);
1095 if (!bd.hasClass('bootstrap-sticky')) {
1096 bd.addClass('bootstrap-sticky');
1097 Roo.select('html',true).setStyle('height', '100%');
1100 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1104 if (this.well.length) {
1105 switch (this.well) {
1108 cfg.cls +=' well well-' +this.well;
1117 cfg.cls += ' hidden';
1121 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1122 cfg.cls +=' alert alert-' + this.alert;
1127 if (this.panel.length) {
1128 cfg.cls += ' panel panel-' + this.panel;
1130 if (this.header.length) {
1134 if(this.expandable){
1136 cfg.cls = cfg.cls + ' expandable';
1140 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1148 cls : 'panel-title',
1149 html : (this.expandable ? ' ' : '') + this.header
1153 cls: 'panel-header-right',
1159 cls : 'panel-heading',
1160 style : this.expandable ? 'cursor: pointer' : '',
1168 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1173 if (this.footer.length) {
1175 cls : 'panel-footer',
1184 body.html = this.html || cfg.html;
1185 // prefix with the icons..
1187 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1190 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1195 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1196 cfg.cls = 'container';
1202 initEvents: function()
1204 if(this.expandable){
1205 var headerEl = this.headerEl();
1208 headerEl.on('click', this.onToggleClick, this);
1213 this.el.on('click', this.onClick, this);
1218 onToggleClick : function()
1220 var headerEl = this.headerEl();
1236 if(this.fireEvent('expand', this)) {
1238 this.expanded = true;
1240 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1242 this.el.select('.panel-body',true).first().removeClass('hide');
1244 var toggleEl = this.toggleEl();
1250 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1255 collapse : function()
1257 if(this.fireEvent('collapse', this)) {
1259 this.expanded = false;
1261 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1262 this.el.select('.panel-body',true).first().addClass('hide');
1264 var toggleEl = this.toggleEl();
1270 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1274 toggleEl : function()
1276 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1280 return this.el.select('.panel-heading .fa',true).first();
1283 headerEl : function()
1285 if(!this.el || !this.panel.length || !this.header.length){
1289 return this.el.select('.panel-heading',true).first()
1292 titleEl : function()
1294 if(!this.el || !this.panel.length || !this.header.length){
1298 return this.el.select('.panel-title',true).first();
1301 setTitle : function(v)
1303 var titleEl = this.titleEl();
1309 titleEl.dom.innerHTML = v;
1312 getTitle : function()
1315 var titleEl = this.titleEl();
1321 return titleEl.dom.innerHTML;
1324 setRightTitle : function(v)
1326 var t = this.el.select('.panel-header-right',true).first();
1332 t.dom.innerHTML = v;
1335 onClick : function(e)
1339 this.fireEvent('click', this, e);
1353 * @class Roo.bootstrap.Img
1354 * @extends Roo.bootstrap.Component
1355 * Bootstrap Img class
1356 * @cfg {Boolean} imgResponsive false | true
1357 * @cfg {String} border rounded | circle | thumbnail
1358 * @cfg {String} src image source
1359 * @cfg {String} alt image alternative text
1360 * @cfg {String} href a tag href
1361 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1362 * @cfg {String} xsUrl xs image source
1363 * @cfg {String} smUrl sm image source
1364 * @cfg {String} mdUrl md image source
1365 * @cfg {String} lgUrl lg image source
1368 * Create a new Input
1369 * @param {Object} config The config object
1372 Roo.bootstrap.Img = function(config){
1373 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1379 * The img click event for the img.
1380 * @param {Roo.EventObject} e
1386 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1388 imgResponsive: true,
1398 getAutoCreate : function()
1400 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1401 return this.createSingleImg();
1406 cls: 'roo-image-responsive-group',
1411 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1413 if(!_this[size + 'Url']){
1419 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1420 html: _this.html || cfg.html,
1421 src: _this[size + 'Url']
1424 img.cls += ' roo-image-responsive-' + size;
1426 var s = ['xs', 'sm', 'md', 'lg'];
1428 s.splice(s.indexOf(size), 1);
1430 Roo.each(s, function(ss){
1431 img.cls += ' hidden-' + ss;
1434 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1435 cfg.cls += ' img-' + _this.border;
1439 cfg.alt = _this.alt;
1452 a.target = _this.target;
1456 cfg.cn.push((_this.href) ? a : img);
1463 createSingleImg : function()
1467 cls: (this.imgResponsive) ? 'img-responsive' : '',
1469 src : 'about:blank' // just incase src get's set to undefined?!?
1472 cfg.html = this.html || cfg.html;
1474 cfg.src = this.src || cfg.src;
1476 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1477 cfg.cls += ' img-' + this.border;
1494 a.target = this.target;
1499 return (this.href) ? a : cfg;
1502 initEvents: function()
1505 this.el.on('click', this.onClick, this);
1510 onClick : function(e)
1512 Roo.log('img onclick');
1513 this.fireEvent('click', this, e);
1527 * @class Roo.bootstrap.Link
1528 * @extends Roo.bootstrap.Component
1529 * Bootstrap Link Class
1530 * @cfg {String} alt image alternative text
1531 * @cfg {String} href a tag href
1532 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1533 * @cfg {String} html the content of the link.
1534 * @cfg {String} anchor name for the anchor link
1535 * @cfg {String} fa - favicon
1537 * @cfg {Boolean} preventDefault (true | false) default false
1541 * Create a new Input
1542 * @param {Object} config The config object
1545 Roo.bootstrap.Link = function(config){
1546 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1552 * The img click event for the img.
1553 * @param {Roo.EventObject} e
1559 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1563 preventDefault: false,
1569 getAutoCreate : function()
1571 var html = this.html || '';
1573 if (this.fa !== false) {
1574 html = '<i class="fa fa-' + this.fa + '"></i>';
1579 // anchor's do not require html/href...
1580 if (this.anchor === false) {
1582 cfg.href = this.href || '#';
1584 cfg.name = this.anchor;
1585 if (this.html !== false || this.fa !== false) {
1588 if (this.href !== false) {
1589 cfg.href = this.href;
1593 if(this.alt !== false){
1598 if(this.target !== false) {
1599 cfg.target = this.target;
1605 initEvents: function() {
1607 if(!this.href || this.preventDefault){
1608 this.el.on('click', this.onClick, this);
1612 onClick : function(e)
1614 if(this.preventDefault){
1617 //Roo.log('img onclick');
1618 this.fireEvent('click', this, e);
1631 * @class Roo.bootstrap.Header
1632 * @extends Roo.bootstrap.Component
1633 * Bootstrap Header class
1634 * @cfg {String} html content of header
1635 * @cfg {Number} level (1|2|3|4|5|6) default 1
1638 * Create a new Header
1639 * @param {Object} config The config object
1643 Roo.bootstrap.Header = function(config){
1644 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1647 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1655 getAutoCreate : function(){
1660 tag: 'h' + (1 *this.level),
1661 html: this.html || ''
1673 * Ext JS Library 1.1.1
1674 * Copyright(c) 2006-2007, Ext JS, LLC.
1676 * Originally Released Under LGPL - original licence link has changed is not relivant.
1679 * <script type="text/javascript">
1683 * @class Roo.bootstrap.MenuMgr
1684 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1687 Roo.bootstrap.MenuMgr = function(){
1688 var menus, active, groups = {}, attached = false, lastShow = new Date();
1690 // private - called when first menu is created
1693 active = new Roo.util.MixedCollection();
1694 Roo.get(document).addKeyListener(27, function(){
1695 if(active.length > 0){
1703 if(active && active.length > 0){
1704 var c = active.clone();
1714 if(active.length < 1){
1715 Roo.get(document).un("mouseup", onMouseDown);
1723 var last = active.last();
1724 lastShow = new Date();
1727 Roo.get(document).on("mouseup", onMouseDown);
1732 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1733 m.parentMenu.activeChild = m;
1734 }else if(last && last.isVisible()){
1735 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1740 function onBeforeHide(m){
1742 m.activeChild.hide();
1744 if(m.autoHideTimer){
1745 clearTimeout(m.autoHideTimer);
1746 delete m.autoHideTimer;
1751 function onBeforeShow(m){
1752 var pm = m.parentMenu;
1753 if(!pm && !m.allowOtherMenus){
1755 }else if(pm && pm.activeChild && active != m){
1756 pm.activeChild.hide();
1760 // private this should really trigger on mouseup..
1761 function onMouseDown(e){
1762 Roo.log("on Mouse Up");
1764 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1765 Roo.log("MenuManager hideAll");
1774 function onBeforeCheck(mi, state){
1776 var g = groups[mi.group];
1777 for(var i = 0, l = g.length; i < l; i++){
1779 g[i].setChecked(false);
1788 * Hides all menus that are currently visible
1790 hideAll : function(){
1795 register : function(menu){
1799 menus[menu.id] = menu;
1800 menu.on("beforehide", onBeforeHide);
1801 menu.on("hide", onHide);
1802 menu.on("beforeshow", onBeforeShow);
1803 menu.on("show", onShow);
1805 if(g && menu.events["checkchange"]){
1809 groups[g].push(menu);
1810 menu.on("checkchange", onCheck);
1815 * Returns a {@link Roo.menu.Menu} object
1816 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1817 * be used to generate and return a new Menu instance.
1819 get : function(menu){
1820 if(typeof menu == "string"){ // menu id
1822 }else if(menu.events){ // menu instance
1825 /*else if(typeof menu.length == 'number'){ // array of menu items?
1826 return new Roo.bootstrap.Menu({items:menu});
1827 }else{ // otherwise, must be a config
1828 return new Roo.bootstrap.Menu(menu);
1835 unregister : function(menu){
1836 delete menus[menu.id];
1837 menu.un("beforehide", onBeforeHide);
1838 menu.un("hide", onHide);
1839 menu.un("beforeshow", onBeforeShow);
1840 menu.un("show", onShow);
1842 if(g && menu.events["checkchange"]){
1843 groups[g].remove(menu);
1844 menu.un("checkchange", onCheck);
1849 registerCheckable : function(menuItem){
1850 var g = menuItem.group;
1855 groups[g].push(menuItem);
1856 menuItem.on("beforecheckchange", onBeforeCheck);
1861 unregisterCheckable : function(menuItem){
1862 var g = menuItem.group;
1864 groups[g].remove(menuItem);
1865 menuItem.un("beforecheckchange", onBeforeCheck);
1877 * @class Roo.bootstrap.Menu
1878 * @extends Roo.bootstrap.Component
1879 * Bootstrap Menu class - container for MenuItems
1880 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1881 * @cfg {bool} hidden if the menu should be hidden when rendered.
1885 * @param {Object} config The config object
1889 Roo.bootstrap.Menu = function(config){
1890 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1891 if (this.registerMenu && this.type != 'treeview') {
1892 Roo.bootstrap.MenuMgr.register(this);
1897 * Fires before this menu is displayed
1898 * @param {Roo.menu.Menu} this
1903 * Fires before this menu is hidden
1904 * @param {Roo.menu.Menu} this
1909 * Fires after this menu is displayed
1910 * @param {Roo.menu.Menu} this
1915 * Fires after this menu is hidden
1916 * @param {Roo.menu.Menu} this
1921 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1922 * @param {Roo.menu.Menu} this
1923 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1924 * @param {Roo.EventObject} e
1929 * Fires when the mouse is hovering over this menu
1930 * @param {Roo.menu.Menu} this
1931 * @param {Roo.EventObject} e
1932 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1937 * Fires when the mouse exits this menu
1938 * @param {Roo.menu.Menu} this
1939 * @param {Roo.EventObject} e
1940 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1945 * Fires when a menu item contained in this menu is clicked
1946 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1947 * @param {Roo.EventObject} e
1951 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1954 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1958 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1961 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1963 registerMenu : true,
1965 menuItems :false, // stores the menu items..
1972 getChildContainer : function() {
1976 getAutoCreate : function(){
1978 //if (['right'].indexOf(this.align)!==-1) {
1979 // cfg.cn[1].cls += ' pull-right'
1985 cls : 'dropdown-menu' ,
1986 style : 'z-index:1000'
1990 if (this.type === 'submenu') {
1991 cfg.cls = 'submenu active';
1993 if (this.type === 'treeview') {
1994 cfg.cls = 'treeview-menu';
1999 initEvents : function() {
2001 // Roo.log("ADD event");
2002 // Roo.log(this.triggerEl.dom);
2003 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2005 this.triggerEl.addClass('dropdown-toggle');
2009 this.el.on('touchstart' , this.onTouch, this);
2011 this.el.on('click' , this.onClick, this);
2013 this.el.on("mouseover", this.onMouseOver, this);
2014 this.el.on("mouseout", this.onMouseOut, this);
2018 findTargetItem : function(e)
2020 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2024 //Roo.log(t); Roo.log(t.id);
2026 //Roo.log(this.menuitems);
2027 return this.menuitems.get(t.id);
2029 //return this.items.get(t.menuItemId);
2035 onTouch : function(e)
2037 //e.stopEvent(); this make the user popdown broken
2041 onClick : function(e)
2043 Roo.log("menu.onClick");
2044 var t = this.findTargetItem(e);
2045 if(!t || t.isContainer){
2050 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2051 if(t == this.activeItem && t.shouldDeactivate(e)){
2052 this.activeItem.deactivate();
2053 delete this.activeItem;
2057 this.setActiveItem(t, true);
2065 Roo.log('pass click event');
2069 this.fireEvent("click", this, t, e);
2073 onMouseOver : function(e){
2074 var t = this.findTargetItem(e);
2077 // if(t.canActivate && !t.disabled){
2078 // this.setActiveItem(t, true);
2082 this.fireEvent("mouseover", this, e, t);
2084 isVisible : function(){
2085 return !this.hidden;
2087 onMouseOut : function(e){
2088 var t = this.findTargetItem(e);
2091 // if(t == this.activeItem && t.shouldDeactivate(e)){
2092 // this.activeItem.deactivate();
2093 // delete this.activeItem;
2096 this.fireEvent("mouseout", this, e, t);
2101 * Displays this menu relative to another element
2102 * @param {String/HTMLElement/Roo.Element} element The element to align to
2103 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2104 * the element (defaults to this.defaultAlign)
2105 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2107 show : function(el, pos, parentMenu){
2108 this.parentMenu = parentMenu;
2112 this.fireEvent("beforeshow", this);
2113 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2116 * Displays this menu at a specific xy position
2117 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2118 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2120 showAt : function(xy, parentMenu, /* private: */_e){
2121 this.parentMenu = parentMenu;
2126 this.fireEvent("beforeshow", this);
2127 //xy = this.el.adjustForConstraints(xy);
2131 this.hideMenuItems();
2132 this.hidden = false;
2133 this.triggerEl.addClass('open');
2135 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2136 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2141 this.fireEvent("show", this);
2147 this.doFocus.defer(50, this);
2151 doFocus : function(){
2153 this.focusEl.focus();
2158 * Hides this menu and optionally all parent menus
2159 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2161 hide : function(deep)
2164 this.hideMenuItems();
2165 if(this.el && this.isVisible()){
2166 this.fireEvent("beforehide", this);
2167 if(this.activeItem){
2168 this.activeItem.deactivate();
2169 this.activeItem = null;
2171 this.triggerEl.removeClass('open');;
2173 this.fireEvent("hide", this);
2175 if(deep === true && this.parentMenu){
2176 this.parentMenu.hide(true);
2180 onTriggerPress : function(e)
2183 Roo.log('trigger press');
2184 //Roo.log(e.getTarget());
2185 // Roo.log(this.triggerEl.dom);
2187 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2188 var pel = Roo.get(e.getTarget());
2189 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2194 if (this.isVisible()) {
2199 this.show(this.triggerEl, false, false);
2208 hideMenuItems : function()
2210 Roo.log("hide Menu Items");
2214 //$(backdrop).remove()
2215 this.el.select('.open',true).each(function(aa) {
2217 aa.removeClass('open');
2218 //var parent = getParent($(this))
2219 //var relatedTarget = { relatedTarget: this }
2221 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2222 //if (e.isDefaultPrevented()) return
2223 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2226 addxtypeChild : function (tree, cntr) {
2227 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2229 this.menuitems.add(comp);
2250 * @class Roo.bootstrap.MenuItem
2251 * @extends Roo.bootstrap.Component
2252 * Bootstrap MenuItem class
2253 * @cfg {String} html the menu label
2254 * @cfg {String} href the link
2255 * @cfg {Boolean} preventDefault do not trigger A href on clicks.
2256 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2257 * @cfg {Boolean} active used on sidebars to highlight active itesm
2258 * @cfg {String} fa favicon to show on left of menu item.
2259 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2263 * Create a new MenuItem
2264 * @param {Object} config The config object
2268 Roo.bootstrap.MenuItem = function(config){
2269 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2274 * The raw click event for the entire grid.
2275 * @param {Roo.bootstrap.MenuItem} this
2276 * @param {Roo.EventObject} e
2282 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2286 preventDefault: true,
2287 isContainer : false,
2291 getAutoCreate : function(){
2293 if(this.isContainer){
2296 cls: 'dropdown-menu-item'
2310 if (this.fa !== false) {
2313 cls : 'fa fa-' + this.fa
2322 cls: 'dropdown-menu-item',
2325 if (this.parent().type == 'treeview') {
2326 cfg.cls = 'treeview-menu';
2329 cfg.cls += ' active';
2334 anc.href = this.href || cfg.cn[0].href ;
2335 ctag.html = this.html || cfg.cn[0].html ;
2339 initEvents: function()
2341 if (this.parent().type == 'treeview') {
2342 this.el.select('a').on('click', this.onClick, this);
2345 this.menu.parentType = this.xtype;
2346 this.menu.triggerEl = this.el;
2347 this.menu = this.addxtype(Roo.apply({}, this.menu));
2351 onClick : function(e)
2353 Roo.log('item on click ');
2354 //if(this.preventDefault){
2355 // e.preventDefault();
2357 //this.parent().hideMenuItems();
2359 this.fireEvent('click', this, e);
2378 * @class Roo.bootstrap.MenuSeparator
2379 * @extends Roo.bootstrap.Component
2380 * Bootstrap MenuSeparator class
2383 * Create a new MenuItem
2384 * @param {Object} config The config object
2388 Roo.bootstrap.MenuSeparator = function(config){
2389 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2392 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2394 getAutoCreate : function(){
2413 * @class Roo.bootstrap.Modal
2414 * @extends Roo.bootstrap.Component
2415 * Bootstrap Modal class
2416 * @cfg {String} title Title of dialog
2417 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2418 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2419 * @cfg {Boolean} specificTitle default false
2420 * @cfg {Array} buttons Array of buttons or standard button set..
2421 * @cfg {String} buttonPosition (left|right|center) default right
2422 * @cfg {Boolean} animate default true
2423 * @cfg {Boolean} allow_close default true
2426 * Create a new Modal Dialog
2427 * @param {Object} config The config object
2430 Roo.bootstrap.Modal = function(config){
2431 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2436 * The raw btnclick event for the button
2437 * @param {Roo.EventObject} e
2441 this.buttons = this.buttons || [];
2444 this.tmpl = Roo.factory(this.tmpl);
2449 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2451 title : 'test dialog',
2461 specificTitle: false,
2463 buttonPosition: 'right',
2477 onRender : function(ct, position)
2479 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2482 var cfg = Roo.apply({}, this.getAutoCreate());
2485 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2487 //if (!cfg.name.length) {
2491 cfg.cls += ' ' + this.cls;
2494 cfg.style = this.style;
2496 this.el = Roo.get(document.body).createChild(cfg, position);
2498 //var type = this.el.dom.type;
2501 if(this.tabIndex !== undefined){
2502 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2506 this.bodyEl = this.el.select('.modal-body',true).first();
2507 this.closeEl = this.el.select('.modal-header .close', true).first();
2508 this.footerEl = this.el.select('.modal-footer',true).first();
2509 this.titleEl = this.el.select('.modal-title',true).first();
2513 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2514 this.maskEl.enableDisplayMode("block");
2516 //this.el.addClass("x-dlg-modal");
2518 if (this.buttons.length) {
2519 Roo.each(this.buttons, function(bb) {
2520 var b = Roo.apply({}, bb);
2521 b.xns = b.xns || Roo.bootstrap;
2522 b.xtype = b.xtype || 'Button';
2523 if (typeof(b.listeners) == 'undefined') {
2524 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2527 var btn = Roo.factory(b);
2529 btn.render(this.el.select('.modal-footer div').first());
2533 // render the children.
2536 if(typeof(this.items) != 'undefined'){
2537 var items = this.items;
2540 for(var i =0;i < items.length;i++) {
2541 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2545 this.items = nitems;
2547 // where are these used - they used to be body/close/footer
2551 //this.el.addClass([this.fieldClass, this.cls]);
2555 getAutoCreate : function(){
2560 html : this.html || ''
2565 cls : 'modal-title',
2569 if(this.specificTitle){
2575 if (this.allow_close) {
2586 style : 'display: none',
2589 cls: "modal-dialog",
2592 cls : "modal-content",
2595 cls : 'modal-header',
2600 cls : 'modal-footer',
2604 cls: 'btn-' + this.buttonPosition
2621 modal.cls += ' fade';
2627 getChildContainer : function() {
2632 getButtonContainer : function() {
2633 return this.el.select('.modal-footer div',true).first();
2636 initEvents : function()
2638 if (this.allow_close) {
2639 this.closeEl.on('click', this.hide, this);
2644 window.addEventListener("resize", function() { _this.resize(); } );
2650 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2655 if (!this.rendered) {
2659 this.el.setStyle('display', 'block');
2661 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2664 this.el.addClass('in');
2667 this.el.addClass('in');
2671 // not sure how we can show data in here..
2673 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2676 Roo.get(document.body).addClass("x-body-masked");
2677 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2679 this.el.setStyle('zIndex', '10001');
2681 this.fireEvent('show', this);
2689 Roo.get(document.body).removeClass("x-body-masked");
2690 this.el.removeClass('in');
2691 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2693 if(this.animate){ // why
2695 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2697 this.el.setStyle('display', 'none');
2700 this.fireEvent('hide', this);
2703 addButton : function(str, cb)
2707 var b = Roo.apply({}, { html : str } );
2708 b.xns = b.xns || Roo.bootstrap;
2709 b.xtype = b.xtype || 'Button';
2710 if (typeof(b.listeners) == 'undefined') {
2711 b.listeners = { click : cb.createDelegate(this) };
2714 var btn = Roo.factory(b);
2716 btn.render(this.el.select('.modal-footer div').first());
2722 setDefaultButton : function(btn)
2724 //this.el.select('.modal-footer').()
2728 resizeTo: function(w,h)
2732 this.el.select('.modal-dialog',true).first().setWidth(w);
2733 if (this.diff === false) {
2734 this.diff = this.el.select('.modal-dialog',true).first().getHeight() - this.el.select('.modal-body',true).first().getHeight();
2737 this.el.select('.modal-body',true).first().setHeight(h-this.diff);
2741 setContentSize : function(w, h)
2745 onButtonClick: function(btn,e)
2748 this.fireEvent('btnclick', btn.name, e);
2751 * Set the title of the Dialog
2752 * @param {String} str new Title
2754 setTitle: function(str) {
2755 this.titleEl.dom.innerHTML = str;
2758 * Set the body of the Dialog
2759 * @param {String} str new Title
2761 setBody: function(str) {
2762 this.bodyEl.dom.innerHTML = str;
2765 * Set the body of the Dialog using the template
2766 * @param {Obj} data - apply this data to the template and replace the body contents.
2768 applyBody: function(obj)
2771 Roo.log("Error - using apply Body without a template");
2774 this.tmpl.overwrite(this.bodyEl, obj);
2780 Roo.apply(Roo.bootstrap.Modal, {
2782 * Button config that displays a single OK button
2791 * Button config that displays Yes and No buttons
2807 * Button config that displays OK and Cancel buttons
2822 * Button config that displays Yes, No and Cancel buttons
2845 * messagebox - can be used as a replace
2849 * @class Roo.MessageBox
2850 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2854 Roo.Msg.alert('Status', 'Changes saved successfully.');
2856 // Prompt for user data:
2857 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2859 // process text value...
2863 // Show a dialog using config options:
2865 title:'Save Changes?',
2866 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2867 buttons: Roo.Msg.YESNOCANCEL,
2874 Roo.bootstrap.MessageBox = function(){
2875 var dlg, opt, mask, waitTimer;
2876 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2877 var buttons, activeTextEl, bwidth;
2881 var handleButton = function(button){
2883 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2887 var handleHide = function(){
2889 dlg.el.removeClass(opt.cls);
2892 // Roo.TaskMgr.stop(waitTimer);
2893 // waitTimer = null;
2898 var updateButtons = function(b){
2901 buttons["ok"].hide();
2902 buttons["cancel"].hide();
2903 buttons["yes"].hide();
2904 buttons["no"].hide();
2905 //dlg.footer.dom.style.display = 'none';
2908 dlg.footerEl.dom.style.display = '';
2909 for(var k in buttons){
2910 if(typeof buttons[k] != "function"){
2913 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2914 width += buttons[k].el.getWidth()+15;
2924 var handleEsc = function(d, k, e){
2925 if(opt && opt.closable !== false){
2935 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2936 * @return {Roo.BasicDialog} The BasicDialog element
2938 getDialog : function(){
2940 dlg = new Roo.bootstrap.Modal( {
2943 //constraintoviewport:false,
2945 //collapsible : false,
2950 //buttonAlign:"center",
2951 closeClick : function(){
2952 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2955 handleButton("cancel");
2960 dlg.on("hide", handleHide);
2962 //dlg.addKeyListener(27, handleEsc);
2964 this.buttons = buttons;
2965 var bt = this.buttonText;
2966 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2967 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2968 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2969 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2971 bodyEl = dlg.bodyEl.createChild({
2973 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2974 '<textarea class="roo-mb-textarea"></textarea>' +
2975 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2977 msgEl = bodyEl.dom.firstChild;
2978 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2979 textboxEl.enableDisplayMode();
2980 textboxEl.addKeyListener([10,13], function(){
2981 if(dlg.isVisible() && opt && opt.buttons){
2984 }else if(opt.buttons.yes){
2985 handleButton("yes");
2989 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2990 textareaEl.enableDisplayMode();
2991 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2992 progressEl.enableDisplayMode();
2993 var pf = progressEl.dom.firstChild;
2995 pp = Roo.get(pf.firstChild);
2996 pp.setHeight(pf.offsetHeight);
3004 * Updates the message box body text
3005 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3006 * the XHTML-compliant non-breaking space character '&#160;')
3007 * @return {Roo.MessageBox} This message box
3009 updateText : function(text){
3010 if(!dlg.isVisible() && !opt.width){
3011 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
3013 msgEl.innerHTML = text || ' ';
3015 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3016 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3018 Math.min(opt.width || cw , this.maxWidth),
3019 Math.max(opt.minWidth || this.minWidth, bwidth)
3022 activeTextEl.setWidth(w);
3024 if(dlg.isVisible()){
3025 dlg.fixedcenter = false;
3027 // to big, make it scroll. = But as usual stupid IE does not support
3030 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3031 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3032 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3034 bodyEl.dom.style.height = '';
3035 bodyEl.dom.style.overflowY = '';
3038 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3040 bodyEl.dom.style.overflowX = '';
3043 dlg.setContentSize(w, bodyEl.getHeight());
3044 if(dlg.isVisible()){
3045 dlg.fixedcenter = true;
3051 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3052 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3053 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3054 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3055 * @return {Roo.MessageBox} This message box
3057 updateProgress : function(value, text){
3059 this.updateText(text);
3061 if (pp) { // weird bug on my firefox - for some reason this is not defined
3062 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3068 * Returns true if the message box is currently displayed
3069 * @return {Boolean} True if the message box is visible, else false
3071 isVisible : function(){
3072 return dlg && dlg.isVisible();
3076 * Hides the message box if it is displayed
3079 if(this.isVisible()){
3085 * Displays a new message box, or reinitializes an existing message box, based on the config options
3086 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3087 * The following config object properties are supported:
3089 Property Type Description
3090 ---------- --------------- ------------------------------------------------------------------------------------
3091 animEl String/Element An id or Element from which the message box should animate as it opens and
3092 closes (defaults to undefined)
3093 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3094 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3095 closable Boolean False to hide the top-right close button (defaults to true). Note that
3096 progress and wait dialogs will ignore this property and always hide the
3097 close button as they can only be closed programmatically.
3098 cls String A custom CSS class to apply to the message box element
3099 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3100 displayed (defaults to 75)
3101 fn Function A callback function to execute after closing the dialog. The arguments to the
3102 function will be btn (the name of the button that was clicked, if applicable,
3103 e.g. "ok"), and text (the value of the active text field, if applicable).
3104 Progress and wait dialogs will ignore this option since they do not respond to
3105 user actions and can only be closed programmatically, so any required function
3106 should be called by the same code after it closes the dialog.
3107 icon String A CSS class that provides a background image to be used as an icon for
3108 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3109 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3110 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3111 modal Boolean False to allow user interaction with the page while the message box is
3112 displayed (defaults to true)
3113 msg String A string that will replace the existing message box body text (defaults
3114 to the XHTML-compliant non-breaking space character ' ')
3115 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3116 progress Boolean True to display a progress bar (defaults to false)
3117 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3118 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3119 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3120 title String The title text
3121 value String The string value to set into the active textbox element if displayed
3122 wait Boolean True to display a progress bar (defaults to false)
3123 width Number The width of the dialog in pixels
3130 msg: 'Please enter your address:',
3132 buttons: Roo.MessageBox.OKCANCEL,
3135 animEl: 'addAddressBtn'
3138 * @param {Object} config Configuration options
3139 * @return {Roo.MessageBox} This message box
3141 show : function(options)
3144 // this causes nightmares if you show one dialog after another
3145 // especially on callbacks..
3147 if(this.isVisible()){
3150 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3151 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3152 Roo.log("New Dialog Message:" + options.msg )
3153 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3154 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3157 var d = this.getDialog();
3159 d.setTitle(opt.title || " ");
3160 d.closeEl.setDisplayed(opt.closable !== false);
3161 activeTextEl = textboxEl;
3162 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3167 textareaEl.setHeight(typeof opt.multiline == "number" ?
3168 opt.multiline : this.defaultTextHeight);
3169 activeTextEl = textareaEl;
3178 progressEl.setDisplayed(opt.progress === true);
3179 this.updateProgress(0);
3180 activeTextEl.dom.value = opt.value || "";
3182 dlg.setDefaultButton(activeTextEl);
3184 var bs = opt.buttons;
3188 }else if(bs && bs.yes){
3189 db = buttons["yes"];
3191 dlg.setDefaultButton(db);
3193 bwidth = updateButtons(opt.buttons);
3194 this.updateText(opt.msg);
3196 d.el.addClass(opt.cls);
3198 d.proxyDrag = opt.proxyDrag === true;
3199 d.modal = opt.modal !== false;
3200 d.mask = opt.modal !== false ? mask : false;
3202 // force it to the end of the z-index stack so it gets a cursor in FF
3203 document.body.appendChild(dlg.el.dom);
3204 d.animateTarget = null;
3205 d.show(options.animEl);
3211 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3212 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3213 * and closing the message box when the process is complete.
3214 * @param {String} title The title bar text
3215 * @param {String} msg The message box body text
3216 * @return {Roo.MessageBox} This message box
3218 progress : function(title, msg){
3225 minWidth: this.minProgressWidth,
3232 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3233 * If a callback function is passed it will be called after the user clicks the button, and the
3234 * id of the button that was clicked will be passed as the only parameter to the callback
3235 * (could also be the top-right close button).
3236 * @param {String} title The title bar text
3237 * @param {String} msg The message box body text
3238 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3239 * @param {Object} scope (optional) The scope of the callback function
3240 * @return {Roo.MessageBox} This message box
3242 alert : function(title, msg, fn, scope){
3255 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3256 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3257 * You are responsible for closing the message box when the process is complete.
3258 * @param {String} msg The message box body text
3259 * @param {String} title (optional) The title bar text
3260 * @return {Roo.MessageBox} This message box
3262 wait : function(msg, title){
3273 waitTimer = Roo.TaskMgr.start({
3275 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3283 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3284 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3285 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3286 * @param {String} title The title bar text
3287 * @param {String} msg The message box body text
3288 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3289 * @param {Object} scope (optional) The scope of the callback function
3290 * @return {Roo.MessageBox} This message box
3292 confirm : function(title, msg, fn, scope){
3296 buttons: this.YESNO,
3305 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3306 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3307 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3308 * (could also be the top-right close button) and the text that was entered will be passed as the two
3309 * parameters to the callback.
3310 * @param {String} title The title bar text
3311 * @param {String} msg The message box body text
3312 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3313 * @param {Object} scope (optional) The scope of the callback function
3314 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3315 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3316 * @return {Roo.MessageBox} This message box
3318 prompt : function(title, msg, fn, scope, multiline){
3322 buttons: this.OKCANCEL,
3327 multiline: multiline,
3334 * Button config that displays a single OK button
3339 * Button config that displays Yes and No buttons
3342 YESNO : {yes:true, no:true},
3344 * Button config that displays OK and Cancel buttons
3347 OKCANCEL : {ok:true, cancel:true},
3349 * Button config that displays Yes, No and Cancel buttons
3352 YESNOCANCEL : {yes:true, no:true, cancel:true},
3355 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3358 defaultTextHeight : 75,
3360 * The maximum width in pixels of the message box (defaults to 600)
3365 * The minimum width in pixels of the message box (defaults to 100)
3370 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3371 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3374 minProgressWidth : 250,
3376 * An object containing the default button text strings that can be overriden for localized language support.
3377 * Supported properties are: ok, cancel, yes and no.
3378 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3391 * Shorthand for {@link Roo.MessageBox}
3393 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3394 Roo.Msg = Roo.Msg || Roo.MessageBox;
3403 * @class Roo.bootstrap.Navbar
3404 * @extends Roo.bootstrap.Component
3405 * Bootstrap Navbar class
3408 * Create a new Navbar
3409 * @param {Object} config The config object
3413 Roo.bootstrap.Navbar = function(config){
3414 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3418 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3427 getAutoCreate : function(){
3430 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3434 initEvents :function ()
3436 //Roo.log(this.el.select('.navbar-toggle',true));
3437 this.el.select('.navbar-toggle',true).on('click', function() {
3438 // Roo.log('click');
3439 this.el.select('.navbar-collapse',true).toggleClass('in');
3447 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3449 var size = this.el.getSize();
3450 this.maskEl.setSize(size.width, size.height);
3451 this.maskEl.enableDisplayMode("block");
3460 getChildContainer : function()
3462 if (this.el.select('.collapse').getCount()) {
3463 return this.el.select('.collapse',true).first();
3496 * @class Roo.bootstrap.NavSimplebar
3497 * @extends Roo.bootstrap.Navbar
3498 * Bootstrap Sidebar class
3500 * @cfg {Boolean} inverse is inverted color
3502 * @cfg {String} type (nav | pills | tabs)
3503 * @cfg {Boolean} arrangement stacked | justified
3504 * @cfg {String} align (left | right) alignment
3506 * @cfg {Boolean} main (true|false) main nav bar? default false
3507 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3509 * @cfg {String} tag (header|footer|nav|div) default is nav
3515 * Create a new Sidebar
3516 * @param {Object} config The config object
3520 Roo.bootstrap.NavSimplebar = function(config){
3521 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3524 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3540 getAutoCreate : function(){
3544 tag : this.tag || 'div',
3557 this.type = this.type || 'nav';
3558 if (['tabs','pills'].indexOf(this.type)!==-1) {
3559 cfg.cn[0].cls += ' nav-' + this.type
3563 if (this.type!=='nav') {
3564 Roo.log('nav type must be nav/tabs/pills')
3566 cfg.cn[0].cls += ' navbar-nav'
3572 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3573 cfg.cn[0].cls += ' nav-' + this.arrangement;
3577 if (this.align === 'right') {
3578 cfg.cn[0].cls += ' navbar-right';
3582 cfg.cls += ' navbar-inverse';
3609 * @class Roo.bootstrap.NavHeaderbar
3610 * @extends Roo.bootstrap.NavSimplebar
3611 * Bootstrap Sidebar class
3613 * @cfg {String} brand what is brand
3614 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3615 * @cfg {String} brand_href href of the brand
3616 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3617 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3618 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3619 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3622 * Create a new Sidebar
3623 * @param {Object} config The config object
3627 Roo.bootstrap.NavHeaderbar = function(config){
3628 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3632 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3639 desktopCenter : false,
3642 getAutoCreate : function(){
3645 tag: this.nav || 'nav',
3652 if (this.desktopCenter) {
3653 cn.push({cls : 'container', cn : []});
3660 cls: 'navbar-header',
3665 cls: 'navbar-toggle',
3666 'data-toggle': 'collapse',
3671 html: 'Toggle navigation'
3693 cls: 'collapse navbar-collapse',
3697 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3699 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3700 cfg.cls += ' navbar-' + this.position;
3702 // tag can override this..
3704 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3707 if (this.brand !== '') {
3710 href: this.brand_href ? this.brand_href : '#',
3711 cls: 'navbar-brand',
3719 cfg.cls += ' main-nav';
3727 getHeaderChildContainer : function()
3729 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3730 return this.el.select('.navbar-header',true).first();
3733 return this.getChildContainer();
3737 initEvents : function()
3739 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3741 if (this.autohide) {
3746 Roo.get(document).on('scroll',function(e) {
3747 var ns = Roo.get(document).getScroll().top;
3748 var os = prevScroll;
3752 ft.removeClass('slideDown');
3753 ft.addClass('slideUp');
3756 ft.removeClass('slideUp');
3757 ft.addClass('slideDown');
3778 * @class Roo.bootstrap.NavSidebar
3779 * @extends Roo.bootstrap.Navbar
3780 * Bootstrap Sidebar class
3783 * Create a new Sidebar
3784 * @param {Object} config The config object
3788 Roo.bootstrap.NavSidebar = function(config){
3789 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3792 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3794 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3796 getAutoCreate : function(){
3801 cls: 'sidebar sidebar-nav'
3823 * @class Roo.bootstrap.NavGroup
3824 * @extends Roo.bootstrap.Component
3825 * Bootstrap NavGroup class
3826 * @cfg {String} align (left|right)
3827 * @cfg {Boolean} inverse
3828 * @cfg {String} type (nav|pills|tab) default nav
3829 * @cfg {String} navId - reference Id for navbar.
3833 * Create a new nav group
3834 * @param {Object} config The config object
3837 Roo.bootstrap.NavGroup = function(config){
3838 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3841 Roo.bootstrap.NavGroup.register(this);
3845 * Fires when the active item changes
3846 * @param {Roo.bootstrap.NavGroup} this
3847 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3848 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3855 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3866 getAutoCreate : function()
3868 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3875 if (['tabs','pills'].indexOf(this.type)!==-1) {
3876 cfg.cls += ' nav-' + this.type
3878 if (this.type!=='nav') {
3879 Roo.log('nav type must be nav/tabs/pills')
3881 cfg.cls += ' navbar-nav'
3884 if (this.parent().sidebar) {
3887 cls: 'dashboard-menu sidebar-menu'
3893 if (this.form === true) {
3899 if (this.align === 'right') {
3900 cfg.cls += ' navbar-right';
3902 cfg.cls += ' navbar-left';
3906 if (this.align === 'right') {
3907 cfg.cls += ' navbar-right';
3911 cfg.cls += ' navbar-inverse';
3919 * sets the active Navigation item
3920 * @param {Roo.bootstrap.NavItem} the new current navitem
3922 setActiveItem : function(item)
3925 Roo.each(this.navItems, function(v){
3930 v.setActive(false, true);
3937 item.setActive(true, true);
3938 this.fireEvent('changed', this, item, prev);
3943 * gets the active Navigation item
3944 * @return {Roo.bootstrap.NavItem} the current navitem
3946 getActive : function()
3950 Roo.each(this.navItems, function(v){
3961 indexOfNav : function()
3965 Roo.each(this.navItems, function(v,i){
3976 * adds a Navigation item
3977 * @param {Roo.bootstrap.NavItem} the navitem to add
3979 addItem : function(cfg)
3981 var cn = new Roo.bootstrap.NavItem(cfg);
3983 cn.parentId = this.id;
3984 cn.onRender(this.el, null);
3988 * register a Navigation item
3989 * @param {Roo.bootstrap.NavItem} the navitem to add
3991 register : function(item)
3993 this.navItems.push( item);
3994 item.navId = this.navId;
3999 * clear all the Navigation item
4002 clearAll : function()
4005 this.el.dom.innerHTML = '';
4008 getNavItem: function(tabId)
4011 Roo.each(this.navItems, function(e) {
4012 if (e.tabId == tabId) {
4022 setActiveNext : function()
4024 var i = this.indexOfNav(this.getActive());
4025 if (i > this.navItems.length) {
4028 this.setActiveItem(this.navItems[i+1]);
4030 setActivePrev : function()
4032 var i = this.indexOfNav(this.getActive());
4036 this.setActiveItem(this.navItems[i-1]);
4038 clearWasActive : function(except) {
4039 Roo.each(this.navItems, function(e) {
4040 if (e.tabId != except.tabId && e.was_active) {
4041 e.was_active = false;
4048 getWasActive : function ()
4051 Roo.each(this.navItems, function(e) {
4066 Roo.apply(Roo.bootstrap.NavGroup, {
4070 * register a Navigation Group
4071 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4073 register : function(navgrp)
4075 this.groups[navgrp.navId] = navgrp;
4079 * fetch a Navigation Group based on the navigation ID
4080 * @param {string} the navgroup to add
4081 * @returns {Roo.bootstrap.NavGroup} the navgroup
4083 get: function(navId) {
4084 if (typeof(this.groups[navId]) == 'undefined') {
4086 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4088 return this.groups[navId] ;
4103 * @class Roo.bootstrap.NavItem
4104 * @extends Roo.bootstrap.Component
4105 * Bootstrap Navbar.NavItem class
4106 * @cfg {String} href link to
4107 * @cfg {String} html content of button
4108 * @cfg {String} badge text inside badge
4109 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4110 * @cfg {String} glyphicon name of glyphicon
4111 * @cfg {String} icon name of font awesome icon
4112 * @cfg {Boolean} active Is item active
4113 * @cfg {Boolean} disabled Is item disabled
4115 * @cfg {Boolean} preventDefault (true | false) default false
4116 * @cfg {String} tabId the tab that this item activates.
4117 * @cfg {String} tagtype (a|span) render as a href or span?
4118 * @cfg {Boolean} animateRef (true|false) link to element default false
4121 * Create a new Navbar Item
4122 * @param {Object} config The config object
4124 Roo.bootstrap.NavItem = function(config){
4125 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4130 * The raw click event for the entire grid.
4131 * @param {Roo.EventObject} e
4136 * Fires when the active item active state changes
4137 * @param {Roo.bootstrap.NavItem} this
4138 * @param {boolean} state the new state
4144 * Fires when scroll to element
4145 * @param {Roo.bootstrap.NavItem} this
4146 * @param {Object} options
4147 * @param {Roo.EventObject} e
4155 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4163 preventDefault : false,
4170 getAutoCreate : function(){
4179 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4181 if (this.disabled) {
4182 cfg.cls += ' disabled';
4185 if (this.href || this.html || this.glyphicon || this.icon) {
4189 href : this.href || "#",
4190 html: this.html || ''
4195 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4198 if(this.glyphicon) {
4199 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4204 cfg.cn[0].html += " <span class='caret'></span>";
4208 if (this.badge !== '') {
4210 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4218 initEvents: function()
4220 if (typeof (this.menu) != 'undefined') {
4221 this.menu.parentType = this.xtype;
4222 this.menu.triggerEl = this.el;
4223 this.menu = this.addxtype(Roo.apply({}, this.menu));
4226 this.el.select('a',true).on('click', this.onClick, this);
4228 if(this.tagtype == 'span'){
4229 this.el.select('span',true).on('click', this.onClick, this);
4232 // at this point parent should be available..
4233 this.parent().register(this);
4236 onClick : function(e)
4238 if (e.getTarget('.dropdown-menu-item')) {
4239 // did you click on a menu itemm.... - then don't trigger onclick..
4244 this.preventDefault ||
4247 Roo.log("NavItem - prevent Default?");
4251 if (this.disabled) {
4255 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4256 if (tg && tg.transition) {
4257 Roo.log("waiting for the transitionend");
4263 //Roo.log("fire event clicked");
4264 if(this.fireEvent('click', this, e) === false){
4268 if(this.tagtype == 'span'){
4272 //Roo.log(this.href);
4273 var ael = this.el.select('a',true).first();
4276 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4277 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4278 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4279 return; // ignore... - it's a 'hash' to another page.
4281 Roo.log("NavItem - prevent Default?");
4283 this.scrollToElement(e);
4287 var p = this.parent();
4289 if (['tabs','pills'].indexOf(p.type)!==-1) {
4290 if (typeof(p.setActiveItem) !== 'undefined') {
4291 p.setActiveItem(this);
4295 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4296 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4297 // remove the collapsed menu expand...
4298 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4302 isActive: function () {
4305 setActive : function(state, fire, is_was_active)
4307 if (this.active && !state && this.navId) {
4308 this.was_active = true;
4309 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4311 nv.clearWasActive(this);
4315 this.active = state;
4318 this.el.removeClass('active');
4319 } else if (!this.el.hasClass('active')) {
4320 this.el.addClass('active');
4323 this.fireEvent('changed', this, state);
4326 // show a panel if it's registered and related..
4328 if (!this.navId || !this.tabId || !state || is_was_active) {
4332 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4336 var pan = tg.getPanelByName(this.tabId);
4340 // if we can not flip to new panel - go back to old nav highlight..
4341 if (false == tg.showPanel(pan)) {
4342 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4344 var onav = nv.getWasActive();
4346 onav.setActive(true, false, true);
4355 // this should not be here...
4356 setDisabled : function(state)
4358 this.disabled = state;
4360 this.el.removeClass('disabled');
4361 } else if (!this.el.hasClass('disabled')) {
4362 this.el.addClass('disabled');
4368 * Fetch the element to display the tooltip on.
4369 * @return {Roo.Element} defaults to this.el
4371 tooltipEl : function()
4373 return this.el.select('' + this.tagtype + '', true).first();
4376 scrollToElement : function(e)
4378 var c = document.body;
4381 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4383 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4384 c = document.documentElement;
4387 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4393 var o = target.calcOffsetsTo(c);
4400 this.fireEvent('scrollto', this, options, e);
4402 Roo.get(c).scrollTo('top', options.value, true);
4415 * <span> icon </span>
4416 * <span> text </span>
4417 * <span>badge </span>
4421 * @class Roo.bootstrap.NavSidebarItem
4422 * @extends Roo.bootstrap.NavItem
4423 * Bootstrap Navbar.NavSidebarItem class
4424 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4425 * {bool} open is the menu open
4427 * Create a new Navbar Button
4428 * @param {Object} config The config object
4430 Roo.bootstrap.NavSidebarItem = function(config){
4431 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4436 * The raw click event for the entire grid.
4437 * @param {Roo.EventObject} e
4442 * Fires when the active item active state changes
4443 * @param {Roo.bootstrap.NavSidebarItem} this
4444 * @param {boolean} state the new state
4452 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4454 badgeWeight : 'default',
4458 getAutoCreate : function(){
4463 href : this.href || '#',
4475 html : this.html || ''
4480 cfg.cls += ' active';
4483 if (this.disabled) {
4484 cfg.cls += ' disabled';
4487 cfg.cls += ' open x-open';
4490 if (this.glyphicon || this.icon) {
4491 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4492 a.cn.push({ tag : 'i', cls : c }) ;
4497 if (this.badge !== '') {
4499 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4503 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4504 a.cls += 'dropdown-toggle treeview' ;
4515 initEvents : function()
4517 if (typeof (this.menu) != 'undefined') {
4518 this.menu.parentType = this.xtype;
4519 this.menu.triggerEl = this.el;
4520 this.menu = this.addxtype(Roo.apply({}, this.menu));
4523 this.el.on('click', this.onClick, this);
4526 if(this.badge !== ''){
4528 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4533 onClick : function(e)
4540 if(this.preventDefault){
4544 this.fireEvent('click', this);
4547 disable : function()
4549 this.setDisabled(true);
4554 this.setDisabled(false);
4557 setDisabled : function(state)
4559 if(this.disabled == state){
4563 this.disabled = state;
4566 this.el.addClass('disabled');
4570 this.el.removeClass('disabled');
4575 setActive : function(state)
4577 if(this.active == state){
4581 this.active = state;
4584 this.el.addClass('active');
4588 this.el.removeClass('active');
4593 isActive: function ()
4598 setBadge : function(str)
4604 this.badgeEl.dom.innerHTML = str;
4621 * @class Roo.bootstrap.Row
4622 * @extends Roo.bootstrap.Component
4623 * Bootstrap Row class (contains columns...)
4627 * @param {Object} config The config object
4630 Roo.bootstrap.Row = function(config){
4631 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4634 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4636 getAutoCreate : function(){
4655 * @class Roo.bootstrap.Element
4656 * @extends Roo.bootstrap.Component
4657 * Bootstrap Element class
4658 * @cfg {String} html contents of the element
4659 * @cfg {String} tag tag of the element
4660 * @cfg {String} cls class of the element
4661 * @cfg {Boolean} preventDefault (true|false) default false
4662 * @cfg {Boolean} clickable (true|false) default false
4665 * Create a new Element
4666 * @param {Object} config The config object
4669 Roo.bootstrap.Element = function(config){
4670 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4676 * When a element is chick
4677 * @param {Roo.bootstrap.Element} this
4678 * @param {Roo.EventObject} e
4684 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4689 preventDefault: false,
4692 getAutoCreate : function(){
4703 initEvents: function()
4705 Roo.bootstrap.Element.superclass.initEvents.call(this);
4708 this.el.on('click', this.onClick, this);
4713 onClick : function(e)
4715 if(this.preventDefault){
4719 this.fireEvent('click', this, e);
4722 getValue : function()
4724 return this.el.dom.innerHTML;
4727 setValue : function(value)
4729 this.el.dom.innerHTML = value;
4744 * @class Roo.bootstrap.Pagination
4745 * @extends Roo.bootstrap.Component
4746 * Bootstrap Pagination class
4747 * @cfg {String} size xs | sm | md | lg
4748 * @cfg {Boolean} inverse false | true
4751 * Create a new Pagination
4752 * @param {Object} config The config object
4755 Roo.bootstrap.Pagination = function(config){
4756 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4759 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4765 getAutoCreate : function(){
4771 cfg.cls += ' inverse';
4777 cfg.cls += " " + this.cls;
4795 * @class Roo.bootstrap.PaginationItem
4796 * @extends Roo.bootstrap.Component
4797 * Bootstrap PaginationItem class
4798 * @cfg {String} html text
4799 * @cfg {String} href the link
4800 * @cfg {Boolean} preventDefault (true | false) default true
4801 * @cfg {Boolean} active (true | false) default false
4802 * @cfg {Boolean} disabled default false
4806 * Create a new PaginationItem
4807 * @param {Object} config The config object
4811 Roo.bootstrap.PaginationItem = function(config){
4812 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4817 * The raw click event for the entire grid.
4818 * @param {Roo.EventObject} e
4824 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4828 preventDefault: true,
4833 getAutoCreate : function(){
4839 href : this.href ? this.href : '#',
4840 html : this.html ? this.html : ''
4850 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4854 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4860 initEvents: function() {
4862 this.el.on('click', this.onClick, this);
4865 onClick : function(e)
4867 Roo.log('PaginationItem on click ');
4868 if(this.preventDefault){
4876 this.fireEvent('click', this, e);
4892 * @class Roo.bootstrap.Slider
4893 * @extends Roo.bootstrap.Component
4894 * Bootstrap Slider class
4897 * Create a new Slider
4898 * @param {Object} config The config object
4901 Roo.bootstrap.Slider = function(config){
4902 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4905 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4907 getAutoCreate : function(){
4911 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4915 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4927 * Ext JS Library 1.1.1
4928 * Copyright(c) 2006-2007, Ext JS, LLC.
4930 * Originally Released Under LGPL - original licence link has changed is not relivant.
4933 * <script type="text/javascript">
4938 * @class Roo.grid.ColumnModel
4939 * @extends Roo.util.Observable
4940 * This is the default implementation of a ColumnModel used by the Grid. It defines
4941 * the columns in the grid.
4944 var colModel = new Roo.grid.ColumnModel([
4945 {header: "Ticker", width: 60, sortable: true, locked: true},
4946 {header: "Company Name", width: 150, sortable: true},
4947 {header: "Market Cap.", width: 100, sortable: true},
4948 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4949 {header: "Employees", width: 100, sortable: true, resizable: false}
4954 * The config options listed for this class are options which may appear in each
4955 * individual column definition.
4956 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4958 * @param {Object} config An Array of column config objects. See this class's
4959 * config objects for details.
4961 Roo.grid.ColumnModel = function(config){
4963 * The config passed into the constructor
4965 this.config = config;
4968 // if no id, create one
4969 // if the column does not have a dataIndex mapping,
4970 // map it to the order it is in the config
4971 for(var i = 0, len = config.length; i < len; i++){
4973 if(typeof c.dataIndex == "undefined"){
4976 if(typeof c.renderer == "string"){
4977 c.renderer = Roo.util.Format[c.renderer];
4979 if(typeof c.id == "undefined"){
4982 if(c.editor && c.editor.xtype){
4983 c.editor = Roo.factory(c.editor, Roo.grid);
4985 if(c.editor && c.editor.isFormField){
4986 c.editor = new Roo.grid.GridEditor(c.editor);
4988 this.lookup[c.id] = c;
4992 * The width of columns which have no width specified (defaults to 100)
4995 this.defaultWidth = 100;
4998 * Default sortable of columns which have no sortable specified (defaults to false)
5001 this.defaultSortable = false;
5005 * @event widthchange
5006 * Fires when the width of a column changes.
5007 * @param {ColumnModel} this
5008 * @param {Number} columnIndex The column index
5009 * @param {Number} newWidth The new width
5011 "widthchange": true,
5013 * @event headerchange
5014 * Fires when the text of a header changes.
5015 * @param {ColumnModel} this
5016 * @param {Number} columnIndex The column index
5017 * @param {Number} newText The new header text
5019 "headerchange": true,
5021 * @event hiddenchange
5022 * Fires when a column is hidden or "unhidden".
5023 * @param {ColumnModel} this
5024 * @param {Number} columnIndex The column index
5025 * @param {Boolean} hidden true if hidden, false otherwise
5027 "hiddenchange": true,
5029 * @event columnmoved
5030 * Fires when a column is moved.
5031 * @param {ColumnModel} this
5032 * @param {Number} oldIndex
5033 * @param {Number} newIndex
5035 "columnmoved" : true,
5037 * @event columlockchange
5038 * Fires when a column's locked state is changed
5039 * @param {ColumnModel} this
5040 * @param {Number} colIndex
5041 * @param {Boolean} locked true if locked
5043 "columnlockchange" : true
5045 Roo.grid.ColumnModel.superclass.constructor.call(this);
5047 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5049 * @cfg {String} header The header text to display in the Grid view.
5052 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5053 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5054 * specified, the column's index is used as an index into the Record's data Array.
5057 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5058 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5061 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5062 * Defaults to the value of the {@link #defaultSortable} property.
5063 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5066 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5069 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5072 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5075 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5078 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5079 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5080 * default renderer uses the raw data value. If an object is returned (bootstrap only)
5081 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5084 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5087 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5090 * @cfg {String} cursor (Optional)
5093 * @cfg {String} tooltip (Optional)
5096 * @cfg {Number} xs (Optional)
5099 * @cfg {Number} sm (Optional)
5102 * @cfg {Number} md (Optional)
5105 * @cfg {Number} lg (Optional)
5108 * Returns the id of the column at the specified index.
5109 * @param {Number} index The column index
5110 * @return {String} the id
5112 getColumnId : function(index){
5113 return this.config[index].id;
5117 * Returns the column for a specified id.
5118 * @param {String} id The column id
5119 * @return {Object} the column
5121 getColumnById : function(id){
5122 return this.lookup[id];
5127 * Returns the column for a specified dataIndex.
5128 * @param {String} dataIndex The column dataIndex
5129 * @return {Object|Boolean} the column or false if not found
5131 getColumnByDataIndex: function(dataIndex){
5132 var index = this.findColumnIndex(dataIndex);
5133 return index > -1 ? this.config[index] : false;
5137 * Returns the index for a specified column id.
5138 * @param {String} id The column id
5139 * @return {Number} the index, or -1 if not found
5141 getIndexById : function(id){
5142 for(var i = 0, len = this.config.length; i < len; i++){
5143 if(this.config[i].id == id){
5151 * Returns the index for a specified column dataIndex.
5152 * @param {String} dataIndex The column dataIndex
5153 * @return {Number} the index, or -1 if not found
5156 findColumnIndex : function(dataIndex){
5157 for(var i = 0, len = this.config.length; i < len; i++){
5158 if(this.config[i].dataIndex == dataIndex){
5166 moveColumn : function(oldIndex, newIndex){
5167 var c = this.config[oldIndex];
5168 this.config.splice(oldIndex, 1);
5169 this.config.splice(newIndex, 0, c);
5170 this.dataMap = null;
5171 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5174 isLocked : function(colIndex){
5175 return this.config[colIndex].locked === true;
5178 setLocked : function(colIndex, value, suppressEvent){
5179 if(this.isLocked(colIndex) == value){
5182 this.config[colIndex].locked = value;
5184 this.fireEvent("columnlockchange", this, colIndex, value);
5188 getTotalLockedWidth : function(){
5190 for(var i = 0; i < this.config.length; i++){
5191 if(this.isLocked(i) && !this.isHidden(i)){
5192 this.totalWidth += this.getColumnWidth(i);
5198 getLockedCount : function(){
5199 for(var i = 0, len = this.config.length; i < len; i++){
5200 if(!this.isLocked(i)){
5205 return this.config.length;
5209 * Returns the number of columns.
5212 getColumnCount : function(visibleOnly){
5213 if(visibleOnly === true){
5215 for(var i = 0, len = this.config.length; i < len; i++){
5216 if(!this.isHidden(i)){
5222 return this.config.length;
5226 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5227 * @param {Function} fn
5228 * @param {Object} scope (optional)
5229 * @return {Array} result
5231 getColumnsBy : function(fn, scope){
5233 for(var i = 0, len = this.config.length; i < len; i++){
5234 var c = this.config[i];
5235 if(fn.call(scope||this, c, i) === true){
5243 * Returns true if the specified column is sortable.
5244 * @param {Number} col The column index
5247 isSortable : function(col){
5248 if(typeof this.config[col].sortable == "undefined"){
5249 return this.defaultSortable;
5251 return this.config[col].sortable;
5255 * Returns the rendering (formatting) function defined for the column.
5256 * @param {Number} col The column index.
5257 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5259 getRenderer : function(col){
5260 if(!this.config[col].renderer){
5261 return Roo.grid.ColumnModel.defaultRenderer;
5263 return this.config[col].renderer;
5267 * Sets the rendering (formatting) function for a column.
5268 * @param {Number} col The column index
5269 * @param {Function} fn The function to use to process the cell's raw data
5270 * to return HTML markup for the grid view. The render function is called with
5271 * the following parameters:<ul>
5272 * <li>Data value.</li>
5273 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5274 * <li>css A CSS style string to apply to the table cell.</li>
5275 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5276 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5277 * <li>Row index</li>
5278 * <li>Column index</li>
5279 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5281 setRenderer : function(col, fn){
5282 this.config[col].renderer = fn;
5286 * Returns the width for the specified column.
5287 * @param {Number} col The column index
5290 getColumnWidth : function(col){
5291 return this.config[col].width * 1 || this.defaultWidth;
5295 * Sets the width for a column.
5296 * @param {Number} col The column index
5297 * @param {Number} width The new width
5299 setColumnWidth : function(col, width, suppressEvent){
5300 this.config[col].width = width;
5301 this.totalWidth = null;
5303 this.fireEvent("widthchange", this, col, width);
5308 * Returns the total width of all columns.
5309 * @param {Boolean} includeHidden True to include hidden column widths
5312 getTotalWidth : function(includeHidden){
5313 if(!this.totalWidth){
5314 this.totalWidth = 0;
5315 for(var i = 0, len = this.config.length; i < len; i++){
5316 if(includeHidden || !this.isHidden(i)){
5317 this.totalWidth += this.getColumnWidth(i);
5321 return this.totalWidth;
5325 * Returns the header for the specified column.
5326 * @param {Number} col The column index
5329 getColumnHeader : function(col){
5330 return this.config[col].header;
5334 * Sets the header for a column.
5335 * @param {Number} col The column index
5336 * @param {String} header The new header
5338 setColumnHeader : function(col, header){
5339 this.config[col].header = header;
5340 this.fireEvent("headerchange", this, col, header);
5344 * Returns the tooltip for the specified column.
5345 * @param {Number} col The column index
5348 getColumnTooltip : function(col){
5349 return this.config[col].tooltip;
5352 * Sets the tooltip for a column.
5353 * @param {Number} col The column index
5354 * @param {String} tooltip The new tooltip
5356 setColumnTooltip : function(col, tooltip){
5357 this.config[col].tooltip = tooltip;
5361 * Returns the dataIndex for the specified column.
5362 * @param {Number} col The column index
5365 getDataIndex : function(col){
5366 return this.config[col].dataIndex;
5370 * Sets the dataIndex for a column.
5371 * @param {Number} col The column index
5372 * @param {Number} dataIndex The new dataIndex
5374 setDataIndex : function(col, dataIndex){
5375 this.config[col].dataIndex = dataIndex;
5381 * Returns true if the cell is editable.
5382 * @param {Number} colIndex The column index
5383 * @param {Number} rowIndex The row index - this is nto actually used..?
5386 isCellEditable : function(colIndex, rowIndex){
5387 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5391 * Returns the editor defined for the cell/column.
5392 * return false or null to disable editing.
5393 * @param {Number} colIndex The column index
5394 * @param {Number} rowIndex The row index
5397 getCellEditor : function(colIndex, rowIndex){
5398 return this.config[colIndex].editor;
5402 * Sets if a column is editable.
5403 * @param {Number} col The column index
5404 * @param {Boolean} editable True if the column is editable
5406 setEditable : function(col, editable){
5407 this.config[col].editable = editable;
5412 * Returns true if the column is hidden.
5413 * @param {Number} colIndex The column index
5416 isHidden : function(colIndex){
5417 return this.config[colIndex].hidden;
5422 * Returns true if the column width cannot be changed
5424 isFixed : function(colIndex){
5425 return this.config[colIndex].fixed;
5429 * Returns true if the column can be resized
5432 isResizable : function(colIndex){
5433 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5436 * Sets if a column is hidden.
5437 * @param {Number} colIndex The column index
5438 * @param {Boolean} hidden True if the column is hidden
5440 setHidden : function(colIndex, hidden){
5441 this.config[colIndex].hidden = hidden;
5442 this.totalWidth = null;
5443 this.fireEvent("hiddenchange", this, colIndex, hidden);
5447 * Sets the editor for a column.
5448 * @param {Number} col The column index
5449 * @param {Object} editor The editor object
5451 setEditor : function(col, editor){
5452 this.config[col].editor = editor;
5456 Roo.grid.ColumnModel.defaultRenderer = function(value){
5457 if(typeof value == "string" && value.length < 1){
5463 // Alias for backwards compatibility
5464 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5467 * Ext JS Library 1.1.1
5468 * Copyright(c) 2006-2007, Ext JS, LLC.
5470 * Originally Released Under LGPL - original licence link has changed is not relivant.
5473 * <script type="text/javascript">
5477 * @class Roo.LoadMask
5478 * A simple utility class for generically masking elements while loading data. If the element being masked has
5479 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5480 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5481 * element's UpdateManager load indicator and will be destroyed after the initial load.
5483 * Create a new LoadMask
5484 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5485 * @param {Object} config The config object
5487 Roo.LoadMask = function(el, config){
5488 this.el = Roo.get(el);
5489 Roo.apply(this, config);
5491 this.store.on('beforeload', this.onBeforeLoad, this);
5492 this.store.on('load', this.onLoad, this);
5493 this.store.on('loadexception', this.onLoadException, this);
5494 this.removeMask = false;
5496 var um = this.el.getUpdateManager();
5497 um.showLoadIndicator = false; // disable the default indicator
5498 um.on('beforeupdate', this.onBeforeLoad, this);
5499 um.on('update', this.onLoad, this);
5500 um.on('failure', this.onLoad, this);
5501 this.removeMask = true;
5505 Roo.LoadMask.prototype = {
5507 * @cfg {Boolean} removeMask
5508 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5509 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5513 * The text to display in a centered loading message box (defaults to 'Loading...')
5517 * @cfg {String} msgCls
5518 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5520 msgCls : 'x-mask-loading',
5523 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5529 * Disables the mask to prevent it from being displayed
5531 disable : function(){
5532 this.disabled = true;
5536 * Enables the mask so that it can be displayed
5538 enable : function(){
5539 this.disabled = false;
5542 onLoadException : function()
5546 if (typeof(arguments[3]) != 'undefined') {
5547 Roo.MessageBox.alert("Error loading",arguments[3]);
5551 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5552 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5561 this.el.unmask(this.removeMask);
5566 this.el.unmask(this.removeMask);
5570 onBeforeLoad : function(){
5572 this.el.mask(this.msg, this.msgCls);
5577 destroy : function(){
5579 this.store.un('beforeload', this.onBeforeLoad, this);
5580 this.store.un('load', this.onLoad, this);
5581 this.store.un('loadexception', this.onLoadException, this);
5583 var um = this.el.getUpdateManager();
5584 um.un('beforeupdate', this.onBeforeLoad, this);
5585 um.un('update', this.onLoad, this);
5586 um.un('failure', this.onLoad, this);
5597 * @class Roo.bootstrap.Table
5598 * @extends Roo.bootstrap.Component
5599 * Bootstrap Table class
5600 * @cfg {String} cls table class
5601 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5602 * @cfg {String} bgcolor Specifies the background color for a table
5603 * @cfg {Number} border Specifies whether the table cells should have borders or not
5604 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5605 * @cfg {Number} cellspacing Specifies the space between cells
5606 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5607 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5608 * @cfg {String} sortable Specifies that the table should be sortable
5609 * @cfg {String} summary Specifies a summary of the content of a table
5610 * @cfg {Number} width Specifies the width of a table
5611 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5613 * @cfg {boolean} striped Should the rows be alternative striped
5614 * @cfg {boolean} bordered Add borders to the table
5615 * @cfg {boolean} hover Add hover highlighting
5616 * @cfg {boolean} condensed Format condensed
5617 * @cfg {boolean} responsive Format condensed
5618 * @cfg {Boolean} loadMask (true|false) default false
5619 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5620 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5621 * @cfg {Boolean} rowSelection (true|false) default false
5622 * @cfg {Boolean} cellSelection (true|false) default false
5623 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5627 * Create a new Table
5628 * @param {Object} config The config object
5631 Roo.bootstrap.Table = function(config){
5632 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5635 this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5636 this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5637 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5638 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5642 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5643 this.sm = this.selModel;
5644 this.sm.xmodule = this.xmodule || false;
5646 if (this.cm && typeof(this.cm.config) == 'undefined') {
5647 this.colModel = new Roo.grid.ColumnModel(this.cm);
5648 this.cm = this.colModel;
5649 this.cm.xmodule = this.xmodule || false;
5652 this.store= Roo.factory(this.store, Roo.data);
5653 this.ds = this.store;
5654 this.ds.xmodule = this.xmodule || false;
5657 if (this.footer && this.store) {
5658 this.footer.dataSource = this.ds;
5659 this.footer = Roo.factory(this.footer);
5666 * Fires when a cell is clicked
5667 * @param {Roo.bootstrap.Table} this
5668 * @param {Roo.Element} el
5669 * @param {Number} rowIndex
5670 * @param {Number} columnIndex
5671 * @param {Roo.EventObject} e
5675 * @event celldblclick
5676 * Fires when a cell is double clicked
5677 * @param {Roo.bootstrap.Table} this
5678 * @param {Roo.Element} el
5679 * @param {Number} rowIndex
5680 * @param {Number} columnIndex
5681 * @param {Roo.EventObject} e
5683 "celldblclick" : true,
5686 * Fires when a row is clicked
5687 * @param {Roo.bootstrap.Table} this
5688 * @param {Roo.Element} el
5689 * @param {Number} rowIndex
5690 * @param {Roo.EventObject} e
5694 * @event rowdblclick
5695 * Fires when a row is double clicked
5696 * @param {Roo.bootstrap.Table} this
5697 * @param {Roo.Element} el
5698 * @param {Number} rowIndex
5699 * @param {Roo.EventObject} e
5701 "rowdblclick" : true,
5704 * Fires when a mouseover occur
5705 * @param {Roo.bootstrap.Table} this
5706 * @param {Roo.Element} el
5707 * @param {Number} rowIndex
5708 * @param {Number} columnIndex
5709 * @param {Roo.EventObject} e
5714 * Fires when a mouseout occur
5715 * @param {Roo.bootstrap.Table} this
5716 * @param {Roo.Element} el
5717 * @param {Number} rowIndex
5718 * @param {Number} columnIndex
5719 * @param {Roo.EventObject} e
5724 * Fires when a row is rendered, so you can change add a style to it.
5725 * @param {Roo.bootstrap.Table} this
5726 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5730 * @event rowsrendered
5731 * Fires when all the rows have been rendered
5732 * @param {Roo.bootstrap.Table} this
5734 'rowsrendered' : true
5739 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5764 rowSelection : false,
5765 cellSelection : false,
5768 // Roo.Element - the tbody
5771 getAutoCreate : function(){
5772 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5781 cfg.cls += ' table-striped';
5785 cfg.cls += ' table-hover';
5787 if (this.bordered) {
5788 cfg.cls += ' table-bordered';
5790 if (this.condensed) {
5791 cfg.cls += ' table-condensed';
5793 if (this.responsive) {
5794 cfg.cls += ' table-responsive';
5798 cfg.cls+= ' ' +this.cls;
5801 // this lot should be simplifed...
5804 cfg.align=this.align;
5807 cfg.bgcolor=this.bgcolor;
5810 cfg.border=this.border;
5812 if (this.cellpadding) {
5813 cfg.cellpadding=this.cellpadding;
5815 if (this.cellspacing) {
5816 cfg.cellspacing=this.cellspacing;
5819 cfg.frame=this.frame;
5822 cfg.rules=this.rules;
5824 if (this.sortable) {
5825 cfg.sortable=this.sortable;
5828 cfg.summary=this.summary;
5831 cfg.width=this.width;
5834 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5837 if(this.store || this.cm){
5838 if(this.headerShow){
5839 cfg.cn.push(this.renderHeader());
5842 cfg.cn.push(this.renderBody());
5844 if(this.footerShow){
5845 cfg.cn.push(this.renderFooter());
5848 cfg.cls+= ' TableGrid';
5851 return { cn : [ cfg ] };
5854 initEvents : function()
5856 if(!this.store || !this.cm){
5860 //Roo.log('initEvents with ds!!!!');
5862 this.mainBody = this.el.select('tbody', true).first();
5867 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5868 e.on('click', _this.sort, _this);
5871 this.el.on("click", this.onClick, this);
5872 this.el.on("dblclick", this.onDblClick, this);
5874 // why is this done????? = it breaks dialogs??
5875 //this.parent().el.setStyle('position', 'relative');
5879 this.footer.parentId = this.id;
5880 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5883 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5885 this.store.on('load', this.onLoad, this);
5886 this.store.on('beforeload', this.onBeforeLoad, this);
5887 this.store.on('update', this.onUpdate, this);
5888 this.store.on('add', this.onAdd, this);
5892 onMouseover : function(e, el)
5894 var cell = Roo.get(el);
5900 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5901 cell = cell.findParent('td', false, true);
5904 var row = cell.findParent('tr', false, true);
5905 var cellIndex = cell.dom.cellIndex;
5906 var rowIndex = row.dom.rowIndex - 1; // start from 0
5908 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5912 onMouseout : function(e, el)
5914 var cell = Roo.get(el);
5920 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5921 cell = cell.findParent('td', false, true);
5924 var row = cell.findParent('tr', false, true);
5925 var cellIndex = cell.dom.cellIndex;
5926 var rowIndex = row.dom.rowIndex - 1; // start from 0
5928 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5932 onClick : function(e, el)
5934 var cell = Roo.get(el);
5936 if(!cell || (!this.cellSelection && !this.rowSelection)){
5940 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5941 cell = cell.findParent('td', false, true);
5944 if(!cell || typeof(cell) == 'undefined'){
5948 var row = cell.findParent('tr', false, true);
5950 if(!row || typeof(row) == 'undefined'){
5954 var cellIndex = cell.dom.cellIndex;
5955 var rowIndex = this.getRowIndex(row);
5957 // why??? - should these not be based on SelectionModel?
5958 if(this.cellSelection){
5959 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5962 if(this.rowSelection){
5963 this.fireEvent('rowclick', this, row, rowIndex, e);
5969 onDblClick : function(e,el)
5971 var cell = Roo.get(el);
5973 if(!cell || (!this.CellSelection && !this.RowSelection)){
5977 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5978 cell = cell.findParent('td', false, true);
5981 if(!cell || typeof(cell) == 'undefined'){
5985 var row = cell.findParent('tr', false, true);
5987 if(!row || typeof(row) == 'undefined'){
5991 var cellIndex = cell.dom.cellIndex;
5992 var rowIndex = this.getRowIndex(row);
5994 if(this.CellSelection){
5995 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5998 if(this.RowSelection){
5999 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6003 sort : function(e,el)
6005 var col = Roo.get(el);
6007 if(!col.hasClass('sortable')){
6011 var sort = col.attr('sort');
6014 if(col.hasClass('glyphicon-arrow-up')){
6018 this.store.sortInfo = {field : sort, direction : dir};
6021 Roo.log("calling footer first");
6022 this.footer.onClick('first');
6025 this.store.load({ params : { start : 0 } });
6029 renderHeader : function()
6038 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6040 var config = cm.config[i];
6045 html: cm.getColumnHeader(i)
6050 if(typeof(config.lgHeader) != 'undefined'){
6051 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6054 if(typeof(config.mdHeader) != 'undefined'){
6055 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6058 if(typeof(config.smHeader) != 'undefined'){
6059 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6062 if(typeof(config.xsHeader) != 'undefined'){
6063 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6070 if(typeof(config.tooltip) != 'undefined'){
6071 c.tooltip = config.tooltip;
6074 if(typeof(config.colspan) != 'undefined'){
6075 c.colspan = config.colspan;
6078 if(typeof(config.hidden) != 'undefined' && config.hidden){
6079 c.style += ' display:none;';
6082 if(typeof(config.dataIndex) != 'undefined'){
6083 c.sort = config.dataIndex;
6086 if(typeof(config.sortable) != 'undefined' && config.sortable){
6090 if(typeof(config.align) != 'undefined' && config.align.length){
6091 c.style += ' text-align:' + config.align + ';';
6094 if(typeof(config.width) != 'undefined'){
6095 c.style += ' width:' + config.width + 'px;';
6098 if(typeof(config.cls) != 'undefined'){
6099 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6102 ['xs','sm','md','lg'].map(function(size){
6104 if(typeof(config[size]) == 'undefined'){
6108 if (!config[size]) { // 0 = hidden
6109 c.cls += ' hidden-' + size;
6113 c.cls += ' col-' + size + '-' + config[size];
6123 renderBody : function()
6133 colspan : this.cm.getColumnCount()
6143 renderFooter : function()
6153 colspan : this.cm.getColumnCount()
6167 // Roo.log('ds onload');
6172 var ds = this.store;
6174 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6175 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6177 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6178 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6181 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6182 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6186 var tbody = this.mainBody;
6188 if(ds.getCount() > 0){
6189 ds.data.each(function(d,rowIndex){
6190 var row = this.renderRow(cm, ds, rowIndex);
6192 tbody.createChild(row);
6196 if(row.cellObjects.length){
6197 Roo.each(row.cellObjects, function(r){
6198 _this.renderCellObject(r);
6205 Roo.each(this.el.select('tbody td', true).elements, function(e){
6206 e.on('mouseover', _this.onMouseover, _this);
6209 Roo.each(this.el.select('tbody td', true).elements, function(e){
6210 e.on('mouseout', _this.onMouseout, _this);
6212 this.fireEvent('rowsrendered', this);
6213 //if(this.loadMask){
6214 // this.maskEl.hide();
6219 onUpdate : function(ds,record)
6221 this.refreshRow(record);
6224 onRemove : function(ds, record, index, isUpdate){
6225 if(isUpdate !== true){
6226 this.fireEvent("beforerowremoved", this, index, record);
6228 var bt = this.mainBody.dom;
6230 var rows = this.el.select('tbody > tr', true).elements;
6232 if(typeof(rows[index]) != 'undefined'){
6233 bt.removeChild(rows[index].dom);
6236 // if(bt.rows[index]){
6237 // bt.removeChild(bt.rows[index]);
6240 if(isUpdate !== true){
6241 //this.stripeRows(index);
6242 //this.syncRowHeights(index, index);
6244 this.fireEvent("rowremoved", this, index, record);
6248 onAdd : function(ds, records, rowIndex)
6250 //Roo.log('on Add called');
6251 // - note this does not handle multiple adding very well..
6252 var bt = this.mainBody.dom;
6253 for (var i =0 ; i < records.length;i++) {
6254 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6255 //Roo.log(records[i]);
6256 //Roo.log(this.store.getAt(rowIndex+i));
6257 this.insertRow(this.store, rowIndex + i, false);
6264 refreshRow : function(record){
6265 var ds = this.store, index;
6266 if(typeof record == 'number'){
6268 record = ds.getAt(index);
6270 index = ds.indexOf(record);
6272 this.insertRow(ds, index, true);
6273 this.onRemove(ds, record, index+1, true);
6274 //this.syncRowHeights(index, index);
6276 this.fireEvent("rowupdated", this, index, record);
6279 insertRow : function(dm, rowIndex, isUpdate){
6282 this.fireEvent("beforerowsinserted", this, rowIndex);
6284 //var s = this.getScrollState();
6285 var row = this.renderRow(this.cm, this.store, rowIndex);
6286 // insert before rowIndex..
6287 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6291 if(row.cellObjects.length){
6292 Roo.each(row.cellObjects, function(r){
6293 _this.renderCellObject(r);
6298 this.fireEvent("rowsinserted", this, rowIndex);
6299 //this.syncRowHeights(firstRow, lastRow);
6300 //this.stripeRows(firstRow);
6307 getRowDom : function(rowIndex)
6309 var rows = this.el.select('tbody > tr', true).elements;
6311 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6314 // returns the object tree for a tr..
6317 renderRow : function(cm, ds, rowIndex)
6320 var d = ds.getAt(rowIndex);
6327 var cellObjects = [];
6329 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6330 var config = cm.config[i];
6332 var renderer = cm.getRenderer(i);
6336 if(typeof(renderer) !== 'undefined'){
6337 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6339 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6340 // and are rendered into the cells after the row is rendered - using the id for the element.
6342 if(typeof(value) === 'object'){
6352 rowIndex : rowIndex,
6357 this.fireEvent('rowclass', this, rowcfg);
6361 cls : rowcfg.rowClass,
6363 html: (typeof(value) === 'object') ? '' : value
6370 if(typeof(config.colspan) != 'undefined'){
6371 td.colspan = config.colspan;
6374 if(typeof(config.hidden) != 'undefined' && config.hidden){
6375 td.style += ' display:none;';
6378 if(typeof(config.align) != 'undefined' && config.align.length){
6379 td.style += ' text-align:' + config.align + ';';
6382 if(typeof(config.width) != 'undefined'){
6383 td.style += ' width:' + config.width + 'px;';
6386 if(typeof(config.cursor) != 'undefined'){
6387 td.style += ' cursor:' + config.cursor + ';';
6390 if(typeof(config.cls) != 'undefined'){
6391 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6394 ['xs','sm','md','lg'].map(function(size){
6396 if(typeof(config[size]) == 'undefined'){
6400 if (!config[size]) { // 0 = hidden
6401 td.cls += ' hidden-' + size;
6405 td.cls += ' col-' + size + '-' + config[size];
6413 row.cellObjects = cellObjects;
6421 onBeforeLoad : function()
6423 //Roo.log('ds onBeforeLoad');
6427 //if(this.loadMask){
6428 // this.maskEl.show();
6436 this.el.select('tbody', true).first().dom.innerHTML = '';
6439 * Show or hide a row.
6440 * @param {Number} rowIndex to show or hide
6441 * @param {Boolean} state hide
6443 setRowVisibility : function(rowIndex, state)
6445 var bt = this.mainBody.dom;
6447 var rows = this.el.select('tbody > tr', true).elements;
6449 if(typeof(rows[rowIndex]) == 'undefined'){
6452 rows[rowIndex].dom.style.display = state ? '' : 'none';
6456 getSelectionModel : function(){
6458 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6460 return this.selModel;
6463 * Render the Roo.bootstrap object from renderder
6465 renderCellObject : function(r)
6469 var t = r.cfg.render(r.container);
6472 Roo.each(r.cfg.cn, function(c){
6474 container: t.getChildContainer(),
6477 _this.renderCellObject(child);
6482 getRowIndex : function(row)
6486 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6509 * @class Roo.bootstrap.TableCell
6510 * @extends Roo.bootstrap.Component
6511 * Bootstrap TableCell class
6512 * @cfg {String} html cell contain text
6513 * @cfg {String} cls cell class
6514 * @cfg {String} tag cell tag (td|th) default td
6515 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6516 * @cfg {String} align Aligns the content in a cell
6517 * @cfg {String} axis Categorizes cells
6518 * @cfg {String} bgcolor Specifies the background color of a cell
6519 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6520 * @cfg {Number} colspan Specifies the number of columns a cell should span
6521 * @cfg {String} headers Specifies one or more header cells a cell is related to
6522 * @cfg {Number} height Sets the height of a cell
6523 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6524 * @cfg {Number} rowspan Sets the number of rows a cell should span
6525 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6526 * @cfg {String} valign Vertical aligns the content in a cell
6527 * @cfg {Number} width Specifies the width of a cell
6530 * Create a new TableCell
6531 * @param {Object} config The config object
6534 Roo.bootstrap.TableCell = function(config){
6535 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6538 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6558 getAutoCreate : function(){
6559 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6579 cfg.align=this.align
6585 cfg.bgcolor=this.bgcolor
6588 cfg.charoff=this.charoff
6591 cfg.colspan=this.colspan
6594 cfg.headers=this.headers
6597 cfg.height=this.height
6600 cfg.nowrap=this.nowrap
6603 cfg.rowspan=this.rowspan
6606 cfg.scope=this.scope
6609 cfg.valign=this.valign
6612 cfg.width=this.width
6631 * @class Roo.bootstrap.TableRow
6632 * @extends Roo.bootstrap.Component
6633 * Bootstrap TableRow class
6634 * @cfg {String} cls row class
6635 * @cfg {String} align Aligns the content in a table row
6636 * @cfg {String} bgcolor Specifies a background color for a table row
6637 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6638 * @cfg {String} valign Vertical aligns the content in a table row
6641 * Create a new TableRow
6642 * @param {Object} config The config object
6645 Roo.bootstrap.TableRow = function(config){
6646 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6649 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6657 getAutoCreate : function(){
6658 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6668 cfg.align = this.align;
6671 cfg.bgcolor = this.bgcolor;
6674 cfg.charoff = this.charoff;
6677 cfg.valign = this.valign;
6695 * @class Roo.bootstrap.TableBody
6696 * @extends Roo.bootstrap.Component
6697 * Bootstrap TableBody class
6698 * @cfg {String} cls element class
6699 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6700 * @cfg {String} align Aligns the content inside the element
6701 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6702 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6705 * Create a new TableBody
6706 * @param {Object} config The config object
6709 Roo.bootstrap.TableBody = function(config){
6710 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6713 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6721 getAutoCreate : function(){
6722 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6736 cfg.align = this.align;
6739 cfg.charoff = this.charoff;
6742 cfg.valign = this.valign;
6749 // initEvents : function()
6756 // this.store = Roo.factory(this.store, Roo.data);
6757 // this.store.on('load', this.onLoad, this);
6759 // this.store.load();
6763 // onLoad: function ()
6765 // this.fireEvent('load', this);
6775 * Ext JS Library 1.1.1
6776 * Copyright(c) 2006-2007, Ext JS, LLC.
6778 * Originally Released Under LGPL - original licence link has changed is not relivant.
6781 * <script type="text/javascript">
6784 // as we use this in bootstrap.
6785 Roo.namespace('Roo.form');
6787 * @class Roo.form.Action
6788 * Internal Class used to handle form actions
6790 * @param {Roo.form.BasicForm} el The form element or its id
6791 * @param {Object} config Configuration options
6796 // define the action interface
6797 Roo.form.Action = function(form, options){
6799 this.options = options || {};
6802 * Client Validation Failed
6805 Roo.form.Action.CLIENT_INVALID = 'client';
6807 * Server Validation Failed
6810 Roo.form.Action.SERVER_INVALID = 'server';
6812 * Connect to Server Failed
6815 Roo.form.Action.CONNECT_FAILURE = 'connect';
6817 * Reading Data from Server Failed
6820 Roo.form.Action.LOAD_FAILURE = 'load';
6822 Roo.form.Action.prototype = {
6824 failureType : undefined,
6825 response : undefined,
6829 run : function(options){
6834 success : function(response){
6839 handleResponse : function(response){
6843 // default connection failure
6844 failure : function(response){
6846 this.response = response;
6847 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6848 this.form.afterAction(this, false);
6851 processResponse : function(response){
6852 this.response = response;
6853 if(!response.responseText){
6856 this.result = this.handleResponse(response);
6860 // utility functions used internally
6861 getUrl : function(appendParams){
6862 var url = this.options.url || this.form.url || this.form.el.dom.action;
6864 var p = this.getParams();
6866 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6872 getMethod : function(){
6873 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6876 getParams : function(){
6877 var bp = this.form.baseParams;
6878 var p = this.options.params;
6880 if(typeof p == "object"){
6881 p = Roo.urlEncode(Roo.applyIf(p, bp));
6882 }else if(typeof p == 'string' && bp){
6883 p += '&' + Roo.urlEncode(bp);
6886 p = Roo.urlEncode(bp);
6891 createCallback : function(){
6893 success: this.success,
6894 failure: this.failure,
6896 timeout: (this.form.timeout*1000),
6897 upload: this.form.fileUpload ? this.success : undefined
6902 Roo.form.Action.Submit = function(form, options){
6903 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6906 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6909 haveProgress : false,
6910 uploadComplete : false,
6912 // uploadProgress indicator.
6913 uploadProgress : function()
6915 if (!this.form.progressUrl) {
6919 if (!this.haveProgress) {
6920 Roo.MessageBox.progress("Uploading", "Uploading");
6922 if (this.uploadComplete) {
6923 Roo.MessageBox.hide();
6927 this.haveProgress = true;
6929 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6931 var c = new Roo.data.Connection();
6933 url : this.form.progressUrl,
6938 success : function(req){
6939 //console.log(data);
6943 rdata = Roo.decode(req.responseText)
6945 Roo.log("Invalid data from server..");
6949 if (!rdata || !rdata.success) {
6951 Roo.MessageBox.alert(Roo.encode(rdata));
6954 var data = rdata.data;
6956 if (this.uploadComplete) {
6957 Roo.MessageBox.hide();
6962 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6963 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6966 this.uploadProgress.defer(2000,this);
6969 failure: function(data) {
6970 Roo.log('progress url failed ');
6981 // run get Values on the form, so it syncs any secondary forms.
6982 this.form.getValues();
6984 var o = this.options;
6985 var method = this.getMethod();
6986 var isPost = method == 'POST';
6987 if(o.clientValidation === false || this.form.isValid()){
6989 if (this.form.progressUrl) {
6990 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6991 (new Date() * 1) + '' + Math.random());
6996 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6997 form:this.form.el.dom,
6998 url:this.getUrl(!isPost),
7000 params:isPost ? this.getParams() : null,
7001 isUpload: this.form.fileUpload
7004 this.uploadProgress();
7006 }else if (o.clientValidation !== false){ // client validation failed
7007 this.failureType = Roo.form.Action.CLIENT_INVALID;
7008 this.form.afterAction(this, false);
7012 success : function(response)
7014 this.uploadComplete= true;
7015 if (this.haveProgress) {
7016 Roo.MessageBox.hide();
7020 var result = this.processResponse(response);
7021 if(result === true || result.success){
7022 this.form.afterAction(this, true);
7026 this.form.markInvalid(result.errors);
7027 this.failureType = Roo.form.Action.SERVER_INVALID;
7029 this.form.afterAction(this, false);
7031 failure : function(response)
7033 this.uploadComplete= true;
7034 if (this.haveProgress) {
7035 Roo.MessageBox.hide();
7038 this.response = response;
7039 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7040 this.form.afterAction(this, false);
7043 handleResponse : function(response){
7044 if(this.form.errorReader){
7045 var rs = this.form.errorReader.read(response);
7048 for(var i = 0, len = rs.records.length; i < len; i++) {
7049 var r = rs.records[i];
7053 if(errors.length < 1){
7057 success : rs.success,
7063 ret = Roo.decode(response.responseText);
7067 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7077 Roo.form.Action.Load = function(form, options){
7078 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7079 this.reader = this.form.reader;
7082 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7087 Roo.Ajax.request(Roo.apply(
7088 this.createCallback(), {
7089 method:this.getMethod(),
7090 url:this.getUrl(false),
7091 params:this.getParams()
7095 success : function(response){
7097 var result = this.processResponse(response);
7098 if(result === true || !result.success || !result.data){
7099 this.failureType = Roo.form.Action.LOAD_FAILURE;
7100 this.form.afterAction(this, false);
7103 this.form.clearInvalid();
7104 this.form.setValues(result.data);
7105 this.form.afterAction(this, true);
7108 handleResponse : function(response){
7109 if(this.form.reader){
7110 var rs = this.form.reader.read(response);
7111 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7113 success : rs.success,
7117 return Roo.decode(response.responseText);
7121 Roo.form.Action.ACTION_TYPES = {
7122 'load' : Roo.form.Action.Load,
7123 'submit' : Roo.form.Action.Submit
7132 * @class Roo.bootstrap.Form
7133 * @extends Roo.bootstrap.Component
7134 * Bootstrap Form class
7135 * @cfg {String} method GET | POST (default POST)
7136 * @cfg {String} labelAlign top | left (default top)
7137 * @cfg {String} align left | right - for navbars
7138 * @cfg {Boolean} loadMask load mask when submit (default true)
7143 * @param {Object} config The config object
7147 Roo.bootstrap.Form = function(config){
7148 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7151 * @event clientvalidation
7152 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7153 * @param {Form} this
7154 * @param {Boolean} valid true if the form has passed client-side validation
7156 clientvalidation: true,
7158 * @event beforeaction
7159 * Fires before any action is performed. Return false to cancel the action.
7160 * @param {Form} this
7161 * @param {Action} action The action to be performed
7165 * @event actionfailed
7166 * Fires when an action fails.
7167 * @param {Form} this
7168 * @param {Action} action The action that failed
7170 actionfailed : true,
7172 * @event actioncomplete
7173 * Fires when an action is completed.
7174 * @param {Form} this
7175 * @param {Action} action The action that completed
7177 actioncomplete : true
7182 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7185 * @cfg {String} method
7186 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7191 * The URL to use for form actions if one isn't supplied in the action options.
7194 * @cfg {Boolean} fileUpload
7195 * Set to true if this form is a file upload.
7199 * @cfg {Object} baseParams
7200 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7204 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7208 * @cfg {Sting} align (left|right) for navbar forms
7213 activeAction : null,
7216 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7217 * element by passing it or its id or mask the form itself by passing in true.
7220 waitMsgTarget : false,
7224 getAutoCreate : function(){
7228 method : this.method || 'POST',
7229 id : this.id || Roo.id(),
7232 if (this.parent().xtype.match(/^Nav/)) {
7233 cfg.cls = 'navbar-form navbar-' + this.align;
7237 if (this.labelAlign == 'left' ) {
7238 cfg.cls += ' form-horizontal';
7244 initEvents : function()
7246 this.el.on('submit', this.onSubmit, this);
7247 // this was added as random key presses on the form where triggering form submit.
7248 this.el.on('keypress', function(e) {
7249 if (e.getCharCode() != 13) {
7252 // we might need to allow it for textareas.. and some other items.
7253 // check e.getTarget().
7255 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7259 Roo.log("keypress blocked");
7267 onSubmit : function(e){
7272 * Returns true if client-side validation on the form is successful.
7275 isValid : function(){
7276 var items = this.getItems();
7278 items.each(function(f){
7287 * Returns true if any fields in this form have changed since their original load.
7290 isDirty : function(){
7292 var items = this.getItems();
7293 items.each(function(f){
7303 * Performs a predefined action (submit or load) or custom actions you define on this form.
7304 * @param {String} actionName The name of the action type
7305 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7306 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7307 * accept other config options):
7309 Property Type Description
7310 ---------------- --------------- ----------------------------------------------------------------------------------
7311 url String The url for the action (defaults to the form's url)
7312 method String The form method to use (defaults to the form's method, or POST if not defined)
7313 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7314 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7315 validate the form on the client (defaults to false)
7317 * @return {BasicForm} this
7319 doAction : function(action, options){
7320 if(typeof action == 'string'){
7321 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7323 if(this.fireEvent('beforeaction', this, action) !== false){
7324 this.beforeAction(action);
7325 action.run.defer(100, action);
7331 beforeAction : function(action){
7332 var o = action.options;
7335 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7337 // not really supported yet.. ??
7339 //if(this.waitMsgTarget === true){
7340 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7341 //}else if(this.waitMsgTarget){
7342 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7343 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7345 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7351 afterAction : function(action, success){
7352 this.activeAction = null;
7353 var o = action.options;
7355 //if(this.waitMsgTarget === true){
7357 //}else if(this.waitMsgTarget){
7358 // this.waitMsgTarget.unmask();
7360 // Roo.MessageBox.updateProgress(1);
7361 // Roo.MessageBox.hide();
7368 Roo.callback(o.success, o.scope, [this, action]);
7369 this.fireEvent('actioncomplete', this, action);
7373 // failure condition..
7374 // we have a scenario where updates need confirming.
7375 // eg. if a locking scenario exists..
7376 // we look for { errors : { needs_confirm : true }} in the response.
7378 (typeof(action.result) != 'undefined') &&
7379 (typeof(action.result.errors) != 'undefined') &&
7380 (typeof(action.result.errors.needs_confirm) != 'undefined')
7383 Roo.log("not supported yet");
7386 Roo.MessageBox.confirm(
7387 "Change requires confirmation",
7388 action.result.errorMsg,
7393 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7403 Roo.callback(o.failure, o.scope, [this, action]);
7404 // show an error message if no failed handler is set..
7405 if (!this.hasListener('actionfailed')) {
7406 Roo.log("need to add dialog support");
7408 Roo.MessageBox.alert("Error",
7409 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7410 action.result.errorMsg :
7411 "Saving Failed, please check your entries or try again"
7416 this.fireEvent('actionfailed', this, action);
7421 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7422 * @param {String} id The value to search for
7425 findField : function(id){
7426 var items = this.getItems();
7427 var field = items.get(id);
7429 items.each(function(f){
7430 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7437 return field || null;
7440 * Mark fields in this form invalid in bulk.
7441 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7442 * @return {BasicForm} this
7444 markInvalid : function(errors){
7445 if(errors instanceof Array){
7446 for(var i = 0, len = errors.length; i < len; i++){
7447 var fieldError = errors[i];
7448 var f = this.findField(fieldError.id);
7450 f.markInvalid(fieldError.msg);
7456 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7457 field.markInvalid(errors[id]);
7461 //Roo.each(this.childForms || [], function (f) {
7462 // f.markInvalid(errors);
7469 * Set values for fields in this form in bulk.
7470 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7471 * @return {BasicForm} this
7473 setValues : function(values){
7474 if(values instanceof Array){ // array of objects
7475 for(var i = 0, len = values.length; i < len; i++){
7477 var f = this.findField(v.id);
7479 f.setValue(v.value);
7480 if(this.trackResetOnLoad){
7481 f.originalValue = f.getValue();
7485 }else{ // object hash
7488 if(typeof values[id] != 'function' && (field = this.findField(id))){
7490 if (field.setFromData &&
7492 field.displayField &&
7493 // combos' with local stores can
7494 // be queried via setValue()
7495 // to set their value..
7496 (field.store && !field.store.isLocal)
7500 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7501 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7502 field.setFromData(sd);
7505 field.setValue(values[id]);
7509 if(this.trackResetOnLoad){
7510 field.originalValue = field.getValue();
7516 //Roo.each(this.childForms || [], function (f) {
7517 // f.setValues(values);
7524 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7525 * they are returned as an array.
7526 * @param {Boolean} asString
7529 getValues : function(asString){
7530 //if (this.childForms) {
7531 // copy values from the child forms
7532 // Roo.each(this.childForms, function (f) {
7533 // this.setValues(f.getValues());
7539 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7540 if(asString === true){
7543 return Roo.urlDecode(fs);
7547 * Returns the fields in this form as an object with key/value pairs.
7548 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7551 getFieldValues : function(with_hidden)
7553 var items = this.getItems();
7555 items.each(function(f){
7559 var v = f.getValue();
7560 if (f.inputType =='radio') {
7561 if (typeof(ret[f.getName()]) == 'undefined') {
7562 ret[f.getName()] = ''; // empty..
7565 if (!f.el.dom.checked) {
7573 // not sure if this supported any more..
7574 if ((typeof(v) == 'object') && f.getRawValue) {
7575 v = f.getRawValue() ; // dates..
7577 // combo boxes where name != hiddenName...
7578 if (f.name != f.getName()) {
7579 ret[f.name] = f.getRawValue();
7581 ret[f.getName()] = v;
7588 * Clears all invalid messages in this form.
7589 * @return {BasicForm} this
7591 clearInvalid : function(){
7592 var items = this.getItems();
7594 items.each(function(f){
7605 * @return {BasicForm} this
7608 var items = this.getItems();
7609 items.each(function(f){
7613 Roo.each(this.childForms || [], function (f) {
7620 getItems : function()
7622 var r=new Roo.util.MixedCollection(false, function(o){
7623 return o.id || (o.id = Roo.id());
7625 var iter = function(el) {
7632 Roo.each(el.items,function(e) {
7652 * Ext JS Library 1.1.1
7653 * Copyright(c) 2006-2007, Ext JS, LLC.
7655 * Originally Released Under LGPL - original licence link has changed is not relivant.
7658 * <script type="text/javascript">
7661 * @class Roo.form.VTypes
7662 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7665 Roo.form.VTypes = function(){
7666 // closure these in so they are only created once.
7667 var alpha = /^[a-zA-Z_]+$/;
7668 var alphanum = /^[a-zA-Z0-9_]+$/;
7669 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7670 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7672 // All these messages and functions are configurable
7675 * The function used to validate email addresses
7676 * @param {String} value The email address
7678 'email' : function(v){
7679 return email.test(v);
7682 * The error text to display when the email validation function returns false
7685 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7687 * The keystroke filter mask to be applied on email input
7690 'emailMask' : /[a-z0-9_\.\-@]/i,
7693 * The function used to validate URLs
7694 * @param {String} value The URL
7696 'url' : function(v){
7700 * The error text to display when the url validation function returns false
7703 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7706 * The function used to validate alpha values
7707 * @param {String} value The value
7709 'alpha' : function(v){
7710 return alpha.test(v);
7713 * The error text to display when the alpha validation function returns false
7716 'alphaText' : 'This field should only contain letters and _',
7718 * The keystroke filter mask to be applied on alpha input
7721 'alphaMask' : /[a-z_]/i,
7724 * The function used to validate alphanumeric values
7725 * @param {String} value The value
7727 'alphanum' : function(v){
7728 return alphanum.test(v);
7731 * The error text to display when the alphanumeric validation function returns false
7734 'alphanumText' : 'This field should only contain letters, numbers and _',
7736 * The keystroke filter mask to be applied on alphanumeric input
7739 'alphanumMask' : /[a-z0-9_]/i
7749 * @class Roo.bootstrap.Input
7750 * @extends Roo.bootstrap.Component
7751 * Bootstrap Input class
7752 * @cfg {Boolean} disabled is it disabled
7753 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7754 * @cfg {String} name name of the input
7755 * @cfg {string} fieldLabel - the label associated
7756 * @cfg {string} placeholder - placeholder to put in text.
7757 * @cfg {string} before - input group add on before
7758 * @cfg {string} after - input group add on after
7759 * @cfg {string} size - (lg|sm) or leave empty..
7760 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7761 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7762 * @cfg {Number} md colspan out of 12 for computer-sized screens
7763 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7764 * @cfg {string} value default value of the input
7765 * @cfg {Number} labelWidth set the width of label (0-12)
7766 * @cfg {String} labelAlign (top|left)
7767 * @cfg {Boolean} readOnly Specifies that the field should be read-only
7768 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7770 * @cfg {String} align (left|center|right) Default left
7771 * @cfg {Boolean} forceFeedback (true|false) Default false
7777 * Create a new Input
7778 * @param {Object} config The config object
7781 Roo.bootstrap.Input = function(config){
7782 Roo.bootstrap.Input.superclass.constructor.call(this, config);
7787 * Fires when this field receives input focus.
7788 * @param {Roo.form.Field} this
7793 * Fires when this field loses input focus.
7794 * @param {Roo.form.Field} this
7799 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
7800 * {@link Roo.EventObject#getKey} to determine which key was pressed.
7801 * @param {Roo.form.Field} this
7802 * @param {Roo.EventObject} e The event object
7807 * Fires just before the field blurs if the field value has changed.
7808 * @param {Roo.form.Field} this
7809 * @param {Mixed} newValue The new value
7810 * @param {Mixed} oldValue The original value
7815 * Fires after the field has been marked as invalid.
7816 * @param {Roo.form.Field} this
7817 * @param {String} msg The validation message
7822 * Fires after the field has been validated with no errors.
7823 * @param {Roo.form.Field} this
7828 * Fires after the key up
7829 * @param {Roo.form.Field} this
7830 * @param {Roo.EventObject} e The event Object
7836 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
7838 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7839 automatic validation (defaults to "keyup").
7841 validationEvent : "keyup",
7843 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7845 validateOnBlur : true,
7847 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7849 validationDelay : 250,
7851 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7853 focusClass : "x-form-focus", // not needed???
7857 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7859 invalidClass : "has-warning",
7862 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7864 validClass : "has-success",
7867 * @cfg {Boolean} hasFeedback (true|false) default true
7872 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7874 invalidFeedbackClass : "glyphicon-warning-sign",
7877 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7879 validFeedbackClass : "glyphicon-ok",
7882 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7884 selectOnFocus : false,
7887 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7891 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7896 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7898 disableKeyFilter : false,
7901 * @cfg {Boolean} disabled True to disable the field (defaults to false).
7905 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7909 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7911 blankText : "This field is required",
7914 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7918 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7920 maxLength : Number.MAX_VALUE,
7922 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7924 minLengthText : "The minimum length for this field is {0}",
7926 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7928 maxLengthText : "The maximum length for this field is {0}",
7932 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7933 * If available, this function will be called only after the basic validators all return true, and will be passed the
7934 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7938 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7939 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7940 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
7944 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7948 autocomplete: false,
7967 formatedValue : false,
7968 forceFeedback : false,
7970 parentLabelAlign : function()
7973 while (parent.parent()) {
7974 parent = parent.parent();
7975 if (typeof(parent.labelAlign) !='undefined') {
7976 return parent.labelAlign;
7983 getAutoCreate : function(){
7985 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7991 if(this.inputType != 'hidden'){
7992 cfg.cls = 'form-group' //input-group
7998 type : this.inputType,
8000 cls : 'form-control',
8001 placeholder : this.placeholder || '',
8002 autocomplete : this.autocomplete || 'new-password'
8007 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8010 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8011 input.maxLength = this.maxLength;
8014 if (this.disabled) {
8015 input.disabled=true;
8018 if (this.readOnly) {
8019 input.readonly=true;
8023 input.name = this.name;
8026 input.cls += ' input-' + this.size;
8029 ['xs','sm','md','lg'].map(function(size){
8030 if (settings[size]) {
8031 cfg.cls += ' col-' + size + '-' + settings[size];
8035 var inputblock = input;
8039 cls: 'glyphicon form-control-feedback'
8042 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8045 cls : 'has-feedback',
8053 if (this.before || this.after) {
8056 cls : 'input-group',
8060 if (this.before && typeof(this.before) == 'string') {
8062 inputblock.cn.push({
8064 cls : 'roo-input-before input-group-addon',
8068 if (this.before && typeof(this.before) == 'object') {
8069 this.before = Roo.factory(this.before);
8071 inputblock.cn.push({
8073 cls : 'roo-input-before input-group-' +
8074 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8078 inputblock.cn.push(input);
8080 if (this.after && typeof(this.after) == 'string') {
8081 inputblock.cn.push({
8083 cls : 'roo-input-after input-group-addon',
8087 if (this.after && typeof(this.after) == 'object') {
8088 this.after = Roo.factory(this.after);
8090 inputblock.cn.push({
8092 cls : 'roo-input-after input-group-' +
8093 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8097 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8098 inputblock.cls += ' has-feedback';
8099 inputblock.cn.push(feedback);
8103 if (align ==='left' && this.fieldLabel.length) {
8110 cls : 'control-label col-sm-' + this.labelWidth,
8111 html : this.fieldLabel
8115 cls : "col-sm-" + (12 - this.labelWidth),
8122 } else if ( this.fieldLabel.length) {
8128 //cls : 'input-group-addon',
8129 html : this.fieldLabel
8148 if (this.parentType === 'Navbar' && this.parent().bar) {
8149 cfg.cls += ' navbar-form';
8156 * return the real input element.
8158 inputEl: function ()
8160 return this.el.select('input.form-control',true).first();
8163 tooltipEl : function()
8165 return this.inputEl();
8168 setDisabled : function(v)
8170 var i = this.inputEl().dom;
8172 i.removeAttribute('disabled');
8176 i.setAttribute('disabled','true');
8178 initEvents : function()
8181 this.inputEl().on("keydown" , this.fireKey, this);
8182 this.inputEl().on("focus", this.onFocus, this);
8183 this.inputEl().on("blur", this.onBlur, this);
8185 this.inputEl().relayEvent('keyup', this);
8187 // reference to original value for reset
8188 this.originalValue = this.getValue();
8189 //Roo.form.TextField.superclass.initEvents.call(this);
8190 if(this.validationEvent == 'keyup'){
8191 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8192 this.inputEl().on('keyup', this.filterValidation, this);
8194 else if(this.validationEvent !== false){
8195 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8198 if(this.selectOnFocus){
8199 this.on("focus", this.preFocus, this);
8202 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8203 this.inputEl().on("keypress", this.filterKeys, this);
8206 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8207 this.el.on("click", this.autoSize, this);
8210 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8211 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8214 if (typeof(this.before) == 'object') {
8215 this.before.render(this.el.select('.roo-input-before',true).first());
8217 if (typeof(this.after) == 'object') {
8218 this.after.render(this.el.select('.roo-input-after',true).first());
8223 filterValidation : function(e){
8224 if(!e.isNavKeyPress()){
8225 this.validationTask.delay(this.validationDelay);
8229 * Validates the field value
8230 * @return {Boolean} True if the value is valid, else false
8232 validate : function(){
8233 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8234 if(this.disabled || this.validateValue(this.getRawValue())){
8245 * Validates a value according to the field's validation rules and marks the field as invalid
8246 * if the validation fails
8247 * @param {Mixed} value The value to validate
8248 * @return {Boolean} True if the value is valid, else false
8250 validateValue : function(value){
8251 if(value.length < 1) { // if it's blank
8252 if(this.allowBlank){
8258 if(value.length < this.minLength){
8261 if(value.length > this.maxLength){
8265 var vt = Roo.form.VTypes;
8266 if(!vt[this.vtype](value, this)){
8270 if(typeof this.validator == "function"){
8271 var msg = this.validator(value);
8277 if(this.regex && !this.regex.test(value)){
8287 fireKey : function(e){
8288 //Roo.log('field ' + e.getKey());
8289 if(e.isNavKeyPress()){
8290 this.fireEvent("specialkey", this, e);
8293 focus : function (selectText){
8295 this.inputEl().focus();
8296 if(selectText === true){
8297 this.inputEl().dom.select();
8303 onFocus : function(){
8304 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8305 // this.el.addClass(this.focusClass);
8308 this.hasFocus = true;
8309 this.startValue = this.getValue();
8310 this.fireEvent("focus", this);
8314 beforeBlur : Roo.emptyFn,
8318 onBlur : function(){
8320 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8321 //this.el.removeClass(this.focusClass);
8323 this.hasFocus = false;
8324 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8327 var v = this.getValue();
8328 if(String(v) !== String(this.startValue)){
8329 this.fireEvent('change', this, v, this.startValue);
8331 this.fireEvent("blur", this);
8335 * Resets the current field value to the originally loaded value and clears any validation messages
8338 this.setValue(this.originalValue);
8342 * Returns the name of the field
8343 * @return {Mixed} name The name field
8345 getName: function(){
8349 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8350 * @return {Mixed} value The field value
8352 getValue : function(){
8354 var v = this.inputEl().getValue();
8359 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8360 * @return {Mixed} value The field value
8362 getRawValue : function(){
8363 var v = this.inputEl().getValue();
8369 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8370 * @param {Mixed} value The value to set
8372 setRawValue : function(v){
8373 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8376 selectText : function(start, end){
8377 var v = this.getRawValue();
8379 start = start === undefined ? 0 : start;
8380 end = end === undefined ? v.length : end;
8381 var d = this.inputEl().dom;
8382 if(d.setSelectionRange){
8383 d.setSelectionRange(start, end);
8384 }else if(d.createTextRange){
8385 var range = d.createTextRange();
8386 range.moveStart("character", start);
8387 range.moveEnd("character", v.length-end);
8394 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8395 * @param {Mixed} value The value to set
8397 setValue : function(v){
8400 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8406 processValue : function(value){
8407 if(this.stripCharsRe){
8408 var newValue = value.replace(this.stripCharsRe, '');
8409 if(newValue !== value){
8410 this.setRawValue(newValue);
8417 preFocus : function(){
8419 if(this.selectOnFocus){
8420 this.inputEl().dom.select();
8423 filterKeys : function(e){
8425 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8428 var c = e.getCharCode(), cc = String.fromCharCode(c);
8429 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8432 if(!this.maskRe.test(cc)){
8437 * Clear any invalid styles/messages for this field
8439 clearInvalid : function(){
8441 if(!this.el || this.preventMark){ // not rendered
8445 var label = this.el.select('label', true).first();
8446 var icon = this.el.select('i.fa-star', true).first();
8452 this.el.removeClass(this.invalidClass);
8454 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8456 var feedback = this.el.select('.form-control-feedback', true).first();
8459 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8464 this.fireEvent('valid', this);
8468 * Mark this field as valid
8470 markValid : function()
8472 if(!this.el || this.preventMark){ // not rendered
8476 this.el.removeClass([this.invalidClass, this.validClass]);
8478 var feedback = this.el.select('.form-control-feedback', true).first();
8481 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8484 if(this.disabled || this.allowBlank){
8488 var formGroup = this.el.findParent('.form-group', false, true);
8492 var label = formGroup.select('label', true).first();
8493 var icon = formGroup.select('i.fa-star', true).first();
8500 this.el.addClass(this.validClass);
8502 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8504 var feedback = this.el.select('.form-control-feedback', true).first();
8507 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8508 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8513 this.fireEvent('valid', this);
8517 * Mark this field as invalid
8518 * @param {String} msg The validation message
8520 markInvalid : function(msg)
8522 if(!this.el || this.preventMark){ // not rendered
8526 this.el.removeClass([this.invalidClass, this.validClass]);
8528 var feedback = this.el.select('.form-control-feedback', true).first();
8531 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8534 if(this.disabled || this.allowBlank){
8538 var formGroup = this.el.findParent('.form-group', false, true);
8541 var label = formGroup.select('label', true).first();
8542 var icon = formGroup.select('i.fa-star', true).first();
8544 if(!this.getValue().length && label && !icon){
8545 this.el.findParent('.form-group', false, true).createChild({
8547 cls : 'text-danger fa fa-lg fa-star',
8548 tooltip : 'This field is required',
8549 style : 'margin-right:5px;'
8555 this.el.addClass(this.invalidClass);
8557 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8559 var feedback = this.el.select('.form-control-feedback', true).first();
8562 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8564 if(this.getValue().length || this.forceFeedback){
8565 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8572 this.fireEvent('invalid', this, msg);
8575 SafariOnKeyDown : function(event)
8577 // this is a workaround for a password hang bug on chrome/ webkit.
8579 var isSelectAll = false;
8581 if(this.inputEl().dom.selectionEnd > 0){
8582 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8584 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8585 event.preventDefault();
8590 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8592 event.preventDefault();
8593 // this is very hacky as keydown always get's upper case.
8595 var cc = String.fromCharCode(event.getCharCode());
8596 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8600 adjustWidth : function(tag, w){
8601 tag = tag.toLowerCase();
8602 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8603 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8607 if(tag == 'textarea'){
8610 }else if(Roo.isOpera){
8614 if(tag == 'textarea'){
8633 * @class Roo.bootstrap.TextArea
8634 * @extends Roo.bootstrap.Input
8635 * Bootstrap TextArea class
8636 * @cfg {Number} cols Specifies the visible width of a text area
8637 * @cfg {Number} rows Specifies the visible number of lines in a text area
8638 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8639 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8640 * @cfg {string} html text
8643 * Create a new TextArea
8644 * @param {Object} config The config object
8647 Roo.bootstrap.TextArea = function(config){
8648 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8652 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8662 getAutoCreate : function(){
8664 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8675 value : this.value || '',
8676 html: this.html || '',
8677 cls : 'form-control',
8678 placeholder : this.placeholder || ''
8682 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8683 input.maxLength = this.maxLength;
8687 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8691 input.cols = this.cols;
8694 if (this.readOnly) {
8695 input.readonly = true;
8699 input.name = this.name;
8703 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8707 ['xs','sm','md','lg'].map(function(size){
8708 if (settings[size]) {
8709 cfg.cls += ' col-' + size + '-' + settings[size];
8713 var inputblock = input;
8715 if(this.hasFeedback && !this.allowBlank){
8719 cls: 'glyphicon form-control-feedback'
8723 cls : 'has-feedback',
8732 if (this.before || this.after) {
8735 cls : 'input-group',
8739 inputblock.cn.push({
8741 cls : 'input-group-addon',
8746 inputblock.cn.push(input);
8748 if(this.hasFeedback && !this.allowBlank){
8749 inputblock.cls += ' has-feedback';
8750 inputblock.cn.push(feedback);
8754 inputblock.cn.push({
8756 cls : 'input-group-addon',
8763 if (align ==='left' && this.fieldLabel.length) {
8764 // Roo.log("left and has label");
8770 cls : 'control-label col-sm-' + this.labelWidth,
8771 html : this.fieldLabel
8775 cls : "col-sm-" + (12 - this.labelWidth),
8782 } else if ( this.fieldLabel.length) {
8783 // Roo.log(" label");
8788 //cls : 'input-group-addon',
8789 html : this.fieldLabel
8799 // Roo.log(" no label && no align");
8809 if (this.disabled) {
8810 input.disabled=true;
8817 * return the real textarea element.
8819 inputEl: function ()
8821 return this.el.select('textarea.form-control',true).first();
8825 * Clear any invalid styles/messages for this field
8827 clearInvalid : function()
8830 if(!this.el || this.preventMark){ // not rendered
8834 var label = this.el.select('label', true).first();
8835 var icon = this.el.select('i.fa-star', true).first();
8841 this.el.removeClass(this.invalidClass);
8843 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8845 var feedback = this.el.select('.form-control-feedback', true).first();
8848 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8853 this.fireEvent('valid', this);
8857 * Mark this field as valid
8859 markValid : function()
8861 if(!this.el || this.preventMark){ // not rendered
8865 this.el.removeClass([this.invalidClass, this.validClass]);
8867 var feedback = this.el.select('.form-control-feedback', true).first();
8870 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8873 if(this.disabled || this.allowBlank){
8877 var label = this.el.select('label', true).first();
8878 var icon = this.el.select('i.fa-star', true).first();
8884 this.el.addClass(this.validClass);
8886 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8888 var feedback = this.el.select('.form-control-feedback', true).first();
8891 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8892 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8897 this.fireEvent('valid', this);
8901 * Mark this field as invalid
8902 * @param {String} msg The validation message
8904 markInvalid : function(msg)
8906 if(!this.el || this.preventMark){ // not rendered
8910 this.el.removeClass([this.invalidClass, this.validClass]);
8912 var feedback = this.el.select('.form-control-feedback', true).first();
8915 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8918 if(this.disabled || this.allowBlank){
8922 var label = this.el.select('label', true).first();
8923 var icon = this.el.select('i.fa-star', true).first();
8925 if(!this.getValue().length && label && !icon){
8926 this.el.createChild({
8928 cls : 'text-danger fa fa-lg fa-star',
8929 tooltip : 'This field is required',
8930 style : 'margin-right:5px;'
8934 this.el.addClass(this.invalidClass);
8936 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8938 var feedback = this.el.select('.form-control-feedback', true).first();
8941 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8943 if(this.getValue().length || this.forceFeedback){
8944 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8951 this.fireEvent('invalid', this, msg);
8959 * trigger field - base class for combo..
8964 * @class Roo.bootstrap.TriggerField
8965 * @extends Roo.bootstrap.Input
8966 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8967 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8968 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8969 * for which you can provide a custom implementation. For example:
8971 var trigger = new Roo.bootstrap.TriggerField();
8972 trigger.onTriggerClick = myTriggerFn;
8973 trigger.applyTo('my-field');
8976 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8977 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8978 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
8979 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8980 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8983 * Create a new TriggerField.
8984 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8985 * to the base TextField)
8987 Roo.bootstrap.TriggerField = function(config){
8988 this.mimicing = false;
8989 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8992 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
8994 * @cfg {String} triggerClass A CSS class to apply to the trigger
8997 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9002 * @cfg {Boolean} removable (true|false) special filter default false
9006 /** @cfg {Boolean} grow @hide */
9007 /** @cfg {Number} growMin @hide */
9008 /** @cfg {Number} growMax @hide */
9014 autoSize: Roo.emptyFn,
9021 actionMode : 'wrap',
9026 getAutoCreate : function(){
9028 var align = this.labelAlign || this.parentLabelAlign();
9033 cls: 'form-group' //input-group
9040 type : this.inputType,
9041 cls : 'form-control',
9042 autocomplete: 'new-password',
9043 placeholder : this.placeholder || ''
9047 input.name = this.name;
9050 input.cls += ' input-' + this.size;
9053 if (this.disabled) {
9054 input.disabled=true;
9057 var inputblock = input;
9059 if(this.hasFeedback && !this.allowBlank){
9063 cls: 'glyphicon form-control-feedback'
9066 if(this.removable && !this.editable && !this.tickable){
9068 cls : 'has-feedback',
9074 cls : 'roo-combo-removable-btn close'
9081 cls : 'has-feedback',
9090 if(this.removable && !this.editable && !this.tickable){
9092 cls : 'roo-removable',
9098 cls : 'roo-combo-removable-btn close'
9105 if (this.before || this.after) {
9108 cls : 'input-group',
9112 inputblock.cn.push({
9114 cls : 'input-group-addon',
9119 inputblock.cn.push(input);
9121 if(this.hasFeedback && !this.allowBlank){
9122 inputblock.cls += ' has-feedback';
9123 inputblock.cn.push(feedback);
9127 inputblock.cn.push({
9129 cls : 'input-group-addon',
9142 cls: 'form-hidden-field'
9156 cls: 'form-hidden-field'
9160 cls: 'select2-choices',
9164 cls: 'select2-search-field',
9177 cls: 'select2-container input-group',
9182 // cls: 'typeahead typeahead-long dropdown-menu',
9183 // style: 'display:none'
9188 if(!this.multiple && this.showToggleBtn){
9194 if (this.caret != false) {
9197 cls: 'fa fa-' + this.caret
9204 cls : 'input-group-addon btn dropdown-toggle',
9209 cls: 'combobox-clear',
9223 combobox.cls += ' select2-container-multi';
9226 if (align ==='left' && this.fieldLabel.length) {
9228 // Roo.log("left and has label");
9234 cls : 'control-label col-sm-' + this.labelWidth,
9235 html : this.fieldLabel
9239 cls : "col-sm-" + (12 - this.labelWidth),
9246 } else if ( this.fieldLabel.length) {
9247 // Roo.log(" label");
9252 //cls : 'input-group-addon',
9253 html : this.fieldLabel
9263 // Roo.log(" no label && no align");
9270 ['xs','sm','md','lg'].map(function(size){
9271 if (settings[size]) {
9272 cfg.cls += ' col-' + size + '-' + settings[size];
9283 onResize : function(w, h){
9284 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9285 // if(typeof w == 'number'){
9286 // var x = w - this.trigger.getWidth();
9287 // this.inputEl().setWidth(this.adjustWidth('input', x));
9288 // this.trigger.setStyle('left', x+'px');
9293 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9296 getResizeEl : function(){
9297 return this.inputEl();
9301 getPositionEl : function(){
9302 return this.inputEl();
9306 alignErrorIcon : function(){
9307 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9311 initEvents : function(){
9315 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9316 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9317 if(!this.multiple && this.showToggleBtn){
9318 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9319 if(this.hideTrigger){
9320 this.trigger.setDisplayed(false);
9322 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9326 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9329 if(this.removable && !this.editable && !this.tickable){
9330 var close = this.closeTriggerEl();
9333 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9334 close.on('click', this.removeBtnClick, this, close);
9338 //this.trigger.addClassOnOver('x-form-trigger-over');
9339 //this.trigger.addClassOnClick('x-form-trigger-click');
9342 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9346 closeTriggerEl : function()
9348 var close = this.el.select('.roo-combo-removable-btn', true).first();
9349 return close ? close : false;
9352 removeBtnClick : function(e, h, el)
9356 if(this.fireEvent("remove", this) !== false){
9361 createList : function()
9363 this.list = Roo.get(document.body).createChild({
9365 cls: 'typeahead typeahead-long dropdown-menu',
9366 style: 'display:none'
9369 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9374 initTrigger : function(){
9379 onDestroy : function(){
9381 this.trigger.removeAllListeners();
9382 // this.trigger.remove();
9385 // this.wrap.remove();
9387 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9391 onFocus : function(){
9392 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9395 this.wrap.addClass('x-trigger-wrap-focus');
9396 this.mimicing = true;
9397 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9398 if(this.monitorTab){
9399 this.el.on("keydown", this.checkTab, this);
9406 checkTab : function(e){
9407 if(e.getKey() == e.TAB){
9413 onBlur : function(){
9418 mimicBlur : function(e, t){
9420 if(!this.wrap.contains(t) && this.validateBlur()){
9427 triggerBlur : function(){
9428 this.mimicing = false;
9429 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9430 if(this.monitorTab){
9431 this.el.un("keydown", this.checkTab, this);
9433 //this.wrap.removeClass('x-trigger-wrap-focus');
9434 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9438 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9439 validateBlur : function(e, t){
9444 onDisable : function(){
9445 this.inputEl().dom.disabled = true;
9446 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9448 // this.wrap.addClass('x-item-disabled');
9453 onEnable : function(){
9454 this.inputEl().dom.disabled = false;
9455 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9457 // this.el.removeClass('x-item-disabled');
9462 onShow : function(){
9463 var ae = this.getActionEl();
9466 ae.dom.style.display = '';
9467 ae.dom.style.visibility = 'visible';
9473 onHide : function(){
9474 var ae = this.getActionEl();
9475 ae.dom.style.display = 'none';
9479 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9480 * by an implementing function.
9482 * @param {EventObject} e
9484 onTriggerClick : Roo.emptyFn
9488 * Ext JS Library 1.1.1
9489 * Copyright(c) 2006-2007, Ext JS, LLC.
9491 * Originally Released Under LGPL - original licence link has changed is not relivant.
9494 * <script type="text/javascript">
9499 * @class Roo.data.SortTypes
9501 * Defines the default sorting (casting?) comparison functions used when sorting data.
9503 Roo.data.SortTypes = {
9505 * Default sort that does nothing
9506 * @param {Mixed} s The value being converted
9507 * @return {Mixed} The comparison value
9514 * The regular expression used to strip tags
9518 stripTagsRE : /<\/?[^>]+>/gi,
9521 * Strips all HTML tags to sort on text only
9522 * @param {Mixed} s The value being converted
9523 * @return {String} The comparison value
9525 asText : function(s){
9526 return String(s).replace(this.stripTagsRE, "");
9530 * Strips all HTML tags to sort on text only - Case insensitive
9531 * @param {Mixed} s The value being converted
9532 * @return {String} The comparison value
9534 asUCText : function(s){
9535 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9539 * Case insensitive string
9540 * @param {Mixed} s The value being converted
9541 * @return {String} The comparison value
9543 asUCString : function(s) {
9544 return String(s).toUpperCase();
9549 * @param {Mixed} s The value being converted
9550 * @return {Number} The comparison value
9552 asDate : function(s) {
9556 if(s instanceof Date){
9559 return Date.parse(String(s));
9564 * @param {Mixed} s The value being converted
9565 * @return {Float} The comparison value
9567 asFloat : function(s) {
9568 var val = parseFloat(String(s).replace(/,/g, ""));
9577 * @param {Mixed} s The value being converted
9578 * @return {Number} The comparison value
9580 asInt : function(s) {
9581 var val = parseInt(String(s).replace(/,/g, ""));
9589 * Ext JS Library 1.1.1
9590 * Copyright(c) 2006-2007, Ext JS, LLC.
9592 * Originally Released Under LGPL - original licence link has changed is not relivant.
9595 * <script type="text/javascript">
9599 * @class Roo.data.Record
9600 * Instances of this class encapsulate both record <em>definition</em> information, and record
9601 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9602 * to access Records cached in an {@link Roo.data.Store} object.<br>
9604 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9605 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9608 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9610 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9611 * {@link #create}. The parameters are the same.
9612 * @param {Array} data An associative Array of data values keyed by the field name.
9613 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9614 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9615 * not specified an integer id is generated.
9617 Roo.data.Record = function(data, id){
9618 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9623 * Generate a constructor for a specific record layout.
9624 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9625 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9626 * Each field definition object may contain the following properties: <ul>
9627 * <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,
9628 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9629 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9630 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9631 * is being used, then this is a string containing the javascript expression to reference the data relative to
9632 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9633 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9634 * this may be omitted.</p></li>
9635 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9636 * <ul><li>auto (Default, implies no conversion)</li>
9641 * <li>date</li></ul></p></li>
9642 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9643 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9644 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9645 * by the Reader into an object that will be stored in the Record. It is passed the
9646 * following parameters:<ul>
9647 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9649 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9651 * <br>usage:<br><pre><code>
9652 var TopicRecord = Roo.data.Record.create(
9653 {name: 'title', mapping: 'topic_title'},
9654 {name: 'author', mapping: 'username'},
9655 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9656 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9657 {name: 'lastPoster', mapping: 'user2'},
9658 {name: 'excerpt', mapping: 'post_text'}
9661 var myNewRecord = new TopicRecord({
9662 title: 'Do my job please',
9665 lastPost: new Date(),
9666 lastPoster: 'Animal',
9667 excerpt: 'No way dude!'
9669 myStore.add(myNewRecord);
9674 Roo.data.Record.create = function(o){
9676 f.superclass.constructor.apply(this, arguments);
9678 Roo.extend(f, Roo.data.Record);
9679 var p = f.prototype;
9680 p.fields = new Roo.util.MixedCollection(false, function(field){
9683 for(var i = 0, len = o.length; i < len; i++){
9684 p.fields.add(new Roo.data.Field(o[i]));
9686 f.getField = function(name){
9687 return p.fields.get(name);
9692 Roo.data.Record.AUTO_ID = 1000;
9693 Roo.data.Record.EDIT = 'edit';
9694 Roo.data.Record.REJECT = 'reject';
9695 Roo.data.Record.COMMIT = 'commit';
9697 Roo.data.Record.prototype = {
9699 * Readonly flag - true if this record has been modified.
9708 join : function(store){
9713 * Set the named field to the specified value.
9714 * @param {String} name The name of the field to set.
9715 * @param {Object} value The value to set the field to.
9717 set : function(name, value){
9718 if(this.data[name] == value){
9725 if(typeof this.modified[name] == 'undefined'){
9726 this.modified[name] = this.data[name];
9728 this.data[name] = value;
9729 if(!this.editing && this.store){
9730 this.store.afterEdit(this);
9735 * Get the value of the named field.
9736 * @param {String} name The name of the field to get the value of.
9737 * @return {Object} The value of the field.
9739 get : function(name){
9740 return this.data[name];
9744 beginEdit : function(){
9745 this.editing = true;
9750 cancelEdit : function(){
9751 this.editing = false;
9752 delete this.modified;
9756 endEdit : function(){
9757 this.editing = false;
9758 if(this.dirty && this.store){
9759 this.store.afterEdit(this);
9764 * Usually called by the {@link Roo.data.Store} which owns the Record.
9765 * Rejects all changes made to the Record since either creation, or the last commit operation.
9766 * Modified fields are reverted to their original values.
9768 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9769 * of reject operations.
9771 reject : function(){
9772 var m = this.modified;
9774 if(typeof m[n] != "function"){
9775 this.data[n] = m[n];
9779 delete this.modified;
9780 this.editing = false;
9782 this.store.afterReject(this);
9787 * Usually called by the {@link Roo.data.Store} which owns the Record.
9788 * Commits all changes made to the Record since either creation, or the last commit operation.
9790 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9791 * of commit operations.
9793 commit : function(){
9795 delete this.modified;
9796 this.editing = false;
9798 this.store.afterCommit(this);
9803 hasError : function(){
9804 return this.error != null;
9808 clearError : function(){
9813 * Creates a copy of this record.
9814 * @param {String} id (optional) A new record id if you don't want to use this record's id
9817 copy : function(newId) {
9818 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9822 * Ext JS Library 1.1.1
9823 * Copyright(c) 2006-2007, Ext JS, LLC.
9825 * Originally Released Under LGPL - original licence link has changed is not relivant.
9828 * <script type="text/javascript">
9834 * @class Roo.data.Store
9835 * @extends Roo.util.Observable
9836 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9837 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9839 * 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
9840 * has no knowledge of the format of the data returned by the Proxy.<br>
9842 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9843 * instances from the data object. These records are cached and made available through accessor functions.
9845 * Creates a new Store.
9846 * @param {Object} config A config object containing the objects needed for the Store to access data,
9847 * and read the data into Records.
9849 Roo.data.Store = function(config){
9850 this.data = new Roo.util.MixedCollection(false);
9851 this.data.getKey = function(o){
9854 this.baseParams = {};
9861 "multisort" : "_multisort"
9864 if(config && config.data){
9865 this.inlineData = config.data;
9869 Roo.apply(this, config);
9871 if(this.reader){ // reader passed
9872 this.reader = Roo.factory(this.reader, Roo.data);
9873 this.reader.xmodule = this.xmodule || false;
9874 if(!this.recordType){
9875 this.recordType = this.reader.recordType;
9877 if(this.reader.onMetaChange){
9878 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9882 if(this.recordType){
9883 this.fields = this.recordType.prototype.fields;
9889 * @event datachanged
9890 * Fires when the data cache has changed, and a widget which is using this Store
9891 * as a Record cache should refresh its view.
9892 * @param {Store} this
9897 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9898 * @param {Store} this
9899 * @param {Object} meta The JSON metadata
9904 * Fires when Records have been added to the Store
9905 * @param {Store} this
9906 * @param {Roo.data.Record[]} records The array of Records added
9907 * @param {Number} index The index at which the record(s) were added
9912 * Fires when a Record has been removed from the Store
9913 * @param {Store} this
9914 * @param {Roo.data.Record} record The Record that was removed
9915 * @param {Number} index The index at which the record was removed
9920 * Fires when a Record has been updated
9921 * @param {Store} this
9922 * @param {Roo.data.Record} record The Record that was updated
9923 * @param {String} operation The update operation being performed. Value may be one of:
9925 Roo.data.Record.EDIT
9926 Roo.data.Record.REJECT
9927 Roo.data.Record.COMMIT
9933 * Fires when the data cache has been cleared.
9934 * @param {Store} this
9939 * Fires before a request is made for a new data object. If the beforeload handler returns false
9940 * the load action will be canceled.
9941 * @param {Store} this
9942 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9946 * @event beforeloadadd
9947 * Fires after a new set of Records has been loaded.
9948 * @param {Store} this
9949 * @param {Roo.data.Record[]} records The Records that were loaded
9950 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9952 beforeloadadd : true,
9955 * Fires after a new set of Records has been loaded, before they are added to the store.
9956 * @param {Store} this
9957 * @param {Roo.data.Record[]} records The Records that were loaded
9958 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9959 * @params {Object} return from reader
9963 * @event loadexception
9964 * Fires if an exception occurs in the Proxy during loading.
9965 * Called with the signature of the Proxy's "loadexception" event.
9966 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9969 * @param {Object} return from JsonData.reader() - success, totalRecords, records
9970 * @param {Object} load options
9971 * @param {Object} jsonData from your request (normally this contains the Exception)
9973 loadexception : true
9977 this.proxy = Roo.factory(this.proxy, Roo.data);
9978 this.proxy.xmodule = this.xmodule || false;
9979 this.relayEvents(this.proxy, ["loadexception"]);
9981 this.sortToggle = {};
9982 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9984 Roo.data.Store.superclass.constructor.call(this);
9986 if(this.inlineData){
9987 this.loadData(this.inlineData);
9988 delete this.inlineData;
9992 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9994 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
9995 * without a remote query - used by combo/forms at present.
9999 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10002 * @cfg {Array} data Inline data to be loaded when the store is initialized.
10005 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10006 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10009 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10010 * on any HTTP request
10013 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10016 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10020 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10021 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10023 remoteSort : false,
10026 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10027 * loaded or when a record is removed. (defaults to false).
10029 pruneModifiedRecords : false,
10032 lastOptions : null,
10035 * Add Records to the Store and fires the add event.
10036 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10038 add : function(records){
10039 records = [].concat(records);
10040 for(var i = 0, len = records.length; i < len; i++){
10041 records[i].join(this);
10043 var index = this.data.length;
10044 this.data.addAll(records);
10045 this.fireEvent("add", this, records, index);
10049 * Remove a Record from the Store and fires the remove event.
10050 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10052 remove : function(record){
10053 var index = this.data.indexOf(record);
10054 this.data.removeAt(index);
10055 if(this.pruneModifiedRecords){
10056 this.modified.remove(record);
10058 this.fireEvent("remove", this, record, index);
10062 * Remove all Records from the Store and fires the clear event.
10064 removeAll : function(){
10066 if(this.pruneModifiedRecords){
10067 this.modified = [];
10069 this.fireEvent("clear", this);
10073 * Inserts Records to the Store at the given index and fires the add event.
10074 * @param {Number} index The start index at which to insert the passed Records.
10075 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10077 insert : function(index, records){
10078 records = [].concat(records);
10079 for(var i = 0, len = records.length; i < len; i++){
10080 this.data.insert(index, records[i]);
10081 records[i].join(this);
10083 this.fireEvent("add", this, records, index);
10087 * Get the index within the cache of the passed Record.
10088 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10089 * @return {Number} The index of the passed Record. Returns -1 if not found.
10091 indexOf : function(record){
10092 return this.data.indexOf(record);
10096 * Get the index within the cache of the Record with the passed id.
10097 * @param {String} id The id of the Record to find.
10098 * @return {Number} The index of the Record. Returns -1 if not found.
10100 indexOfId : function(id){
10101 return this.data.indexOfKey(id);
10105 * Get the Record with the specified id.
10106 * @param {String} id The id of the Record to find.
10107 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10109 getById : function(id){
10110 return this.data.key(id);
10114 * Get the Record at the specified index.
10115 * @param {Number} index The index of the Record to find.
10116 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10118 getAt : function(index){
10119 return this.data.itemAt(index);
10123 * Returns a range of Records between specified indices.
10124 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10125 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10126 * @return {Roo.data.Record[]} An array of Records
10128 getRange : function(start, end){
10129 return this.data.getRange(start, end);
10133 storeOptions : function(o){
10134 o = Roo.apply({}, o);
10137 this.lastOptions = o;
10141 * Loads the Record cache from the configured Proxy using the configured Reader.
10143 * If using remote paging, then the first load call must specify the <em>start</em>
10144 * and <em>limit</em> properties in the options.params property to establish the initial
10145 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10147 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10148 * and this call will return before the new data has been loaded. Perform any post-processing
10149 * in a callback function, or in a "load" event handler.</strong>
10151 * @param {Object} options An object containing properties which control loading options:<ul>
10152 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10153 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10154 * passed the following arguments:<ul>
10155 * <li>r : Roo.data.Record[]</li>
10156 * <li>options: Options object from the load call</li>
10157 * <li>success: Boolean success indicator</li></ul></li>
10158 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10159 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10162 load : function(options){
10163 options = options || {};
10164 if(this.fireEvent("beforeload", this, options) !== false){
10165 this.storeOptions(options);
10166 var p = Roo.apply(options.params || {}, this.baseParams);
10167 // if meta was not loaded from remote source.. try requesting it.
10168 if (!this.reader.metaFromRemote) {
10169 p._requestMeta = 1;
10171 if(this.sortInfo && this.remoteSort){
10172 var pn = this.paramNames;
10173 p[pn["sort"]] = this.sortInfo.field;
10174 p[pn["dir"]] = this.sortInfo.direction;
10176 if (this.multiSort) {
10177 var pn = this.paramNames;
10178 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10181 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10186 * Reloads the Record cache from the configured Proxy using the configured Reader and
10187 * the options from the last load operation performed.
10188 * @param {Object} options (optional) An object containing properties which may override the options
10189 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10190 * the most recently used options are reused).
10192 reload : function(options){
10193 this.load(Roo.applyIf(options||{}, this.lastOptions));
10197 // Called as a callback by the Reader during a load operation.
10198 loadRecords : function(o, options, success){
10199 if(!o || success === false){
10200 if(success !== false){
10201 this.fireEvent("load", this, [], options, o);
10203 if(options.callback){
10204 options.callback.call(options.scope || this, [], options, false);
10208 // if data returned failure - throw an exception.
10209 if (o.success === false) {
10210 // show a message if no listener is registered.
10211 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10212 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10214 // loadmask wil be hooked into this..
10215 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10218 var r = o.records, t = o.totalRecords || r.length;
10220 this.fireEvent("beforeloadadd", this, r, options, o);
10222 if(!options || options.add !== true){
10223 if(this.pruneModifiedRecords){
10224 this.modified = [];
10226 for(var i = 0, len = r.length; i < len; i++){
10230 this.data = this.snapshot;
10231 delete this.snapshot;
10234 this.data.addAll(r);
10235 this.totalLength = t;
10237 this.fireEvent("datachanged", this);
10239 this.totalLength = Math.max(t, this.data.length+r.length);
10242 this.fireEvent("load", this, r, options, o);
10243 if(options.callback){
10244 options.callback.call(options.scope || this, r, options, true);
10250 * Loads data from a passed data block. A Reader which understands the format of the data
10251 * must have been configured in the constructor.
10252 * @param {Object} data The data block from which to read the Records. The format of the data expected
10253 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10254 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10256 loadData : function(o, append){
10257 var r = this.reader.readRecords(o);
10258 this.loadRecords(r, {add: append}, true);
10262 * Gets the number of cached records.
10264 * <em>If using paging, this may not be the total size of the dataset. If the data object
10265 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10266 * the data set size</em>
10268 getCount : function(){
10269 return this.data.length || 0;
10273 * Gets the total number of records in the dataset as returned by the server.
10275 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10276 * the dataset size</em>
10278 getTotalCount : function(){
10279 return this.totalLength || 0;
10283 * Returns the sort state of the Store as an object with two properties:
10285 field {String} The name of the field by which the Records are sorted
10286 direction {String} The sort order, "ASC" or "DESC"
10289 getSortState : function(){
10290 return this.sortInfo;
10294 applySort : function(){
10295 if(this.sortInfo && !this.remoteSort){
10296 var s = this.sortInfo, f = s.field;
10297 var st = this.fields.get(f).sortType;
10298 var fn = function(r1, r2){
10299 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10300 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10302 this.data.sort(s.direction, fn);
10303 if(this.snapshot && this.snapshot != this.data){
10304 this.snapshot.sort(s.direction, fn);
10310 * Sets the default sort column and order to be used by the next load operation.
10311 * @param {String} fieldName The name of the field to sort by.
10312 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10314 setDefaultSort : function(field, dir){
10315 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10319 * Sort the Records.
10320 * If remote sorting is used, the sort is performed on the server, and the cache is
10321 * reloaded. If local sorting is used, the cache is sorted internally.
10322 * @param {String} fieldName The name of the field to sort by.
10323 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10325 sort : function(fieldName, dir){
10326 var f = this.fields.get(fieldName);
10328 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10330 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10331 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10336 this.sortToggle[f.name] = dir;
10337 this.sortInfo = {field: f.name, direction: dir};
10338 if(!this.remoteSort){
10340 this.fireEvent("datachanged", this);
10342 this.load(this.lastOptions);
10347 * Calls the specified function for each of the Records in the cache.
10348 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10349 * Returning <em>false</em> aborts and exits the iteration.
10350 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10352 each : function(fn, scope){
10353 this.data.each(fn, scope);
10357 * Gets all records modified since the last commit. Modified records are persisted across load operations
10358 * (e.g., during paging).
10359 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10361 getModifiedRecords : function(){
10362 return this.modified;
10366 createFilterFn : function(property, value, anyMatch){
10367 if(!value.exec){ // not a regex
10368 value = String(value);
10369 if(value.length == 0){
10372 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10374 return function(r){
10375 return value.test(r.data[property]);
10380 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10381 * @param {String} property A field on your records
10382 * @param {Number} start The record index to start at (defaults to 0)
10383 * @param {Number} end The last record index to include (defaults to length - 1)
10384 * @return {Number} The sum
10386 sum : function(property, start, end){
10387 var rs = this.data.items, v = 0;
10388 start = start || 0;
10389 end = (end || end === 0) ? end : rs.length-1;
10391 for(var i = start; i <= end; i++){
10392 v += (rs[i].data[property] || 0);
10398 * Filter the records by a specified property.
10399 * @param {String} field A field on your records
10400 * @param {String/RegExp} value Either a string that the field
10401 * should start with or a RegExp to test against the field
10402 * @param {Boolean} anyMatch True to match any part not just the beginning
10404 filter : function(property, value, anyMatch){
10405 var fn = this.createFilterFn(property, value, anyMatch);
10406 return fn ? this.filterBy(fn) : this.clearFilter();
10410 * Filter by a function. The specified function will be called with each
10411 * record in this data source. If the function returns true the record is included,
10412 * otherwise it is filtered.
10413 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10414 * @param {Object} scope (optional) The scope of the function (defaults to this)
10416 filterBy : function(fn, scope){
10417 this.snapshot = this.snapshot || this.data;
10418 this.data = this.queryBy(fn, scope||this);
10419 this.fireEvent("datachanged", this);
10423 * Query the records by a specified property.
10424 * @param {String} field A field on your records
10425 * @param {String/RegExp} value Either a string that the field
10426 * should start with or a RegExp to test against the field
10427 * @param {Boolean} anyMatch True to match any part not just the beginning
10428 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10430 query : function(property, value, anyMatch){
10431 var fn = this.createFilterFn(property, value, anyMatch);
10432 return fn ? this.queryBy(fn) : this.data.clone();
10436 * Query by a function. The specified function will be called with each
10437 * record in this data source. If the function returns true the record is included
10439 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10440 * @param {Object} scope (optional) The scope of the function (defaults to this)
10441 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10443 queryBy : function(fn, scope){
10444 var data = this.snapshot || this.data;
10445 return data.filterBy(fn, scope||this);
10449 * Collects unique values for a particular dataIndex from this store.
10450 * @param {String} dataIndex The property to collect
10451 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10452 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10453 * @return {Array} An array of the unique values
10455 collect : function(dataIndex, allowNull, bypassFilter){
10456 var d = (bypassFilter === true && this.snapshot) ?
10457 this.snapshot.items : this.data.items;
10458 var v, sv, r = [], l = {};
10459 for(var i = 0, len = d.length; i < len; i++){
10460 v = d[i].data[dataIndex];
10462 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10471 * Revert to a view of the Record cache with no filtering applied.
10472 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10474 clearFilter : function(suppressEvent){
10475 if(this.snapshot && this.snapshot != this.data){
10476 this.data = this.snapshot;
10477 delete this.snapshot;
10478 if(suppressEvent !== true){
10479 this.fireEvent("datachanged", this);
10485 afterEdit : function(record){
10486 if(this.modified.indexOf(record) == -1){
10487 this.modified.push(record);
10489 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10493 afterReject : function(record){
10494 this.modified.remove(record);
10495 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10499 afterCommit : function(record){
10500 this.modified.remove(record);
10501 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10505 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10506 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10508 commitChanges : function(){
10509 var m = this.modified.slice(0);
10510 this.modified = [];
10511 for(var i = 0, len = m.length; i < len; i++){
10517 * Cancel outstanding changes on all changed records.
10519 rejectChanges : function(){
10520 var m = this.modified.slice(0);
10521 this.modified = [];
10522 for(var i = 0, len = m.length; i < len; i++){
10527 onMetaChange : function(meta, rtype, o){
10528 this.recordType = rtype;
10529 this.fields = rtype.prototype.fields;
10530 delete this.snapshot;
10531 this.sortInfo = meta.sortInfo || this.sortInfo;
10532 this.modified = [];
10533 this.fireEvent('metachange', this, this.reader.meta);
10536 moveIndex : function(data, type)
10538 var index = this.indexOf(data);
10540 var newIndex = index + type;
10544 this.insert(newIndex, data);
10549 * Ext JS Library 1.1.1
10550 * Copyright(c) 2006-2007, Ext JS, LLC.
10552 * Originally Released Under LGPL - original licence link has changed is not relivant.
10555 * <script type="text/javascript">
10559 * @class Roo.data.SimpleStore
10560 * @extends Roo.data.Store
10561 * Small helper class to make creating Stores from Array data easier.
10562 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10563 * @cfg {Array} fields An array of field definition objects, or field name strings.
10564 * @cfg {Array} data The multi-dimensional array of data
10566 * @param {Object} config
10568 Roo.data.SimpleStore = function(config){
10569 Roo.data.SimpleStore.superclass.constructor.call(this, {
10571 reader: new Roo.data.ArrayReader({
10574 Roo.data.Record.create(config.fields)
10576 proxy : new Roo.data.MemoryProxy(config.data)
10580 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10582 * Ext JS Library 1.1.1
10583 * Copyright(c) 2006-2007, Ext JS, LLC.
10585 * Originally Released Under LGPL - original licence link has changed is not relivant.
10588 * <script type="text/javascript">
10593 * @extends Roo.data.Store
10594 * @class Roo.data.JsonStore
10595 * Small helper class to make creating Stores for JSON data easier. <br/>
10597 var store = new Roo.data.JsonStore({
10598 url: 'get-images.php',
10600 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10603 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10604 * JsonReader and HttpProxy (unless inline data is provided).</b>
10605 * @cfg {Array} fields An array of field definition objects, or field name strings.
10607 * @param {Object} config
10609 Roo.data.JsonStore = function(c){
10610 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10611 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10612 reader: new Roo.data.JsonReader(c, c.fields)
10615 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10617 * Ext JS Library 1.1.1
10618 * Copyright(c) 2006-2007, Ext JS, LLC.
10620 * Originally Released Under LGPL - original licence link has changed is not relivant.
10623 * <script type="text/javascript">
10627 Roo.data.Field = function(config){
10628 if(typeof config == "string"){
10629 config = {name: config};
10631 Roo.apply(this, config);
10634 this.type = "auto";
10637 var st = Roo.data.SortTypes;
10638 // named sortTypes are supported, here we look them up
10639 if(typeof this.sortType == "string"){
10640 this.sortType = st[this.sortType];
10643 // set default sortType for strings and dates
10644 if(!this.sortType){
10647 this.sortType = st.asUCString;
10650 this.sortType = st.asDate;
10653 this.sortType = st.none;
10658 var stripRe = /[\$,%]/g;
10660 // prebuilt conversion function for this field, instead of
10661 // switching every time we're reading a value
10663 var cv, dateFormat = this.dateFormat;
10668 cv = function(v){ return v; };
10671 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10675 return v !== undefined && v !== null && v !== '' ?
10676 parseInt(String(v).replace(stripRe, ""), 10) : '';
10681 return v !== undefined && v !== null && v !== '' ?
10682 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10687 cv = function(v){ return v === true || v === "true" || v == 1; };
10694 if(v instanceof Date){
10698 if(dateFormat == "timestamp"){
10699 return new Date(v*1000);
10701 return Date.parseDate(v, dateFormat);
10703 var parsed = Date.parse(v);
10704 return parsed ? new Date(parsed) : null;
10713 Roo.data.Field.prototype = {
10721 * Ext JS Library 1.1.1
10722 * Copyright(c) 2006-2007, Ext JS, LLC.
10724 * Originally Released Under LGPL - original licence link has changed is not relivant.
10727 * <script type="text/javascript">
10730 // Base class for reading structured data from a data source. This class is intended to be
10731 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10734 * @class Roo.data.DataReader
10735 * Base class for reading structured data from a data source. This class is intended to be
10736 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10739 Roo.data.DataReader = function(meta, recordType){
10743 this.recordType = recordType instanceof Array ?
10744 Roo.data.Record.create(recordType) : recordType;
10747 Roo.data.DataReader.prototype = {
10749 * Create an empty record
10750 * @param {Object} data (optional) - overlay some values
10751 * @return {Roo.data.Record} record created.
10753 newRow : function(d) {
10755 this.recordType.prototype.fields.each(function(c) {
10757 case 'int' : da[c.name] = 0; break;
10758 case 'date' : da[c.name] = new Date(); break;
10759 case 'float' : da[c.name] = 0.0; break;
10760 case 'boolean' : da[c.name] = false; break;
10761 default : da[c.name] = ""; break;
10765 return new this.recordType(Roo.apply(da, d));
10770 * Ext JS Library 1.1.1
10771 * Copyright(c) 2006-2007, Ext JS, LLC.
10773 * Originally Released Under LGPL - original licence link has changed is not relivant.
10776 * <script type="text/javascript">
10780 * @class Roo.data.DataProxy
10781 * @extends Roo.data.Observable
10782 * This class is an abstract base class for implementations which provide retrieval of
10783 * unformatted data objects.<br>
10785 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10786 * (of the appropriate type which knows how to parse the data object) to provide a block of
10787 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10789 * Custom implementations must implement the load method as described in
10790 * {@link Roo.data.HttpProxy#load}.
10792 Roo.data.DataProxy = function(){
10795 * @event beforeload
10796 * Fires before a network request is made to retrieve a data object.
10797 * @param {Object} This DataProxy object.
10798 * @param {Object} params The params parameter to the load function.
10803 * Fires before the load method's callback is called.
10804 * @param {Object} This DataProxy object.
10805 * @param {Object} o The data object.
10806 * @param {Object} arg The callback argument object passed to the load function.
10810 * @event loadexception
10811 * Fires if an Exception occurs during data retrieval.
10812 * @param {Object} This DataProxy object.
10813 * @param {Object} o The data object.
10814 * @param {Object} arg The callback argument object passed to the load function.
10815 * @param {Object} e The Exception.
10817 loadexception : true
10819 Roo.data.DataProxy.superclass.constructor.call(this);
10822 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10825 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10829 * Ext JS Library 1.1.1
10830 * Copyright(c) 2006-2007, Ext JS, LLC.
10832 * Originally Released Under LGPL - original licence link has changed is not relivant.
10835 * <script type="text/javascript">
10838 * @class Roo.data.MemoryProxy
10839 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10840 * to the Reader when its load method is called.
10842 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10844 Roo.data.MemoryProxy = function(data){
10848 Roo.data.MemoryProxy.superclass.constructor.call(this);
10852 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10854 * Load data from the requested source (in this case an in-memory
10855 * data object passed to the constructor), read the data object into
10856 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10857 * process that block using the passed callback.
10858 * @param {Object} params This parameter is not used by the MemoryProxy class.
10859 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10860 * object into a block of Roo.data.Records.
10861 * @param {Function} callback The function into which to pass the block of Roo.data.records.
10862 * The function must be passed <ul>
10863 * <li>The Record block object</li>
10864 * <li>The "arg" argument from the load function</li>
10865 * <li>A boolean success indicator</li>
10867 * @param {Object} scope The scope in which to call the callback
10868 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10870 load : function(params, reader, callback, scope, arg){
10871 params = params || {};
10874 result = reader.readRecords(this.data);
10876 this.fireEvent("loadexception", this, arg, null, e);
10877 callback.call(scope, null, arg, false);
10880 callback.call(scope, result, arg, true);
10884 update : function(params, records){
10889 * Ext JS Library 1.1.1
10890 * Copyright(c) 2006-2007, Ext JS, LLC.
10892 * Originally Released Under LGPL - original licence link has changed is not relivant.
10895 * <script type="text/javascript">
10898 * @class Roo.data.HttpProxy
10899 * @extends Roo.data.DataProxy
10900 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10901 * configured to reference a certain URL.<br><br>
10903 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10904 * from which the running page was served.<br><br>
10906 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10908 * Be aware that to enable the browser to parse an XML document, the server must set
10909 * the Content-Type header in the HTTP response to "text/xml".
10911 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10912 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
10913 * will be used to make the request.
10915 Roo.data.HttpProxy = function(conn){
10916 Roo.data.HttpProxy.superclass.constructor.call(this);
10917 // is conn a conn config or a real conn?
10919 this.useAjax = !conn || !conn.events;
10923 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10924 // thse are take from connection...
10927 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10930 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10931 * extra parameters to each request made by this object. (defaults to undefined)
10934 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10935 * to each request made by this object. (defaults to undefined)
10938 * @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)
10941 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10944 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10950 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10954 * Return the {@link Roo.data.Connection} object being used by this Proxy.
10955 * @return {Connection} The Connection object. This object may be used to subscribe to events on
10956 * a finer-grained basis than the DataProxy events.
10958 getConnection : function(){
10959 return this.useAjax ? Roo.Ajax : this.conn;
10963 * Load data from the configured {@link Roo.data.Connection}, read the data object into
10964 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10965 * process that block using the passed callback.
10966 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10967 * for the request to the remote server.
10968 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10969 * object into a block of Roo.data.Records.
10970 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10971 * The function must be passed <ul>
10972 * <li>The Record block object</li>
10973 * <li>The "arg" argument from the load function</li>
10974 * <li>A boolean success indicator</li>
10976 * @param {Object} scope The scope in which to call the callback
10977 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10979 load : function(params, reader, callback, scope, arg){
10980 if(this.fireEvent("beforeload", this, params) !== false){
10982 params : params || {},
10984 callback : callback,
10989 callback : this.loadResponse,
10993 Roo.applyIf(o, this.conn);
10994 if(this.activeRequest){
10995 Roo.Ajax.abort(this.activeRequest);
10997 this.activeRequest = Roo.Ajax.request(o);
10999 this.conn.request(o);
11002 callback.call(scope||this, null, arg, false);
11007 loadResponse : function(o, success, response){
11008 delete this.activeRequest;
11010 this.fireEvent("loadexception", this, o, response);
11011 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11016 result = o.reader.read(response);
11018 this.fireEvent("loadexception", this, o, response, e);
11019 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11023 this.fireEvent("load", this, o, o.request.arg);
11024 o.request.callback.call(o.request.scope, result, o.request.arg, true);
11028 update : function(dataSet){
11033 updateResponse : function(dataSet){
11038 * Ext JS Library 1.1.1
11039 * Copyright(c) 2006-2007, Ext JS, LLC.
11041 * Originally Released Under LGPL - original licence link has changed is not relivant.
11044 * <script type="text/javascript">
11048 * @class Roo.data.ScriptTagProxy
11049 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11050 * other than the originating domain of the running page.<br><br>
11052 * <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
11053 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11055 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11056 * source code that is used as the source inside a <script> tag.<br><br>
11058 * In order for the browser to process the returned data, the server must wrap the data object
11059 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11060 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11061 * depending on whether the callback name was passed:
11064 boolean scriptTag = false;
11065 String cb = request.getParameter("callback");
11068 response.setContentType("text/javascript");
11070 response.setContentType("application/x-json");
11072 Writer out = response.getWriter();
11074 out.write(cb + "(");
11076 out.print(dataBlock.toJsonString());
11083 * @param {Object} config A configuration object.
11085 Roo.data.ScriptTagProxy = function(config){
11086 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11087 Roo.apply(this, config);
11088 this.head = document.getElementsByTagName("head")[0];
11091 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11093 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11095 * @cfg {String} url The URL from which to request the data object.
11098 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11102 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11103 * the server the name of the callback function set up by the load call to process the returned data object.
11104 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11105 * javascript output which calls this named function passing the data object as its only parameter.
11107 callbackParam : "callback",
11109 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11110 * name to the request.
11115 * Load data from the configured URL, read the data object into
11116 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11117 * process that block using the passed callback.
11118 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11119 * for the request to the remote server.
11120 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11121 * object into a block of Roo.data.Records.
11122 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11123 * The function must be passed <ul>
11124 * <li>The Record block object</li>
11125 * <li>The "arg" argument from the load function</li>
11126 * <li>A boolean success indicator</li>
11128 * @param {Object} scope The scope in which to call the callback
11129 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11131 load : function(params, reader, callback, scope, arg){
11132 if(this.fireEvent("beforeload", this, params) !== false){
11134 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11136 var url = this.url;
11137 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11139 url += "&_dc=" + (new Date().getTime());
11141 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11144 cb : "stcCallback"+transId,
11145 scriptId : "stcScript"+transId,
11149 callback : callback,
11155 window[trans.cb] = function(o){
11156 conn.handleResponse(o, trans);
11159 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11161 if(this.autoAbort !== false){
11165 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11167 var script = document.createElement("script");
11168 script.setAttribute("src", url);
11169 script.setAttribute("type", "text/javascript");
11170 script.setAttribute("id", trans.scriptId);
11171 this.head.appendChild(script);
11173 this.trans = trans;
11175 callback.call(scope||this, null, arg, false);
11180 isLoading : function(){
11181 return this.trans ? true : false;
11185 * Abort the current server request.
11187 abort : function(){
11188 if(this.isLoading()){
11189 this.destroyTrans(this.trans);
11194 destroyTrans : function(trans, isLoaded){
11195 this.head.removeChild(document.getElementById(trans.scriptId));
11196 clearTimeout(trans.timeoutId);
11198 window[trans.cb] = undefined;
11200 delete window[trans.cb];
11203 // if hasn't been loaded, wait for load to remove it to prevent script error
11204 window[trans.cb] = function(){
11205 window[trans.cb] = undefined;
11207 delete window[trans.cb];
11214 handleResponse : function(o, trans){
11215 this.trans = false;
11216 this.destroyTrans(trans, true);
11219 result = trans.reader.readRecords(o);
11221 this.fireEvent("loadexception", this, o, trans.arg, e);
11222 trans.callback.call(trans.scope||window, null, trans.arg, false);
11225 this.fireEvent("load", this, o, trans.arg);
11226 trans.callback.call(trans.scope||window, result, trans.arg, true);
11230 handleFailure : function(trans){
11231 this.trans = false;
11232 this.destroyTrans(trans, false);
11233 this.fireEvent("loadexception", this, null, trans.arg);
11234 trans.callback.call(trans.scope||window, null, trans.arg, false);
11238 * Ext JS Library 1.1.1
11239 * Copyright(c) 2006-2007, Ext JS, LLC.
11241 * Originally Released Under LGPL - original licence link has changed is not relivant.
11244 * <script type="text/javascript">
11248 * @class Roo.data.JsonReader
11249 * @extends Roo.data.DataReader
11250 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11251 * based on mappings in a provided Roo.data.Record constructor.
11253 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11254 * in the reply previously.
11259 var RecordDef = Roo.data.Record.create([
11260 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11261 {name: 'occupation'} // This field will use "occupation" as the mapping.
11263 var myReader = new Roo.data.JsonReader({
11264 totalProperty: "results", // The property which contains the total dataset size (optional)
11265 root: "rows", // The property which contains an Array of row objects
11266 id: "id" // The property within each row object that provides an ID for the record (optional)
11270 * This would consume a JSON file like this:
11272 { 'results': 2, 'rows': [
11273 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11274 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11277 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11278 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11279 * paged from the remote server.
11280 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11281 * @cfg {String} root name of the property which contains the Array of row objects.
11282 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11283 * @cfg {Array} fields Array of field definition objects
11285 * Create a new JsonReader
11286 * @param {Object} meta Metadata configuration options
11287 * @param {Object} recordType Either an Array of field definition objects,
11288 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11290 Roo.data.JsonReader = function(meta, recordType){
11293 // set some defaults:
11294 Roo.applyIf(meta, {
11295 totalProperty: 'total',
11296 successProperty : 'success',
11301 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11303 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11306 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11307 * Used by Store query builder to append _requestMeta to params.
11310 metaFromRemote : false,
11312 * This method is only used by a DataProxy which has retrieved data from a remote server.
11313 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11314 * @return {Object} data A data block which is used by an Roo.data.Store object as
11315 * a cache of Roo.data.Records.
11317 read : function(response){
11318 var json = response.responseText;
11320 var o = /* eval:var:o */ eval("("+json+")");
11322 throw {message: "JsonReader.read: Json object not found"};
11328 this.metaFromRemote = true;
11329 this.meta = o.metaData;
11330 this.recordType = Roo.data.Record.create(o.metaData.fields);
11331 this.onMetaChange(this.meta, this.recordType, o);
11333 return this.readRecords(o);
11336 // private function a store will implement
11337 onMetaChange : function(meta, recordType, o){
11344 simpleAccess: function(obj, subsc) {
11351 getJsonAccessor: function(){
11353 return function(expr) {
11355 return(re.test(expr))
11356 ? new Function("obj", "return obj." + expr)
11361 return Roo.emptyFn;
11366 * Create a data block containing Roo.data.Records from an XML document.
11367 * @param {Object} o An object which contains an Array of row objects in the property specified
11368 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11369 * which contains the total size of the dataset.
11370 * @return {Object} data A data block which is used by an Roo.data.Store object as
11371 * a cache of Roo.data.Records.
11373 readRecords : function(o){
11375 * After any data loads, the raw JSON data is available for further custom processing.
11379 var s = this.meta, Record = this.recordType,
11380 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11382 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11384 if(s.totalProperty) {
11385 this.getTotal = this.getJsonAccessor(s.totalProperty);
11387 if(s.successProperty) {
11388 this.getSuccess = this.getJsonAccessor(s.successProperty);
11390 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11392 var g = this.getJsonAccessor(s.id);
11393 this.getId = function(rec) {
11395 return (r === undefined || r === "") ? null : r;
11398 this.getId = function(){return null;};
11401 for(var jj = 0; jj < fl; jj++){
11403 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11404 this.ef[jj] = this.getJsonAccessor(map);
11408 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11409 if(s.totalProperty){
11410 var vt = parseInt(this.getTotal(o), 10);
11415 if(s.successProperty){
11416 var vs = this.getSuccess(o);
11417 if(vs === false || vs === 'false'){
11422 for(var i = 0; i < c; i++){
11425 var id = this.getId(n);
11426 for(var j = 0; j < fl; j++){
11428 var v = this.ef[j](n);
11430 Roo.log('missing convert for ' + f.name);
11434 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11436 var record = new Record(values, id);
11438 records[i] = record;
11444 totalRecords : totalRecords
11449 * Ext JS Library 1.1.1
11450 * Copyright(c) 2006-2007, Ext JS, LLC.
11452 * Originally Released Under LGPL - original licence link has changed is not relivant.
11455 * <script type="text/javascript">
11459 * @class Roo.data.ArrayReader
11460 * @extends Roo.data.DataReader
11461 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11462 * Each element of that Array represents a row of data fields. The
11463 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11464 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11468 var RecordDef = Roo.data.Record.create([
11469 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11470 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11472 var myReader = new Roo.data.ArrayReader({
11473 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11477 * This would consume an Array like this:
11479 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11481 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11483 * Create a new JsonReader
11484 * @param {Object} meta Metadata configuration options.
11485 * @param {Object} recordType Either an Array of field definition objects
11486 * as specified to {@link Roo.data.Record#create},
11487 * or an {@link Roo.data.Record} object
11488 * created using {@link Roo.data.Record#create}.
11490 Roo.data.ArrayReader = function(meta, recordType){
11491 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11494 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11496 * Create a data block containing Roo.data.Records from an XML document.
11497 * @param {Object} o An Array of row objects which represents the dataset.
11498 * @return {Object} data A data block which is used by an Roo.data.Store object as
11499 * a cache of Roo.data.Records.
11501 readRecords : function(o){
11502 var sid = this.meta ? this.meta.id : null;
11503 var recordType = this.recordType, fields = recordType.prototype.fields;
11506 for(var i = 0; i < root.length; i++){
11509 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11510 for(var j = 0, jlen = fields.length; j < jlen; j++){
11511 var f = fields.items[j];
11512 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11513 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11515 values[f.name] = v;
11517 var record = new recordType(values, id);
11519 records[records.length] = record;
11523 totalRecords : records.length
11532 * @class Roo.bootstrap.ComboBox
11533 * @extends Roo.bootstrap.TriggerField
11534 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11535 * @cfg {Boolean} append (true|false) default false
11536 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11537 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11538 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11539 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11540 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11541 * @cfg {Boolean} animate default true
11542 * @cfg {Boolean} emptyResultText only for touch device
11543 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11545 * Create a new ComboBox.
11546 * @param {Object} config Configuration options
11548 Roo.bootstrap.ComboBox = function(config){
11549 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11553 * Fires when the dropdown list is expanded
11554 * @param {Roo.bootstrap.ComboBox} combo This combo box
11559 * Fires when the dropdown list is collapsed
11560 * @param {Roo.bootstrap.ComboBox} combo This combo box
11564 * @event beforeselect
11565 * Fires before a list item is selected. Return false to cancel the selection.
11566 * @param {Roo.bootstrap.ComboBox} combo This combo box
11567 * @param {Roo.data.Record} record The data record returned from the underlying store
11568 * @param {Number} index The index of the selected item in the dropdown list
11570 'beforeselect' : true,
11573 * Fires when a list item is selected
11574 * @param {Roo.bootstrap.ComboBox} combo This combo box
11575 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11576 * @param {Number} index The index of the selected item in the dropdown list
11580 * @event beforequery
11581 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11582 * The event object passed has these properties:
11583 * @param {Roo.bootstrap.ComboBox} combo This combo box
11584 * @param {String} query The query
11585 * @param {Boolean} forceAll true to force "all" query
11586 * @param {Boolean} cancel true to cancel the query
11587 * @param {Object} e The query event object
11589 'beforequery': true,
11592 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11593 * @param {Roo.bootstrap.ComboBox} combo This combo box
11598 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11599 * @param {Roo.bootstrap.ComboBox} combo This combo box
11600 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11605 * Fires when the remove value from the combobox array
11606 * @param {Roo.bootstrap.ComboBox} combo This combo box
11610 * @event specialfilter
11611 * Fires when specialfilter
11612 * @param {Roo.bootstrap.ComboBox} combo This combo box
11614 'specialfilter' : true,
11617 * Fires when tick the element
11618 * @param {Roo.bootstrap.ComboBox} combo This combo box
11622 * @event touchviewdisplay
11623 * Fires when touch view require special display (default is using displayField)
11624 * @param {Roo.bootstrap.ComboBox} combo This combo box
11625 * @param {Object} cfg set html .
11627 'touchviewdisplay' : true
11632 this.tickItems = [];
11634 this.selectedIndex = -1;
11635 if(this.mode == 'local'){
11636 if(config.queryDelay === undefined){
11637 this.queryDelay = 10;
11639 if(config.minChars === undefined){
11645 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11648 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11649 * rendering into an Roo.Editor, defaults to false)
11652 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11653 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11656 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11659 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11660 * the dropdown list (defaults to undefined, with no header element)
11664 * @cfg {String/Roo.Template} tpl The template to use to render the output
11668 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11670 listWidth: undefined,
11672 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11673 * mode = 'remote' or 'text' if mode = 'local')
11675 displayField: undefined,
11678 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11679 * mode = 'remote' or 'value' if mode = 'local').
11680 * Note: use of a valueField requires the user make a selection
11681 * in order for a value to be mapped.
11683 valueField: undefined,
11687 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11688 * field's data value (defaults to the underlying DOM element's name)
11690 hiddenName: undefined,
11692 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11696 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11698 selectedClass: 'active',
11701 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11705 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11706 * anchor positions (defaults to 'tl-bl')
11708 listAlign: 'tl-bl?',
11710 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11714 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11715 * query specified by the allQuery config option (defaults to 'query')
11717 triggerAction: 'query',
11719 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11720 * (defaults to 4, does not apply if editable = false)
11724 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11725 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11729 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11730 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11734 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11735 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11739 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11740 * when editable = true (defaults to false)
11742 selectOnFocus:false,
11744 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11746 queryParam: 'query',
11748 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
11749 * when mode = 'remote' (defaults to 'Loading...')
11751 loadingText: 'Loading...',
11753 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11757 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11761 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11762 * traditional select (defaults to true)
11766 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11770 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11774 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11775 * listWidth has a higher value)
11779 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11780 * allow the user to set arbitrary text into the field (defaults to false)
11782 forceSelection:false,
11784 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11785 * if typeAhead = true (defaults to 250)
11787 typeAheadDelay : 250,
11789 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11790 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11792 valueNotFoundText : undefined,
11794 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11796 blockFocus : false,
11799 * @cfg {Boolean} disableClear Disable showing of clear button.
11801 disableClear : false,
11803 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
11805 alwaysQuery : false,
11808 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
11813 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11815 invalidClass : "has-warning",
11818 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11820 validClass : "has-success",
11823 * @cfg {Boolean} specialFilter (true|false) special filter default false
11825 specialFilter : false,
11828 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11830 mobileTouchView : true,
11842 btnPosition : 'right',
11843 triggerList : true,
11844 showToggleBtn : true,
11846 emptyResultText: 'Empty',
11847 triggerText : 'Select',
11849 // element that contains real text value.. (when hidden is used..)
11851 getAutoCreate : function()
11859 if(Roo.isTouch && this.mobileTouchView){
11860 cfg = this.getAutoCreateTouchView();
11867 if(!this.tickable){
11868 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11873 * ComboBox with tickable selections
11876 var align = this.labelAlign || this.parentLabelAlign();
11879 cls : 'form-group roo-combobox-tickable' //input-group
11884 cls : 'tickable-buttons',
11889 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11890 html : this.triggerText
11896 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11903 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11910 buttons.cn.unshift({
11912 cls: 'select2-search-field-input'
11918 Roo.each(buttons.cn, function(c){
11920 c.cls += ' btn-' + _this.size;
11923 if (_this.disabled) {
11934 cls: 'form-hidden-field'
11938 cls: 'select2-choices',
11942 cls: 'select2-search-field',
11954 cls: 'select2-container input-group select2-container-multi',
11959 // cls: 'typeahead typeahead-long dropdown-menu',
11960 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
11965 if(this.hasFeedback && !this.allowBlank){
11969 cls: 'glyphicon form-control-feedback'
11972 combobox.cn.push(feedback);
11975 if (align ==='left' && this.fieldLabel.length) {
11977 // Roo.log("left and has label");
11983 cls : 'control-label col-sm-' + this.labelWidth,
11984 html : this.fieldLabel
11988 cls : "col-sm-" + (12 - this.labelWidth),
11995 } else if ( this.fieldLabel.length) {
11996 // Roo.log(" label");
12001 //cls : 'input-group-addon',
12002 html : this.fieldLabel
12012 // Roo.log(" no label && no align");
12019 ['xs','sm','md','lg'].map(function(size){
12020 if (settings[size]) {
12021 cfg.cls += ' col-' + size + '-' + settings[size];
12029 _initEventsCalled : false,
12032 initEvents: function()
12035 if (this._initEventsCalled) { // as we call render... prevent looping...
12038 this._initEventsCalled = true;
12041 throw "can not find store for combo";
12044 this.store = Roo.factory(this.store, Roo.data);
12046 // if we are building from html. then this element is so complex, that we can not really
12047 // use the rendered HTML.
12048 // so we have to trash and replace the previous code.
12049 if (Roo.XComponent.build_from_html) {
12051 // remove this element....
12052 var e = this.el.dom, k=0;
12053 while (e ) { e = e.previousSibling; ++k;}
12058 this.rendered = false;
12060 this.render(this.parent().getChildContainer(true), k);
12071 if(Roo.isTouch && this.mobileTouchView){
12072 this.initTouchView();
12077 this.initTickableEvents();
12081 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12083 if(this.hiddenName){
12085 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12087 this.hiddenField.dom.value =
12088 this.hiddenValue !== undefined ? this.hiddenValue :
12089 this.value !== undefined ? this.value : '';
12091 // prevent input submission
12092 this.el.dom.removeAttribute('name');
12093 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12098 // this.el.dom.setAttribute('autocomplete', 'off');
12101 var cls = 'x-combo-list';
12103 //this.list = new Roo.Layer({
12104 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12110 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12111 _this.list.setWidth(lw);
12114 this.list.on('mouseover', this.onViewOver, this);
12115 this.list.on('mousemove', this.onViewMove, this);
12117 this.list.on('scroll', this.onViewScroll, this);
12120 this.list.swallowEvent('mousewheel');
12121 this.assetHeight = 0;
12124 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12125 this.assetHeight += this.header.getHeight();
12128 this.innerList = this.list.createChild({cls:cls+'-inner'});
12129 this.innerList.on('mouseover', this.onViewOver, this);
12130 this.innerList.on('mousemove', this.onViewMove, this);
12131 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12133 if(this.allowBlank && !this.pageSize && !this.disableClear){
12134 this.footer = this.list.createChild({cls:cls+'-ft'});
12135 this.pageTb = new Roo.Toolbar(this.footer);
12139 this.footer = this.list.createChild({cls:cls+'-ft'});
12140 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12141 {pageSize: this.pageSize});
12145 if (this.pageTb && this.allowBlank && !this.disableClear) {
12147 this.pageTb.add(new Roo.Toolbar.Fill(), {
12148 cls: 'x-btn-icon x-btn-clear',
12150 handler: function()
12153 _this.clearValue();
12154 _this.onSelect(false, -1);
12159 this.assetHeight += this.footer.getHeight();
12164 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12167 this.view = new Roo.View(this.list, this.tpl, {
12168 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12170 //this.view.wrapEl.setDisplayed(false);
12171 this.view.on('click', this.onViewClick, this);
12175 this.store.on('beforeload', this.onBeforeLoad, this);
12176 this.store.on('load', this.onLoad, this);
12177 this.store.on('loadexception', this.onLoadException, this);
12179 if(this.resizable){
12180 this.resizer = new Roo.Resizable(this.list, {
12181 pinned:true, handles:'se'
12183 this.resizer.on('resize', function(r, w, h){
12184 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12185 this.listWidth = w;
12186 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12187 this.restrictHeight();
12189 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12192 if(!this.editable){
12193 this.editable = true;
12194 this.setEditable(false);
12199 if (typeof(this.events.add.listeners) != 'undefined') {
12201 this.addicon = this.wrap.createChild(
12202 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12204 this.addicon.on('click', function(e) {
12205 this.fireEvent('add', this);
12208 if (typeof(this.events.edit.listeners) != 'undefined') {
12210 this.editicon = this.wrap.createChild(
12211 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12212 if (this.addicon) {
12213 this.editicon.setStyle('margin-left', '40px');
12215 this.editicon.on('click', function(e) {
12217 // we fire even if inothing is selected..
12218 this.fireEvent('edit', this, this.lastData );
12224 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12225 "up" : function(e){
12226 this.inKeyMode = true;
12230 "down" : function(e){
12231 if(!this.isExpanded()){
12232 this.onTriggerClick();
12234 this.inKeyMode = true;
12239 "enter" : function(e){
12240 // this.onViewClick();
12244 if(this.fireEvent("specialkey", this, e)){
12245 this.onViewClick(false);
12251 "esc" : function(e){
12255 "tab" : function(e){
12258 if(this.fireEvent("specialkey", this, e)){
12259 this.onViewClick(false);
12267 doRelay : function(foo, bar, hname){
12268 if(hname == 'down' || this.scope.isExpanded()){
12269 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12278 this.queryDelay = Math.max(this.queryDelay || 10,
12279 this.mode == 'local' ? 10 : 250);
12282 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12284 if(this.typeAhead){
12285 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12287 if(this.editable !== false){
12288 this.inputEl().on("keyup", this.onKeyUp, this);
12290 if(this.forceSelection){
12291 this.inputEl().on('blur', this.doForce, this);
12295 this.choices = this.el.select('ul.select2-choices', true).first();
12296 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12300 initTickableEvents: function()
12304 if(this.hiddenName){
12306 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12308 this.hiddenField.dom.value =
12309 this.hiddenValue !== undefined ? this.hiddenValue :
12310 this.value !== undefined ? this.value : '';
12312 // prevent input submission
12313 this.el.dom.removeAttribute('name');
12314 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12319 // this.list = this.el.select('ul.dropdown-menu',true).first();
12321 this.choices = this.el.select('ul.select2-choices', true).first();
12322 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12323 if(this.triggerList){
12324 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12327 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12328 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12330 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12331 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12333 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12334 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12336 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12337 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12338 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12341 this.cancelBtn.hide();
12346 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12347 _this.list.setWidth(lw);
12350 this.list.on('mouseover', this.onViewOver, this);
12351 this.list.on('mousemove', this.onViewMove, this);
12353 this.list.on('scroll', this.onViewScroll, this);
12356 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>';
12359 this.view = new Roo.View(this.list, this.tpl, {
12360 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12363 //this.view.wrapEl.setDisplayed(false);
12364 this.view.on('click', this.onViewClick, this);
12368 this.store.on('beforeload', this.onBeforeLoad, this);
12369 this.store.on('load', this.onLoad, this);
12370 this.store.on('loadexception', this.onLoadException, this);
12373 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12374 "up" : function(e){
12375 this.inKeyMode = true;
12379 "down" : function(e){
12380 this.inKeyMode = true;
12384 "enter" : function(e){
12385 if(this.fireEvent("specialkey", this, e)){
12386 this.onViewClick(false);
12392 "esc" : function(e){
12393 this.onTickableFooterButtonClick(e, false, false);
12396 "tab" : function(e){
12397 this.fireEvent("specialkey", this, e);
12399 this.onTickableFooterButtonClick(e, false, false);
12406 doRelay : function(e, fn, key){
12407 if(this.scope.isExpanded()){
12408 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12417 this.queryDelay = Math.max(this.queryDelay || 10,
12418 this.mode == 'local' ? 10 : 250);
12421 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12423 if(this.typeAhead){
12424 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12427 if(this.editable !== false){
12428 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12433 onDestroy : function(){
12435 this.view.setStore(null);
12436 this.view.el.removeAllListeners();
12437 this.view.el.remove();
12438 this.view.purgeListeners();
12441 this.list.dom.innerHTML = '';
12445 this.store.un('beforeload', this.onBeforeLoad, this);
12446 this.store.un('load', this.onLoad, this);
12447 this.store.un('loadexception', this.onLoadException, this);
12449 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12453 fireKey : function(e){
12454 if(e.isNavKeyPress() && !this.list.isVisible()){
12455 this.fireEvent("specialkey", this, e);
12460 onResize: function(w, h){
12461 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12463 // if(typeof w != 'number'){
12464 // // we do not handle it!?!?
12467 // var tw = this.trigger.getWidth();
12468 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12469 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12471 // this.inputEl().setWidth( this.adjustWidth('input', x));
12473 // //this.trigger.setStyle('left', x+'px');
12475 // if(this.list && this.listWidth === undefined){
12476 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12477 // this.list.setWidth(lw);
12478 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12486 * Allow or prevent the user from directly editing the field text. If false is passed,
12487 * the user will only be able to select from the items defined in the dropdown list. This method
12488 * is the runtime equivalent of setting the 'editable' config option at config time.
12489 * @param {Boolean} value True to allow the user to directly edit the field text
12491 setEditable : function(value){
12492 if(value == this.editable){
12495 this.editable = value;
12497 this.inputEl().dom.setAttribute('readOnly', true);
12498 this.inputEl().on('mousedown', this.onTriggerClick, this);
12499 this.inputEl().addClass('x-combo-noedit');
12501 this.inputEl().dom.setAttribute('readOnly', false);
12502 this.inputEl().un('mousedown', this.onTriggerClick, this);
12503 this.inputEl().removeClass('x-combo-noedit');
12509 onBeforeLoad : function(combo,opts){
12510 if(!this.hasFocus){
12514 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12516 this.restrictHeight();
12517 this.selectedIndex = -1;
12521 onLoad : function(){
12523 this.hasQuery = false;
12525 if(!this.hasFocus){
12529 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12530 this.loading.hide();
12533 if(this.store.getCount() > 0){
12535 this.restrictHeight();
12536 if(this.lastQuery == this.allQuery){
12537 if(this.editable && !this.tickable){
12538 this.inputEl().dom.select();
12542 !this.selectByValue(this.value, true) &&
12545 !this.store.lastOptions ||
12546 typeof(this.store.lastOptions.add) == 'undefined' ||
12547 this.store.lastOptions.add != true
12550 this.select(0, true);
12553 if(this.autoFocus){
12556 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12557 this.taTask.delay(this.typeAheadDelay);
12561 this.onEmptyResults();
12567 onLoadException : function()
12569 this.hasQuery = false;
12571 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12572 this.loading.hide();
12575 if(this.tickable && this.editable){
12580 // only causes errors at present
12581 //Roo.log(this.store.reader.jsonData);
12582 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12584 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12590 onTypeAhead : function(){
12591 if(this.store.getCount() > 0){
12592 var r = this.store.getAt(0);
12593 var newValue = r.data[this.displayField];
12594 var len = newValue.length;
12595 var selStart = this.getRawValue().length;
12597 if(selStart != len){
12598 this.setRawValue(newValue);
12599 this.selectText(selStart, newValue.length);
12605 onSelect : function(record, index){
12607 if(this.fireEvent('beforeselect', this, record, index) !== false){
12609 this.setFromData(index > -1 ? record.data : false);
12612 this.fireEvent('select', this, record, index);
12617 * Returns the currently selected field value or empty string if no value is set.
12618 * @return {String} value The selected value
12620 getValue : function(){
12623 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12626 if(this.valueField){
12627 return typeof this.value != 'undefined' ? this.value : '';
12629 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12634 * Clears any text/value currently set in the field
12636 clearValue : function(){
12637 if(this.hiddenField){
12638 this.hiddenField.dom.value = '';
12641 this.setRawValue('');
12642 this.lastSelectionText = '';
12643 this.lastData = false;
12645 var close = this.closeTriggerEl();
12654 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12655 * will be displayed in the field. If the value does not match the data value of an existing item,
12656 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12657 * Otherwise the field will be blank (although the value will still be set).
12658 * @param {String} value The value to match
12660 setValue : function(v){
12667 if(this.valueField){
12668 var r = this.findRecord(this.valueField, v);
12670 text = r.data[this.displayField];
12671 }else if(this.valueNotFoundText !== undefined){
12672 text = this.valueNotFoundText;
12675 this.lastSelectionText = text;
12676 if(this.hiddenField){
12677 this.hiddenField.dom.value = v;
12679 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12682 var close = this.closeTriggerEl();
12685 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12689 * @property {Object} the last set data for the element
12694 * Sets the value of the field based on a object which is related to the record format for the store.
12695 * @param {Object} value the value to set as. or false on reset?
12697 setFromData : function(o){
12704 var dv = ''; // display value
12705 var vv = ''; // value value..
12707 if (this.displayField) {
12708 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12710 // this is an error condition!!!
12711 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12714 if(this.valueField){
12715 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12718 var close = this.closeTriggerEl();
12721 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12724 if(this.hiddenField){
12725 this.hiddenField.dom.value = vv;
12727 this.lastSelectionText = dv;
12728 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12732 // no hidden field.. - we store the value in 'value', but still display
12733 // display field!!!!
12734 this.lastSelectionText = dv;
12735 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12742 reset : function(){
12743 // overridden so that last data is reset..
12750 this.setValue(this.originalValue);
12751 this.clearInvalid();
12752 this.lastData = false;
12754 this.view.clearSelections();
12758 findRecord : function(prop, value){
12760 if(this.store.getCount() > 0){
12761 this.store.each(function(r){
12762 if(r.data[prop] == value){
12772 getName: function()
12774 // returns hidden if it's set..
12775 if (!this.rendered) {return ''};
12776 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
12780 onViewMove : function(e, t){
12781 this.inKeyMode = false;
12785 onViewOver : function(e, t){
12786 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12789 var item = this.view.findItemFromChild(t);
12792 var index = this.view.indexOf(item);
12793 this.select(index, false);
12798 onViewClick : function(view, doFocus, el, e)
12800 var index = this.view.getSelectedIndexes()[0];
12802 var r = this.store.getAt(index);
12806 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12813 Roo.each(this.tickItems, function(v,k){
12815 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12817 _this.tickItems.splice(k, 1);
12819 if(typeof(e) == 'undefined' && view == false){
12820 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12832 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
12833 this.tickItems.push(r.data);
12836 if(typeof(e) == 'undefined' && view == false){
12837 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12844 this.onSelect(r, index);
12846 if(doFocus !== false && !this.blockFocus){
12847 this.inputEl().focus();
12852 restrictHeight : function(){
12853 //this.innerList.dom.style.height = '';
12854 //var inner = this.innerList.dom;
12855 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12856 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12857 //this.list.beginUpdate();
12858 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12859 this.list.alignTo(this.inputEl(), this.listAlign);
12860 this.list.alignTo(this.inputEl(), this.listAlign);
12861 //this.list.endUpdate();
12865 onEmptyResults : function(){
12867 if(this.tickable && this.editable){
12868 this.restrictHeight();
12876 * Returns true if the dropdown list is expanded, else false.
12878 isExpanded : function(){
12879 return this.list.isVisible();
12883 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12884 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12885 * @param {String} value The data value of the item to select
12886 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12887 * selected item if it is not currently in view (defaults to true)
12888 * @return {Boolean} True if the value matched an item in the list, else false
12890 selectByValue : function(v, scrollIntoView){
12891 if(v !== undefined && v !== null){
12892 var r = this.findRecord(this.valueField || this.displayField, v);
12894 this.select(this.store.indexOf(r), scrollIntoView);
12902 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12903 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12904 * @param {Number} index The zero-based index of the list item to select
12905 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12906 * selected item if it is not currently in view (defaults to true)
12908 select : function(index, scrollIntoView){
12909 this.selectedIndex = index;
12910 this.view.select(index);
12911 if(scrollIntoView !== false){
12912 var el = this.view.getNode(index);
12914 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12917 this.list.scrollChildIntoView(el, false);
12923 selectNext : function(){
12924 var ct = this.store.getCount();
12926 if(this.selectedIndex == -1){
12928 }else if(this.selectedIndex < ct-1){
12929 this.select(this.selectedIndex+1);
12935 selectPrev : function(){
12936 var ct = this.store.getCount();
12938 if(this.selectedIndex == -1){
12940 }else if(this.selectedIndex != 0){
12941 this.select(this.selectedIndex-1);
12947 onKeyUp : function(e){
12948 if(this.editable !== false && !e.isSpecialKey()){
12949 this.lastKey = e.getKey();
12950 this.dqTask.delay(this.queryDelay);
12955 validateBlur : function(){
12956 return !this.list || !this.list.isVisible();
12960 initQuery : function(){
12962 var v = this.getRawValue();
12964 if(this.tickable && this.editable){
12965 v = this.tickableInputEl().getValue();
12972 doForce : function(){
12973 if(this.inputEl().dom.value.length > 0){
12974 this.inputEl().dom.value =
12975 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12981 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
12982 * query allowing the query action to be canceled if needed.
12983 * @param {String} query The SQL query to execute
12984 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12985 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
12986 * saved in the current store (defaults to false)
12988 doQuery : function(q, forceAll){
12990 if(q === undefined || q === null){
12995 forceAll: forceAll,
12999 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13004 forceAll = qe.forceAll;
13005 if(forceAll === true || (q.length >= this.minChars)){
13007 this.hasQuery = true;
13009 if(this.lastQuery != q || this.alwaysQuery){
13010 this.lastQuery = q;
13011 if(this.mode == 'local'){
13012 this.selectedIndex = -1;
13014 this.store.clearFilter();
13017 if(this.specialFilter){
13018 this.fireEvent('specialfilter', this);
13023 this.store.filter(this.displayField, q);
13026 this.store.fireEvent("datachanged", this.store);
13033 this.store.baseParams[this.queryParam] = q;
13035 var options = {params : this.getParams(q)};
13038 options.add = true;
13039 options.params.start = this.page * this.pageSize;
13042 this.store.load(options);
13045 * this code will make the page width larger, at the beginning, the list not align correctly,
13046 * we should expand the list on onLoad
13047 * so command out it
13052 this.selectedIndex = -1;
13057 this.loadNext = false;
13061 getParams : function(q){
13063 //p[this.queryParam] = q;
13067 p.limit = this.pageSize;
13073 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13075 collapse : function(){
13076 if(!this.isExpanded()){
13083 this.hasFocus = false;
13085 this.cancelBtn.hide();
13086 this.trigger.show();
13089 this.tickableInputEl().dom.value = '';
13090 this.tickableInputEl().blur();
13095 Roo.get(document).un('mousedown', this.collapseIf, this);
13096 Roo.get(document).un('mousewheel', this.collapseIf, this);
13097 if (!this.editable) {
13098 Roo.get(document).un('keydown', this.listKeyPress, this);
13100 this.fireEvent('collapse', this);
13104 collapseIf : function(e){
13105 var in_combo = e.within(this.el);
13106 var in_list = e.within(this.list);
13107 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13109 if (in_combo || in_list || is_list) {
13110 //e.stopPropagation();
13115 this.onTickableFooterButtonClick(e, false, false);
13123 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13125 expand : function(){
13127 if(this.isExpanded() || !this.hasFocus){
13131 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13132 this.list.setWidth(lw);
13139 this.restrictHeight();
13143 this.tickItems = Roo.apply([], this.item);
13146 this.cancelBtn.show();
13147 this.trigger.hide();
13150 this.tickableInputEl().focus();
13155 Roo.get(document).on('mousedown', this.collapseIf, this);
13156 Roo.get(document).on('mousewheel', this.collapseIf, this);
13157 if (!this.editable) {
13158 Roo.get(document).on('keydown', this.listKeyPress, this);
13161 this.fireEvent('expand', this);
13165 // Implements the default empty TriggerField.onTriggerClick function
13166 onTriggerClick : function(e)
13168 Roo.log('trigger click');
13170 if(this.disabled || !this.triggerList){
13175 this.loadNext = false;
13177 if(this.isExpanded()){
13179 if (!this.blockFocus) {
13180 this.inputEl().focus();
13184 this.hasFocus = true;
13185 if(this.triggerAction == 'all') {
13186 this.doQuery(this.allQuery, true);
13188 this.doQuery(this.getRawValue());
13190 if (!this.blockFocus) {
13191 this.inputEl().focus();
13196 onTickableTriggerClick : function(e)
13203 this.loadNext = false;
13204 this.hasFocus = true;
13206 if(this.triggerAction == 'all') {
13207 this.doQuery(this.allQuery, true);
13209 this.doQuery(this.getRawValue());
13213 onSearchFieldClick : function(e)
13215 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13216 this.onTickableFooterButtonClick(e, false, false);
13220 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13225 this.loadNext = false;
13226 this.hasFocus = true;
13228 if(this.triggerAction == 'all') {
13229 this.doQuery(this.allQuery, true);
13231 this.doQuery(this.getRawValue());
13235 listKeyPress : function(e)
13237 //Roo.log('listkeypress');
13238 // scroll to first matching element based on key pres..
13239 if (e.isSpecialKey()) {
13242 var k = String.fromCharCode(e.getKey()).toUpperCase();
13245 var csel = this.view.getSelectedNodes();
13246 var cselitem = false;
13248 var ix = this.view.indexOf(csel[0]);
13249 cselitem = this.store.getAt(ix);
13250 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13256 this.store.each(function(v) {
13258 // start at existing selection.
13259 if (cselitem.id == v.id) {
13265 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13266 match = this.store.indexOf(v);
13272 if (match === false) {
13273 return true; // no more action?
13276 this.view.select(match);
13277 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13278 sn.scrollIntoView(sn.dom.parentNode, false);
13281 onViewScroll : function(e, t){
13283 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){
13287 this.hasQuery = true;
13289 this.loading = this.list.select('.loading', true).first();
13291 if(this.loading === null){
13292 this.list.createChild({
13294 cls: 'loading select2-more-results select2-active',
13295 html: 'Loading more results...'
13298 this.loading = this.list.select('.loading', true).first();
13300 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13302 this.loading.hide();
13305 this.loading.show();
13310 this.loadNext = true;
13312 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13317 addItem : function(o)
13319 var dv = ''; // display value
13321 if (this.displayField) {
13322 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13324 // this is an error condition!!!
13325 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13332 var choice = this.choices.createChild({
13334 cls: 'select2-search-choice',
13343 cls: 'select2-search-choice-close',
13348 }, this.searchField);
13350 var close = choice.select('a.select2-search-choice-close', true).first();
13352 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13360 this.inputEl().dom.value = '';
13365 onRemoveItem : function(e, _self, o)
13367 e.preventDefault();
13369 this.lastItem = Roo.apply([], this.item);
13371 var index = this.item.indexOf(o.data) * 1;
13374 Roo.log('not this item?!');
13378 this.item.splice(index, 1);
13383 this.fireEvent('remove', this, e);
13389 syncValue : function()
13391 if(!this.item.length){
13398 Roo.each(this.item, function(i){
13399 if(_this.valueField){
13400 value.push(i[_this.valueField]);
13407 this.value = value.join(',');
13409 if(this.hiddenField){
13410 this.hiddenField.dom.value = this.value;
13413 this.store.fireEvent("datachanged", this.store);
13416 clearItem : function()
13418 if(!this.multiple){
13424 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13432 if(this.tickable && !Roo.isTouch){
13433 this.view.refresh();
13437 inputEl: function ()
13439 if(Roo.isTouch && this.mobileTouchView){
13440 return this.el.select('input.form-control',true).first();
13444 return this.searchField;
13447 return this.el.select('input.form-control',true).first();
13451 onTickableFooterButtonClick : function(e, btn, el)
13453 e.preventDefault();
13455 this.lastItem = Roo.apply([], this.item);
13457 if(btn && btn.name == 'cancel'){
13458 this.tickItems = Roo.apply([], this.item);
13467 Roo.each(this.tickItems, function(o){
13475 validate : function()
13477 var v = this.getRawValue();
13480 v = this.getValue();
13483 if(this.disabled || this.allowBlank || v.length){
13488 this.markInvalid();
13492 tickableInputEl : function()
13494 if(!this.tickable || !this.editable){
13495 return this.inputEl();
13498 return this.inputEl().select('.select2-search-field-input', true).first();
13502 getAutoCreateTouchView : function()
13507 cls: 'form-group' //input-group
13513 type : this.inputType,
13514 cls : 'form-control x-combo-noedit',
13515 autocomplete: 'new-password',
13516 placeholder : this.placeholder || '',
13521 input.name = this.name;
13525 input.cls += ' input-' + this.size;
13528 if (this.disabled) {
13529 input.disabled = true;
13540 inputblock.cls += ' input-group';
13542 inputblock.cn.unshift({
13544 cls : 'input-group-addon',
13549 if(this.removable && !this.multiple){
13550 inputblock.cls += ' roo-removable';
13552 inputblock.cn.push({
13555 cls : 'roo-combo-removable-btn close'
13559 if(this.hasFeedback && !this.allowBlank){
13561 inputblock.cls += ' has-feedback';
13563 inputblock.cn.push({
13565 cls: 'glyphicon form-control-feedback'
13572 inputblock.cls += (this.before) ? '' : ' input-group';
13574 inputblock.cn.push({
13576 cls : 'input-group-addon',
13587 cls: 'form-hidden-field'
13601 cls: 'form-hidden-field'
13605 cls: 'select2-choices',
13609 cls: 'select2-search-field',
13622 cls: 'select2-container input-group',
13629 combobox.cls += ' select2-container-multi';
13632 var align = this.labelAlign || this.parentLabelAlign();
13636 if(this.fieldLabel.length){
13638 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13639 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13644 cls : 'control-label ' + lw,
13645 html : this.fieldLabel
13657 var settings = this;
13659 ['xs','sm','md','lg'].map(function(size){
13660 if (settings[size]) {
13661 cfg.cls += ' col-' + size + '-' + settings[size];
13668 initTouchView : function()
13670 this.renderTouchView();
13672 this.touchViewEl.on('scroll', function(){
13673 this.el.dom.scrollTop = 0;
13676 this.originalValue = this.getValue();
13678 this.inputEl().on("click", this.showTouchView, this);
13680 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13681 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13683 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13685 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13686 this.store.on('load', this.onTouchViewLoad, this);
13687 this.store.on('loadexception', this.onTouchViewLoadException, this);
13689 if(this.hiddenName){
13691 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13693 this.hiddenField.dom.value =
13694 this.hiddenValue !== undefined ? this.hiddenValue :
13695 this.value !== undefined ? this.value : '';
13697 this.el.dom.removeAttribute('name');
13698 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13702 this.choices = this.el.select('ul.select2-choices', true).first();
13703 this.searchField = this.el.select('ul li.select2-search-field', true).first();
13706 if(this.removable && !this.multiple){
13707 var close = this.closeTriggerEl();
13709 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13710 close.on('click', this.removeBtnClick, this, close);
13714 * fix the bug in Safari iOS8
13716 this.inputEl().on("focus", function(e){
13717 document.activeElement.blur();
13725 renderTouchView : function()
13727 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13728 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13730 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13731 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13733 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13734 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13735 this.touchViewBodyEl.setStyle('overflow', 'auto');
13737 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13738 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13740 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13741 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13745 showTouchView : function()
13751 this.touchViewHeaderEl.hide();
13753 if(this.fieldLabel.length){
13754 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13755 this.touchViewHeaderEl.show();
13758 this.touchViewEl.show();
13760 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13761 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13763 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13765 if(this.fieldLabel.length){
13766 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13769 this.touchViewBodyEl.setHeight(bodyHeight);
13773 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13775 this.touchViewEl.addClass('in');
13778 this.doTouchViewQuery();
13782 hideTouchView : function()
13784 this.touchViewEl.removeClass('in');
13788 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13790 this.touchViewEl.setStyle('display', 'none');
13795 setTouchViewValue : function()
13802 Roo.each(this.tickItems, function(o){
13807 this.hideTouchView();
13810 doTouchViewQuery : function()
13819 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13823 if(!this.alwaysQuery || this.mode == 'local'){
13824 this.onTouchViewLoad();
13831 onTouchViewBeforeLoad : function(combo,opts)
13837 onTouchViewLoad : function()
13839 if(this.store.getCount() < 1){
13840 this.onTouchViewEmptyResults();
13844 this.clearTouchView();
13846 var rawValue = this.getRawValue();
13848 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13850 this.tickItems = [];
13852 this.store.data.each(function(d, rowIndex){
13853 var row = this.touchViewListGroup.createChild(template);
13855 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13858 html : d.data[this.displayField]
13861 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
13862 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
13866 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13867 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13870 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13871 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13872 this.tickItems.push(d.data);
13875 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13879 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13881 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13883 if(this.fieldLabel.length){
13884 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13887 var listHeight = this.touchViewListGroup.getHeight();
13891 if(firstChecked && listHeight > bodyHeight){
13892 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13897 onTouchViewLoadException : function()
13899 this.hideTouchView();
13902 onTouchViewEmptyResults : function()
13904 this.clearTouchView();
13906 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13908 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13912 clearTouchView : function()
13914 this.touchViewListGroup.dom.innerHTML = '';
13917 onTouchViewClick : function(e, el, o)
13919 e.preventDefault();
13922 var rowIndex = o.rowIndex;
13924 var r = this.store.getAt(rowIndex);
13926 if(!this.multiple){
13927 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13928 c.dom.removeAttribute('checked');
13931 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13933 this.setFromData(r.data);
13935 var close = this.closeTriggerEl();
13941 this.hideTouchView();
13943 this.fireEvent('select', this, r, rowIndex);
13948 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13949 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13950 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13954 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13955 this.addItem(r.data);
13956 this.tickItems.push(r.data);
13962 * @cfg {Boolean} grow
13966 * @cfg {Number} growMin
13970 * @cfg {Number} growMax
13979 Roo.apply(Roo.bootstrap.ComboBox, {
13983 cls: 'modal-header',
14005 cls: 'list-group-item',
14009 cls: 'roo-combobox-list-group-item-value'
14013 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14027 listItemCheckbox : {
14029 cls: 'list-group-item',
14033 cls: 'roo-combobox-list-group-item-value'
14037 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14053 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14058 cls: 'modal-footer',
14066 cls: 'col-xs-6 text-left',
14069 cls: 'btn btn-danger roo-touch-view-cancel',
14075 cls: 'col-xs-6 text-right',
14078 cls: 'btn btn-success roo-touch-view-ok',
14089 Roo.apply(Roo.bootstrap.ComboBox, {
14091 touchViewTemplate : {
14093 cls: 'modal fade roo-combobox-touch-view',
14097 cls: 'modal-dialog',
14098 style : 'position:fixed', // we have to fix position....
14102 cls: 'modal-content',
14104 Roo.bootstrap.ComboBox.header,
14105 Roo.bootstrap.ComboBox.body,
14106 Roo.bootstrap.ComboBox.footer
14115 * Ext JS Library 1.1.1
14116 * Copyright(c) 2006-2007, Ext JS, LLC.
14118 * Originally Released Under LGPL - original licence link has changed is not relivant.
14121 * <script type="text/javascript">
14126 * @extends Roo.util.Observable
14127 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14128 * This class also supports single and multi selection modes. <br>
14129 * Create a data model bound view:
14131 var store = new Roo.data.Store(...);
14133 var view = new Roo.View({
14135 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14137 singleSelect: true,
14138 selectedClass: "ydataview-selected",
14142 // listen for node click?
14143 view.on("click", function(vw, index, node, e){
14144 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14148 dataModel.load("foobar.xml");
14150 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14152 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14153 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14155 * Note: old style constructor is still suported (container, template, config)
14158 * Create a new View
14159 * @param {Object} config The config object
14162 Roo.View = function(config, depreciated_tpl, depreciated_config){
14164 this.parent = false;
14166 if (typeof(depreciated_tpl) == 'undefined') {
14167 // new way.. - universal constructor.
14168 Roo.apply(this, config);
14169 this.el = Roo.get(this.el);
14172 this.el = Roo.get(config);
14173 this.tpl = depreciated_tpl;
14174 Roo.apply(this, depreciated_config);
14176 this.wrapEl = this.el.wrap().wrap();
14177 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14180 if(typeof(this.tpl) == "string"){
14181 this.tpl = new Roo.Template(this.tpl);
14183 // support xtype ctors..
14184 this.tpl = new Roo.factory(this.tpl, Roo);
14188 this.tpl.compile();
14193 * @event beforeclick
14194 * Fires before a click is processed. Returns false to cancel the default action.
14195 * @param {Roo.View} this
14196 * @param {Number} index The index of the target node
14197 * @param {HTMLElement} node The target node
14198 * @param {Roo.EventObject} e The raw event object
14200 "beforeclick" : true,
14203 * Fires when a template node is clicked.
14204 * @param {Roo.View} this
14205 * @param {Number} index The index of the target node
14206 * @param {HTMLElement} node The target node
14207 * @param {Roo.EventObject} e The raw event object
14212 * Fires when a template node is double clicked.
14213 * @param {Roo.View} this
14214 * @param {Number} index The index of the target node
14215 * @param {HTMLElement} node The target node
14216 * @param {Roo.EventObject} e The raw event object
14220 * @event contextmenu
14221 * Fires when a template node is right clicked.
14222 * @param {Roo.View} this
14223 * @param {Number} index The index of the target node
14224 * @param {HTMLElement} node The target node
14225 * @param {Roo.EventObject} e The raw event object
14227 "contextmenu" : true,
14229 * @event selectionchange
14230 * Fires when the selected nodes change.
14231 * @param {Roo.View} this
14232 * @param {Array} selections Array of the selected nodes
14234 "selectionchange" : true,
14237 * @event beforeselect
14238 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14239 * @param {Roo.View} this
14240 * @param {HTMLElement} node The node to be selected
14241 * @param {Array} selections Array of currently selected nodes
14243 "beforeselect" : true,
14245 * @event preparedata
14246 * Fires on every row to render, to allow you to change the data.
14247 * @param {Roo.View} this
14248 * @param {Object} data to be rendered (change this)
14250 "preparedata" : true
14258 "click": this.onClick,
14259 "dblclick": this.onDblClick,
14260 "contextmenu": this.onContextMenu,
14264 this.selections = [];
14266 this.cmp = new Roo.CompositeElementLite([]);
14268 this.store = Roo.factory(this.store, Roo.data);
14269 this.setStore(this.store, true);
14272 if ( this.footer && this.footer.xtype) {
14274 var fctr = this.wrapEl.appendChild(document.createElement("div"));
14276 this.footer.dataSource = this.store;
14277 this.footer.container = fctr;
14278 this.footer = Roo.factory(this.footer, Roo);
14279 fctr.insertFirst(this.el);
14281 // this is a bit insane - as the paging toolbar seems to detach the el..
14282 // dom.parentNode.parentNode.parentNode
14283 // they get detached?
14287 Roo.View.superclass.constructor.call(this);
14292 Roo.extend(Roo.View, Roo.util.Observable, {
14295 * @cfg {Roo.data.Store} store Data store to load data from.
14300 * @cfg {String|Roo.Element} el The container element.
14305 * @cfg {String|Roo.Template} tpl The template used by this View
14309 * @cfg {String} dataName the named area of the template to use as the data area
14310 * Works with domtemplates roo-name="name"
14314 * @cfg {String} selectedClass The css class to add to selected nodes
14316 selectedClass : "x-view-selected",
14318 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14323 * @cfg {String} text to display on mask (default Loading)
14327 * @cfg {Boolean} multiSelect Allow multiple selection
14329 multiSelect : false,
14331 * @cfg {Boolean} singleSelect Allow single selection
14333 singleSelect: false,
14336 * @cfg {Boolean} toggleSelect - selecting
14338 toggleSelect : false,
14341 * @cfg {Boolean} tickable - selecting
14346 * Returns the element this view is bound to.
14347 * @return {Roo.Element}
14349 getEl : function(){
14350 return this.wrapEl;
14356 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14358 refresh : function(){
14359 //Roo.log('refresh');
14362 // if we are using something like 'domtemplate', then
14363 // the what gets used is:
14364 // t.applySubtemplate(NAME, data, wrapping data..)
14365 // the outer template then get' applied with
14366 // the store 'extra data'
14367 // and the body get's added to the
14368 // roo-name="data" node?
14369 // <span class='roo-tpl-{name}'></span> ?????
14373 this.clearSelections();
14374 this.el.update("");
14376 var records = this.store.getRange();
14377 if(records.length < 1) {
14379 // is this valid?? = should it render a template??
14381 this.el.update(this.emptyText);
14385 if (this.dataName) {
14386 this.el.update(t.apply(this.store.meta)); //????
14387 el = this.el.child('.roo-tpl-' + this.dataName);
14390 for(var i = 0, len = records.length; i < len; i++){
14391 var data = this.prepareData(records[i].data, i, records[i]);
14392 this.fireEvent("preparedata", this, data, i, records[i]);
14394 var d = Roo.apply({}, data);
14397 Roo.apply(d, {'roo-id' : Roo.id()});
14401 Roo.each(this.parent.item, function(item){
14402 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14405 Roo.apply(d, {'roo-data-checked' : 'checked'});
14409 html[html.length] = Roo.util.Format.trim(
14411 t.applySubtemplate(this.dataName, d, this.store.meta) :
14418 el.update(html.join(""));
14419 this.nodes = el.dom.childNodes;
14420 this.updateIndexes(0);
14425 * Function to override to reformat the data that is sent to
14426 * the template for each node.
14427 * DEPRICATED - use the preparedata event handler.
14428 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14429 * a JSON object for an UpdateManager bound view).
14431 prepareData : function(data, index, record)
14433 this.fireEvent("preparedata", this, data, index, record);
14437 onUpdate : function(ds, record){
14438 // Roo.log('on update');
14439 this.clearSelections();
14440 var index = this.store.indexOf(record);
14441 var n = this.nodes[index];
14442 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14443 n.parentNode.removeChild(n);
14444 this.updateIndexes(index, index);
14450 onAdd : function(ds, records, index)
14452 //Roo.log(['on Add', ds, records, index] );
14453 this.clearSelections();
14454 if(this.nodes.length == 0){
14458 var n = this.nodes[index];
14459 for(var i = 0, len = records.length; i < len; i++){
14460 var d = this.prepareData(records[i].data, i, records[i]);
14462 this.tpl.insertBefore(n, d);
14465 this.tpl.append(this.el, d);
14468 this.updateIndexes(index);
14471 onRemove : function(ds, record, index){
14472 // Roo.log('onRemove');
14473 this.clearSelections();
14474 var el = this.dataName ?
14475 this.el.child('.roo-tpl-' + this.dataName) :
14478 el.dom.removeChild(this.nodes[index]);
14479 this.updateIndexes(index);
14483 * Refresh an individual node.
14484 * @param {Number} index
14486 refreshNode : function(index){
14487 this.onUpdate(this.store, this.store.getAt(index));
14490 updateIndexes : function(startIndex, endIndex){
14491 var ns = this.nodes;
14492 startIndex = startIndex || 0;
14493 endIndex = endIndex || ns.length - 1;
14494 for(var i = startIndex; i <= endIndex; i++){
14495 ns[i].nodeIndex = i;
14500 * Changes the data store this view uses and refresh the view.
14501 * @param {Store} store
14503 setStore : function(store, initial){
14504 if(!initial && this.store){
14505 this.store.un("datachanged", this.refresh);
14506 this.store.un("add", this.onAdd);
14507 this.store.un("remove", this.onRemove);
14508 this.store.un("update", this.onUpdate);
14509 this.store.un("clear", this.refresh);
14510 this.store.un("beforeload", this.onBeforeLoad);
14511 this.store.un("load", this.onLoad);
14512 this.store.un("loadexception", this.onLoad);
14516 store.on("datachanged", this.refresh, this);
14517 store.on("add", this.onAdd, this);
14518 store.on("remove", this.onRemove, this);
14519 store.on("update", this.onUpdate, this);
14520 store.on("clear", this.refresh, this);
14521 store.on("beforeload", this.onBeforeLoad, this);
14522 store.on("load", this.onLoad, this);
14523 store.on("loadexception", this.onLoad, this);
14531 * onbeforeLoad - masks the loading area.
14534 onBeforeLoad : function(store,opts)
14536 //Roo.log('onBeforeLoad');
14538 this.el.update("");
14540 this.el.mask(this.mask ? this.mask : "Loading" );
14542 onLoad : function ()
14549 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14550 * @param {HTMLElement} node
14551 * @return {HTMLElement} The template node
14553 findItemFromChild : function(node){
14554 var el = this.dataName ?
14555 this.el.child('.roo-tpl-' + this.dataName,true) :
14558 if(!node || node.parentNode == el){
14561 var p = node.parentNode;
14562 while(p && p != el){
14563 if(p.parentNode == el){
14572 onClick : function(e){
14573 var item = this.findItemFromChild(e.getTarget());
14575 var index = this.indexOf(item);
14576 if(this.onItemClick(item, index, e) !== false){
14577 this.fireEvent("click", this, index, item, e);
14580 this.clearSelections();
14585 onContextMenu : function(e){
14586 var item = this.findItemFromChild(e.getTarget());
14588 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14593 onDblClick : function(e){
14594 var item = this.findItemFromChild(e.getTarget());
14596 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14600 onItemClick : function(item, index, e)
14602 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14605 if (this.toggleSelect) {
14606 var m = this.isSelected(item) ? 'unselect' : 'select';
14609 _t[m](item, true, false);
14612 if(this.multiSelect || this.singleSelect){
14613 if(this.multiSelect && e.shiftKey && this.lastSelection){
14614 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14616 this.select(item, this.multiSelect && e.ctrlKey);
14617 this.lastSelection = item;
14620 if(!this.tickable){
14621 e.preventDefault();
14629 * Get the number of selected nodes.
14632 getSelectionCount : function(){
14633 return this.selections.length;
14637 * Get the currently selected nodes.
14638 * @return {Array} An array of HTMLElements
14640 getSelectedNodes : function(){
14641 return this.selections;
14645 * Get the indexes of the selected nodes.
14648 getSelectedIndexes : function(){
14649 var indexes = [], s = this.selections;
14650 for(var i = 0, len = s.length; i < len; i++){
14651 indexes.push(s[i].nodeIndex);
14657 * Clear all selections
14658 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14660 clearSelections : function(suppressEvent){
14661 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14662 this.cmp.elements = this.selections;
14663 this.cmp.removeClass(this.selectedClass);
14664 this.selections = [];
14665 if(!suppressEvent){
14666 this.fireEvent("selectionchange", this, this.selections);
14672 * Returns true if the passed node is selected
14673 * @param {HTMLElement/Number} node The node or node index
14674 * @return {Boolean}
14676 isSelected : function(node){
14677 var s = this.selections;
14681 node = this.getNode(node);
14682 return s.indexOf(node) !== -1;
14687 * @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
14688 * @param {Boolean} keepExisting (optional) true to keep existing selections
14689 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14691 select : function(nodeInfo, keepExisting, suppressEvent){
14692 if(nodeInfo instanceof Array){
14694 this.clearSelections(true);
14696 for(var i = 0, len = nodeInfo.length; i < len; i++){
14697 this.select(nodeInfo[i], true, true);
14701 var node = this.getNode(nodeInfo);
14702 if(!node || this.isSelected(node)){
14703 return; // already selected.
14706 this.clearSelections(true);
14709 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14710 Roo.fly(node).addClass(this.selectedClass);
14711 this.selections.push(node);
14712 if(!suppressEvent){
14713 this.fireEvent("selectionchange", this, this.selections);
14721 * @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
14722 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14723 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14725 unselect : function(nodeInfo, keepExisting, suppressEvent)
14727 if(nodeInfo instanceof Array){
14728 Roo.each(this.selections, function(s) {
14729 this.unselect(s, nodeInfo);
14733 var node = this.getNode(nodeInfo);
14734 if(!node || !this.isSelected(node)){
14735 //Roo.log("not selected");
14736 return; // not selected.
14740 Roo.each(this.selections, function(s) {
14742 Roo.fly(node).removeClass(this.selectedClass);
14749 this.selections= ns;
14750 this.fireEvent("selectionchange", this, this.selections);
14754 * Gets a template node.
14755 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14756 * @return {HTMLElement} The node or null if it wasn't found
14758 getNode : function(nodeInfo){
14759 if(typeof nodeInfo == "string"){
14760 return document.getElementById(nodeInfo);
14761 }else if(typeof nodeInfo == "number"){
14762 return this.nodes[nodeInfo];
14768 * Gets a range template nodes.
14769 * @param {Number} startIndex
14770 * @param {Number} endIndex
14771 * @return {Array} An array of nodes
14773 getNodes : function(start, end){
14774 var ns = this.nodes;
14775 start = start || 0;
14776 end = typeof end == "undefined" ? ns.length - 1 : end;
14779 for(var i = start; i <= end; i++){
14783 for(var i = start; i >= end; i--){
14791 * Finds the index of the passed node
14792 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14793 * @return {Number} The index of the node or -1
14795 indexOf : function(node){
14796 node = this.getNode(node);
14797 if(typeof node.nodeIndex == "number"){
14798 return node.nodeIndex;
14800 var ns = this.nodes;
14801 for(var i = 0, len = ns.length; i < len; i++){
14812 * based on jquery fullcalendar
14816 Roo.bootstrap = Roo.bootstrap || {};
14818 * @class Roo.bootstrap.Calendar
14819 * @extends Roo.bootstrap.Component
14820 * Bootstrap Calendar class
14821 * @cfg {Boolean} loadMask (true|false) default false
14822 * @cfg {Object} header generate the user specific header of the calendar, default false
14825 * Create a new Container
14826 * @param {Object} config The config object
14831 Roo.bootstrap.Calendar = function(config){
14832 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14836 * Fires when a date is selected
14837 * @param {DatePicker} this
14838 * @param {Date} date The selected date
14842 * @event monthchange
14843 * Fires when the displayed month changes
14844 * @param {DatePicker} this
14845 * @param {Date} date The selected month
14847 'monthchange': true,
14849 * @event evententer
14850 * Fires when mouse over an event
14851 * @param {Calendar} this
14852 * @param {event} Event
14854 'evententer': true,
14856 * @event eventleave
14857 * Fires when the mouse leaves an
14858 * @param {Calendar} this
14861 'eventleave': true,
14863 * @event eventclick
14864 * Fires when the mouse click an
14865 * @param {Calendar} this
14874 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
14877 * @cfg {Number} startDay
14878 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14886 getAutoCreate : function(){
14889 var fc_button = function(name, corner, style, content ) {
14890 return Roo.apply({},{
14892 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
14894 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14897 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14908 style : 'width:100%',
14915 cls : 'fc-header-left',
14917 fc_button('prev', 'left', 'arrow', '‹' ),
14918 fc_button('next', 'right', 'arrow', '›' ),
14919 { tag: 'span', cls: 'fc-header-space' },
14920 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
14928 cls : 'fc-header-center',
14932 cls: 'fc-header-title',
14935 html : 'month / year'
14943 cls : 'fc-header-right',
14945 /* fc_button('month', 'left', '', 'month' ),
14946 fc_button('week', '', '', 'week' ),
14947 fc_button('day', 'right', '', 'day' )
14959 header = this.header;
14962 var cal_heads = function() {
14964 // fixme - handle this.
14966 for (var i =0; i < Date.dayNames.length; i++) {
14967 var d = Date.dayNames[i];
14970 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14971 html : d.substring(0,3)
14975 ret[0].cls += ' fc-first';
14976 ret[6].cls += ' fc-last';
14979 var cal_cell = function(n) {
14982 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14987 cls: 'fc-day-number',
14991 cls: 'fc-day-content',
14995 style: 'position: relative;' // height: 17px;
15007 var cal_rows = function() {
15010 for (var r = 0; r < 6; r++) {
15017 for (var i =0; i < Date.dayNames.length; i++) {
15018 var d = Date.dayNames[i];
15019 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15022 row.cn[0].cls+=' fc-first';
15023 row.cn[0].cn[0].style = 'min-height:90px';
15024 row.cn[6].cls+=' fc-last';
15028 ret[0].cls += ' fc-first';
15029 ret[4].cls += ' fc-prev-last';
15030 ret[5].cls += ' fc-last';
15037 cls: 'fc-border-separate',
15038 style : 'width:100%',
15046 cls : 'fc-first fc-last',
15064 cls : 'fc-content',
15065 style : "position: relative;",
15068 cls : 'fc-view fc-view-month fc-grid',
15069 style : 'position: relative',
15070 unselectable : 'on',
15073 cls : 'fc-event-container',
15074 style : 'position:absolute;z-index:8;top:0;left:0;'
15092 initEvents : function()
15095 throw "can not find store for calendar";
15101 style: "text-align:center",
15105 style: "background-color:white;width:50%;margin:250 auto",
15109 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15120 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15122 var size = this.el.select('.fc-content', true).first().getSize();
15123 this.maskEl.setSize(size.width, size.height);
15124 this.maskEl.enableDisplayMode("block");
15125 if(!this.loadMask){
15126 this.maskEl.hide();
15129 this.store = Roo.factory(this.store, Roo.data);
15130 this.store.on('load', this.onLoad, this);
15131 this.store.on('beforeload', this.onBeforeLoad, this);
15135 this.cells = this.el.select('.fc-day',true);
15136 //Roo.log(this.cells);
15137 this.textNodes = this.el.query('.fc-day-number');
15138 this.cells.addClassOnOver('fc-state-hover');
15140 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15141 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15142 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15143 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15145 this.on('monthchange', this.onMonthChange, this);
15147 this.update(new Date().clearTime());
15150 resize : function() {
15151 var sz = this.el.getSize();
15153 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15154 this.el.select('.fc-day-content div',true).setHeight(34);
15159 showPrevMonth : function(e){
15160 this.update(this.activeDate.add("mo", -1));
15162 showToday : function(e){
15163 this.update(new Date().clearTime());
15166 showNextMonth : function(e){
15167 this.update(this.activeDate.add("mo", 1));
15171 showPrevYear : function(){
15172 this.update(this.activeDate.add("y", -1));
15176 showNextYear : function(){
15177 this.update(this.activeDate.add("y", 1));
15182 update : function(date)
15184 var vd = this.activeDate;
15185 this.activeDate = date;
15186 // if(vd && this.el){
15187 // var t = date.getTime();
15188 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15189 // Roo.log('using add remove');
15191 // this.fireEvent('monthchange', this, date);
15193 // this.cells.removeClass("fc-state-highlight");
15194 // this.cells.each(function(c){
15195 // if(c.dateValue == t){
15196 // c.addClass("fc-state-highlight");
15197 // setTimeout(function(){
15198 // try{c.dom.firstChild.focus();}catch(e){}
15208 var days = date.getDaysInMonth();
15210 var firstOfMonth = date.getFirstDateOfMonth();
15211 var startingPos = firstOfMonth.getDay()-this.startDay;
15213 if(startingPos < this.startDay){
15217 var pm = date.add(Date.MONTH, -1);
15218 var prevStart = pm.getDaysInMonth()-startingPos;
15220 this.cells = this.el.select('.fc-day',true);
15221 this.textNodes = this.el.query('.fc-day-number');
15222 this.cells.addClassOnOver('fc-state-hover');
15224 var cells = this.cells.elements;
15225 var textEls = this.textNodes;
15227 Roo.each(cells, function(cell){
15228 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15231 days += startingPos;
15233 // convert everything to numbers so it's fast
15234 var day = 86400000;
15235 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15238 //Roo.log(prevStart);
15240 var today = new Date().clearTime().getTime();
15241 var sel = date.clearTime().getTime();
15242 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15243 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15244 var ddMatch = this.disabledDatesRE;
15245 var ddText = this.disabledDatesText;
15246 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15247 var ddaysText = this.disabledDaysText;
15248 var format = this.format;
15250 var setCellClass = function(cal, cell){
15254 //Roo.log('set Cell Class');
15256 var t = d.getTime();
15260 cell.dateValue = t;
15262 cell.className += " fc-today";
15263 cell.className += " fc-state-highlight";
15264 cell.title = cal.todayText;
15267 // disable highlight in other month..
15268 //cell.className += " fc-state-highlight";
15273 cell.className = " fc-state-disabled";
15274 cell.title = cal.minText;
15278 cell.className = " fc-state-disabled";
15279 cell.title = cal.maxText;
15283 if(ddays.indexOf(d.getDay()) != -1){
15284 cell.title = ddaysText;
15285 cell.className = " fc-state-disabled";
15288 if(ddMatch && format){
15289 var fvalue = d.dateFormat(format);
15290 if(ddMatch.test(fvalue)){
15291 cell.title = ddText.replace("%0", fvalue);
15292 cell.className = " fc-state-disabled";
15296 if (!cell.initialClassName) {
15297 cell.initialClassName = cell.dom.className;
15300 cell.dom.className = cell.initialClassName + ' ' + cell.className;
15305 for(; i < startingPos; i++) {
15306 textEls[i].innerHTML = (++prevStart);
15307 d.setDate(d.getDate()+1);
15309 cells[i].className = "fc-past fc-other-month";
15310 setCellClass(this, cells[i]);
15315 for(; i < days; i++){
15316 intDay = i - startingPos + 1;
15317 textEls[i].innerHTML = (intDay);
15318 d.setDate(d.getDate()+1);
15320 cells[i].className = ''; // "x-date-active";
15321 setCellClass(this, cells[i]);
15325 for(; i < 42; i++) {
15326 textEls[i].innerHTML = (++extraDays);
15327 d.setDate(d.getDate()+1);
15329 cells[i].className = "fc-future fc-other-month";
15330 setCellClass(this, cells[i]);
15333 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15335 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15337 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15338 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15340 if(totalRows != 6){
15341 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15342 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15345 this.fireEvent('monthchange', this, date);
15349 if(!this.internalRender){
15350 var main = this.el.dom.firstChild;
15351 var w = main.offsetWidth;
15352 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15353 Roo.fly(main).setWidth(w);
15354 this.internalRender = true;
15355 // opera does not respect the auto grow header center column
15356 // then, after it gets a width opera refuses to recalculate
15357 // without a second pass
15358 if(Roo.isOpera && !this.secondPass){
15359 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15360 this.secondPass = true;
15361 this.update.defer(10, this, [date]);
15368 findCell : function(dt) {
15369 dt = dt.clearTime().getTime();
15371 this.cells.each(function(c){
15372 //Roo.log("check " +c.dateValue + '?=' + dt);
15373 if(c.dateValue == dt){
15383 findCells : function(ev) {
15384 var s = ev.start.clone().clearTime().getTime();
15386 var e= ev.end.clone().clearTime().getTime();
15389 this.cells.each(function(c){
15390 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15392 if(c.dateValue > e){
15395 if(c.dateValue < s){
15404 // findBestRow: function(cells)
15408 // for (var i =0 ; i < cells.length;i++) {
15409 // ret = Math.max(cells[i].rows || 0,ret);
15416 addItem : function(ev)
15418 // look for vertical location slot in
15419 var cells = this.findCells(ev);
15421 // ev.row = this.findBestRow(cells);
15423 // work out the location.
15427 for(var i =0; i < cells.length; i++) {
15429 cells[i].row = cells[0].row;
15432 cells[i].row = cells[i].row + 1;
15442 if (crow.start.getY() == cells[i].getY()) {
15444 crow.end = cells[i];
15461 cells[0].events.push(ev);
15463 this.calevents.push(ev);
15466 clearEvents: function() {
15468 if(!this.calevents){
15472 Roo.each(this.cells.elements, function(c){
15478 Roo.each(this.calevents, function(e) {
15479 Roo.each(e.els, function(el) {
15480 el.un('mouseenter' ,this.onEventEnter, this);
15481 el.un('mouseleave' ,this.onEventLeave, this);
15486 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15492 renderEvents: function()
15496 this.cells.each(function(c) {
15505 if(c.row != c.events.length){
15506 r = 4 - (4 - (c.row - c.events.length));
15509 c.events = ev.slice(0, r);
15510 c.more = ev.slice(r);
15512 if(c.more.length && c.more.length == 1){
15513 c.events.push(c.more.pop());
15516 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15520 this.cells.each(function(c) {
15522 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15525 for (var e = 0; e < c.events.length; e++){
15526 var ev = c.events[e];
15527 var rows = ev.rows;
15529 for(var i = 0; i < rows.length; i++) {
15531 // how many rows should it span..
15534 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15535 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15537 unselectable : "on",
15540 cls: 'fc-event-inner',
15544 // cls: 'fc-event-time',
15545 // html : cells.length > 1 ? '' : ev.time
15549 cls: 'fc-event-title',
15550 html : String.format('{0}', ev.title)
15557 cls: 'ui-resizable-handle ui-resizable-e',
15558 html : '  '
15565 cfg.cls += ' fc-event-start';
15567 if ((i+1) == rows.length) {
15568 cfg.cls += ' fc-event-end';
15571 var ctr = _this.el.select('.fc-event-container',true).first();
15572 var cg = ctr.createChild(cfg);
15574 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15575 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15577 var r = (c.more.length) ? 1 : 0;
15578 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15579 cg.setWidth(ebox.right - sbox.x -2);
15581 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15582 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15583 cg.on('click', _this.onEventClick, _this, ev);
15594 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15595 style : 'position: absolute',
15596 unselectable : "on",
15599 cls: 'fc-event-inner',
15603 cls: 'fc-event-title',
15611 cls: 'ui-resizable-handle ui-resizable-e',
15612 html : '  '
15618 var ctr = _this.el.select('.fc-event-container',true).first();
15619 var cg = ctr.createChild(cfg);
15621 var sbox = c.select('.fc-day-content',true).first().getBox();
15622 var ebox = c.select('.fc-day-content',true).first().getBox();
15624 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15625 cg.setWidth(ebox.right - sbox.x -2);
15627 cg.on('click', _this.onMoreEventClick, _this, c.more);
15637 onEventEnter: function (e, el,event,d) {
15638 this.fireEvent('evententer', this, el, event);
15641 onEventLeave: function (e, el,event,d) {
15642 this.fireEvent('eventleave', this, el, event);
15645 onEventClick: function (e, el,event,d) {
15646 this.fireEvent('eventclick', this, el, event);
15649 onMonthChange: function () {
15653 onMoreEventClick: function(e, el, more)
15657 this.calpopover.placement = 'right';
15658 this.calpopover.setTitle('More');
15660 this.calpopover.setContent('');
15662 var ctr = this.calpopover.el.select('.popover-content', true).first();
15664 Roo.each(more, function(m){
15666 cls : 'fc-event-hori fc-event-draggable',
15669 var cg = ctr.createChild(cfg);
15671 cg.on('click', _this.onEventClick, _this, m);
15674 this.calpopover.show(el);
15679 onLoad: function ()
15681 this.calevents = [];
15684 if(this.store.getCount() > 0){
15685 this.store.data.each(function(d){
15688 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15689 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15690 time : d.data.start_time,
15691 title : d.data.title,
15692 description : d.data.description,
15693 venue : d.data.venue
15698 this.renderEvents();
15700 if(this.calevents.length && this.loadMask){
15701 this.maskEl.hide();
15705 onBeforeLoad: function()
15707 this.clearEvents();
15709 this.maskEl.show();
15723 * @class Roo.bootstrap.Popover
15724 * @extends Roo.bootstrap.Component
15725 * Bootstrap Popover class
15726 * @cfg {String} html contents of the popover (or false to use children..)
15727 * @cfg {String} title of popover (or false to hide)
15728 * @cfg {String} placement how it is placed
15729 * @cfg {String} trigger click || hover (or false to trigger manually)
15730 * @cfg {String} over what (parent or false to trigger manually.)
15731 * @cfg {Number} delay - delay before showing
15734 * Create a new Popover
15735 * @param {Object} config The config object
15738 Roo.bootstrap.Popover = function(config){
15739 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15745 * After the popover show
15747 * @param {Roo.bootstrap.Popover} this
15752 * After the popover hide
15754 * @param {Roo.bootstrap.Popover} this
15760 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
15762 title: 'Fill in a title',
15765 placement : 'right',
15766 trigger : 'hover', // hover
15772 can_build_overlaid : false,
15774 getChildContainer : function()
15776 return this.el.select('.popover-content',true).first();
15779 getAutoCreate : function(){
15782 cls : 'popover roo-dynamic',
15783 style: 'display:block',
15789 cls : 'popover-inner',
15793 cls: 'popover-title',
15797 cls : 'popover-content',
15808 setTitle: function(str)
15811 this.el.select('.popover-title',true).first().dom.innerHTML = str;
15813 setContent: function(str)
15816 this.el.select('.popover-content',true).first().dom.innerHTML = str;
15818 // as it get's added to the bottom of the page.
15819 onRender : function(ct, position)
15821 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15823 var cfg = Roo.apply({}, this.getAutoCreate());
15827 cfg.cls += ' ' + this.cls;
15830 cfg.style = this.style;
15832 //Roo.log("adding to ");
15833 this.el = Roo.get(document.body).createChild(cfg, position);
15834 // Roo.log(this.el);
15839 initEvents : function()
15841 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15842 this.el.enableDisplayMode('block');
15844 if (this.over === false) {
15847 if (this.triggers === false) {
15850 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15851 var triggers = this.trigger ? this.trigger.split(' ') : [];
15852 Roo.each(triggers, function(trigger) {
15854 if (trigger == 'click') {
15855 on_el.on('click', this.toggle, this);
15856 } else if (trigger != 'manual') {
15857 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
15858 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15860 on_el.on(eventIn ,this.enter, this);
15861 on_el.on(eventOut, this.leave, this);
15872 toggle : function () {
15873 this.hoverState == 'in' ? this.leave() : this.enter();
15876 enter : function () {
15879 clearTimeout(this.timeout);
15881 this.hoverState = 'in';
15883 if (!this.delay || !this.delay.show) {
15888 this.timeout = setTimeout(function () {
15889 if (_t.hoverState == 'in') {
15892 }, this.delay.show)
15894 leave : function() {
15895 clearTimeout(this.timeout);
15897 this.hoverState = 'out';
15899 if (!this.delay || !this.delay.hide) {
15904 this.timeout = setTimeout(function () {
15905 if (_t.hoverState == 'out') {
15908 }, this.delay.hide)
15911 show : function (on_el)
15914 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15917 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15918 if (this.html !== false) {
15919 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15921 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15922 if (!this.title.length) {
15923 this.el.select('.popover-title',true).hide();
15926 var placement = typeof this.placement == 'function' ?
15927 this.placement.call(this, this.el, on_el) :
15930 var autoToken = /\s?auto?\s?/i;
15931 var autoPlace = autoToken.test(placement);
15933 placement = placement.replace(autoToken, '') || 'top';
15937 //this.el.setXY([0,0]);
15939 this.el.dom.style.display='block';
15940 this.el.addClass(placement);
15942 //this.el.appendTo(on_el);
15944 var p = this.getPosition();
15945 var box = this.el.getBox();
15950 var align = Roo.bootstrap.Popover.alignment[placement];
15951 this.el.alignTo(on_el, align[0],align[1]);
15952 //var arrow = this.el.select('.arrow',true).first();
15953 //arrow.set(align[2],
15955 this.el.addClass('in');
15958 if (this.el.hasClass('fade')) {
15962 this.fireEvent('show', this);
15967 this.el.setXY([0,0]);
15968 this.el.removeClass('in');
15970 this.hoverState = null;
15972 this.fireEvent('hide', this);
15977 Roo.bootstrap.Popover.alignment = {
15978 'left' : ['r-l', [-10,0], 'right'],
15979 'right' : ['l-r', [10,0], 'left'],
15980 'bottom' : ['t-b', [0,10], 'top'],
15981 'top' : [ 'b-t', [0,-10], 'bottom']
15992 * @class Roo.bootstrap.Progress
15993 * @extends Roo.bootstrap.Component
15994 * Bootstrap Progress class
15995 * @cfg {Boolean} striped striped of the progress bar
15996 * @cfg {Boolean} active animated of the progress bar
16000 * Create a new Progress
16001 * @param {Object} config The config object
16004 Roo.bootstrap.Progress = function(config){
16005 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16008 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
16013 getAutoCreate : function(){
16021 cfg.cls += ' progress-striped';
16025 cfg.cls += ' active';
16044 * @class Roo.bootstrap.ProgressBar
16045 * @extends Roo.bootstrap.Component
16046 * Bootstrap ProgressBar class
16047 * @cfg {Number} aria_valuenow aria-value now
16048 * @cfg {Number} aria_valuemin aria-value min
16049 * @cfg {Number} aria_valuemax aria-value max
16050 * @cfg {String} label label for the progress bar
16051 * @cfg {String} panel (success | info | warning | danger )
16052 * @cfg {String} role role of the progress bar
16053 * @cfg {String} sr_only text
16057 * Create a new ProgressBar
16058 * @param {Object} config The config object
16061 Roo.bootstrap.ProgressBar = function(config){
16062 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16065 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
16069 aria_valuemax : 100,
16075 getAutoCreate : function()
16080 cls: 'progress-bar',
16081 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16093 cfg.role = this.role;
16096 if(this.aria_valuenow){
16097 cfg['aria-valuenow'] = this.aria_valuenow;
16100 if(this.aria_valuemin){
16101 cfg['aria-valuemin'] = this.aria_valuemin;
16104 if(this.aria_valuemax){
16105 cfg['aria-valuemax'] = this.aria_valuemax;
16108 if(this.label && !this.sr_only){
16109 cfg.html = this.label;
16113 cfg.cls += ' progress-bar-' + this.panel;
16119 update : function(aria_valuenow)
16121 this.aria_valuenow = aria_valuenow;
16123 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16138 * @class Roo.bootstrap.TabGroup
16139 * @extends Roo.bootstrap.Column
16140 * Bootstrap Column class
16141 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16142 * @cfg {Boolean} carousel true to make the group behave like a carousel
16143 * @cfg {Boolean} bullets show bullets for the panels
16144 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16145 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16146 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16149 * Create a new TabGroup
16150 * @param {Object} config The config object
16153 Roo.bootstrap.TabGroup = function(config){
16154 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16156 this.navId = Roo.id();
16159 Roo.bootstrap.TabGroup.register(this);
16163 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16166 transition : false,
16171 slideOnTouch : false,
16173 getAutoCreate : function()
16175 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16177 cfg.cls += ' tab-content';
16179 if (this.carousel) {
16180 cfg.cls += ' carousel slide';
16183 cls : 'carousel-inner'
16186 if(this.bullets && !Roo.isTouch){
16189 cls : 'carousel-bullets',
16193 if(this.bullets_cls){
16194 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16197 for (var i = 0; i < this.bullets; i++){
16199 cls : 'bullet bullet-' + i
16207 cfg.cn[0].cn = bullets;
16214 initEvents: function()
16216 if(Roo.isTouch && this.slideOnTouch){
16217 this.el.on("touchstart", this.onTouchStart, this);
16220 if(this.autoslide){
16223 this.slideFn = window.setInterval(function() {
16224 _this.showPanelNext();
16230 onTouchStart : function(e, el, o)
16232 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16236 this.showPanelNext();
16239 getChildContainer : function()
16241 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16245 * register a Navigation item
16246 * @param {Roo.bootstrap.NavItem} the navitem to add
16248 register : function(item)
16250 this.tabs.push( item);
16251 item.navId = this.navId; // not really needed..
16256 getActivePanel : function()
16259 Roo.each(this.tabs, function(t) {
16269 getPanelByName : function(n)
16272 Roo.each(this.tabs, function(t) {
16273 if (t.tabId == n) {
16281 indexOfPanel : function(p)
16284 Roo.each(this.tabs, function(t,i) {
16285 if (t.tabId == p.tabId) {
16294 * show a specific panel
16295 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16296 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16298 showPanel : function (pan)
16300 if(this.transition || typeof(pan) == 'undefined'){
16301 Roo.log("waiting for the transitionend");
16305 if (typeof(pan) == 'number') {
16306 pan = this.tabs[pan];
16309 if (typeof(pan) == 'string') {
16310 pan = this.getPanelByName(pan);
16313 var cur = this.getActivePanel();
16316 Roo.log('pan or acitve pan is undefined');
16320 if (pan.tabId == this.getActivePanel().tabId) {
16324 if (false === cur.fireEvent('beforedeactivate')) {
16328 if(this.bullets > 0 && !Roo.isTouch){
16329 this.setActiveBullet(this.indexOfPanel(pan));
16332 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16334 this.transition = true;
16335 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16336 var lr = dir == 'next' ? 'left' : 'right';
16337 pan.el.addClass(dir); // or prev
16338 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16339 cur.el.addClass(lr); // or right
16340 pan.el.addClass(lr);
16343 cur.el.on('transitionend', function() {
16344 Roo.log("trans end?");
16346 pan.el.removeClass([lr,dir]);
16347 pan.setActive(true);
16349 cur.el.removeClass([lr]);
16350 cur.setActive(false);
16352 _this.transition = false;
16354 }, this, { single: true } );
16359 cur.setActive(false);
16360 pan.setActive(true);
16365 showPanelNext : function()
16367 var i = this.indexOfPanel(this.getActivePanel());
16369 if (i >= this.tabs.length - 1 && !this.autoslide) {
16373 if (i >= this.tabs.length - 1 && this.autoslide) {
16377 this.showPanel(this.tabs[i+1]);
16380 showPanelPrev : function()
16382 var i = this.indexOfPanel(this.getActivePanel());
16384 if (i < 1 && !this.autoslide) {
16388 if (i < 1 && this.autoslide) {
16389 i = this.tabs.length;
16392 this.showPanel(this.tabs[i-1]);
16396 addBullet: function()
16398 if(!this.bullets || Roo.isTouch){
16401 var ctr = this.el.select('.carousel-bullets',true).first();
16402 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16403 var bullet = ctr.createChild({
16404 cls : 'bullet bullet-' + i
16405 },ctr.dom.lastChild);
16410 bullet.on('click', (function(e, el, o, ii, t){
16412 e.preventDefault();
16414 this.showPanel(ii);
16416 if(this.autoslide && this.slideFn){
16417 clearInterval(this.slideFn);
16418 this.slideFn = window.setInterval(function() {
16419 _this.showPanelNext();
16423 }).createDelegate(this, [i, bullet], true));
16428 setActiveBullet : function(i)
16434 Roo.each(this.el.select('.bullet', true).elements, function(el){
16435 el.removeClass('selected');
16438 var bullet = this.el.select('.bullet-' + i, true).first();
16444 bullet.addClass('selected');
16455 Roo.apply(Roo.bootstrap.TabGroup, {
16459 * register a Navigation Group
16460 * @param {Roo.bootstrap.NavGroup} the navgroup to add
16462 register : function(navgrp)
16464 this.groups[navgrp.navId] = navgrp;
16468 * fetch a Navigation Group based on the navigation ID
16469 * if one does not exist , it will get created.
16470 * @param {string} the navgroup to add
16471 * @returns {Roo.bootstrap.NavGroup} the navgroup
16473 get: function(navId) {
16474 if (typeof(this.groups[navId]) == 'undefined') {
16475 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16477 return this.groups[navId] ;
16492 * @class Roo.bootstrap.TabPanel
16493 * @extends Roo.bootstrap.Component
16494 * Bootstrap TabPanel class
16495 * @cfg {Boolean} active panel active
16496 * @cfg {String} html panel content
16497 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16498 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16502 * Create a new TabPanel
16503 * @param {Object} config The config object
16506 Roo.bootstrap.TabPanel = function(config){
16507 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16511 * Fires when the active status changes
16512 * @param {Roo.bootstrap.TabPanel} this
16513 * @param {Boolean} state the new state
16518 * @event beforedeactivate
16519 * Fires before a tab is de-activated - can be used to do validation on a form.
16520 * @param {Roo.bootstrap.TabPanel} this
16521 * @return {Boolean} false if there is an error
16524 'beforedeactivate': true
16527 this.tabId = this.tabId || Roo.id();
16531 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16538 getAutoCreate : function(){
16541 // item is needed for carousel - not sure if it has any effect otherwise
16542 cls: 'tab-pane item',
16543 html: this.html || ''
16547 cfg.cls += ' active';
16551 cfg.tabId = this.tabId;
16558 initEvents: function()
16560 var p = this.parent();
16561 this.navId = this.navId || p.navId;
16563 if (typeof(this.navId) != 'undefined') {
16564 // not really needed.. but just in case.. parent should be a NavGroup.
16565 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16569 var i = tg.tabs.length - 1;
16571 if(this.active && tg.bullets > 0 && i < tg.bullets){
16572 tg.setActiveBullet(i);
16579 onRender : function(ct, position)
16581 // Roo.log("Call onRender: " + this.xtype);
16583 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16591 setActive: function(state)
16593 Roo.log("panel - set active " + this.tabId + "=" + state);
16595 this.active = state;
16597 this.el.removeClass('active');
16599 } else if (!this.el.hasClass('active')) {
16600 this.el.addClass('active');
16603 this.fireEvent('changed', this, state);
16620 * @class Roo.bootstrap.DateField
16621 * @extends Roo.bootstrap.Input
16622 * Bootstrap DateField class
16623 * @cfg {Number} weekStart default 0
16624 * @cfg {String} viewMode default empty, (months|years)
16625 * @cfg {String} minViewMode default empty, (months|years)
16626 * @cfg {Number} startDate default -Infinity
16627 * @cfg {Number} endDate default Infinity
16628 * @cfg {Boolean} todayHighlight default false
16629 * @cfg {Boolean} todayBtn default false
16630 * @cfg {Boolean} calendarWeeks default false
16631 * @cfg {Object} daysOfWeekDisabled default empty
16632 * @cfg {Boolean} singleMode default false (true | false)
16634 * @cfg {Boolean} keyboardNavigation default true
16635 * @cfg {String} language default en
16638 * Create a new DateField
16639 * @param {Object} config The config object
16642 Roo.bootstrap.DateField = function(config){
16643 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16647 * Fires when this field show.
16648 * @param {Roo.bootstrap.DateField} this
16649 * @param {Mixed} date The date value
16654 * Fires when this field hide.
16655 * @param {Roo.bootstrap.DateField} this
16656 * @param {Mixed} date The date value
16661 * Fires when select a date.
16662 * @param {Roo.bootstrap.DateField} this
16663 * @param {Mixed} date The date value
16667 * @event beforeselect
16668 * Fires when before select a date.
16669 * @param {Roo.bootstrap.DateField} this
16670 * @param {Mixed} date The date value
16672 beforeselect : true
16676 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16679 * @cfg {String} format
16680 * The default date format string which can be overriden for localization support. The format must be
16681 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16685 * @cfg {String} altFormats
16686 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16687 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16689 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16697 todayHighlight : false,
16703 keyboardNavigation: true,
16705 calendarWeeks: false,
16707 startDate: -Infinity,
16711 daysOfWeekDisabled: [],
16715 singleMode : false,
16717 UTCDate: function()
16719 return new Date(Date.UTC.apply(Date, arguments));
16722 UTCToday: function()
16724 var today = new Date();
16725 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16728 getDate: function() {
16729 var d = this.getUTCDate();
16730 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16733 getUTCDate: function() {
16737 setDate: function(d) {
16738 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16741 setUTCDate: function(d) {
16743 this.setValue(this.formatDate(this.date));
16746 onRender: function(ct, position)
16749 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16751 this.language = this.language || 'en';
16752 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16753 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16755 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16756 this.format = this.format || 'm/d/y';
16757 this.isInline = false;
16758 this.isInput = true;
16759 this.component = this.el.select('.add-on', true).first() || false;
16760 this.component = (this.component && this.component.length === 0) ? false : this.component;
16761 this.hasInput = this.component && this.inputEL().length;
16763 if (typeof(this.minViewMode === 'string')) {
16764 switch (this.minViewMode) {
16766 this.minViewMode = 1;
16769 this.minViewMode = 2;
16772 this.minViewMode = 0;
16777 if (typeof(this.viewMode === 'string')) {
16778 switch (this.viewMode) {
16791 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16793 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16795 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16797 this.picker().on('mousedown', this.onMousedown, this);
16798 this.picker().on('click', this.onClick, this);
16800 this.picker().addClass('datepicker-dropdown');
16802 this.startViewMode = this.viewMode;
16804 if(this.singleMode){
16805 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16806 v.setVisibilityMode(Roo.Element.DISPLAY);
16810 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16811 v.setStyle('width', '189px');
16815 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16816 if(!this.calendarWeeks){
16821 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16822 v.attr('colspan', function(i, val){
16823 return parseInt(val) + 1;
16828 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16830 this.setStartDate(this.startDate);
16831 this.setEndDate(this.endDate);
16833 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16840 if(this.isInline) {
16845 picker : function()
16847 return this.pickerEl;
16848 // return this.el.select('.datepicker', true).first();
16851 fillDow: function()
16853 var dowCnt = this.weekStart;
16862 if(this.calendarWeeks){
16870 while (dowCnt < this.weekStart + 7) {
16874 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16878 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16881 fillMonths: function()
16884 var months = this.picker().select('>.datepicker-months td', true).first();
16886 months.dom.innerHTML = '';
16892 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16895 months.createChild(month);
16902 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;
16904 if (this.date < this.startDate) {
16905 this.viewDate = new Date(this.startDate);
16906 } else if (this.date > this.endDate) {
16907 this.viewDate = new Date(this.endDate);
16909 this.viewDate = new Date(this.date);
16917 var d = new Date(this.viewDate),
16918 year = d.getUTCFullYear(),
16919 month = d.getUTCMonth(),
16920 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16921 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16922 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16923 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16924 currentDate = this.date && this.date.valueOf(),
16925 today = this.UTCToday();
16927 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16929 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16931 // this.picker.select('>tfoot th.today').
16932 // .text(dates[this.language].today)
16933 // .toggle(this.todayBtn !== false);
16935 this.updateNavArrows();
16938 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16940 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16942 prevMonth.setUTCDate(day);
16944 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16946 var nextMonth = new Date(prevMonth);
16948 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16950 nextMonth = nextMonth.valueOf();
16952 var fillMonths = false;
16954 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16956 while(prevMonth.valueOf() < nextMonth) {
16959 if (prevMonth.getUTCDay() === this.weekStart) {
16961 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16969 if(this.calendarWeeks){
16970 // ISO 8601: First week contains first thursday.
16971 // ISO also states week starts on Monday, but we can be more abstract here.
16973 // Start of current week: based on weekstart/current date
16974 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16975 // Thursday of this week
16976 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16977 // First Thursday of year, year from thursday
16978 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16979 // Calendar week: ms between thursdays, div ms per day, div 7 days
16980 calWeek = (th - yth) / 864e5 / 7 + 1;
16982 fillMonths.cn.push({
16990 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16992 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16995 if (this.todayHighlight &&
16996 prevMonth.getUTCFullYear() == today.getFullYear() &&
16997 prevMonth.getUTCMonth() == today.getMonth() &&
16998 prevMonth.getUTCDate() == today.getDate()) {
16999 clsName += ' today';
17002 if (currentDate && prevMonth.valueOf() === currentDate) {
17003 clsName += ' active';
17006 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17007 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17008 clsName += ' disabled';
17011 fillMonths.cn.push({
17013 cls: 'day ' + clsName,
17014 html: prevMonth.getDate()
17017 prevMonth.setDate(prevMonth.getDate()+1);
17020 var currentYear = this.date && this.date.getUTCFullYear();
17021 var currentMonth = this.date && this.date.getUTCMonth();
17023 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17025 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17026 v.removeClass('active');
17028 if(currentYear === year && k === currentMonth){
17029 v.addClass('active');
17032 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17033 v.addClass('disabled');
17039 year = parseInt(year/10, 10) * 10;
17041 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17043 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17046 for (var i = -1; i < 11; i++) {
17047 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17049 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17057 showMode: function(dir)
17060 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17063 Roo.each(this.picker().select('>div',true).elements, function(v){
17064 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17067 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17072 if(this.isInline) {
17076 this.picker().removeClass(['bottom', 'top']);
17078 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17080 * place to the top of element!
17084 this.picker().addClass('top');
17085 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17090 this.picker().addClass('bottom');
17092 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17095 parseDate : function(value)
17097 if(!value || value instanceof Date){
17100 var v = Date.parseDate(value, this.format);
17101 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17102 v = Date.parseDate(value, 'Y-m-d');
17104 if(!v && this.altFormats){
17105 if(!this.altFormatsArray){
17106 this.altFormatsArray = this.altFormats.split("|");
17108 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17109 v = Date.parseDate(value, this.altFormatsArray[i]);
17115 formatDate : function(date, fmt)
17117 return (!date || !(date instanceof Date)) ?
17118 date : date.dateFormat(fmt || this.format);
17121 onFocus : function()
17123 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17127 onBlur : function()
17129 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17131 var d = this.inputEl().getValue();
17140 this.picker().show();
17144 this.fireEvent('show', this, this.date);
17149 if(this.isInline) {
17152 this.picker().hide();
17153 this.viewMode = this.startViewMode;
17156 this.fireEvent('hide', this, this.date);
17160 onMousedown: function(e)
17162 e.stopPropagation();
17163 e.preventDefault();
17168 Roo.bootstrap.DateField.superclass.keyup.call(this);
17172 setValue: function(v)
17174 if(this.fireEvent('beforeselect', this, v) !== false){
17175 var d = new Date(this.parseDate(v) ).clearTime();
17177 if(isNaN(d.getTime())){
17178 this.date = this.viewDate = '';
17179 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17183 v = this.formatDate(d);
17185 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17187 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17191 this.fireEvent('select', this, this.date);
17195 getValue: function()
17197 return this.formatDate(this.date);
17200 fireKey: function(e)
17202 if (!this.picker().isVisible()){
17203 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17209 var dateChanged = false,
17211 newDate, newViewDate;
17216 e.preventDefault();
17220 if (!this.keyboardNavigation) {
17223 dir = e.keyCode == 37 ? -1 : 1;
17226 newDate = this.moveYear(this.date, dir);
17227 newViewDate = this.moveYear(this.viewDate, dir);
17228 } else if (e.shiftKey){
17229 newDate = this.moveMonth(this.date, dir);
17230 newViewDate = this.moveMonth(this.viewDate, dir);
17232 newDate = new Date(this.date);
17233 newDate.setUTCDate(this.date.getUTCDate() + dir);
17234 newViewDate = new Date(this.viewDate);
17235 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17237 if (this.dateWithinRange(newDate)){
17238 this.date = newDate;
17239 this.viewDate = newViewDate;
17240 this.setValue(this.formatDate(this.date));
17242 e.preventDefault();
17243 dateChanged = true;
17248 if (!this.keyboardNavigation) {
17251 dir = e.keyCode == 38 ? -1 : 1;
17253 newDate = this.moveYear(this.date, dir);
17254 newViewDate = this.moveYear(this.viewDate, dir);
17255 } else if (e.shiftKey){
17256 newDate = this.moveMonth(this.date, dir);
17257 newViewDate = this.moveMonth(this.viewDate, dir);
17259 newDate = new Date(this.date);
17260 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17261 newViewDate = new Date(this.viewDate);
17262 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17264 if (this.dateWithinRange(newDate)){
17265 this.date = newDate;
17266 this.viewDate = newViewDate;
17267 this.setValue(this.formatDate(this.date));
17269 e.preventDefault();
17270 dateChanged = true;
17274 this.setValue(this.formatDate(this.date));
17276 e.preventDefault();
17279 this.setValue(this.formatDate(this.date));
17293 onClick: function(e)
17295 e.stopPropagation();
17296 e.preventDefault();
17298 var target = e.getTarget();
17300 if(target.nodeName.toLowerCase() === 'i'){
17301 target = Roo.get(target).dom.parentNode;
17304 var nodeName = target.nodeName;
17305 var className = target.className;
17306 var html = target.innerHTML;
17307 //Roo.log(nodeName);
17309 switch(nodeName.toLowerCase()) {
17311 switch(className) {
17317 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17318 switch(this.viewMode){
17320 this.viewDate = this.moveMonth(this.viewDate, dir);
17324 this.viewDate = this.moveYear(this.viewDate, dir);
17330 var date = new Date();
17331 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17333 this.setValue(this.formatDate(this.date));
17340 if (className.indexOf('disabled') < 0) {
17341 this.viewDate.setUTCDate(1);
17342 if (className.indexOf('month') > -1) {
17343 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17345 var year = parseInt(html, 10) || 0;
17346 this.viewDate.setUTCFullYear(year);
17350 if(this.singleMode){
17351 this.setValue(this.formatDate(this.viewDate));
17362 //Roo.log(className);
17363 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17364 var day = parseInt(html, 10) || 1;
17365 var year = this.viewDate.getUTCFullYear(),
17366 month = this.viewDate.getUTCMonth();
17368 if (className.indexOf('old') > -1) {
17375 } else if (className.indexOf('new') > -1) {
17383 //Roo.log([year,month,day]);
17384 this.date = this.UTCDate(year, month, day,0,0,0,0);
17385 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17387 //Roo.log(this.formatDate(this.date));
17388 this.setValue(this.formatDate(this.date));
17395 setStartDate: function(startDate)
17397 this.startDate = startDate || -Infinity;
17398 if (this.startDate !== -Infinity) {
17399 this.startDate = this.parseDate(this.startDate);
17402 this.updateNavArrows();
17405 setEndDate: function(endDate)
17407 this.endDate = endDate || Infinity;
17408 if (this.endDate !== Infinity) {
17409 this.endDate = this.parseDate(this.endDate);
17412 this.updateNavArrows();
17415 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17417 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17418 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17419 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17421 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17422 return parseInt(d, 10);
17425 this.updateNavArrows();
17428 updateNavArrows: function()
17430 if(this.singleMode){
17434 var d = new Date(this.viewDate),
17435 year = d.getUTCFullYear(),
17436 month = d.getUTCMonth();
17438 Roo.each(this.picker().select('.prev', true).elements, function(v){
17440 switch (this.viewMode) {
17443 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17449 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17456 Roo.each(this.picker().select('.next', true).elements, function(v){
17458 switch (this.viewMode) {
17461 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17467 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17475 moveMonth: function(date, dir)
17480 var new_date = new Date(date.valueOf()),
17481 day = new_date.getUTCDate(),
17482 month = new_date.getUTCMonth(),
17483 mag = Math.abs(dir),
17485 dir = dir > 0 ? 1 : -1;
17488 // If going back one month, make sure month is not current month
17489 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17491 return new_date.getUTCMonth() == month;
17493 // If going forward one month, make sure month is as expected
17494 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17496 return new_date.getUTCMonth() != new_month;
17498 new_month = month + dir;
17499 new_date.setUTCMonth(new_month);
17500 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17501 if (new_month < 0 || new_month > 11) {
17502 new_month = (new_month + 12) % 12;
17505 // For magnitudes >1, move one month at a time...
17506 for (var i=0; i<mag; i++) {
17507 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17508 new_date = this.moveMonth(new_date, dir);
17510 // ...then reset the day, keeping it in the new month
17511 new_month = new_date.getUTCMonth();
17512 new_date.setUTCDate(day);
17514 return new_month != new_date.getUTCMonth();
17517 // Common date-resetting loop -- if date is beyond end of month, make it
17520 new_date.setUTCDate(--day);
17521 new_date.setUTCMonth(new_month);
17526 moveYear: function(date, dir)
17528 return this.moveMonth(date, dir*12);
17531 dateWithinRange: function(date)
17533 return date >= this.startDate && date <= this.endDate;
17539 this.picker().remove();
17544 Roo.apply(Roo.bootstrap.DateField, {
17555 html: '<i class="fa fa-arrow-left"/>'
17565 html: '<i class="fa fa-arrow-right"/>'
17607 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17608 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17609 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17610 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17611 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17624 navFnc: 'FullYear',
17629 navFnc: 'FullYear',
17634 Roo.apply(Roo.bootstrap.DateField, {
17638 cls: 'datepicker dropdown-menu roo-dynamic',
17642 cls: 'datepicker-days',
17646 cls: 'table-condensed',
17648 Roo.bootstrap.DateField.head,
17652 Roo.bootstrap.DateField.footer
17659 cls: 'datepicker-months',
17663 cls: 'table-condensed',
17665 Roo.bootstrap.DateField.head,
17666 Roo.bootstrap.DateField.content,
17667 Roo.bootstrap.DateField.footer
17674 cls: 'datepicker-years',
17678 cls: 'table-condensed',
17680 Roo.bootstrap.DateField.head,
17681 Roo.bootstrap.DateField.content,
17682 Roo.bootstrap.DateField.footer
17701 * @class Roo.bootstrap.TimeField
17702 * @extends Roo.bootstrap.Input
17703 * Bootstrap DateField class
17707 * Create a new TimeField
17708 * @param {Object} config The config object
17711 Roo.bootstrap.TimeField = function(config){
17712 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17716 * Fires when this field show.
17717 * @param {Roo.bootstrap.DateField} thisthis
17718 * @param {Mixed} date The date value
17723 * Fires when this field hide.
17724 * @param {Roo.bootstrap.DateField} this
17725 * @param {Mixed} date The date value
17730 * Fires when select a date.
17731 * @param {Roo.bootstrap.DateField} this
17732 * @param {Mixed} date The date value
17738 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17741 * @cfg {String} format
17742 * The default time format string which can be overriden for localization support. The format must be
17743 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17747 onRender: function(ct, position)
17750 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17752 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17754 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17756 this.pop = this.picker().select('>.datepicker-time',true).first();
17757 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17759 this.picker().on('mousedown', this.onMousedown, this);
17760 this.picker().on('click', this.onClick, this);
17762 this.picker().addClass('datepicker-dropdown');
17767 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17768 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17769 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17770 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17771 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17772 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17776 fireKey: function(e){
17777 if (!this.picker().isVisible()){
17778 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17784 e.preventDefault();
17792 this.onTogglePeriod();
17795 this.onIncrementMinutes();
17798 this.onDecrementMinutes();
17807 onClick: function(e) {
17808 e.stopPropagation();
17809 e.preventDefault();
17812 picker : function()
17814 return this.el.select('.datepicker', true).first();
17817 fillTime: function()
17819 var time = this.pop.select('tbody', true).first();
17821 time.dom.innerHTML = '';
17836 cls: 'hours-up glyphicon glyphicon-chevron-up'
17856 cls: 'minutes-up glyphicon glyphicon-chevron-up'
17877 cls: 'timepicker-hour',
17892 cls: 'timepicker-minute',
17907 cls: 'btn btn-primary period',
17929 cls: 'hours-down glyphicon glyphicon-chevron-down'
17949 cls: 'minutes-down glyphicon glyphicon-chevron-down'
17967 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17974 var hours = this.time.getHours();
17975 var minutes = this.time.getMinutes();
17988 hours = hours - 12;
17992 hours = '0' + hours;
17996 minutes = '0' + minutes;
17999 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18000 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18001 this.pop.select('button', true).first().dom.innerHTML = period;
18007 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18009 var cls = ['bottom'];
18011 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18018 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18023 this.picker().addClass(cls.join('-'));
18027 Roo.each(cls, function(c){
18029 _this.picker().setTop(_this.inputEl().getHeight());
18033 _this.picker().setTop(0 - _this.picker().getHeight());
18038 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18042 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18049 onFocus : function()
18051 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18055 onBlur : function()
18057 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18063 this.picker().show();
18068 this.fireEvent('show', this, this.date);
18073 this.picker().hide();
18076 this.fireEvent('hide', this, this.date);
18079 setTime : function()
18082 this.setValue(this.time.format(this.format));
18084 this.fireEvent('select', this, this.date);
18089 onMousedown: function(e){
18090 e.stopPropagation();
18091 e.preventDefault();
18094 onIncrementHours: function()
18096 Roo.log('onIncrementHours');
18097 this.time = this.time.add(Date.HOUR, 1);
18102 onDecrementHours: function()
18104 Roo.log('onDecrementHours');
18105 this.time = this.time.add(Date.HOUR, -1);
18109 onIncrementMinutes: function()
18111 Roo.log('onIncrementMinutes');
18112 this.time = this.time.add(Date.MINUTE, 1);
18116 onDecrementMinutes: function()
18118 Roo.log('onDecrementMinutes');
18119 this.time = this.time.add(Date.MINUTE, -1);
18123 onTogglePeriod: function()
18125 Roo.log('onTogglePeriod');
18126 this.time = this.time.add(Date.HOUR, 12);
18133 Roo.apply(Roo.bootstrap.TimeField, {
18163 cls: 'btn btn-info ok',
18175 Roo.apply(Roo.bootstrap.TimeField, {
18179 cls: 'datepicker dropdown-menu',
18183 cls: 'datepicker-time',
18187 cls: 'table-condensed',
18189 Roo.bootstrap.TimeField.content,
18190 Roo.bootstrap.TimeField.footer
18209 * @class Roo.bootstrap.MonthField
18210 * @extends Roo.bootstrap.Input
18211 * Bootstrap MonthField class
18213 * @cfg {String} language default en
18216 * Create a new MonthField
18217 * @param {Object} config The config object
18220 Roo.bootstrap.MonthField = function(config){
18221 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18226 * Fires when this field show.
18227 * @param {Roo.bootstrap.MonthField} this
18228 * @param {Mixed} date The date value
18233 * Fires when this field hide.
18234 * @param {Roo.bootstrap.MonthField} this
18235 * @param {Mixed} date The date value
18240 * Fires when select a date.
18241 * @param {Roo.bootstrap.MonthField} this
18242 * @param {String} oldvalue The old value
18243 * @param {String} newvalue The new value
18249 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
18251 onRender: function(ct, position)
18254 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18256 this.language = this.language || 'en';
18257 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18258 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18260 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18261 this.isInline = false;
18262 this.isInput = true;
18263 this.component = this.el.select('.add-on', true).first() || false;
18264 this.component = (this.component && this.component.length === 0) ? false : this.component;
18265 this.hasInput = this.component && this.inputEL().length;
18267 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18269 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18271 this.picker().on('mousedown', this.onMousedown, this);
18272 this.picker().on('click', this.onClick, this);
18274 this.picker().addClass('datepicker-dropdown');
18276 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18277 v.setStyle('width', '189px');
18284 if(this.isInline) {
18290 setValue: function(v, suppressEvent)
18292 var o = this.getValue();
18294 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18298 if(suppressEvent !== true){
18299 this.fireEvent('select', this, o, v);
18304 getValue: function()
18309 onClick: function(e)
18311 e.stopPropagation();
18312 e.preventDefault();
18314 var target = e.getTarget();
18316 if(target.nodeName.toLowerCase() === 'i'){
18317 target = Roo.get(target).dom.parentNode;
18320 var nodeName = target.nodeName;
18321 var className = target.className;
18322 var html = target.innerHTML;
18324 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18328 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18330 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18336 picker : function()
18338 return this.pickerEl;
18341 fillMonths: function()
18344 var months = this.picker().select('>.datepicker-months td', true).first();
18346 months.dom.innerHTML = '';
18352 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18355 months.createChild(month);
18364 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18365 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18368 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18369 e.removeClass('active');
18371 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18372 e.addClass('active');
18379 if(this.isInline) {
18383 this.picker().removeClass(['bottom', 'top']);
18385 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18387 * place to the top of element!
18391 this.picker().addClass('top');
18392 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18397 this.picker().addClass('bottom');
18399 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18402 onFocus : function()
18404 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18408 onBlur : function()
18410 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18412 var d = this.inputEl().getValue();
18421 this.picker().show();
18422 this.picker().select('>.datepicker-months', true).first().show();
18426 this.fireEvent('show', this, this.date);
18431 if(this.isInline) {
18434 this.picker().hide();
18435 this.fireEvent('hide', this, this.date);
18439 onMousedown: function(e)
18441 e.stopPropagation();
18442 e.preventDefault();
18447 Roo.bootstrap.MonthField.superclass.keyup.call(this);
18451 fireKey: function(e)
18453 if (!this.picker().isVisible()){
18454 if (e.keyCode == 27) {// allow escape to hide and re-show picker
18465 e.preventDefault();
18469 dir = e.keyCode == 37 ? -1 : 1;
18471 this.vIndex = this.vIndex + dir;
18473 if(this.vIndex < 0){
18477 if(this.vIndex > 11){
18481 if(isNaN(this.vIndex)){
18485 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18491 dir = e.keyCode == 38 ? -1 : 1;
18493 this.vIndex = this.vIndex + dir * 4;
18495 if(this.vIndex < 0){
18499 if(this.vIndex > 11){
18503 if(isNaN(this.vIndex)){
18507 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18512 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18513 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18517 e.preventDefault();
18520 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18521 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18537 this.picker().remove();
18542 Roo.apply(Roo.bootstrap.MonthField, {
18561 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18562 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18567 Roo.apply(Roo.bootstrap.MonthField, {
18571 cls: 'datepicker dropdown-menu roo-dynamic',
18575 cls: 'datepicker-months',
18579 cls: 'table-condensed',
18581 Roo.bootstrap.DateField.content
18601 * @class Roo.bootstrap.CheckBox
18602 * @extends Roo.bootstrap.Input
18603 * Bootstrap CheckBox class
18605 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18606 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18607 * @cfg {String} boxLabel The text that appears beside the checkbox
18608 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18609 * @cfg {Boolean} checked initnal the element
18610 * @cfg {Boolean} inline inline the element (default false)
18611 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18614 * Create a new CheckBox
18615 * @param {Object} config The config object
18618 Roo.bootstrap.CheckBox = function(config){
18619 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18624 * Fires when the element is checked or unchecked.
18625 * @param {Roo.bootstrap.CheckBox} this This input
18626 * @param {Boolean} checked The new checked value
18633 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18635 inputType: 'checkbox',
18643 getAutoCreate : function()
18645 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18651 cfg.cls = 'form-group ' + this.inputType; //input-group
18654 cfg.cls += ' ' + this.inputType + '-inline';
18660 type : this.inputType,
18661 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18662 cls : 'roo-' + this.inputType, //'form-box',
18663 placeholder : this.placeholder || ''
18667 if (this.weight) { // Validity check?
18668 cfg.cls += " " + this.inputType + "-" + this.weight;
18671 if (this.disabled) {
18672 input.disabled=true;
18676 input.checked = this.checked;
18680 input.name = this.name;
18684 input.cls += ' input-' + this.size;
18689 ['xs','sm','md','lg'].map(function(size){
18690 if (settings[size]) {
18691 cfg.cls += ' col-' + size + '-' + settings[size];
18695 var inputblock = input;
18697 if (this.before || this.after) {
18700 cls : 'input-group',
18705 inputblock.cn.push({
18707 cls : 'input-group-addon',
18712 inputblock.cn.push(input);
18715 inputblock.cn.push({
18717 cls : 'input-group-addon',
18724 if (align ==='left' && this.fieldLabel.length) {
18725 // Roo.log("left and has label");
18731 cls : 'control-label col-md-' + this.labelWidth,
18732 html : this.fieldLabel
18736 cls : "col-md-" + (12 - this.labelWidth),
18743 } else if ( this.fieldLabel.length) {
18744 // Roo.log(" label");
18748 tag: this.boxLabel ? 'span' : 'label',
18750 cls: 'control-label box-input-label',
18751 //cls : 'input-group-addon',
18752 html : this.fieldLabel
18762 // Roo.log(" no label && no align");
18763 cfg.cn = [ inputblock ] ;
18768 var boxLabelCfg = {
18770 //'for': id, // box label is handled by onclick - so no for...
18772 html: this.boxLabel
18776 boxLabelCfg.tooltip = this.tooltip;
18779 cfg.cn.push(boxLabelCfg);
18789 * return the real input element.
18791 inputEl: function ()
18793 return this.el.select('input.roo-' + this.inputType,true).first();
18796 labelEl: function()
18798 return this.el.select('label.control-label',true).first();
18800 /* depricated... */
18804 return this.labelEl();
18807 initEvents : function()
18809 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18811 this.inputEl().on('click', this.onClick, this);
18813 if (this.boxLabel) {
18814 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18817 this.startValue = this.getValue();
18820 Roo.bootstrap.CheckBox.register(this);
18824 onClick : function()
18826 this.setChecked(!this.checked);
18829 setChecked : function(state,suppressEvent)
18831 this.startValue = this.getValue();
18833 if(this.inputType == 'radio'){
18835 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18836 e.dom.checked = false;
18839 this.inputEl().dom.checked = true;
18841 this.inputEl().dom.value = this.inputValue;
18843 if(suppressEvent !== true){
18844 this.fireEvent('check', this, true);
18852 this.checked = state;
18854 this.inputEl().dom.checked = state;
18856 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18858 if(suppressEvent !== true){
18859 this.fireEvent('check', this, state);
18865 getValue : function()
18867 if(this.inputType == 'radio'){
18868 return this.getGroupValue();
18871 return this.inputEl().getValue();
18875 getGroupValue : function()
18877 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18881 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18884 setValue : function(v,suppressEvent)
18886 if(this.inputType == 'radio'){
18887 this.setGroupValue(v, suppressEvent);
18891 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18896 setGroupValue : function(v, suppressEvent)
18898 this.startValue = this.getValue();
18900 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18901 e.dom.checked = false;
18903 if(e.dom.value == v){
18904 e.dom.checked = true;
18908 if(suppressEvent !== true){
18909 this.fireEvent('check', this, true);
18917 validate : function()
18921 (this.inputType == 'radio' && this.validateRadio()) ||
18922 (this.inputType == 'checkbox' && this.validateCheckbox())
18928 this.markInvalid();
18932 validateRadio : function()
18936 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18937 if(!e.dom.checked){
18949 validateCheckbox : function()
18952 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18955 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18963 for(var i in group){
18968 r = (group[i].getValue() == group[i].inputValue) ? true : false;
18975 * Mark this field as valid
18977 markValid : function()
18979 if(this.allowBlank){
18985 this.fireEvent('valid', this);
18987 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18990 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18997 if(this.inputType == 'radio'){
18998 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18999 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19000 e.findParent('.form-group', false, true).addClass(_this.validClass);
19007 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19008 this.el.findParent('.form-group', false, true).addClass(this.validClass);
19012 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19018 for(var i in group){
19019 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19020 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19025 * Mark this field as invalid
19026 * @param {String} msg The validation message
19028 markInvalid : function(msg)
19030 if(this.allowBlank){
19036 this.fireEvent('invalid', this, msg);
19038 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19041 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19045 label.markInvalid();
19048 if(this.inputType == 'radio'){
19049 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19050 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19051 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19058 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19059 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19063 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19069 for(var i in group){
19070 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19071 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19078 Roo.apply(Roo.bootstrap.CheckBox, {
19083 * register a CheckBox Group
19084 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19086 register : function(checkbox)
19088 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19089 this.groups[checkbox.groupId] = {};
19092 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19096 this.groups[checkbox.groupId][checkbox.name] = checkbox;
19100 * fetch a CheckBox Group based on the group ID
19101 * @param {string} the group ID
19102 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19104 get: function(groupId) {
19105 if (typeof(this.groups[groupId]) == 'undefined') {
19109 return this.groups[groupId] ;
19121 *<div class="radio">
19123 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19124 Option one is this and that—be sure to include why it's great
19131 *<label class="radio-inline">fieldLabel</label>
19132 *<label class="radio-inline">
19133 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19141 * @class Roo.bootstrap.Radio
19142 * @extends Roo.bootstrap.CheckBox
19143 * Bootstrap Radio class
19146 * Create a new Radio
19147 * @param {Object} config The config object
19150 Roo.bootstrap.Radio = function(config){
19151 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19155 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
19157 inputType: 'radio',
19161 getAutoCreate : function()
19163 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19164 align = align || 'left'; // default...
19171 tag : this.inline ? 'span' : 'div',
19176 var inline = this.inline ? ' radio-inline' : '';
19180 // does not need for, as we wrap the input with it..
19182 cls : 'control-label box-label' + inline,
19185 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19189 //cls : 'control-label' + inline,
19190 html : this.fieldLabel,
19191 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19200 type : this.inputType,
19201 //value : (!this.checked) ? this.valueOff : this.inputValue,
19202 value : this.inputValue,
19204 placeholder : this.placeholder || '' // ?? needed????
19207 if (this.weight) { // Validity check?
19208 input.cls += " radio-" + this.weight;
19210 if (this.disabled) {
19211 input.disabled=true;
19215 input.checked = this.checked;
19219 input.name = this.name;
19223 input.cls += ' input-' + this.size;
19226 //?? can span's inline have a width??
19229 ['xs','sm','md','lg'].map(function(size){
19230 if (settings[size]) {
19231 cfg.cls += ' col-' + size + '-' + settings[size];
19235 var inputblock = input;
19237 if (this.before || this.after) {
19240 cls : 'input-group',
19245 inputblock.cn.push({
19247 cls : 'input-group-addon',
19251 inputblock.cn.push(input);
19253 inputblock.cn.push({
19255 cls : 'input-group-addon',
19263 if (this.fieldLabel && this.fieldLabel.length) {
19264 cfg.cn.push(fieldLabel);
19267 // normal bootstrap puts the input inside the label.
19268 // however with our styled version - it has to go after the input.
19270 //lbl.cn.push(inputblock);
19274 cls: 'radio' + inline,
19281 cfg.cn.push( lblwrap);
19286 html: this.boxLabel
19295 initEvents : function()
19297 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19299 this.inputEl().on('click', this.onClick, this);
19300 if (this.boxLabel) {
19301 //Roo.log('find label');
19302 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
19307 inputEl: function ()
19309 return this.el.select('input.roo-radio',true).first();
19311 onClick : function()
19314 this.setChecked(true);
19317 setChecked : function(state,suppressEvent)
19320 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19321 v.dom.checked = false;
19324 Roo.log(this.inputEl().dom);
19325 this.checked = state;
19326 this.inputEl().dom.checked = state;
19328 if(suppressEvent !== true){
19329 this.fireEvent('check', this, state);
19332 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19336 getGroupValue : function()
19339 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19340 if(v.dom.checked == true){
19341 value = v.dom.value;
19349 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
19350 * @return {Mixed} value The field value
19352 getValue : function(){
19353 return this.getGroupValue();
19359 //<script type="text/javascript">
19362 * Based Ext JS Library 1.1.1
19363 * Copyright(c) 2006-2007, Ext JS, LLC.
19369 * @class Roo.HtmlEditorCore
19370 * @extends Roo.Component
19371 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19373 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19376 Roo.HtmlEditorCore = function(config){
19379 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19384 * @event initialize
19385 * Fires when the editor is fully initialized (including the iframe)
19386 * @param {Roo.HtmlEditorCore} this
19391 * Fires when the editor is first receives the focus. Any insertion must wait
19392 * until after this event.
19393 * @param {Roo.HtmlEditorCore} this
19397 * @event beforesync
19398 * Fires before the textarea is updated with content from the editor iframe. Return false
19399 * to cancel the sync.
19400 * @param {Roo.HtmlEditorCore} this
19401 * @param {String} html
19405 * @event beforepush
19406 * Fires before the iframe editor is updated with content from the textarea. Return false
19407 * to cancel the push.
19408 * @param {Roo.HtmlEditorCore} this
19409 * @param {String} html
19414 * Fires when the textarea is updated with content from the editor iframe.
19415 * @param {Roo.HtmlEditorCore} this
19416 * @param {String} html
19421 * Fires when the iframe editor is updated with content from the textarea.
19422 * @param {Roo.HtmlEditorCore} this
19423 * @param {String} html
19428 * @event editorevent
19429 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19430 * @param {Roo.HtmlEditorCore} this
19436 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19438 // defaults : white / black...
19439 this.applyBlacklists();
19446 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
19450 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
19456 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
19461 * @cfg {Number} height (in pixels)
19465 * @cfg {Number} width (in pixels)
19470 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19473 stylesheets: false,
19478 // private properties
19479 validationEvent : false,
19481 initialized : false,
19483 sourceEditMode : false,
19484 onFocus : Roo.emptyFn,
19486 hideMode:'offsets',
19490 // blacklist + whitelisted elements..
19497 * Protected method that will not generally be called directly. It
19498 * is called when the editor initializes the iframe with HTML contents. Override this method if you
19499 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19501 getDocMarkup : function(){
19505 // inherit styels from page...??
19506 if (this.stylesheets === false) {
19508 Roo.get(document.head).select('style').each(function(node) {
19509 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19512 Roo.get(document.head).select('link').each(function(node) {
19513 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19516 } else if (!this.stylesheets.length) {
19518 st = '<style type="text/css">' +
19519 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19525 st += '<style type="text/css">' +
19526 'IMG { cursor: pointer } ' +
19530 return '<html><head>' + st +
19531 //<style type="text/css">' +
19532 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19534 ' </head><body class="roo-htmleditor-body"></body></html>';
19538 onRender : function(ct, position)
19541 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19542 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19545 this.el.dom.style.border = '0 none';
19546 this.el.dom.setAttribute('tabIndex', -1);
19547 this.el.addClass('x-hidden hide');
19551 if(Roo.isIE){ // fix IE 1px bogus margin
19552 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19556 this.frameId = Roo.id();
19560 var iframe = this.owner.wrap.createChild({
19562 cls: 'form-control', // bootstrap..
19564 name: this.frameId,
19565 frameBorder : 'no',
19566 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19571 this.iframe = iframe.dom;
19573 this.assignDocWin();
19575 this.doc.designMode = 'on';
19578 this.doc.write(this.getDocMarkup());
19582 var task = { // must defer to wait for browser to be ready
19584 //console.log("run task?" + this.doc.readyState);
19585 this.assignDocWin();
19586 if(this.doc.body || this.doc.readyState == 'complete'){
19588 this.doc.designMode="on";
19592 Roo.TaskMgr.stop(task);
19593 this.initEditor.defer(10, this);
19600 Roo.TaskMgr.start(task);
19605 onResize : function(w, h)
19607 Roo.log('resize: ' +w + ',' + h );
19608 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19612 if(typeof w == 'number'){
19614 this.iframe.style.width = w + 'px';
19616 if(typeof h == 'number'){
19618 this.iframe.style.height = h + 'px';
19620 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19627 * Toggles the editor between standard and source edit mode.
19628 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19630 toggleSourceEdit : function(sourceEditMode){
19632 this.sourceEditMode = sourceEditMode === true;
19634 if(this.sourceEditMode){
19636 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19639 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19640 //this.iframe.className = '';
19643 //this.setSize(this.owner.wrap.getSize());
19644 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19651 * Protected method that will not generally be called directly. If you need/want
19652 * custom HTML cleanup, this is the method you should override.
19653 * @param {String} html The HTML to be cleaned
19654 * return {String} The cleaned HTML
19656 cleanHtml : function(html){
19657 html = String(html);
19658 if(html.length > 5){
19659 if(Roo.isSafari){ // strip safari nonsense
19660 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19663 if(html == ' '){
19670 * HTML Editor -> Textarea
19671 * Protected method that will not generally be called directly. Syncs the contents
19672 * of the editor iframe with the textarea.
19674 syncValue : function(){
19675 if(this.initialized){
19676 var bd = (this.doc.body || this.doc.documentElement);
19677 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19678 var html = bd.innerHTML;
19680 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19681 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19683 html = '<div style="'+m[0]+'">' + html + '</div>';
19686 html = this.cleanHtml(html);
19687 // fix up the special chars.. normaly like back quotes in word...
19688 // however we do not want to do this with chinese..
19689 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19690 var cc = b.charCodeAt();
19692 (cc >= 0x4E00 && cc < 0xA000 ) ||
19693 (cc >= 0x3400 && cc < 0x4E00 ) ||
19694 (cc >= 0xf900 && cc < 0xfb00 )
19700 if(this.owner.fireEvent('beforesync', this, html) !== false){
19701 this.el.dom.value = html;
19702 this.owner.fireEvent('sync', this, html);
19708 * Protected method that will not generally be called directly. Pushes the value of the textarea
19709 * into the iframe editor.
19711 pushValue : function(){
19712 if(this.initialized){
19713 var v = this.el.dom.value.trim();
19715 // if(v.length < 1){
19719 if(this.owner.fireEvent('beforepush', this, v) !== false){
19720 var d = (this.doc.body || this.doc.documentElement);
19722 this.cleanUpPaste();
19723 this.el.dom.value = d.innerHTML;
19724 this.owner.fireEvent('push', this, v);
19730 deferFocus : function(){
19731 this.focus.defer(10, this);
19735 focus : function(){
19736 if(this.win && !this.sourceEditMode){
19743 assignDocWin: function()
19745 var iframe = this.iframe;
19748 this.doc = iframe.contentWindow.document;
19749 this.win = iframe.contentWindow;
19751 // if (!Roo.get(this.frameId)) {
19754 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19755 // this.win = Roo.get(this.frameId).dom.contentWindow;
19757 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19761 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19762 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19767 initEditor : function(){
19768 //console.log("INIT EDITOR");
19769 this.assignDocWin();
19773 this.doc.designMode="on";
19775 this.doc.write(this.getDocMarkup());
19778 var dbody = (this.doc.body || this.doc.documentElement);
19779 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19780 // this copies styles from the containing element into thsi one..
19781 // not sure why we need all of this..
19782 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19784 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19785 //ss['background-attachment'] = 'fixed'; // w3c
19786 dbody.bgProperties = 'fixed'; // ie
19787 //Roo.DomHelper.applyStyles(dbody, ss);
19788 Roo.EventManager.on(this.doc, {
19789 //'mousedown': this.onEditorEvent,
19790 'mouseup': this.onEditorEvent,
19791 'dblclick': this.onEditorEvent,
19792 'click': this.onEditorEvent,
19793 'keyup': this.onEditorEvent,
19798 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19800 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19801 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19803 this.initialized = true;
19805 this.owner.fireEvent('initialize', this);
19810 onDestroy : function(){
19816 //for (var i =0; i < this.toolbars.length;i++) {
19817 // // fixme - ask toolbars for heights?
19818 // this.toolbars[i].onDestroy();
19821 //this.wrap.dom.innerHTML = '';
19822 //this.wrap.remove();
19827 onFirstFocus : function(){
19829 this.assignDocWin();
19832 this.activated = true;
19835 if(Roo.isGecko){ // prevent silly gecko errors
19837 var s = this.win.getSelection();
19838 if(!s.focusNode || s.focusNode.nodeType != 3){
19839 var r = s.getRangeAt(0);
19840 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19845 this.execCmd('useCSS', true);
19846 this.execCmd('styleWithCSS', false);
19849 this.owner.fireEvent('activate', this);
19853 adjustFont: function(btn){
19854 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19855 //if(Roo.isSafari){ // safari
19858 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19859 if(Roo.isSafari){ // safari
19860 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19861 v = (v < 10) ? 10 : v;
19862 v = (v > 48) ? 48 : v;
19863 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19868 v = Math.max(1, v+adjust);
19870 this.execCmd('FontSize', v );
19873 onEditorEvent : function(e)
19875 this.owner.fireEvent('editorevent', this, e);
19876 // this.updateToolbar();
19877 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19880 insertTag : function(tg)
19882 // could be a bit smarter... -> wrap the current selected tRoo..
19883 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19885 range = this.createRange(this.getSelection());
19886 var wrappingNode = this.doc.createElement(tg.toLowerCase());
19887 wrappingNode.appendChild(range.extractContents());
19888 range.insertNode(wrappingNode);
19895 this.execCmd("formatblock", tg);
19899 insertText : function(txt)
19903 var range = this.createRange();
19904 range.deleteContents();
19905 //alert(Sender.getAttribute('label'));
19907 range.insertNode(this.doc.createTextNode(txt));
19913 * Executes a Midas editor command on the editor document and performs necessary focus and
19914 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19915 * @param {String} cmd The Midas command
19916 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19918 relayCmd : function(cmd, value){
19920 this.execCmd(cmd, value);
19921 this.owner.fireEvent('editorevent', this);
19922 //this.updateToolbar();
19923 this.owner.deferFocus();
19927 * Executes a Midas editor command directly on the editor document.
19928 * For visual commands, you should use {@link #relayCmd} instead.
19929 * <b>This should only be called after the editor is initialized.</b>
19930 * @param {String} cmd The Midas command
19931 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19933 execCmd : function(cmd, value){
19934 this.doc.execCommand(cmd, false, value === undefined ? null : value);
19941 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19943 * @param {String} text | dom node..
19945 insertAtCursor : function(text)
19950 if(!this.activated){
19956 var r = this.doc.selection.createRange();
19967 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19971 // from jquery ui (MIT licenced)
19973 var win = this.win;
19975 if (win.getSelection && win.getSelection().getRangeAt) {
19976 range = win.getSelection().getRangeAt(0);
19977 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19978 range.insertNode(node);
19979 } else if (win.document.selection && win.document.selection.createRange) {
19980 // no firefox support
19981 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19982 win.document.selection.createRange().pasteHTML(txt);
19984 // no firefox support
19985 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19986 this.execCmd('InsertHTML', txt);
19995 mozKeyPress : function(e){
19997 var c = e.getCharCode(), cmd;
20000 c = String.fromCharCode(c).toLowerCase();
20014 this.cleanUpPaste.defer(100, this);
20022 e.preventDefault();
20030 fixKeys : function(){ // load time branching for fastest keydown performance
20032 return function(e){
20033 var k = e.getKey(), r;
20036 r = this.doc.selection.createRange();
20039 r.pasteHTML('    ');
20046 r = this.doc.selection.createRange();
20048 var target = r.parentElement();
20049 if(!target || target.tagName.toLowerCase() != 'li'){
20051 r.pasteHTML('<br />');
20057 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20058 this.cleanUpPaste.defer(100, this);
20064 }else if(Roo.isOpera){
20065 return function(e){
20066 var k = e.getKey();
20070 this.execCmd('InsertHTML','    ');
20073 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20074 this.cleanUpPaste.defer(100, this);
20079 }else if(Roo.isSafari){
20080 return function(e){
20081 var k = e.getKey();
20085 this.execCmd('InsertText','\t');
20089 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20090 this.cleanUpPaste.defer(100, this);
20098 getAllAncestors: function()
20100 var p = this.getSelectedNode();
20103 a.push(p); // push blank onto stack..
20104 p = this.getParentElement();
20108 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20112 a.push(this.doc.body);
20116 lastSelNode : false,
20119 getSelection : function()
20121 this.assignDocWin();
20122 return Roo.isIE ? this.doc.selection : this.win.getSelection();
20125 getSelectedNode: function()
20127 // this may only work on Gecko!!!
20129 // should we cache this!!!!
20134 var range = this.createRange(this.getSelection()).cloneRange();
20137 var parent = range.parentElement();
20139 var testRange = range.duplicate();
20140 testRange.moveToElementText(parent);
20141 if (testRange.inRange(range)) {
20144 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20147 parent = parent.parentElement;
20152 // is ancestor a text element.
20153 var ac = range.commonAncestorContainer;
20154 if (ac.nodeType == 3) {
20155 ac = ac.parentNode;
20158 var ar = ac.childNodes;
20161 var other_nodes = [];
20162 var has_other_nodes = false;
20163 for (var i=0;i<ar.length;i++) {
20164 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
20167 // fullly contained node.
20169 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20174 // probably selected..
20175 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20176 other_nodes.push(ar[i]);
20180 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
20185 has_other_nodes = true;
20187 if (!nodes.length && other_nodes.length) {
20188 nodes= other_nodes;
20190 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20196 createRange: function(sel)
20198 // this has strange effects when using with
20199 // top toolbar - not sure if it's a great idea.
20200 //this.editor.contentWindow.focus();
20201 if (typeof sel != "undefined") {
20203 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20205 return this.doc.createRange();
20208 return this.doc.createRange();
20211 getParentElement: function()
20214 this.assignDocWin();
20215 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20217 var range = this.createRange(sel);
20220 var p = range.commonAncestorContainer;
20221 while (p.nodeType == 3) { // text node
20232 * Range intersection.. the hard stuff...
20236 * [ -- selected range --- ]
20240 * if end is before start or hits it. fail.
20241 * if start is after end or hits it fail.
20243 * if either hits (but other is outside. - then it's not
20249 // @see http://www.thismuchiknow.co.uk/?p=64.
20250 rangeIntersectsNode : function(range, node)
20252 var nodeRange = node.ownerDocument.createRange();
20254 nodeRange.selectNode(node);
20256 nodeRange.selectNodeContents(node);
20259 var rangeStartRange = range.cloneRange();
20260 rangeStartRange.collapse(true);
20262 var rangeEndRange = range.cloneRange();
20263 rangeEndRange.collapse(false);
20265 var nodeStartRange = nodeRange.cloneRange();
20266 nodeStartRange.collapse(true);
20268 var nodeEndRange = nodeRange.cloneRange();
20269 nodeEndRange.collapse(false);
20271 return rangeStartRange.compareBoundaryPoints(
20272 Range.START_TO_START, nodeEndRange) == -1 &&
20273 rangeEndRange.compareBoundaryPoints(
20274 Range.START_TO_START, nodeStartRange) == 1;
20278 rangeCompareNode : function(range, node)
20280 var nodeRange = node.ownerDocument.createRange();
20282 nodeRange.selectNode(node);
20284 nodeRange.selectNodeContents(node);
20288 range.collapse(true);
20290 nodeRange.collapse(true);
20292 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20293 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
20295 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20297 var nodeIsBefore = ss == 1;
20298 var nodeIsAfter = ee == -1;
20300 if (nodeIsBefore && nodeIsAfter) {
20303 if (!nodeIsBefore && nodeIsAfter) {
20304 return 1; //right trailed.
20307 if (nodeIsBefore && !nodeIsAfter) {
20308 return 2; // left trailed.
20314 // private? - in a new class?
20315 cleanUpPaste : function()
20317 // cleans up the whole document..
20318 Roo.log('cleanuppaste');
20320 this.cleanUpChildren(this.doc.body);
20321 var clean = this.cleanWordChars(this.doc.body.innerHTML);
20322 if (clean != this.doc.body.innerHTML) {
20323 this.doc.body.innerHTML = clean;
20328 cleanWordChars : function(input) {// change the chars to hex code
20329 var he = Roo.HtmlEditorCore;
20331 var output = input;
20332 Roo.each(he.swapCodes, function(sw) {
20333 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20335 output = output.replace(swapper, sw[1]);
20342 cleanUpChildren : function (n)
20344 if (!n.childNodes.length) {
20347 for (var i = n.childNodes.length-1; i > -1 ; i--) {
20348 this.cleanUpChild(n.childNodes[i]);
20355 cleanUpChild : function (node)
20358 //console.log(node);
20359 if (node.nodeName == "#text") {
20360 // clean up silly Windows -- stuff?
20363 if (node.nodeName == "#comment") {
20364 node.parentNode.removeChild(node);
20365 // clean up silly Windows -- stuff?
20368 var lcname = node.tagName.toLowerCase();
20369 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20370 // whitelist of tags..
20372 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20374 node.parentNode.removeChild(node);
20379 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20381 // remove <a name=....> as rendering on yahoo mailer is borked with this.
20382 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20384 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20385 // remove_keep_children = true;
20388 if (remove_keep_children) {
20389 this.cleanUpChildren(node);
20390 // inserts everything just before this node...
20391 while (node.childNodes.length) {
20392 var cn = node.childNodes[0];
20393 node.removeChild(cn);
20394 node.parentNode.insertBefore(cn, node);
20396 node.parentNode.removeChild(node);
20400 if (!node.attributes || !node.attributes.length) {
20401 this.cleanUpChildren(node);
20405 function cleanAttr(n,v)
20408 if (v.match(/^\./) || v.match(/^\//)) {
20411 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20414 if (v.match(/^#/)) {
20417 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20418 node.removeAttribute(n);
20422 var cwhite = this.cwhite;
20423 var cblack = this.cblack;
20425 function cleanStyle(n,v)
20427 if (v.match(/expression/)) { //XSS?? should we even bother..
20428 node.removeAttribute(n);
20432 var parts = v.split(/;/);
20435 Roo.each(parts, function(p) {
20436 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20440 var l = p.split(':').shift().replace(/\s+/g,'');
20441 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20443 if ( cwhite.length && cblack.indexOf(l) > -1) {
20444 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20445 //node.removeAttribute(n);
20449 // only allow 'c whitelisted system attributes'
20450 if ( cwhite.length && cwhite.indexOf(l) < 0) {
20451 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20452 //node.removeAttribute(n);
20462 if (clean.length) {
20463 node.setAttribute(n, clean.join(';'));
20465 node.removeAttribute(n);
20471 for (var i = node.attributes.length-1; i > -1 ; i--) {
20472 var a = node.attributes[i];
20475 if (a.name.toLowerCase().substr(0,2)=='on') {
20476 node.removeAttribute(a.name);
20479 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20480 node.removeAttribute(a.name);
20483 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20484 cleanAttr(a.name,a.value); // fixme..
20487 if (a.name == 'style') {
20488 cleanStyle(a.name,a.value);
20491 /// clean up MS crap..
20492 // tecnically this should be a list of valid class'es..
20495 if (a.name == 'class') {
20496 if (a.value.match(/^Mso/)) {
20497 node.className = '';
20500 if (a.value.match(/body/)) {
20501 node.className = '';
20512 this.cleanUpChildren(node);
20518 * Clean up MS wordisms...
20520 cleanWord : function(node)
20525 this.cleanWord(this.doc.body);
20528 if (node.nodeName == "#text") {
20529 // clean up silly Windows -- stuff?
20532 if (node.nodeName == "#comment") {
20533 node.parentNode.removeChild(node);
20534 // clean up silly Windows -- stuff?
20538 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20539 node.parentNode.removeChild(node);
20543 // remove - but keep children..
20544 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20545 while (node.childNodes.length) {
20546 var cn = node.childNodes[0];
20547 node.removeChild(cn);
20548 node.parentNode.insertBefore(cn, node);
20550 node.parentNode.removeChild(node);
20551 this.iterateChildren(node, this.cleanWord);
20555 if (node.className.length) {
20557 var cn = node.className.split(/\W+/);
20559 Roo.each(cn, function(cls) {
20560 if (cls.match(/Mso[a-zA-Z]+/)) {
20565 node.className = cna.length ? cna.join(' ') : '';
20567 node.removeAttribute("class");
20571 if (node.hasAttribute("lang")) {
20572 node.removeAttribute("lang");
20575 if (node.hasAttribute("style")) {
20577 var styles = node.getAttribute("style").split(";");
20579 Roo.each(styles, function(s) {
20580 if (!s.match(/:/)) {
20583 var kv = s.split(":");
20584 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20587 // what ever is left... we allow.
20590 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20591 if (!nstyle.length) {
20592 node.removeAttribute('style');
20595 this.iterateChildren(node, this.cleanWord);
20601 * iterateChildren of a Node, calling fn each time, using this as the scole..
20602 * @param {DomNode} node node to iterate children of.
20603 * @param {Function} fn method of this class to call on each item.
20605 iterateChildren : function(node, fn)
20607 if (!node.childNodes.length) {
20610 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20611 fn.call(this, node.childNodes[i])
20617 * cleanTableWidths.
20619 * Quite often pasting from word etc.. results in tables with column and widths.
20620 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20623 cleanTableWidths : function(node)
20628 this.cleanTableWidths(this.doc.body);
20633 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20636 Roo.log(node.tagName);
20637 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20638 this.iterateChildren(node, this.cleanTableWidths);
20641 if (node.hasAttribute('width')) {
20642 node.removeAttribute('width');
20646 if (node.hasAttribute("style")) {
20649 var styles = node.getAttribute("style").split(";");
20651 Roo.each(styles, function(s) {
20652 if (!s.match(/:/)) {
20655 var kv = s.split(":");
20656 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20659 // what ever is left... we allow.
20662 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20663 if (!nstyle.length) {
20664 node.removeAttribute('style');
20668 this.iterateChildren(node, this.cleanTableWidths);
20676 domToHTML : function(currentElement, depth, nopadtext) {
20678 depth = depth || 0;
20679 nopadtext = nopadtext || false;
20681 if (!currentElement) {
20682 return this.domToHTML(this.doc.body);
20685 //Roo.log(currentElement);
20687 var allText = false;
20688 var nodeName = currentElement.nodeName;
20689 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20691 if (nodeName == '#text') {
20693 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20698 if (nodeName != 'BODY') {
20701 // Prints the node tagName, such as <A>, <IMG>, etc
20704 for(i = 0; i < currentElement.attributes.length;i++) {
20706 var aname = currentElement.attributes.item(i).name;
20707 if (!currentElement.attributes.item(i).value.length) {
20710 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20713 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20722 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20725 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20730 // Traverse the tree
20732 var currentElementChild = currentElement.childNodes.item(i);
20733 var allText = true;
20734 var innerHTML = '';
20736 while (currentElementChild) {
20737 // Formatting code (indent the tree so it looks nice on the screen)
20738 var nopad = nopadtext;
20739 if (lastnode == 'SPAN') {
20743 if (currentElementChild.nodeName == '#text') {
20744 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20745 toadd = nopadtext ? toadd : toadd.trim();
20746 if (!nopad && toadd.length > 80) {
20747 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20749 innerHTML += toadd;
20752 currentElementChild = currentElement.childNodes.item(i);
20758 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20760 // Recursively traverse the tree structure of the child node
20761 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20762 lastnode = currentElementChild.nodeName;
20764 currentElementChild=currentElement.childNodes.item(i);
20770 // The remaining code is mostly for formatting the tree
20771 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20776 ret+= "</"+tagName+">";
20782 applyBlacklists : function()
20784 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20785 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20789 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20790 if (b.indexOf(tag) > -1) {
20793 this.white.push(tag);
20797 Roo.each(w, function(tag) {
20798 if (b.indexOf(tag) > -1) {
20801 if (this.white.indexOf(tag) > -1) {
20804 this.white.push(tag);
20809 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20810 if (w.indexOf(tag) > -1) {
20813 this.black.push(tag);
20817 Roo.each(b, function(tag) {
20818 if (w.indexOf(tag) > -1) {
20821 if (this.black.indexOf(tag) > -1) {
20824 this.black.push(tag);
20829 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20830 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20834 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20835 if (b.indexOf(tag) > -1) {
20838 this.cwhite.push(tag);
20842 Roo.each(w, function(tag) {
20843 if (b.indexOf(tag) > -1) {
20846 if (this.cwhite.indexOf(tag) > -1) {
20849 this.cwhite.push(tag);
20854 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20855 if (w.indexOf(tag) > -1) {
20858 this.cblack.push(tag);
20862 Roo.each(b, function(tag) {
20863 if (w.indexOf(tag) > -1) {
20866 if (this.cblack.indexOf(tag) > -1) {
20869 this.cblack.push(tag);
20874 setStylesheets : function(stylesheets)
20876 if(typeof(stylesheets) == 'string'){
20877 Roo.get(this.iframe.contentDocument.head).createChild({
20879 rel : 'stylesheet',
20888 Roo.each(stylesheets, function(s) {
20893 Roo.get(_this.iframe.contentDocument.head).createChild({
20895 rel : 'stylesheet',
20904 removeStylesheets : function()
20908 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20913 // hide stuff that is not compatible
20927 * @event specialkey
20931 * @cfg {String} fieldClass @hide
20934 * @cfg {String} focusClass @hide
20937 * @cfg {String} autoCreate @hide
20940 * @cfg {String} inputType @hide
20943 * @cfg {String} invalidClass @hide
20946 * @cfg {String} invalidText @hide
20949 * @cfg {String} msgFx @hide
20952 * @cfg {String} validateOnBlur @hide
20956 Roo.HtmlEditorCore.white = [
20957 'area', 'br', 'img', 'input', 'hr', 'wbr',
20959 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
20960 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
20961 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
20962 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
20963 'table', 'ul', 'xmp',
20965 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
20968 'dir', 'menu', 'ol', 'ul', 'dl',
20974 Roo.HtmlEditorCore.black = [
20975 // 'embed', 'object', // enable - backend responsiblity to clean thiese
20977 'base', 'basefont', 'bgsound', 'blink', 'body',
20978 'frame', 'frameset', 'head', 'html', 'ilayer',
20979 'iframe', 'layer', 'link', 'meta', 'object',
20980 'script', 'style' ,'title', 'xml' // clean later..
20982 Roo.HtmlEditorCore.clean = [
20983 'script', 'style', 'title', 'xml'
20985 Roo.HtmlEditorCore.remove = [
20990 Roo.HtmlEditorCore.ablack = [
20994 Roo.HtmlEditorCore.aclean = [
20995 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
20999 Roo.HtmlEditorCore.pwhite= [
21000 'http', 'https', 'mailto'
21003 // white listed style attributes.
21004 Roo.HtmlEditorCore.cwhite= [
21005 // 'text-align', /// default is to allow most things..
21011 // black listed style attributes.
21012 Roo.HtmlEditorCore.cblack= [
21013 // 'font-size' -- this can be set by the project
21017 Roo.HtmlEditorCore.swapCodes =[
21036 * @class Roo.bootstrap.HtmlEditor
21037 * @extends Roo.bootstrap.TextArea
21038 * Bootstrap HtmlEditor class
21041 * Create a new HtmlEditor
21042 * @param {Object} config The config object
21045 Roo.bootstrap.HtmlEditor = function(config){
21046 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21047 if (!this.toolbars) {
21048 this.toolbars = [];
21050 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21053 * @event initialize
21054 * Fires when the editor is fully initialized (including the iframe)
21055 * @param {HtmlEditor} this
21060 * Fires when the editor is first receives the focus. Any insertion must wait
21061 * until after this event.
21062 * @param {HtmlEditor} this
21066 * @event beforesync
21067 * Fires before the textarea is updated with content from the editor iframe. Return false
21068 * to cancel the sync.
21069 * @param {HtmlEditor} this
21070 * @param {String} html
21074 * @event beforepush
21075 * Fires before the iframe editor is updated with content from the textarea. Return false
21076 * to cancel the push.
21077 * @param {HtmlEditor} this
21078 * @param {String} html
21083 * Fires when the textarea is updated with content from the editor iframe.
21084 * @param {HtmlEditor} this
21085 * @param {String} html
21090 * Fires when the iframe editor is updated with content from the textarea.
21091 * @param {HtmlEditor} this
21092 * @param {String} html
21096 * @event editmodechange
21097 * Fires when the editor switches edit modes
21098 * @param {HtmlEditor} this
21099 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21101 editmodechange: true,
21103 * @event editorevent
21104 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21105 * @param {HtmlEditor} this
21109 * @event firstfocus
21110 * Fires when on first focus - needed by toolbars..
21111 * @param {HtmlEditor} this
21116 * Auto save the htmlEditor value as a file into Events
21117 * @param {HtmlEditor} this
21121 * @event savedpreview
21122 * preview the saved version of htmlEditor
21123 * @param {HtmlEditor} this
21130 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
21134 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21139 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21144 * @cfg {Number} height (in pixels)
21148 * @cfg {Number} width (in pixels)
21153 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21156 stylesheets: false,
21161 // private properties
21162 validationEvent : false,
21164 initialized : false,
21167 onFocus : Roo.emptyFn,
21169 hideMode:'offsets',
21172 tbContainer : false,
21174 toolbarContainer :function() {
21175 return this.wrap.select('.x-html-editor-tb',true).first();
21179 * Protected method that will not generally be called directly. It
21180 * is called when the editor creates its toolbar. Override this method if you need to
21181 * add custom toolbar buttons.
21182 * @param {HtmlEditor} editor
21184 createToolbar : function(){
21186 Roo.log("create toolbars");
21188 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21189 this.toolbars[0].render(this.toolbarContainer());
21193 // if (!editor.toolbars || !editor.toolbars.length) {
21194 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21197 // for (var i =0 ; i < editor.toolbars.length;i++) {
21198 // editor.toolbars[i] = Roo.factory(
21199 // typeof(editor.toolbars[i]) == 'string' ?
21200 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
21201 // Roo.bootstrap.HtmlEditor);
21202 // editor.toolbars[i].init(editor);
21208 onRender : function(ct, position)
21210 // Roo.log("Call onRender: " + this.xtype);
21212 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21214 this.wrap = this.inputEl().wrap({
21215 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21218 this.editorcore.onRender(ct, position);
21220 if (this.resizable) {
21221 this.resizeEl = new Roo.Resizable(this.wrap, {
21225 minHeight : this.height,
21226 height: this.height,
21227 handles : this.resizable,
21230 resize : function(r, w, h) {
21231 _t.onResize(w,h); // -something
21237 this.createToolbar(this);
21240 if(!this.width && this.resizable){
21241 this.setSize(this.wrap.getSize());
21243 if (this.resizeEl) {
21244 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21245 // should trigger onReize..
21251 onResize : function(w, h)
21253 Roo.log('resize: ' +w + ',' + h );
21254 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21258 if(this.inputEl() ){
21259 if(typeof w == 'number'){
21260 var aw = w - this.wrap.getFrameWidth('lr');
21261 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21264 if(typeof h == 'number'){
21265 var tbh = -11; // fixme it needs to tool bar size!
21266 for (var i =0; i < this.toolbars.length;i++) {
21267 // fixme - ask toolbars for heights?
21268 tbh += this.toolbars[i].el.getHeight();
21269 //if (this.toolbars[i].footer) {
21270 // tbh += this.toolbars[i].footer.el.getHeight();
21278 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21279 ah -= 5; // knock a few pixes off for look..
21280 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21284 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21285 this.editorcore.onResize(ew,eh);
21290 * Toggles the editor between standard and source edit mode.
21291 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21293 toggleSourceEdit : function(sourceEditMode)
21295 this.editorcore.toggleSourceEdit(sourceEditMode);
21297 if(this.editorcore.sourceEditMode){
21298 Roo.log('editor - showing textarea');
21301 // Roo.log(this.syncValue());
21303 this.inputEl().removeClass(['hide', 'x-hidden']);
21304 this.inputEl().dom.removeAttribute('tabIndex');
21305 this.inputEl().focus();
21307 Roo.log('editor - hiding textarea');
21309 // Roo.log(this.pushValue());
21312 this.inputEl().addClass(['hide', 'x-hidden']);
21313 this.inputEl().dom.setAttribute('tabIndex', -1);
21314 //this.deferFocus();
21317 if(this.resizable){
21318 this.setSize(this.wrap.getSize());
21321 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21324 // private (for BoxComponent)
21325 adjustSize : Roo.BoxComponent.prototype.adjustSize,
21327 // private (for BoxComponent)
21328 getResizeEl : function(){
21332 // private (for BoxComponent)
21333 getPositionEl : function(){
21338 initEvents : function(){
21339 this.originalValue = this.getValue();
21343 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21346 // markInvalid : Roo.emptyFn,
21348 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21351 // clearInvalid : Roo.emptyFn,
21353 setValue : function(v){
21354 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21355 this.editorcore.pushValue();
21360 deferFocus : function(){
21361 this.focus.defer(10, this);
21365 focus : function(){
21366 this.editorcore.focus();
21372 onDestroy : function(){
21378 for (var i =0; i < this.toolbars.length;i++) {
21379 // fixme - ask toolbars for heights?
21380 this.toolbars[i].onDestroy();
21383 this.wrap.dom.innerHTML = '';
21384 this.wrap.remove();
21389 onFirstFocus : function(){
21390 //Roo.log("onFirstFocus");
21391 this.editorcore.onFirstFocus();
21392 for (var i =0; i < this.toolbars.length;i++) {
21393 this.toolbars[i].onFirstFocus();
21399 syncValue : function()
21401 this.editorcore.syncValue();
21404 pushValue : function()
21406 this.editorcore.pushValue();
21410 // hide stuff that is not compatible
21424 * @event specialkey
21428 * @cfg {String} fieldClass @hide
21431 * @cfg {String} focusClass @hide
21434 * @cfg {String} autoCreate @hide
21437 * @cfg {String} inputType @hide
21440 * @cfg {String} invalidClass @hide
21443 * @cfg {String} invalidText @hide
21446 * @cfg {String} msgFx @hide
21449 * @cfg {String} validateOnBlur @hide
21458 Roo.namespace('Roo.bootstrap.htmleditor');
21460 * @class Roo.bootstrap.HtmlEditorToolbar1
21465 new Roo.bootstrap.HtmlEditor({
21468 new Roo.bootstrap.HtmlEditorToolbar1({
21469 disable : { fonts: 1 , format: 1, ..., ... , ...],
21475 * @cfg {Object} disable List of elements to disable..
21476 * @cfg {Array} btns List of additional buttons.
21480 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21483 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21486 Roo.apply(this, config);
21488 // default disabled, based on 'good practice'..
21489 this.disable = this.disable || {};
21490 Roo.applyIf(this.disable, {
21493 specialElements : true
21495 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21497 this.editor = config.editor;
21498 this.editorcore = config.editor.editorcore;
21500 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21502 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21503 // dont call parent... till later.
21505 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
21510 editorcore : false,
21515 "h1","h2","h3","h4","h5","h6",
21517 "abbr", "acronym", "address", "cite", "samp", "var",
21521 onRender : function(ct, position)
21523 // Roo.log("Call onRender: " + this.xtype);
21525 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21527 this.el.dom.style.marginBottom = '0';
21529 var editorcore = this.editorcore;
21530 var editor= this.editor;
21533 var btn = function(id,cmd , toggle, handler){
21535 var event = toggle ? 'toggle' : 'click';
21540 xns: Roo.bootstrap,
21543 enableToggle:toggle !== false,
21545 pressed : toggle ? false : null,
21548 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21549 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21558 xns: Roo.bootstrap,
21559 glyphicon : 'font',
21563 xns: Roo.bootstrap,
21567 Roo.each(this.formats, function(f) {
21568 style.menu.items.push({
21570 xns: Roo.bootstrap,
21571 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21576 editorcore.insertTag(this.tagname);
21583 children.push(style);
21586 btn('bold',false,true);
21587 btn('italic',false,true);
21588 btn('align-left', 'justifyleft',true);
21589 btn('align-center', 'justifycenter',true);
21590 btn('align-right' , 'justifyright',true);
21591 btn('link', false, false, function(btn) {
21592 //Roo.log("create link?");
21593 var url = prompt(this.createLinkText, this.defaultLinkValue);
21594 if(url && url != 'http:/'+'/'){
21595 this.editorcore.relayCmd('createlink', url);
21598 btn('list','insertunorderedlist',true);
21599 btn('pencil', false,true, function(btn){
21602 this.toggleSourceEdit(btn.pressed);
21608 xns: Roo.bootstrap,
21613 xns: Roo.bootstrap,
21618 cog.menu.items.push({
21620 xns: Roo.bootstrap,
21621 html : Clean styles,
21626 editorcore.insertTag(this.tagname);
21635 this.xtype = 'NavSimplebar';
21637 for(var i=0;i< children.length;i++) {
21639 this.buttons.add(this.addxtypeChild(children[i]));
21643 editor.on('editorevent', this.updateToolbar, this);
21645 onBtnClick : function(id)
21647 this.editorcore.relayCmd(id);
21648 this.editorcore.focus();
21652 * Protected method that will not generally be called directly. It triggers
21653 * a toolbar update by reading the markup state of the current selection in the editor.
21655 updateToolbar: function(){
21657 if(!this.editorcore.activated){
21658 this.editor.onFirstFocus(); // is this neeed?
21662 var btns = this.buttons;
21663 var doc = this.editorcore.doc;
21664 btns.get('bold').setActive(doc.queryCommandState('bold'));
21665 btns.get('italic').setActive(doc.queryCommandState('italic'));
21666 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21668 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21669 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21670 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21672 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21673 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21676 var ans = this.editorcore.getAllAncestors();
21677 if (this.formatCombo) {
21680 var store = this.formatCombo.store;
21681 this.formatCombo.setValue("");
21682 for (var i =0; i < ans.length;i++) {
21683 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21685 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21693 // hides menus... - so this cant be on a menu...
21694 Roo.bootstrap.MenuMgr.hideAll();
21696 Roo.bootstrap.MenuMgr.hideAll();
21697 //this.editorsyncValue();
21699 onFirstFocus: function() {
21700 this.buttons.each(function(item){
21704 toggleSourceEdit : function(sourceEditMode){
21707 if(sourceEditMode){
21708 Roo.log("disabling buttons");
21709 this.buttons.each( function(item){
21710 if(item.cmd != 'pencil'){
21716 Roo.log("enabling buttons");
21717 if(this.editorcore.initialized){
21718 this.buttons.each( function(item){
21724 Roo.log("calling toggole on editor");
21725 // tell the editor that it's been pressed..
21726 this.editor.toggleSourceEdit(sourceEditMode);
21736 * @class Roo.bootstrap.Table.AbstractSelectionModel
21737 * @extends Roo.util.Observable
21738 * Abstract base class for grid SelectionModels. It provides the interface that should be
21739 * implemented by descendant classes. This class should not be directly instantiated.
21742 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21743 this.locked = false;
21744 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21748 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21749 /** @ignore Called by the grid automatically. Do not call directly. */
21750 init : function(grid){
21756 * Locks the selections.
21759 this.locked = true;
21763 * Unlocks the selections.
21765 unlock : function(){
21766 this.locked = false;
21770 * Returns true if the selections are locked.
21771 * @return {Boolean}
21773 isLocked : function(){
21774 return this.locked;
21778 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21779 * @class Roo.bootstrap.Table.RowSelectionModel
21780 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21781 * It supports multiple selections and keyboard selection/navigation.
21783 * @param {Object} config
21786 Roo.bootstrap.Table.RowSelectionModel = function(config){
21787 Roo.apply(this, config);
21788 this.selections = new Roo.util.MixedCollection(false, function(o){
21793 this.lastActive = false;
21797 * @event selectionchange
21798 * Fires when the selection changes
21799 * @param {SelectionModel} this
21801 "selectionchange" : true,
21803 * @event afterselectionchange
21804 * Fires after the selection changes (eg. by key press or clicking)
21805 * @param {SelectionModel} this
21807 "afterselectionchange" : true,
21809 * @event beforerowselect
21810 * Fires when a row is selected being selected, return false to cancel.
21811 * @param {SelectionModel} this
21812 * @param {Number} rowIndex The selected index
21813 * @param {Boolean} keepExisting False if other selections will be cleared
21815 "beforerowselect" : true,
21818 * Fires when a row is selected.
21819 * @param {SelectionModel} this
21820 * @param {Number} rowIndex The selected index
21821 * @param {Roo.data.Record} r The record
21823 "rowselect" : true,
21825 * @event rowdeselect
21826 * Fires when a row is deselected.
21827 * @param {SelectionModel} this
21828 * @param {Number} rowIndex The selected index
21830 "rowdeselect" : true
21832 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21833 this.locked = false;
21836 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21838 * @cfg {Boolean} singleSelect
21839 * True to allow selection of only one row at a time (defaults to false)
21841 singleSelect : false,
21844 initEvents : function(){
21846 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21847 this.grid.on("mousedown", this.handleMouseDown, this);
21848 }else{ // allow click to work like normal
21849 this.grid.on("rowclick", this.handleDragableRowClick, this);
21852 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21853 "up" : function(e){
21855 this.selectPrevious(e.shiftKey);
21856 }else if(this.last !== false && this.lastActive !== false){
21857 var last = this.last;
21858 this.selectRange(this.last, this.lastActive-1);
21859 this.grid.getView().focusRow(this.lastActive);
21860 if(last !== false){
21864 this.selectFirstRow();
21866 this.fireEvent("afterselectionchange", this);
21868 "down" : function(e){
21870 this.selectNext(e.shiftKey);
21871 }else if(this.last !== false && this.lastActive !== false){
21872 var last = this.last;
21873 this.selectRange(this.last, this.lastActive+1);
21874 this.grid.getView().focusRow(this.lastActive);
21875 if(last !== false){
21879 this.selectFirstRow();
21881 this.fireEvent("afterselectionchange", this);
21886 var view = this.grid.view;
21887 view.on("refresh", this.onRefresh, this);
21888 view.on("rowupdated", this.onRowUpdated, this);
21889 view.on("rowremoved", this.onRemove, this);
21893 onRefresh : function(){
21894 var ds = this.grid.dataSource, i, v = this.grid.view;
21895 var s = this.selections;
21896 s.each(function(r){
21897 if((i = ds.indexOfId(r.id)) != -1){
21906 onRemove : function(v, index, r){
21907 this.selections.remove(r);
21911 onRowUpdated : function(v, index, r){
21912 if(this.isSelected(r)){
21913 v.onRowSelect(index);
21919 * @param {Array} records The records to select
21920 * @param {Boolean} keepExisting (optional) True to keep existing selections
21922 selectRecords : function(records, keepExisting){
21924 this.clearSelections();
21926 var ds = this.grid.dataSource;
21927 for(var i = 0, len = records.length; i < len; i++){
21928 this.selectRow(ds.indexOf(records[i]), true);
21933 * Gets the number of selected rows.
21936 getCount : function(){
21937 return this.selections.length;
21941 * Selects the first row in the grid.
21943 selectFirstRow : function(){
21948 * Select the last row.
21949 * @param {Boolean} keepExisting (optional) True to keep existing selections
21951 selectLastRow : function(keepExisting){
21952 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21956 * Selects the row immediately following the last selected row.
21957 * @param {Boolean} keepExisting (optional) True to keep existing selections
21959 selectNext : function(keepExisting){
21960 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21961 this.selectRow(this.last+1, keepExisting);
21962 this.grid.getView().focusRow(this.last);
21967 * Selects the row that precedes the last selected row.
21968 * @param {Boolean} keepExisting (optional) True to keep existing selections
21970 selectPrevious : function(keepExisting){
21972 this.selectRow(this.last-1, keepExisting);
21973 this.grid.getView().focusRow(this.last);
21978 * Returns the selected records
21979 * @return {Array} Array of selected records
21981 getSelections : function(){
21982 return [].concat(this.selections.items);
21986 * Returns the first selected record.
21989 getSelected : function(){
21990 return this.selections.itemAt(0);
21995 * Clears all selections.
21997 clearSelections : function(fast){
22002 var ds = this.grid.dataSource;
22003 var s = this.selections;
22004 s.each(function(r){
22005 this.deselectRow(ds.indexOfId(r.id));
22009 this.selections.clear();
22016 * Selects all rows.
22018 selectAll : function(){
22022 this.selections.clear();
22023 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
22024 this.selectRow(i, true);
22029 * Returns True if there is a selection.
22030 * @return {Boolean}
22032 hasSelection : function(){
22033 return this.selections.length > 0;
22037 * Returns True if the specified row is selected.
22038 * @param {Number/Record} record The record or index of the record to check
22039 * @return {Boolean}
22041 isSelected : function(index){
22042 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
22043 return (r && this.selections.key(r.id) ? true : false);
22047 * Returns True if the specified record id is selected.
22048 * @param {String} id The id of record to check
22049 * @return {Boolean}
22051 isIdSelected : function(id){
22052 return (this.selections.key(id) ? true : false);
22056 handleMouseDown : function(e, t){
22057 var view = this.grid.getView(), rowIndex;
22058 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
22061 if(e.shiftKey && this.last !== false){
22062 var last = this.last;
22063 this.selectRange(last, rowIndex, e.ctrlKey);
22064 this.last = last; // reset the last
22065 view.focusRow(rowIndex);
22067 var isSelected = this.isSelected(rowIndex);
22068 if(e.button !== 0 && isSelected){
22069 view.focusRow(rowIndex);
22070 }else if(e.ctrlKey && isSelected){
22071 this.deselectRow(rowIndex);
22072 }else if(!isSelected){
22073 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22074 view.focusRow(rowIndex);
22077 this.fireEvent("afterselectionchange", this);
22080 handleDragableRowClick : function(grid, rowIndex, e)
22082 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22083 this.selectRow(rowIndex, false);
22084 grid.view.focusRow(rowIndex);
22085 this.fireEvent("afterselectionchange", this);
22090 * Selects multiple rows.
22091 * @param {Array} rows Array of the indexes of the row to select
22092 * @param {Boolean} keepExisting (optional) True to keep existing selections
22094 selectRows : function(rows, keepExisting){
22096 this.clearSelections();
22098 for(var i = 0, len = rows.length; i < len; i++){
22099 this.selectRow(rows[i], true);
22104 * Selects a range of rows. All rows in between startRow and endRow are also selected.
22105 * @param {Number} startRow The index of the first row in the range
22106 * @param {Number} endRow The index of the last row in the range
22107 * @param {Boolean} keepExisting (optional) True to retain existing selections
22109 selectRange : function(startRow, endRow, keepExisting){
22114 this.clearSelections();
22116 if(startRow <= endRow){
22117 for(var i = startRow; i <= endRow; i++){
22118 this.selectRow(i, true);
22121 for(var i = startRow; i >= endRow; i--){
22122 this.selectRow(i, true);
22128 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22129 * @param {Number} startRow The index of the first row in the range
22130 * @param {Number} endRow The index of the last row in the range
22132 deselectRange : function(startRow, endRow, preventViewNotify){
22136 for(var i = startRow; i <= endRow; i++){
22137 this.deselectRow(i, preventViewNotify);
22143 * @param {Number} row The index of the row to select
22144 * @param {Boolean} keepExisting (optional) True to keep existing selections
22146 selectRow : function(index, keepExisting, preventViewNotify){
22147 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22150 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22151 if(!keepExisting || this.singleSelect){
22152 this.clearSelections();
22154 var r = this.grid.dataSource.getAt(index);
22155 this.selections.add(r);
22156 this.last = this.lastActive = index;
22157 if(!preventViewNotify){
22158 this.grid.getView().onRowSelect(index);
22160 this.fireEvent("rowselect", this, index, r);
22161 this.fireEvent("selectionchange", this);
22167 * @param {Number} row The index of the row to deselect
22169 deselectRow : function(index, preventViewNotify){
22173 if(this.last == index){
22176 if(this.lastActive == index){
22177 this.lastActive = false;
22179 var r = this.grid.dataSource.getAt(index);
22180 this.selections.remove(r);
22181 if(!preventViewNotify){
22182 this.grid.getView().onRowDeselect(index);
22184 this.fireEvent("rowdeselect", this, index);
22185 this.fireEvent("selectionchange", this);
22189 restoreLast : function(){
22191 this.last = this._last;
22196 acceptsNav : function(row, col, cm){
22197 return !cm.isHidden(col) && cm.isCellEditable(col, row);
22201 onEditorKey : function(field, e){
22202 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22207 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22209 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22211 }else if(k == e.ENTER && !e.ctrlKey){
22215 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22217 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22219 }else if(k == e.ESC){
22223 g.startEditing(newCell[0], newCell[1]);
22228 * Ext JS Library 1.1.1
22229 * Copyright(c) 2006-2007, Ext JS, LLC.
22231 * Originally Released Under LGPL - original licence link has changed is not relivant.
22234 * <script type="text/javascript">
22238 * @class Roo.bootstrap.PagingToolbar
22239 * @extends Roo.bootstrap.NavSimplebar
22240 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22242 * Create a new PagingToolbar
22243 * @param {Object} config The config object
22244 * @param {Roo.data.Store} store
22246 Roo.bootstrap.PagingToolbar = function(config)
22248 // old args format still supported... - xtype is prefered..
22249 // created from xtype...
22251 this.ds = config.dataSource;
22253 if (config.store && !this.ds) {
22254 this.store= Roo.factory(config.store, Roo.data);
22255 this.ds = this.store;
22256 this.ds.xmodule = this.xmodule || false;
22259 this.toolbarItems = [];
22260 if (config.items) {
22261 this.toolbarItems = config.items;
22264 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22269 this.bind(this.ds);
22272 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22276 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22278 * @cfg {Roo.data.Store} dataSource
22279 * The underlying data store providing the paged data
22282 * @cfg {String/HTMLElement/Element} container
22283 * container The id or element that will contain the toolbar
22286 * @cfg {Boolean} displayInfo
22287 * True to display the displayMsg (defaults to false)
22290 * @cfg {Number} pageSize
22291 * The number of records to display per page (defaults to 20)
22295 * @cfg {String} displayMsg
22296 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22298 displayMsg : 'Displaying {0} - {1} of {2}',
22300 * @cfg {String} emptyMsg
22301 * The message to display when no records are found (defaults to "No data to display")
22303 emptyMsg : 'No data to display',
22305 * Customizable piece of the default paging text (defaults to "Page")
22308 beforePageText : "Page",
22310 * Customizable piece of the default paging text (defaults to "of %0")
22313 afterPageText : "of {0}",
22315 * Customizable piece of the default paging text (defaults to "First Page")
22318 firstText : "First Page",
22320 * Customizable piece of the default paging text (defaults to "Previous Page")
22323 prevText : "Previous Page",
22325 * Customizable piece of the default paging text (defaults to "Next Page")
22328 nextText : "Next Page",
22330 * Customizable piece of the default paging text (defaults to "Last Page")
22333 lastText : "Last Page",
22335 * Customizable piece of the default paging text (defaults to "Refresh")
22338 refreshText : "Refresh",
22342 onRender : function(ct, position)
22344 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22345 this.navgroup.parentId = this.id;
22346 this.navgroup.onRender(this.el, null);
22347 // add the buttons to the navgroup
22349 if(this.displayInfo){
22350 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22351 this.displayEl = this.el.select('.x-paging-info', true).first();
22352 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22353 // this.displayEl = navel.el.select('span',true).first();
22359 Roo.each(_this.buttons, function(e){ // this might need to use render????
22360 Roo.factory(e).onRender(_this.el, null);
22364 Roo.each(_this.toolbarItems, function(e) {
22365 _this.navgroup.addItem(e);
22369 this.first = this.navgroup.addItem({
22370 tooltip: this.firstText,
22372 icon : 'fa fa-backward',
22374 preventDefault: true,
22375 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22378 this.prev = this.navgroup.addItem({
22379 tooltip: this.prevText,
22381 icon : 'fa fa-step-backward',
22383 preventDefault: true,
22384 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
22386 //this.addSeparator();
22389 var field = this.navgroup.addItem( {
22391 cls : 'x-paging-position',
22393 html : this.beforePageText +
22394 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22395 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
22398 this.field = field.el.select('input', true).first();
22399 this.field.on("keydown", this.onPagingKeydown, this);
22400 this.field.on("focus", function(){this.dom.select();});
22403 this.afterTextEl = field.el.select('.x-paging-after',true).first();
22404 //this.field.setHeight(18);
22405 //this.addSeparator();
22406 this.next = this.navgroup.addItem({
22407 tooltip: this.nextText,
22409 html : ' <i class="fa fa-step-forward">',
22411 preventDefault: true,
22412 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
22414 this.last = this.navgroup.addItem({
22415 tooltip: this.lastText,
22416 icon : 'fa fa-forward',
22419 preventDefault: true,
22420 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
22422 //this.addSeparator();
22423 this.loading = this.navgroup.addItem({
22424 tooltip: this.refreshText,
22425 icon: 'fa fa-refresh',
22426 preventDefault: true,
22427 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22433 updateInfo : function(){
22434 if(this.displayEl){
22435 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22436 var msg = count == 0 ?
22440 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
22442 this.displayEl.update(msg);
22447 onLoad : function(ds, r, o){
22448 this.cursor = o.params ? o.params.start : 0;
22449 var d = this.getPageData(),
22453 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22454 this.field.dom.value = ap;
22455 this.first.setDisabled(ap == 1);
22456 this.prev.setDisabled(ap == 1);
22457 this.next.setDisabled(ap == ps);
22458 this.last.setDisabled(ap == ps);
22459 this.loading.enable();
22464 getPageData : function(){
22465 var total = this.ds.getTotalCount();
22468 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22469 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22474 onLoadError : function(){
22475 this.loading.enable();
22479 onPagingKeydown : function(e){
22480 var k = e.getKey();
22481 var d = this.getPageData();
22483 var v = this.field.dom.value, pageNum;
22484 if(!v || isNaN(pageNum = parseInt(v, 10))){
22485 this.field.dom.value = d.activePage;
22488 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22489 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22492 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))
22494 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22495 this.field.dom.value = pageNum;
22496 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22499 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22501 var v = this.field.dom.value, pageNum;
22502 var increment = (e.shiftKey) ? 10 : 1;
22503 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22506 if(!v || isNaN(pageNum = parseInt(v, 10))) {
22507 this.field.dom.value = d.activePage;
22510 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22512 this.field.dom.value = parseInt(v, 10) + increment;
22513 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22514 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22521 beforeLoad : function(){
22523 this.loading.disable();
22528 onClick : function(which){
22537 ds.load({params:{start: 0, limit: this.pageSize}});
22540 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22543 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22546 var total = ds.getTotalCount();
22547 var extra = total % this.pageSize;
22548 var lastStart = extra ? (total - extra) : total-this.pageSize;
22549 ds.load({params:{start: lastStart, limit: this.pageSize}});
22552 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22558 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22559 * @param {Roo.data.Store} store The data store to unbind
22561 unbind : function(ds){
22562 ds.un("beforeload", this.beforeLoad, this);
22563 ds.un("load", this.onLoad, this);
22564 ds.un("loadexception", this.onLoadError, this);
22565 ds.un("remove", this.updateInfo, this);
22566 ds.un("add", this.updateInfo, this);
22567 this.ds = undefined;
22571 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22572 * @param {Roo.data.Store} store The data store to bind
22574 bind : function(ds){
22575 ds.on("beforeload", this.beforeLoad, this);
22576 ds.on("load", this.onLoad, this);
22577 ds.on("loadexception", this.onLoadError, this);
22578 ds.on("remove", this.updateInfo, this);
22579 ds.on("add", this.updateInfo, this);
22590 * @class Roo.bootstrap.MessageBar
22591 * @extends Roo.bootstrap.Component
22592 * Bootstrap MessageBar class
22593 * @cfg {String} html contents of the MessageBar
22594 * @cfg {String} weight (info | success | warning | danger) default info
22595 * @cfg {String} beforeClass insert the bar before the given class
22596 * @cfg {Boolean} closable (true | false) default false
22597 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22600 * Create a new Element
22601 * @param {Object} config The config object
22604 Roo.bootstrap.MessageBar = function(config){
22605 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22608 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22614 beforeClass: 'bootstrap-sticky-wrap',
22616 getAutoCreate : function(){
22620 cls: 'alert alert-dismissable alert-' + this.weight,
22625 html: this.html || ''
22631 cfg.cls += ' alert-messages-fixed';
22645 onRender : function(ct, position)
22647 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22650 var cfg = Roo.apply({}, this.getAutoCreate());
22654 cfg.cls += ' ' + this.cls;
22657 cfg.style = this.style;
22659 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22661 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22664 this.el.select('>button.close').on('click', this.hide, this);
22670 if (!this.rendered) {
22676 this.fireEvent('show', this);
22682 if (!this.rendered) {
22688 this.fireEvent('hide', this);
22691 update : function()
22693 // var e = this.el.dom.firstChild;
22695 // if(this.closable){
22696 // e = e.nextSibling;
22699 // e.data = this.html || '';
22701 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22717 * @class Roo.bootstrap.Graph
22718 * @extends Roo.bootstrap.Component
22719 * Bootstrap Graph class
22723 @cfg {String} graphtype bar | vbar | pie
22724 @cfg {number} g_x coodinator | centre x (pie)
22725 @cfg {number} g_y coodinator | centre y (pie)
22726 @cfg {number} g_r radius (pie)
22727 @cfg {number} g_height height of the chart (respected by all elements in the set)
22728 @cfg {number} g_width width of the chart (respected by all elements in the set)
22729 @cfg {Object} title The title of the chart
22732 -opts (object) options for the chart
22734 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22735 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22737 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.
22738 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22740 o stretch (boolean)
22742 -opts (object) options for the pie
22745 o startAngle (number)
22746 o endAngle (number)
22750 * Create a new Input
22751 * @param {Object} config The config object
22754 Roo.bootstrap.Graph = function(config){
22755 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22761 * The img click event for the img.
22762 * @param {Roo.EventObject} e
22768 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22779 //g_colors: this.colors,
22786 getAutoCreate : function(){
22797 onRender : function(ct,position){
22800 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22802 if (typeof(Raphael) == 'undefined') {
22803 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
22807 this.raphael = Raphael(this.el.dom);
22809 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22810 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22811 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22812 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22814 r.text(160, 10, "Single Series Chart").attr(txtattr);
22815 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22816 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22817 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22819 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22820 r.barchart(330, 10, 300, 220, data1);
22821 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22822 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22825 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22826 // r.barchart(30, 30, 560, 250, xdata, {
22827 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22828 // axis : "0 0 1 1",
22829 // axisxlabels : xdata
22830 // //yvalues : cols,
22833 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22835 // this.load(null,xdata,{
22836 // axis : "0 0 1 1",
22837 // axisxlabels : xdata
22842 load : function(graphtype,xdata,opts)
22844 this.raphael.clear();
22846 graphtype = this.graphtype;
22851 var r = this.raphael,
22852 fin = function () {
22853 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22855 fout = function () {
22856 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22858 pfin = function() {
22859 this.sector.stop();
22860 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22863 this.label[0].stop();
22864 this.label[0].attr({ r: 7.5 });
22865 this.label[1].attr({ "font-weight": 800 });
22868 pfout = function() {
22869 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22872 this.label[0].animate({ r: 5 }, 500, "bounce");
22873 this.label[1].attr({ "font-weight": 400 });
22879 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22882 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22885 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
22886 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22888 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22895 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22900 setTitle: function(o)
22905 initEvents: function() {
22908 this.el.on('click', this.onClick, this);
22912 onClick : function(e)
22914 Roo.log('img onclick');
22915 this.fireEvent('click', this, e);
22927 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22930 * @class Roo.bootstrap.dash.NumberBox
22931 * @extends Roo.bootstrap.Component
22932 * Bootstrap NumberBox class
22933 * @cfg {String} headline Box headline
22934 * @cfg {String} content Box content
22935 * @cfg {String} icon Box icon
22936 * @cfg {String} footer Footer text
22937 * @cfg {String} fhref Footer href
22940 * Create a new NumberBox
22941 * @param {Object} config The config object
22945 Roo.bootstrap.dash.NumberBox = function(config){
22946 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22950 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
22959 getAutoCreate : function(){
22963 cls : 'small-box ',
22971 cls : 'roo-headline',
22972 html : this.headline
22976 cls : 'roo-content',
22977 html : this.content
22991 cls : 'ion ' + this.icon
23000 cls : 'small-box-footer',
23001 href : this.fhref || '#',
23005 cfg.cn.push(footer);
23012 onRender : function(ct,position){
23013 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23020 setHeadline: function (value)
23022 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23025 setFooter: function (value, href)
23027 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23030 this.el.select('a.small-box-footer',true).first().attr('href', href);
23035 setContent: function (value)
23037 this.el.select('.roo-content',true).first().dom.innerHTML = value;
23040 initEvents: function()
23054 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23057 * @class Roo.bootstrap.dash.TabBox
23058 * @extends Roo.bootstrap.Component
23059 * Bootstrap TabBox class
23060 * @cfg {String} title Title of the TabBox
23061 * @cfg {String} icon Icon of the TabBox
23062 * @cfg {Boolean} showtabs (true|false) show the tabs default true
23063 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23066 * Create a new TabBox
23067 * @param {Object} config The config object
23071 Roo.bootstrap.dash.TabBox = function(config){
23072 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23077 * When a pane is added
23078 * @param {Roo.bootstrap.dash.TabPane} pane
23082 * @event activatepane
23083 * When a pane is activated
23084 * @param {Roo.bootstrap.dash.TabPane} pane
23086 "activatepane" : true
23094 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
23099 tabScrollable : false,
23101 getChildContainer : function()
23103 return this.el.select('.tab-content', true).first();
23106 getAutoCreate : function(){
23110 cls: 'pull-left header',
23118 cls: 'fa ' + this.icon
23124 cls: 'nav nav-tabs pull-right',
23130 if(this.tabScrollable){
23137 cls: 'nav nav-tabs pull-right',
23148 cls: 'nav-tabs-custom',
23153 cls: 'tab-content no-padding',
23161 initEvents : function()
23163 //Roo.log('add add pane handler');
23164 this.on('addpane', this.onAddPane, this);
23167 * Updates the box title
23168 * @param {String} html to set the title to.
23170 setTitle : function(value)
23172 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23174 onAddPane : function(pane)
23176 this.panes.push(pane);
23177 //Roo.log('addpane');
23179 // tabs are rendere left to right..
23180 if(!this.showtabs){
23184 var ctr = this.el.select('.nav-tabs', true).first();
23187 var existing = ctr.select('.nav-tab',true);
23188 var qty = existing.getCount();;
23191 var tab = ctr.createChild({
23193 cls : 'nav-tab' + (qty ? '' : ' active'),
23201 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23204 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23206 pane.el.addClass('active');
23211 onTabClick : function(ev,un,ob,pane)
23213 //Roo.log('tab - prev default');
23214 ev.preventDefault();
23217 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23218 pane.tab.addClass('active');
23219 //Roo.log(pane.title);
23220 this.getChildContainer().select('.tab-pane',true).removeClass('active');
23221 // technically we should have a deactivate event.. but maybe add later.
23222 // and it should not de-activate the selected tab...
23223 this.fireEvent('activatepane', pane);
23224 pane.el.addClass('active');
23225 pane.fireEvent('activate');
23230 getActivePane : function()
23233 Roo.each(this.panes, function(p) {
23234 if(p.el.hasClass('active')){
23255 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23257 * @class Roo.bootstrap.TabPane
23258 * @extends Roo.bootstrap.Component
23259 * Bootstrap TabPane class
23260 * @cfg {Boolean} active (false | true) Default false
23261 * @cfg {String} title title of panel
23265 * Create a new TabPane
23266 * @param {Object} config The config object
23269 Roo.bootstrap.dash.TabPane = function(config){
23270 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23276 * When a pane is activated
23277 * @param {Roo.bootstrap.dash.TabPane} pane
23284 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
23289 // the tabBox that this is attached to.
23292 getAutoCreate : function()
23300 cfg.cls += ' active';
23305 initEvents : function()
23307 //Roo.log('trigger add pane handler');
23308 this.parent().fireEvent('addpane', this)
23312 * Updates the tab title
23313 * @param {String} html to set the title to.
23315 setTitle: function(str)
23321 this.tab.select('a', true).first().dom.innerHTML = str;
23338 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23341 * @class Roo.bootstrap.menu.Menu
23342 * @extends Roo.bootstrap.Component
23343 * Bootstrap Menu class - container for Menu
23344 * @cfg {String} html Text of the menu
23345 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23346 * @cfg {String} icon Font awesome icon
23347 * @cfg {String} pos Menu align to (top | bottom) default bottom
23351 * Create a new Menu
23352 * @param {Object} config The config object
23356 Roo.bootstrap.menu.Menu = function(config){
23357 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23361 * @event beforeshow
23362 * Fires before this menu is displayed
23363 * @param {Roo.bootstrap.menu.Menu} this
23367 * @event beforehide
23368 * Fires before this menu is hidden
23369 * @param {Roo.bootstrap.menu.Menu} this
23374 * Fires after this menu is displayed
23375 * @param {Roo.bootstrap.menu.Menu} this
23380 * Fires after this menu is hidden
23381 * @param {Roo.bootstrap.menu.Menu} this
23386 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23387 * @param {Roo.bootstrap.menu.Menu} this
23388 * @param {Roo.EventObject} e
23395 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
23399 weight : 'default',
23404 getChildContainer : function() {
23405 if(this.isSubMenu){
23409 return this.el.select('ul.dropdown-menu', true).first();
23412 getAutoCreate : function()
23417 cls : 'roo-menu-text',
23425 cls : 'fa ' + this.icon
23436 cls : 'dropdown-button btn btn-' + this.weight,
23441 cls : 'dropdown-toggle btn btn-' + this.weight,
23451 cls : 'dropdown-menu'
23457 if(this.pos == 'top'){
23458 cfg.cls += ' dropup';
23461 if(this.isSubMenu){
23464 cls : 'dropdown-menu'
23471 onRender : function(ct, position)
23473 this.isSubMenu = ct.hasClass('dropdown-submenu');
23475 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23478 initEvents : function()
23480 if(this.isSubMenu){
23484 this.hidden = true;
23486 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23487 this.triggerEl.on('click', this.onTriggerPress, this);
23489 this.buttonEl = this.el.select('button.dropdown-button', true).first();
23490 this.buttonEl.on('click', this.onClick, this);
23496 if(this.isSubMenu){
23500 return this.el.select('ul.dropdown-menu', true).first();
23503 onClick : function(e)
23505 this.fireEvent("click", this, e);
23508 onTriggerPress : function(e)
23510 if (this.isVisible()) {
23517 isVisible : function(){
23518 return !this.hidden;
23523 this.fireEvent("beforeshow", this);
23525 this.hidden = false;
23526 this.el.addClass('open');
23528 Roo.get(document).on("mouseup", this.onMouseUp, this);
23530 this.fireEvent("show", this);
23537 this.fireEvent("beforehide", this);
23539 this.hidden = true;
23540 this.el.removeClass('open');
23542 Roo.get(document).un("mouseup", this.onMouseUp);
23544 this.fireEvent("hide", this);
23547 onMouseUp : function()
23561 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23564 * @class Roo.bootstrap.menu.Item
23565 * @extends Roo.bootstrap.Component
23566 * Bootstrap MenuItem class
23567 * @cfg {Boolean} submenu (true | false) default false
23568 * @cfg {String} html text of the item
23569 * @cfg {String} href the link
23570 * @cfg {Boolean} disable (true | false) default false
23571 * @cfg {Boolean} preventDefault (true | false) default true
23572 * @cfg {String} icon Font awesome icon
23573 * @cfg {String} pos Submenu align to (left | right) default right
23577 * Create a new Item
23578 * @param {Object} config The config object
23582 Roo.bootstrap.menu.Item = function(config){
23583 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23587 * Fires when the mouse is hovering over this menu
23588 * @param {Roo.bootstrap.menu.Item} this
23589 * @param {Roo.EventObject} e
23594 * Fires when the mouse exits this menu
23595 * @param {Roo.bootstrap.menu.Item} this
23596 * @param {Roo.EventObject} e
23602 * The raw click event for the entire grid.
23603 * @param {Roo.EventObject} e
23609 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23614 preventDefault: true,
23619 getAutoCreate : function()
23624 cls : 'roo-menu-item-text',
23632 cls : 'fa ' + this.icon
23641 href : this.href || '#',
23648 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23652 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23654 if(this.pos == 'left'){
23655 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23662 initEvents : function()
23664 this.el.on('mouseover', this.onMouseOver, this);
23665 this.el.on('mouseout', this.onMouseOut, this);
23667 this.el.select('a', true).first().on('click', this.onClick, this);
23671 onClick : function(e)
23673 if(this.preventDefault){
23674 e.preventDefault();
23677 this.fireEvent("click", this, e);
23680 onMouseOver : function(e)
23682 if(this.submenu && this.pos == 'left'){
23683 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23686 this.fireEvent("mouseover", this, e);
23689 onMouseOut : function(e)
23691 this.fireEvent("mouseout", this, e);
23703 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23706 * @class Roo.bootstrap.menu.Separator
23707 * @extends Roo.bootstrap.Component
23708 * Bootstrap Separator class
23711 * Create a new Separator
23712 * @param {Object} config The config object
23716 Roo.bootstrap.menu.Separator = function(config){
23717 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23720 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23722 getAutoCreate : function(){
23743 * @class Roo.bootstrap.Tooltip
23744 * Bootstrap Tooltip class
23745 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23746 * to determine which dom element triggers the tooltip.
23748 * It needs to add support for additional attributes like tooltip-position
23751 * Create a new Toolti
23752 * @param {Object} config The config object
23755 Roo.bootstrap.Tooltip = function(config){
23756 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23759 Roo.apply(Roo.bootstrap.Tooltip, {
23761 * @function init initialize tooltip monitoring.
23765 currentTip : false,
23766 currentRegion : false,
23772 Roo.get(document).on('mouseover', this.enter ,this);
23773 Roo.get(document).on('mouseout', this.leave, this);
23776 this.currentTip = new Roo.bootstrap.Tooltip();
23779 enter : function(ev)
23781 var dom = ev.getTarget();
23783 //Roo.log(['enter',dom]);
23784 var el = Roo.fly(dom);
23785 if (this.currentEl) {
23787 //Roo.log(this.currentEl);
23788 //Roo.log(this.currentEl.contains(dom));
23789 if (this.currentEl == el) {
23792 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23798 if (this.currentTip.el) {
23799 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23804 // you can not look for children, as if el is the body.. then everythign is the child..
23805 if (!el.attr('tooltip')) { //
23806 if (!el.select("[tooltip]").elements.length) {
23809 // is the mouse over this child...?
23810 bindEl = el.select("[tooltip]").first();
23811 var xy = ev.getXY();
23812 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23813 //Roo.log("not in region.");
23816 //Roo.log("child element over..");
23819 this.currentEl = bindEl;
23820 this.currentTip.bind(bindEl);
23821 this.currentRegion = Roo.lib.Region.getRegion(dom);
23822 this.currentTip.enter();
23825 leave : function(ev)
23827 var dom = ev.getTarget();
23828 //Roo.log(['leave',dom]);
23829 if (!this.currentEl) {
23834 if (dom != this.currentEl.dom) {
23837 var xy = ev.getXY();
23838 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23841 // only activate leave if mouse cursor is outside... bounding box..
23846 if (this.currentTip) {
23847 this.currentTip.leave();
23849 //Roo.log('clear currentEl');
23850 this.currentEl = false;
23855 'left' : ['r-l', [-2,0], 'right'],
23856 'right' : ['l-r', [2,0], 'left'],
23857 'bottom' : ['t-b', [0,2], 'top'],
23858 'top' : [ 'b-t', [0,-2], 'bottom']
23864 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
23869 delay : null, // can be { show : 300 , hide: 500}
23873 hoverState : null, //???
23875 placement : 'bottom',
23877 getAutoCreate : function(){
23884 cls : 'tooltip-arrow'
23887 cls : 'tooltip-inner'
23894 bind : function(el)
23900 enter : function () {
23902 if (this.timeout != null) {
23903 clearTimeout(this.timeout);
23906 this.hoverState = 'in';
23907 //Roo.log("enter - show");
23908 if (!this.delay || !this.delay.show) {
23913 this.timeout = setTimeout(function () {
23914 if (_t.hoverState == 'in') {
23917 }, this.delay.show);
23921 clearTimeout(this.timeout);
23923 this.hoverState = 'out';
23924 if (!this.delay || !this.delay.hide) {
23930 this.timeout = setTimeout(function () {
23931 //Roo.log("leave - timeout");
23933 if (_t.hoverState == 'out') {
23935 Roo.bootstrap.Tooltip.currentEl = false;
23943 this.render(document.body);
23946 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23948 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23950 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23952 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23954 var placement = typeof this.placement == 'function' ?
23955 this.placement.call(this, this.el, on_el) :
23958 var autoToken = /\s?auto?\s?/i;
23959 var autoPlace = autoToken.test(placement);
23961 placement = placement.replace(autoToken, '') || 'top';
23965 //this.el.setXY([0,0]);
23967 //this.el.dom.style.display='block';
23969 //this.el.appendTo(on_el);
23971 var p = this.getPosition();
23972 var box = this.el.getBox();
23978 var align = Roo.bootstrap.Tooltip.alignment[placement];
23980 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23982 if(placement == 'top' || placement == 'bottom'){
23984 placement = 'right';
23987 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23988 placement = 'left';
23992 align = Roo.bootstrap.Tooltip.alignment[placement];
23994 this.el.alignTo(this.bindEl, align[0],align[1]);
23995 //var arrow = this.el.select('.arrow',true).first();
23996 //arrow.set(align[2],
23998 this.el.addClass(placement);
24000 this.el.addClass('in fade');
24002 this.hoverState = null;
24004 if (this.el.hasClass('fade')) {
24015 //this.el.setXY([0,0]);
24016 this.el.removeClass('in');
24032 * @class Roo.bootstrap.LocationPicker
24033 * @extends Roo.bootstrap.Component
24034 * Bootstrap LocationPicker class
24035 * @cfg {Number} latitude Position when init default 0
24036 * @cfg {Number} longitude Position when init default 0
24037 * @cfg {Number} zoom default 15
24038 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24039 * @cfg {Boolean} mapTypeControl default false
24040 * @cfg {Boolean} disableDoubleClickZoom default false
24041 * @cfg {Boolean} scrollwheel default true
24042 * @cfg {Boolean} streetViewControl default false
24043 * @cfg {Number} radius default 0
24044 * @cfg {String} locationName
24045 * @cfg {Boolean} draggable default true
24046 * @cfg {Boolean} enableAutocomplete default false
24047 * @cfg {Boolean} enableReverseGeocode default true
24048 * @cfg {String} markerTitle
24051 * Create a new LocationPicker
24052 * @param {Object} config The config object
24056 Roo.bootstrap.LocationPicker = function(config){
24058 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24063 * Fires when the picker initialized.
24064 * @param {Roo.bootstrap.LocationPicker} this
24065 * @param {Google Location} location
24069 * @event positionchanged
24070 * Fires when the picker position changed.
24071 * @param {Roo.bootstrap.LocationPicker} this
24072 * @param {Google Location} location
24074 positionchanged : true,
24077 * Fires when the map resize.
24078 * @param {Roo.bootstrap.LocationPicker} this
24083 * Fires when the map show.
24084 * @param {Roo.bootstrap.LocationPicker} this
24089 * Fires when the map hide.
24090 * @param {Roo.bootstrap.LocationPicker} this
24095 * Fires when click the map.
24096 * @param {Roo.bootstrap.LocationPicker} this
24097 * @param {Map event} e
24101 * @event mapRightClick
24102 * Fires when right click the map.
24103 * @param {Roo.bootstrap.LocationPicker} this
24104 * @param {Map event} e
24106 mapRightClick : true,
24108 * @event markerClick
24109 * Fires when click the marker.
24110 * @param {Roo.bootstrap.LocationPicker} this
24111 * @param {Map event} e
24113 markerClick : true,
24115 * @event markerRightClick
24116 * Fires when right click the marker.
24117 * @param {Roo.bootstrap.LocationPicker} this
24118 * @param {Map event} e
24120 markerRightClick : true,
24122 * @event OverlayViewDraw
24123 * Fires when OverlayView Draw
24124 * @param {Roo.bootstrap.LocationPicker} this
24126 OverlayViewDraw : true,
24128 * @event OverlayViewOnAdd
24129 * Fires when OverlayView Draw
24130 * @param {Roo.bootstrap.LocationPicker} this
24132 OverlayViewOnAdd : true,
24134 * @event OverlayViewOnRemove
24135 * Fires when OverlayView Draw
24136 * @param {Roo.bootstrap.LocationPicker} this
24138 OverlayViewOnRemove : true,
24140 * @event OverlayViewShow
24141 * Fires when OverlayView Draw
24142 * @param {Roo.bootstrap.LocationPicker} this
24143 * @param {Pixel} cpx
24145 OverlayViewShow : true,
24147 * @event OverlayViewHide
24148 * Fires when OverlayView Draw
24149 * @param {Roo.bootstrap.LocationPicker} this
24151 OverlayViewHide : true,
24153 * @event loadexception
24154 * Fires when load google lib failed.
24155 * @param {Roo.bootstrap.LocationPicker} this
24157 loadexception : true
24162 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
24164 gMapContext: false,
24170 mapTypeControl: false,
24171 disableDoubleClickZoom: false,
24173 streetViewControl: false,
24177 enableAutocomplete: false,
24178 enableReverseGeocode: true,
24181 getAutoCreate: function()
24186 cls: 'roo-location-picker'
24192 initEvents: function(ct, position)
24194 if(!this.el.getWidth() || this.isApplied()){
24198 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24203 initial: function()
24205 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24206 this.fireEvent('loadexception', this);
24210 if(!this.mapTypeId){
24211 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24214 this.gMapContext = this.GMapContext();
24216 this.initOverlayView();
24218 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24222 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24223 _this.setPosition(_this.gMapContext.marker.position);
24226 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24227 _this.fireEvent('mapClick', this, event);
24231 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24232 _this.fireEvent('mapRightClick', this, event);
24236 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24237 _this.fireEvent('markerClick', this, event);
24241 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24242 _this.fireEvent('markerRightClick', this, event);
24246 this.setPosition(this.gMapContext.location);
24248 this.fireEvent('initial', this, this.gMapContext.location);
24251 initOverlayView: function()
24255 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24259 _this.fireEvent('OverlayViewDraw', _this);
24264 _this.fireEvent('OverlayViewOnAdd', _this);
24267 onRemove: function()
24269 _this.fireEvent('OverlayViewOnRemove', _this);
24272 show: function(cpx)
24274 _this.fireEvent('OverlayViewShow', _this, cpx);
24279 _this.fireEvent('OverlayViewHide', _this);
24285 fromLatLngToContainerPixel: function(event)
24287 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24290 isApplied: function()
24292 return this.getGmapContext() == false ? false : true;
24295 getGmapContext: function()
24297 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24300 GMapContext: function()
24302 var position = new google.maps.LatLng(this.latitude, this.longitude);
24304 var _map = new google.maps.Map(this.el.dom, {
24307 mapTypeId: this.mapTypeId,
24308 mapTypeControl: this.mapTypeControl,
24309 disableDoubleClickZoom: this.disableDoubleClickZoom,
24310 scrollwheel: this.scrollwheel,
24311 streetViewControl: this.streetViewControl,
24312 locationName: this.locationName,
24313 draggable: this.draggable,
24314 enableAutocomplete: this.enableAutocomplete,
24315 enableReverseGeocode: this.enableReverseGeocode
24318 var _marker = new google.maps.Marker({
24319 position: position,
24321 title: this.markerTitle,
24322 draggable: this.draggable
24329 location: position,
24330 radius: this.radius,
24331 locationName: this.locationName,
24332 addressComponents: {
24333 formatted_address: null,
24334 addressLine1: null,
24335 addressLine2: null,
24337 streetNumber: null,
24341 stateOrProvince: null
24344 domContainer: this.el.dom,
24345 geodecoder: new google.maps.Geocoder()
24349 drawCircle: function(center, radius, options)
24351 if (this.gMapContext.circle != null) {
24352 this.gMapContext.circle.setMap(null);
24356 options = Roo.apply({}, options, {
24357 strokeColor: "#0000FF",
24358 strokeOpacity: .35,
24360 fillColor: "#0000FF",
24364 options.map = this.gMapContext.map;
24365 options.radius = radius;
24366 options.center = center;
24367 this.gMapContext.circle = new google.maps.Circle(options);
24368 return this.gMapContext.circle;
24374 setPosition: function(location)
24376 this.gMapContext.location = location;
24377 this.gMapContext.marker.setPosition(location);
24378 this.gMapContext.map.panTo(location);
24379 this.drawCircle(location, this.gMapContext.radius, {});
24383 if (this.gMapContext.settings.enableReverseGeocode) {
24384 this.gMapContext.geodecoder.geocode({
24385 latLng: this.gMapContext.location
24386 }, function(results, status) {
24388 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24389 _this.gMapContext.locationName = results[0].formatted_address;
24390 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24392 _this.fireEvent('positionchanged', this, location);
24399 this.fireEvent('positionchanged', this, location);
24404 google.maps.event.trigger(this.gMapContext.map, "resize");
24406 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24408 this.fireEvent('resize', this);
24411 setPositionByLatLng: function(latitude, longitude)
24413 this.setPosition(new google.maps.LatLng(latitude, longitude));
24416 getCurrentPosition: function()
24419 latitude: this.gMapContext.location.lat(),
24420 longitude: this.gMapContext.location.lng()
24424 getAddressName: function()
24426 return this.gMapContext.locationName;
24429 getAddressComponents: function()
24431 return this.gMapContext.addressComponents;
24434 address_component_from_google_geocode: function(address_components)
24438 for (var i = 0; i < address_components.length; i++) {
24439 var component = address_components[i];
24440 if (component.types.indexOf("postal_code") >= 0) {
24441 result.postalCode = component.short_name;
24442 } else if (component.types.indexOf("street_number") >= 0) {
24443 result.streetNumber = component.short_name;
24444 } else if (component.types.indexOf("route") >= 0) {
24445 result.streetName = component.short_name;
24446 } else if (component.types.indexOf("neighborhood") >= 0) {
24447 result.city = component.short_name;
24448 } else if (component.types.indexOf("locality") >= 0) {
24449 result.city = component.short_name;
24450 } else if (component.types.indexOf("sublocality") >= 0) {
24451 result.district = component.short_name;
24452 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24453 result.stateOrProvince = component.short_name;
24454 } else if (component.types.indexOf("country") >= 0) {
24455 result.country = component.short_name;
24459 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24460 result.addressLine2 = "";
24464 setZoomLevel: function(zoom)
24466 this.gMapContext.map.setZoom(zoom);
24479 this.fireEvent('show', this);
24490 this.fireEvent('hide', this);
24495 Roo.apply(Roo.bootstrap.LocationPicker, {
24497 OverlayView : function(map, options)
24499 options = options || {};
24513 * @class Roo.bootstrap.Alert
24514 * @extends Roo.bootstrap.Component
24515 * Bootstrap Alert class
24516 * @cfg {String} title The title of alert
24517 * @cfg {String} html The content of alert
24518 * @cfg {String} weight ( success | info | warning | danger )
24519 * @cfg {String} faicon font-awesomeicon
24522 * Create a new alert
24523 * @param {Object} config The config object
24527 Roo.bootstrap.Alert = function(config){
24528 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24532 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
24539 getAutoCreate : function()
24548 cls : 'roo-alert-icon'
24553 cls : 'roo-alert-title',
24558 cls : 'roo-alert-text',
24565 cfg.cn[0].cls += ' fa ' + this.faicon;
24569 cfg.cls += ' alert-' + this.weight;
24575 initEvents: function()
24577 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24580 setTitle : function(str)
24582 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24585 setText : function(str)
24587 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24590 setWeight : function(weight)
24593 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24596 this.weight = weight;
24598 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24601 setIcon : function(icon)
24604 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24607 this.faicon = icon;
24609 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24630 * @class Roo.bootstrap.UploadCropbox
24631 * @extends Roo.bootstrap.Component
24632 * Bootstrap UploadCropbox class
24633 * @cfg {String} emptyText show when image has been loaded
24634 * @cfg {String} rotateNotify show when image too small to rotate
24635 * @cfg {Number} errorTimeout default 3000
24636 * @cfg {Number} minWidth default 300
24637 * @cfg {Number} minHeight default 300
24638 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24639 * @cfg {Boolean} isDocument (true|false) default false
24640 * @cfg {String} url action url
24641 * @cfg {String} paramName default 'imageUpload'
24642 * @cfg {String} method default POST
24643 * @cfg {Boolean} loadMask (true|false) default true
24644 * @cfg {Boolean} loadingText default 'Loading...'
24647 * Create a new UploadCropbox
24648 * @param {Object} config The config object
24651 Roo.bootstrap.UploadCropbox = function(config){
24652 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24656 * @event beforeselectfile
24657 * Fire before select file
24658 * @param {Roo.bootstrap.UploadCropbox} this
24660 "beforeselectfile" : true,
24663 * Fire after initEvent
24664 * @param {Roo.bootstrap.UploadCropbox} this
24669 * Fire after initEvent
24670 * @param {Roo.bootstrap.UploadCropbox} this
24671 * @param {String} data
24676 * Fire when preparing the file data
24677 * @param {Roo.bootstrap.UploadCropbox} this
24678 * @param {Object} file
24683 * Fire when get exception
24684 * @param {Roo.bootstrap.UploadCropbox} this
24685 * @param {XMLHttpRequest} xhr
24687 "exception" : true,
24689 * @event beforeloadcanvas
24690 * Fire before load the canvas
24691 * @param {Roo.bootstrap.UploadCropbox} this
24692 * @param {String} src
24694 "beforeloadcanvas" : true,
24697 * Fire when trash image
24698 * @param {Roo.bootstrap.UploadCropbox} this
24703 * Fire when download the image
24704 * @param {Roo.bootstrap.UploadCropbox} this
24708 * @event footerbuttonclick
24709 * Fire when footerbuttonclick
24710 * @param {Roo.bootstrap.UploadCropbox} this
24711 * @param {String} type
24713 "footerbuttonclick" : true,
24717 * @param {Roo.bootstrap.UploadCropbox} this
24722 * Fire when rotate the image
24723 * @param {Roo.bootstrap.UploadCropbox} this
24724 * @param {String} pos
24729 * Fire when inspect the file
24730 * @param {Roo.bootstrap.UploadCropbox} this
24731 * @param {Object} file
24736 * Fire when xhr upload the file
24737 * @param {Roo.bootstrap.UploadCropbox} this
24738 * @param {Object} data
24743 * Fire when arrange the file data
24744 * @param {Roo.bootstrap.UploadCropbox} this
24745 * @param {Object} formData
24750 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24753 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24755 emptyText : 'Click to upload image',
24756 rotateNotify : 'Image is too small to rotate',
24757 errorTimeout : 3000,
24771 cropType : 'image/jpeg',
24773 canvasLoaded : false,
24774 isDocument : false,
24776 paramName : 'imageUpload',
24778 loadingText : 'Loading...',
24781 getAutoCreate : function()
24785 cls : 'roo-upload-cropbox',
24789 cls : 'roo-upload-cropbox-selector',
24794 cls : 'roo-upload-cropbox-body',
24795 style : 'cursor:pointer',
24799 cls : 'roo-upload-cropbox-preview'
24803 cls : 'roo-upload-cropbox-thumb'
24807 cls : 'roo-upload-cropbox-empty-notify',
24808 html : this.emptyText
24812 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24813 html : this.rotateNotify
24819 cls : 'roo-upload-cropbox-footer',
24822 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24832 onRender : function(ct, position)
24834 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24836 if (this.buttons.length) {
24838 Roo.each(this.buttons, function(bb) {
24840 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24842 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24848 this.maskEl = this.el;
24852 initEvents : function()
24854 this.urlAPI = (window.createObjectURL && window) ||
24855 (window.URL && URL.revokeObjectURL && URL) ||
24856 (window.webkitURL && webkitURL);
24858 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24859 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24861 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24862 this.selectorEl.hide();
24864 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24865 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24867 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24868 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24869 this.thumbEl.hide();
24871 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24872 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24874 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24875 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24876 this.errorEl.hide();
24878 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24879 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24880 this.footerEl.hide();
24882 this.setThumbBoxSize();
24888 this.fireEvent('initial', this);
24895 window.addEventListener("resize", function() { _this.resize(); } );
24897 this.bodyEl.on('click', this.beforeSelectFile, this);
24900 this.bodyEl.on('touchstart', this.onTouchStart, this);
24901 this.bodyEl.on('touchmove', this.onTouchMove, this);
24902 this.bodyEl.on('touchend', this.onTouchEnd, this);
24906 this.bodyEl.on('mousedown', this.onMouseDown, this);
24907 this.bodyEl.on('mousemove', this.onMouseMove, this);
24908 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24909 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24910 Roo.get(document).on('mouseup', this.onMouseUp, this);
24913 this.selectorEl.on('change', this.onFileSelected, this);
24919 this.baseScale = 1;
24921 this.baseRotate = 1;
24922 this.dragable = false;
24923 this.pinching = false;
24926 this.cropData = false;
24927 this.notifyEl.dom.innerHTML = this.emptyText;
24929 this.selectorEl.dom.value = '';
24933 resize : function()
24935 if(this.fireEvent('resize', this) != false){
24936 this.setThumbBoxPosition();
24937 this.setCanvasPosition();
24941 onFooterButtonClick : function(e, el, o, type)
24944 case 'rotate-left' :
24945 this.onRotateLeft(e);
24947 case 'rotate-right' :
24948 this.onRotateRight(e);
24951 this.beforeSelectFile(e);
24966 this.fireEvent('footerbuttonclick', this, type);
24969 beforeSelectFile : function(e)
24971 e.preventDefault();
24973 if(this.fireEvent('beforeselectfile', this) != false){
24974 this.selectorEl.dom.click();
24978 onFileSelected : function(e)
24980 e.preventDefault();
24982 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24986 var file = this.selectorEl.dom.files[0];
24988 if(this.fireEvent('inspect', this, file) != false){
24989 this.prepare(file);
24994 trash : function(e)
24996 this.fireEvent('trash', this);
24999 download : function(e)
25001 this.fireEvent('download', this);
25004 loadCanvas : function(src)
25006 if(this.fireEvent('beforeloadcanvas', this, src) != false){
25010 this.imageEl = document.createElement('img');
25014 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25016 this.imageEl.src = src;
25020 onLoadCanvas : function()
25022 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25023 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25025 this.bodyEl.un('click', this.beforeSelectFile, this);
25027 this.notifyEl.hide();
25028 this.thumbEl.show();
25029 this.footerEl.show();
25031 this.baseRotateLevel();
25033 if(this.isDocument){
25034 this.setThumbBoxSize();
25037 this.setThumbBoxPosition();
25039 this.baseScaleLevel();
25045 this.canvasLoaded = true;
25048 this.maskEl.unmask();
25053 setCanvasPosition : function()
25055 if(!this.canvasEl){
25059 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25060 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25062 this.previewEl.setLeft(pw);
25063 this.previewEl.setTop(ph);
25067 onMouseDown : function(e)
25071 this.dragable = true;
25072 this.pinching = false;
25074 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25075 this.dragable = false;
25079 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25080 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25084 onMouseMove : function(e)
25088 if(!this.canvasLoaded){
25092 if (!this.dragable){
25096 var minX = Math.ceil(this.thumbEl.getLeft(true));
25097 var minY = Math.ceil(this.thumbEl.getTop(true));
25099 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25100 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25102 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25103 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25105 x = x - this.mouseX;
25106 y = y - this.mouseY;
25108 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25109 var bgY = Math.ceil(y + this.previewEl.getTop(true));
25111 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25112 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25114 this.previewEl.setLeft(bgX);
25115 this.previewEl.setTop(bgY);
25117 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25118 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25121 onMouseUp : function(e)
25125 this.dragable = false;
25128 onMouseWheel : function(e)
25132 this.startScale = this.scale;
25134 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25136 if(!this.zoomable()){
25137 this.scale = this.startScale;
25146 zoomable : function()
25148 var minScale = this.thumbEl.getWidth() / this.minWidth;
25150 if(this.minWidth < this.minHeight){
25151 minScale = this.thumbEl.getHeight() / this.minHeight;
25154 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25155 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25159 (this.rotate == 0 || this.rotate == 180) &&
25161 width > this.imageEl.OriginWidth ||
25162 height > this.imageEl.OriginHeight ||
25163 (width < this.minWidth && height < this.minHeight)
25171 (this.rotate == 90 || this.rotate == 270) &&
25173 width > this.imageEl.OriginWidth ||
25174 height > this.imageEl.OriginHeight ||
25175 (width < this.minHeight && height < this.minWidth)
25182 !this.isDocument &&
25183 (this.rotate == 0 || this.rotate == 180) &&
25185 width < this.minWidth ||
25186 width > this.imageEl.OriginWidth ||
25187 height < this.minHeight ||
25188 height > this.imageEl.OriginHeight
25195 !this.isDocument &&
25196 (this.rotate == 90 || this.rotate == 270) &&
25198 width < this.minHeight ||
25199 width > this.imageEl.OriginWidth ||
25200 height < this.minWidth ||
25201 height > this.imageEl.OriginHeight
25211 onRotateLeft : function(e)
25213 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25215 var minScale = this.thumbEl.getWidth() / this.minWidth;
25217 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25218 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25220 this.startScale = this.scale;
25222 while (this.getScaleLevel() < minScale){
25224 this.scale = this.scale + 1;
25226 if(!this.zoomable()){
25231 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25232 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25237 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25244 this.scale = this.startScale;
25246 this.onRotateFail();
25251 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25253 if(this.isDocument){
25254 this.setThumbBoxSize();
25255 this.setThumbBoxPosition();
25256 this.setCanvasPosition();
25261 this.fireEvent('rotate', this, 'left');
25265 onRotateRight : function(e)
25267 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25269 var minScale = this.thumbEl.getWidth() / this.minWidth;
25271 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25272 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25274 this.startScale = this.scale;
25276 while (this.getScaleLevel() < minScale){
25278 this.scale = this.scale + 1;
25280 if(!this.zoomable()){
25285 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25286 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25291 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25298 this.scale = this.startScale;
25300 this.onRotateFail();
25305 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25307 if(this.isDocument){
25308 this.setThumbBoxSize();
25309 this.setThumbBoxPosition();
25310 this.setCanvasPosition();
25315 this.fireEvent('rotate', this, 'right');
25318 onRotateFail : function()
25320 this.errorEl.show(true);
25324 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25329 this.previewEl.dom.innerHTML = '';
25331 var canvasEl = document.createElement("canvas");
25333 var contextEl = canvasEl.getContext("2d");
25335 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25336 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25337 var center = this.imageEl.OriginWidth / 2;
25339 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25340 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25341 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25342 center = this.imageEl.OriginHeight / 2;
25345 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25347 contextEl.translate(center, center);
25348 contextEl.rotate(this.rotate * Math.PI / 180);
25350 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25352 this.canvasEl = document.createElement("canvas");
25354 this.contextEl = this.canvasEl.getContext("2d");
25356 switch (this.rotate) {
25359 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25360 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25362 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25367 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25368 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25370 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25371 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);
25375 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25380 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25381 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25383 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25384 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);
25388 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);
25393 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25394 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25396 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25397 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25401 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);
25408 this.previewEl.appendChild(this.canvasEl);
25410 this.setCanvasPosition();
25415 if(!this.canvasLoaded){
25419 var imageCanvas = document.createElement("canvas");
25421 var imageContext = imageCanvas.getContext("2d");
25423 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25424 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25426 var center = imageCanvas.width / 2;
25428 imageContext.translate(center, center);
25430 imageContext.rotate(this.rotate * Math.PI / 180);
25432 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25434 var canvas = document.createElement("canvas");
25436 var context = canvas.getContext("2d");
25438 canvas.width = this.minWidth;
25439 canvas.height = this.minHeight;
25441 switch (this.rotate) {
25444 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25445 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25447 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25448 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25450 var targetWidth = this.minWidth - 2 * x;
25451 var targetHeight = this.minHeight - 2 * y;
25455 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25456 scale = targetWidth / width;
25459 if(x > 0 && y == 0){
25460 scale = targetHeight / height;
25463 if(x > 0 && y > 0){
25464 scale = targetWidth / width;
25466 if(width < height){
25467 scale = targetHeight / height;
25471 context.scale(scale, scale);
25473 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25474 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25476 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25477 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25479 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25484 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25485 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25487 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25488 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25490 var targetWidth = this.minWidth - 2 * x;
25491 var targetHeight = this.minHeight - 2 * y;
25495 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25496 scale = targetWidth / width;
25499 if(x > 0 && y == 0){
25500 scale = targetHeight / height;
25503 if(x > 0 && y > 0){
25504 scale = targetWidth / width;
25506 if(width < height){
25507 scale = targetHeight / height;
25511 context.scale(scale, scale);
25513 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25514 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25516 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25517 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25519 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25521 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25526 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25527 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25529 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25530 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25532 var targetWidth = this.minWidth - 2 * x;
25533 var targetHeight = this.minHeight - 2 * y;
25537 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25538 scale = targetWidth / width;
25541 if(x > 0 && y == 0){
25542 scale = targetHeight / height;
25545 if(x > 0 && y > 0){
25546 scale = targetWidth / width;
25548 if(width < height){
25549 scale = targetHeight / height;
25553 context.scale(scale, scale);
25555 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25556 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25558 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25559 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25561 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25562 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25564 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25569 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25570 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25572 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25573 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25575 var targetWidth = this.minWidth - 2 * x;
25576 var targetHeight = this.minHeight - 2 * y;
25580 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25581 scale = targetWidth / width;
25584 if(x > 0 && y == 0){
25585 scale = targetHeight / height;
25588 if(x > 0 && y > 0){
25589 scale = targetWidth / width;
25591 if(width < height){
25592 scale = targetHeight / height;
25596 context.scale(scale, scale);
25598 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25599 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25601 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25602 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25604 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25606 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25613 this.cropData = canvas.toDataURL(this.cropType);
25615 if(this.fireEvent('crop', this, this.cropData) !== false){
25616 this.process(this.file, this.cropData);
25623 setThumbBoxSize : function()
25627 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25628 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25629 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25631 this.minWidth = width;
25632 this.minHeight = height;
25634 if(this.rotate == 90 || this.rotate == 270){
25635 this.minWidth = height;
25636 this.minHeight = width;
25641 width = Math.ceil(this.minWidth * height / this.minHeight);
25643 if(this.minWidth > this.minHeight){
25645 height = Math.ceil(this.minHeight * width / this.minWidth);
25648 this.thumbEl.setStyle({
25649 width : width + 'px',
25650 height : height + 'px'
25657 setThumbBoxPosition : function()
25659 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25660 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25662 this.thumbEl.setLeft(x);
25663 this.thumbEl.setTop(y);
25667 baseRotateLevel : function()
25669 this.baseRotate = 1;
25672 typeof(this.exif) != 'undefined' &&
25673 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25674 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25676 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25679 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25683 baseScaleLevel : function()
25687 if(this.isDocument){
25689 if(this.baseRotate == 6 || this.baseRotate == 8){
25691 height = this.thumbEl.getHeight();
25692 this.baseScale = height / this.imageEl.OriginWidth;
25694 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25695 width = this.thumbEl.getWidth();
25696 this.baseScale = width / this.imageEl.OriginHeight;
25702 height = this.thumbEl.getHeight();
25703 this.baseScale = height / this.imageEl.OriginHeight;
25705 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25706 width = this.thumbEl.getWidth();
25707 this.baseScale = width / this.imageEl.OriginWidth;
25713 if(this.baseRotate == 6 || this.baseRotate == 8){
25715 width = this.thumbEl.getHeight();
25716 this.baseScale = width / this.imageEl.OriginHeight;
25718 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25719 height = this.thumbEl.getWidth();
25720 this.baseScale = height / this.imageEl.OriginHeight;
25723 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25724 height = this.thumbEl.getWidth();
25725 this.baseScale = height / this.imageEl.OriginHeight;
25727 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25728 width = this.thumbEl.getHeight();
25729 this.baseScale = width / this.imageEl.OriginWidth;
25736 width = this.thumbEl.getWidth();
25737 this.baseScale = width / this.imageEl.OriginWidth;
25739 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25740 height = this.thumbEl.getHeight();
25741 this.baseScale = height / this.imageEl.OriginHeight;
25744 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25746 height = this.thumbEl.getHeight();
25747 this.baseScale = height / this.imageEl.OriginHeight;
25749 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25750 width = this.thumbEl.getWidth();
25751 this.baseScale = width / this.imageEl.OriginWidth;
25759 getScaleLevel : function()
25761 return this.baseScale * Math.pow(1.1, this.scale);
25764 onTouchStart : function(e)
25766 if(!this.canvasLoaded){
25767 this.beforeSelectFile(e);
25771 var touches = e.browserEvent.touches;
25777 if(touches.length == 1){
25778 this.onMouseDown(e);
25782 if(touches.length != 2){
25788 for(var i = 0, finger; finger = touches[i]; i++){
25789 coords.push(finger.pageX, finger.pageY);
25792 var x = Math.pow(coords[0] - coords[2], 2);
25793 var y = Math.pow(coords[1] - coords[3], 2);
25795 this.startDistance = Math.sqrt(x + y);
25797 this.startScale = this.scale;
25799 this.pinching = true;
25800 this.dragable = false;
25804 onTouchMove : function(e)
25806 if(!this.pinching && !this.dragable){
25810 var touches = e.browserEvent.touches;
25817 this.onMouseMove(e);
25823 for(var i = 0, finger; finger = touches[i]; i++){
25824 coords.push(finger.pageX, finger.pageY);
25827 var x = Math.pow(coords[0] - coords[2], 2);
25828 var y = Math.pow(coords[1] - coords[3], 2);
25830 this.endDistance = Math.sqrt(x + y);
25832 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25834 if(!this.zoomable()){
25835 this.scale = this.startScale;
25843 onTouchEnd : function(e)
25845 this.pinching = false;
25846 this.dragable = false;
25850 process : function(file, crop)
25853 this.maskEl.mask(this.loadingText);
25856 this.xhr = new XMLHttpRequest();
25858 file.xhr = this.xhr;
25860 this.xhr.open(this.method, this.url, true);
25863 "Accept": "application/json",
25864 "Cache-Control": "no-cache",
25865 "X-Requested-With": "XMLHttpRequest"
25868 for (var headerName in headers) {
25869 var headerValue = headers[headerName];
25871 this.xhr.setRequestHeader(headerName, headerValue);
25877 this.xhr.onload = function()
25879 _this.xhrOnLoad(_this.xhr);
25882 this.xhr.onerror = function()
25884 _this.xhrOnError(_this.xhr);
25887 var formData = new FormData();
25889 formData.append('returnHTML', 'NO');
25892 formData.append('crop', crop);
25895 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25896 formData.append(this.paramName, file, file.name);
25899 if(typeof(file.filename) != 'undefined'){
25900 formData.append('filename', file.filename);
25903 if(typeof(file.mimetype) != 'undefined'){
25904 formData.append('mimetype', file.mimetype);
25907 if(this.fireEvent('arrange', this, formData) != false){
25908 this.xhr.send(formData);
25912 xhrOnLoad : function(xhr)
25915 this.maskEl.unmask();
25918 if (xhr.readyState !== 4) {
25919 this.fireEvent('exception', this, xhr);
25923 var response = Roo.decode(xhr.responseText);
25925 if(!response.success){
25926 this.fireEvent('exception', this, xhr);
25930 var response = Roo.decode(xhr.responseText);
25932 this.fireEvent('upload', this, response);
25936 xhrOnError : function()
25939 this.maskEl.unmask();
25942 Roo.log('xhr on error');
25944 var response = Roo.decode(xhr.responseText);
25950 prepare : function(file)
25953 this.maskEl.mask(this.loadingText);
25959 if(typeof(file) === 'string'){
25960 this.loadCanvas(file);
25964 if(!file || !this.urlAPI){
25969 this.cropType = file.type;
25973 if(this.fireEvent('prepare', this, this.file) != false){
25975 var reader = new FileReader();
25977 reader.onload = function (e) {
25978 if (e.target.error) {
25979 Roo.log(e.target.error);
25983 var buffer = e.target.result,
25984 dataView = new DataView(buffer),
25986 maxOffset = dataView.byteLength - 4,
25990 if (dataView.getUint16(0) === 0xffd8) {
25991 while (offset < maxOffset) {
25992 markerBytes = dataView.getUint16(offset);
25994 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25995 markerLength = dataView.getUint16(offset + 2) + 2;
25996 if (offset + markerLength > dataView.byteLength) {
25997 Roo.log('Invalid meta data: Invalid segment size.');
26001 if(markerBytes == 0xffe1){
26002 _this.parseExifData(
26009 offset += markerLength;
26019 var url = _this.urlAPI.createObjectURL(_this.file);
26021 _this.loadCanvas(url);
26026 reader.readAsArrayBuffer(this.file);
26032 parseExifData : function(dataView, offset, length)
26034 var tiffOffset = offset + 10,
26038 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26039 // No Exif data, might be XMP data instead
26043 // Check for the ASCII code for "Exif" (0x45786966):
26044 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26045 // No Exif data, might be XMP data instead
26048 if (tiffOffset + 8 > dataView.byteLength) {
26049 Roo.log('Invalid Exif data: Invalid segment size.');
26052 // Check for the two null bytes:
26053 if (dataView.getUint16(offset + 8) !== 0x0000) {
26054 Roo.log('Invalid Exif data: Missing byte alignment offset.');
26057 // Check the byte alignment:
26058 switch (dataView.getUint16(tiffOffset)) {
26060 littleEndian = true;
26063 littleEndian = false;
26066 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26069 // Check for the TIFF tag marker (0x002A):
26070 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26071 Roo.log('Invalid Exif data: Missing TIFF marker.');
26074 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26075 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26077 this.parseExifTags(
26080 tiffOffset + dirOffset,
26085 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26090 if (dirOffset + 6 > dataView.byteLength) {
26091 Roo.log('Invalid Exif data: Invalid directory offset.');
26094 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26095 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26096 if (dirEndOffset + 4 > dataView.byteLength) {
26097 Roo.log('Invalid Exif data: Invalid directory size.');
26100 for (i = 0; i < tagsNumber; i += 1) {
26104 dirOffset + 2 + 12 * i, // tag offset
26108 // Return the offset to the next directory:
26109 return dataView.getUint32(dirEndOffset, littleEndian);
26112 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
26114 var tag = dataView.getUint16(offset, littleEndian);
26116 this.exif[tag] = this.getExifValue(
26120 dataView.getUint16(offset + 2, littleEndian), // tag type
26121 dataView.getUint32(offset + 4, littleEndian), // tag length
26126 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26128 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26137 Roo.log('Invalid Exif data: Invalid tag type.');
26141 tagSize = tagType.size * length;
26142 // Determine if the value is contained in the dataOffset bytes,
26143 // or if the value at the dataOffset is a pointer to the actual data:
26144 dataOffset = tagSize > 4 ?
26145 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26146 if (dataOffset + tagSize > dataView.byteLength) {
26147 Roo.log('Invalid Exif data: Invalid data offset.');
26150 if (length === 1) {
26151 return tagType.getValue(dataView, dataOffset, littleEndian);
26154 for (i = 0; i < length; i += 1) {
26155 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26158 if (tagType.ascii) {
26160 // Concatenate the chars:
26161 for (i = 0; i < values.length; i += 1) {
26163 // Ignore the terminating NULL byte(s):
26164 if (c === '\u0000') {
26176 Roo.apply(Roo.bootstrap.UploadCropbox, {
26178 'Orientation': 0x0112
26182 1: 0, //'top-left',
26184 3: 180, //'bottom-right',
26185 // 4: 'bottom-left',
26187 6: 90, //'right-top',
26188 // 7: 'right-bottom',
26189 8: 270 //'left-bottom'
26193 // byte, 8-bit unsigned int:
26195 getValue: function (dataView, dataOffset) {
26196 return dataView.getUint8(dataOffset);
26200 // ascii, 8-bit byte:
26202 getValue: function (dataView, dataOffset) {
26203 return String.fromCharCode(dataView.getUint8(dataOffset));
26208 // short, 16 bit int:
26210 getValue: function (dataView, dataOffset, littleEndian) {
26211 return dataView.getUint16(dataOffset, littleEndian);
26215 // long, 32 bit int:
26217 getValue: function (dataView, dataOffset, littleEndian) {
26218 return dataView.getUint32(dataOffset, littleEndian);
26222 // rational = two long values, first is numerator, second is denominator:
26224 getValue: function (dataView, dataOffset, littleEndian) {
26225 return dataView.getUint32(dataOffset, littleEndian) /
26226 dataView.getUint32(dataOffset + 4, littleEndian);
26230 // slong, 32 bit signed int:
26232 getValue: function (dataView, dataOffset, littleEndian) {
26233 return dataView.getInt32(dataOffset, littleEndian);
26237 // srational, two slongs, first is numerator, second is denominator:
26239 getValue: function (dataView, dataOffset, littleEndian) {
26240 return dataView.getInt32(dataOffset, littleEndian) /
26241 dataView.getInt32(dataOffset + 4, littleEndian);
26251 cls : 'btn-group roo-upload-cropbox-rotate-left',
26252 action : 'rotate-left',
26256 cls : 'btn btn-default',
26257 html : '<i class="fa fa-undo"></i>'
26263 cls : 'btn-group roo-upload-cropbox-picture',
26264 action : 'picture',
26268 cls : 'btn btn-default',
26269 html : '<i class="fa fa-picture-o"></i>'
26275 cls : 'btn-group roo-upload-cropbox-rotate-right',
26276 action : 'rotate-right',
26280 cls : 'btn btn-default',
26281 html : '<i class="fa fa-repeat"></i>'
26289 cls : 'btn-group roo-upload-cropbox-rotate-left',
26290 action : 'rotate-left',
26294 cls : 'btn btn-default',
26295 html : '<i class="fa fa-undo"></i>'
26301 cls : 'btn-group roo-upload-cropbox-download',
26302 action : 'download',
26306 cls : 'btn btn-default',
26307 html : '<i class="fa fa-download"></i>'
26313 cls : 'btn-group roo-upload-cropbox-crop',
26318 cls : 'btn btn-default',
26319 html : '<i class="fa fa-crop"></i>'
26325 cls : 'btn-group roo-upload-cropbox-trash',
26330 cls : 'btn btn-default',
26331 html : '<i class="fa fa-trash"></i>'
26337 cls : 'btn-group roo-upload-cropbox-rotate-right',
26338 action : 'rotate-right',
26342 cls : 'btn btn-default',
26343 html : '<i class="fa fa-repeat"></i>'
26351 cls : 'btn-group roo-upload-cropbox-rotate-left',
26352 action : 'rotate-left',
26356 cls : 'btn btn-default',
26357 html : '<i class="fa fa-undo"></i>'
26363 cls : 'btn-group roo-upload-cropbox-rotate-right',
26364 action : 'rotate-right',
26368 cls : 'btn btn-default',
26369 html : '<i class="fa fa-repeat"></i>'
26382 * @class Roo.bootstrap.DocumentManager
26383 * @extends Roo.bootstrap.Component
26384 * Bootstrap DocumentManager class
26385 * @cfg {String} paramName default 'imageUpload'
26386 * @cfg {String} method default POST
26387 * @cfg {String} url action url
26388 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26389 * @cfg {Boolean} multiple multiple upload default true
26390 * @cfg {Number} thumbSize default 300
26391 * @cfg {String} fieldLabel
26392 * @cfg {Number} labelWidth default 4
26393 * @cfg {String} labelAlign (left|top) default left
26394 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26397 * Create a new DocumentManager
26398 * @param {Object} config The config object
26401 Roo.bootstrap.DocumentManager = function(config){
26402 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26407 * Fire when initial the DocumentManager
26408 * @param {Roo.bootstrap.DocumentManager} this
26413 * inspect selected file
26414 * @param {Roo.bootstrap.DocumentManager} this
26415 * @param {File} file
26420 * Fire when xhr load exception
26421 * @param {Roo.bootstrap.DocumentManager} this
26422 * @param {XMLHttpRequest} xhr
26424 "exception" : true,
26427 * prepare the form data
26428 * @param {Roo.bootstrap.DocumentManager} this
26429 * @param {Object} formData
26434 * Fire when remove the file
26435 * @param {Roo.bootstrap.DocumentManager} this
26436 * @param {Object} file
26441 * Fire after refresh the file
26442 * @param {Roo.bootstrap.DocumentManager} this
26447 * Fire after click the image
26448 * @param {Roo.bootstrap.DocumentManager} this
26449 * @param {Object} file
26454 * Fire when upload a image and editable set to true
26455 * @param {Roo.bootstrap.DocumentManager} this
26456 * @param {Object} file
26460 * @event beforeselectfile
26461 * Fire before select file
26462 * @param {Roo.bootstrap.DocumentManager} this
26464 "beforeselectfile" : true,
26467 * Fire before process file
26468 * @param {Roo.bootstrap.DocumentManager} this
26469 * @param {Object} file
26476 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
26485 paramName : 'imageUpload',
26488 labelAlign : 'left',
26495 getAutoCreate : function()
26497 var managerWidget = {
26499 cls : 'roo-document-manager',
26503 cls : 'roo-document-manager-selector',
26508 cls : 'roo-document-manager-uploader',
26512 cls : 'roo-document-manager-upload-btn',
26513 html : '<i class="fa fa-plus"></i>'
26524 cls : 'column col-md-12',
26529 if(this.fieldLabel.length){
26534 cls : 'column col-md-12',
26535 html : this.fieldLabel
26539 cls : 'column col-md-12',
26544 if(this.labelAlign == 'left'){
26548 cls : 'column col-md-' + this.labelWidth,
26549 html : this.fieldLabel
26553 cls : 'column col-md-' + (12 - this.labelWidth),
26563 cls : 'row clearfix',
26571 initEvents : function()
26573 this.managerEl = this.el.select('.roo-document-manager', true).first();
26574 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26576 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26577 this.selectorEl.hide();
26580 this.selectorEl.attr('multiple', 'multiple');
26583 this.selectorEl.on('change', this.onFileSelected, this);
26585 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26586 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26588 this.uploader.on('click', this.onUploaderClick, this);
26590 this.renderProgressDialog();
26594 window.addEventListener("resize", function() { _this.refresh(); } );
26596 this.fireEvent('initial', this);
26599 renderProgressDialog : function()
26603 this.progressDialog = new Roo.bootstrap.Modal({
26604 cls : 'roo-document-manager-progress-dialog',
26605 allow_close : false,
26615 btnclick : function() {
26616 _this.uploadCancel();
26622 this.progressDialog.render(Roo.get(document.body));
26624 this.progress = new Roo.bootstrap.Progress({
26625 cls : 'roo-document-manager-progress',
26630 this.progress.render(this.progressDialog.getChildContainer());
26632 this.progressBar = new Roo.bootstrap.ProgressBar({
26633 cls : 'roo-document-manager-progress-bar',
26636 aria_valuemax : 12,
26640 this.progressBar.render(this.progress.getChildContainer());
26643 onUploaderClick : function(e)
26645 e.preventDefault();
26647 if(this.fireEvent('beforeselectfile', this) != false){
26648 this.selectorEl.dom.click();
26653 onFileSelected : function(e)
26655 e.preventDefault();
26657 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26661 Roo.each(this.selectorEl.dom.files, function(file){
26662 if(this.fireEvent('inspect', this, file) != false){
26663 this.files.push(file);
26673 this.selectorEl.dom.value = '';
26675 if(!this.files.length){
26679 if(this.boxes > 0 && this.files.length > this.boxes){
26680 this.files = this.files.slice(0, this.boxes);
26683 this.uploader.show();
26685 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26686 this.uploader.hide();
26695 Roo.each(this.files, function(file){
26697 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26698 var f = this.renderPreview(file);
26703 if(file.type.indexOf('image') != -1){
26704 this.delegates.push(
26706 _this.process(file);
26707 }).createDelegate(this)
26715 _this.process(file);
26716 }).createDelegate(this)
26721 this.files = files;
26723 this.delegates = this.delegates.concat(docs);
26725 if(!this.delegates.length){
26730 this.progressBar.aria_valuemax = this.delegates.length;
26737 arrange : function()
26739 if(!this.delegates.length){
26740 this.progressDialog.hide();
26745 var delegate = this.delegates.shift();
26747 this.progressDialog.show();
26749 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26751 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26756 refresh : function()
26758 this.uploader.show();
26760 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26761 this.uploader.hide();
26764 Roo.isTouch ? this.closable(false) : this.closable(true);
26766 this.fireEvent('refresh', this);
26769 onRemove : function(e, el, o)
26771 e.preventDefault();
26773 this.fireEvent('remove', this, o);
26777 remove : function(o)
26781 Roo.each(this.files, function(file){
26782 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26791 this.files = files;
26798 Roo.each(this.files, function(file){
26803 file.target.remove();
26812 onClick : function(e, el, o)
26814 e.preventDefault();
26816 this.fireEvent('click', this, o);
26820 closable : function(closable)
26822 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26824 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26836 xhrOnLoad : function(xhr)
26838 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26842 if (xhr.readyState !== 4) {
26844 this.fireEvent('exception', this, xhr);
26848 var response = Roo.decode(xhr.responseText);
26850 if(!response.success){
26852 this.fireEvent('exception', this, xhr);
26856 var file = this.renderPreview(response.data);
26858 this.files.push(file);
26864 xhrOnError : function(xhr)
26866 Roo.log('xhr on error');
26868 var response = Roo.decode(xhr.responseText);
26875 process : function(file)
26877 if(this.fireEvent('process', this, file) !== false){
26878 if(this.editable && file.type.indexOf('image') != -1){
26879 this.fireEvent('edit', this, file);
26883 this.uploadStart(file, false);
26890 uploadStart : function(file, crop)
26892 this.xhr = new XMLHttpRequest();
26894 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26899 file.xhr = this.xhr;
26901 this.managerEl.createChild({
26903 cls : 'roo-document-manager-loading',
26907 tooltip : file.name,
26908 cls : 'roo-document-manager-thumb',
26909 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26915 this.xhr.open(this.method, this.url, true);
26918 "Accept": "application/json",
26919 "Cache-Control": "no-cache",
26920 "X-Requested-With": "XMLHttpRequest"
26923 for (var headerName in headers) {
26924 var headerValue = headers[headerName];
26926 this.xhr.setRequestHeader(headerName, headerValue);
26932 this.xhr.onload = function()
26934 _this.xhrOnLoad(_this.xhr);
26937 this.xhr.onerror = function()
26939 _this.xhrOnError(_this.xhr);
26942 var formData = new FormData();
26944 formData.append('returnHTML', 'NO');
26947 formData.append('crop', crop);
26950 formData.append(this.paramName, file, file.name);
26952 if(this.fireEvent('prepare', this, formData) != false){
26953 this.xhr.send(formData);
26957 uploadCancel : function()
26964 this.delegates = [];
26966 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26973 renderPreview : function(file)
26975 if(typeof(file.target) != 'undefined' && file.target){
26979 var previewEl = this.managerEl.createChild({
26981 cls : 'roo-document-manager-preview',
26985 tooltip : file.filename,
26986 cls : 'roo-document-manager-thumb',
26987 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26992 html : '<i class="fa fa-times-circle"></i>'
26997 var close = previewEl.select('button.close', true).first();
26999 close.on('click', this.onRemove, this, file);
27001 file.target = previewEl;
27003 var image = previewEl.select('img', true).first();
27007 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27009 image.on('click', this.onClick, this, file);
27015 onPreviewLoad : function(file, image)
27017 if(typeof(file.target) == 'undefined' || !file.target){
27021 var width = image.dom.naturalWidth || image.dom.width;
27022 var height = image.dom.naturalHeight || image.dom.height;
27024 if(width > height){
27025 file.target.addClass('wide');
27029 file.target.addClass('tall');
27034 uploadFromSource : function(file, crop)
27036 this.xhr = new XMLHttpRequest();
27038 this.managerEl.createChild({
27040 cls : 'roo-document-manager-loading',
27044 tooltip : file.name,
27045 cls : 'roo-document-manager-thumb',
27046 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27052 this.xhr.open(this.method, this.url, true);
27055 "Accept": "application/json",
27056 "Cache-Control": "no-cache",
27057 "X-Requested-With": "XMLHttpRequest"
27060 for (var headerName in headers) {
27061 var headerValue = headers[headerName];
27063 this.xhr.setRequestHeader(headerName, headerValue);
27069 this.xhr.onload = function()
27071 _this.xhrOnLoad(_this.xhr);
27074 this.xhr.onerror = function()
27076 _this.xhrOnError(_this.xhr);
27079 var formData = new FormData();
27081 formData.append('returnHTML', 'NO');
27083 formData.append('crop', crop);
27085 if(typeof(file.filename) != 'undefined'){
27086 formData.append('filename', file.filename);
27089 if(typeof(file.mimetype) != 'undefined'){
27090 formData.append('mimetype', file.mimetype);
27093 if(this.fireEvent('prepare', this, formData) != false){
27094 this.xhr.send(formData);
27104 * @class Roo.bootstrap.DocumentViewer
27105 * @extends Roo.bootstrap.Component
27106 * Bootstrap DocumentViewer class
27109 * Create a new DocumentViewer
27110 * @param {Object} config The config object
27113 Roo.bootstrap.DocumentViewer = function(config){
27114 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27119 * Fire after initEvent
27120 * @param {Roo.bootstrap.DocumentViewer} this
27126 * @param {Roo.bootstrap.DocumentViewer} this
27131 * Fire after trash button
27132 * @param {Roo.bootstrap.DocumentViewer} this
27139 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
27141 getAutoCreate : function()
27145 cls : 'roo-document-viewer',
27149 cls : 'roo-document-viewer-body',
27153 cls : 'roo-document-viewer-thumb',
27157 cls : 'roo-document-viewer-image'
27165 cls : 'roo-document-viewer-footer',
27168 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27176 cls : 'btn btn-default roo-document-viewer-trash',
27177 html : '<i class="fa fa-trash"></i>'
27190 initEvents : function()
27193 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27194 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27196 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27197 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27199 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27200 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27202 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27203 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27205 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27206 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27208 this.bodyEl.on('click', this.onClick, this);
27210 this.trashBtn.on('click', this.onTrash, this);
27214 initial : function()
27216 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27219 this.fireEvent('initial', this);
27223 onClick : function(e)
27225 e.preventDefault();
27227 this.fireEvent('click', this);
27230 onTrash : function(e)
27232 e.preventDefault();
27234 this.fireEvent('trash', this);
27246 * @class Roo.bootstrap.NavProgressBar
27247 * @extends Roo.bootstrap.Component
27248 * Bootstrap NavProgressBar class
27251 * Create a new nav progress bar
27252 * @param {Object} config The config object
27255 Roo.bootstrap.NavProgressBar = function(config){
27256 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27258 this.bullets = this.bullets || [];
27260 // Roo.bootstrap.NavProgressBar.register(this);
27264 * Fires when the active item changes
27265 * @param {Roo.bootstrap.NavProgressBar} this
27266 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27267 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
27274 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
27279 getAutoCreate : function()
27281 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27285 cls : 'roo-navigation-bar-group',
27289 cls : 'roo-navigation-top-bar'
27293 cls : 'roo-navigation-bullets-bar',
27297 cls : 'roo-navigation-bar'
27304 cls : 'roo-navigation-bottom-bar'
27314 initEvents: function()
27319 onRender : function(ct, position)
27321 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27323 if(this.bullets.length){
27324 Roo.each(this.bullets, function(b){
27333 addItem : function(cfg)
27335 var item = new Roo.bootstrap.NavProgressItem(cfg);
27337 item.parentId = this.id;
27338 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27341 var top = new Roo.bootstrap.Element({
27343 cls : 'roo-navigation-bar-text'
27346 var bottom = new Roo.bootstrap.Element({
27348 cls : 'roo-navigation-bar-text'
27351 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27352 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27354 var topText = new Roo.bootstrap.Element({
27356 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27359 var bottomText = new Roo.bootstrap.Element({
27361 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27364 topText.onRender(top.el, null);
27365 bottomText.onRender(bottom.el, null);
27368 item.bottomEl = bottom;
27371 this.barItems.push(item);
27376 getActive : function()
27378 var active = false;
27380 Roo.each(this.barItems, function(v){
27382 if (!v.isActive()) {
27394 setActiveItem : function(item)
27398 Roo.each(this.barItems, function(v){
27399 if (v.rid == item.rid) {
27403 if (v.isActive()) {
27404 v.setActive(false);
27409 item.setActive(true);
27411 this.fireEvent('changed', this, item, prev);
27414 getBarItem: function(rid)
27418 Roo.each(this.barItems, function(e) {
27419 if (e.rid != rid) {
27430 indexOfItem : function(item)
27434 Roo.each(this.barItems, function(v, i){
27436 if (v.rid != item.rid) {
27447 setActiveNext : function()
27449 var i = this.indexOfItem(this.getActive());
27451 if (i > this.barItems.length) {
27455 this.setActiveItem(this.barItems[i+1]);
27458 setActivePrev : function()
27460 var i = this.indexOfItem(this.getActive());
27466 this.setActiveItem(this.barItems[i-1]);
27469 format : function()
27471 if(!this.barItems.length){
27475 var width = 100 / this.barItems.length;
27477 Roo.each(this.barItems, function(i){
27478 i.el.setStyle('width', width + '%');
27479 i.topEl.el.setStyle('width', width + '%');
27480 i.bottomEl.el.setStyle('width', width + '%');
27489 * Nav Progress Item
27494 * @class Roo.bootstrap.NavProgressItem
27495 * @extends Roo.bootstrap.Component
27496 * Bootstrap NavProgressItem class
27497 * @cfg {String} rid the reference id
27498 * @cfg {Boolean} active (true|false) Is item active default false
27499 * @cfg {Boolean} disabled (true|false) Is item active default false
27500 * @cfg {String} html
27501 * @cfg {String} position (top|bottom) text position default bottom
27502 * @cfg {String} icon show icon instead of number
27505 * Create a new NavProgressItem
27506 * @param {Object} config The config object
27508 Roo.bootstrap.NavProgressItem = function(config){
27509 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27514 * The raw click event for the entire grid.
27515 * @param {Roo.bootstrap.NavProgressItem} this
27516 * @param {Roo.EventObject} e
27523 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
27529 position : 'bottom',
27532 getAutoCreate : function()
27534 var iconCls = 'roo-navigation-bar-item-icon';
27536 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27540 cls: 'roo-navigation-bar-item',
27550 cfg.cls += ' active';
27553 cfg.cls += ' disabled';
27559 disable : function()
27561 this.setDisabled(true);
27564 enable : function()
27566 this.setDisabled(false);
27569 initEvents: function()
27571 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27573 this.iconEl.on('click', this.onClick, this);
27576 onClick : function(e)
27578 e.preventDefault();
27584 if(this.fireEvent('click', this, e) === false){
27588 this.parent().setActiveItem(this);
27591 isActive: function ()
27593 return this.active;
27596 setActive : function(state)
27598 if(this.active == state){
27602 this.active = state;
27605 this.el.addClass('active');
27609 this.el.removeClass('active');
27614 setDisabled : function(state)
27616 if(this.disabled == state){
27620 this.disabled = state;
27623 this.el.addClass('disabled');
27627 this.el.removeClass('disabled');
27630 tooltipEl : function()
27632 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27645 * @class Roo.bootstrap.FieldLabel
27646 * @extends Roo.bootstrap.Component
27647 * Bootstrap FieldLabel class
27648 * @cfg {String} html contents of the element
27649 * @cfg {String} tag tag of the element default label
27650 * @cfg {String} cls class of the element
27651 * @cfg {String} target label target
27652 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27653 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27654 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27655 * @cfg {String} iconTooltip default "This field is required"
27658 * Create a new FieldLabel
27659 * @param {Object} config The config object
27662 Roo.bootstrap.FieldLabel = function(config){
27663 Roo.bootstrap.Element.superclass.constructor.call(this, config);
27668 * Fires after the field has been marked as invalid.
27669 * @param {Roo.form.FieldLabel} this
27670 * @param {String} msg The validation message
27675 * Fires after the field has been validated with no errors.
27676 * @param {Roo.form.FieldLabel} this
27682 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
27689 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27690 validClass : 'text-success fa fa-lg fa-check',
27691 iconTooltip : 'This field is required',
27693 getAutoCreate : function(){
27697 cls : 'roo-bootstrap-field-label ' + this.cls,
27703 tooltip : this.iconTooltip
27715 initEvents: function()
27717 Roo.bootstrap.Element.superclass.initEvents.call(this);
27719 this.iconEl = this.el.select('i', true).first();
27721 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27723 Roo.bootstrap.FieldLabel.register(this);
27727 * Mark this field as valid
27729 markValid : function()
27731 this.iconEl.show();
27733 this.iconEl.removeClass(this.invalidClass);
27735 this.iconEl.addClass(this.validClass);
27737 this.fireEvent('valid', this);
27741 * Mark this field as invalid
27742 * @param {String} msg The validation message
27744 markInvalid : function(msg)
27746 this.iconEl.show();
27748 this.iconEl.removeClass(this.validClass);
27750 this.iconEl.addClass(this.invalidClass);
27752 this.fireEvent('invalid', this, msg);
27758 Roo.apply(Roo.bootstrap.FieldLabel, {
27763 * register a FieldLabel Group
27764 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27766 register : function(label)
27768 if(this.groups.hasOwnProperty(label.target)){
27772 this.groups[label.target] = label;
27776 * fetch a FieldLabel Group based on the target
27777 * @param {string} target
27778 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27780 get: function(target) {
27781 if (typeof(this.groups[target]) == 'undefined') {
27785 return this.groups[target] ;
27794 * page DateSplitField.
27800 * @class Roo.bootstrap.DateSplitField
27801 * @extends Roo.bootstrap.Component
27802 * Bootstrap DateSplitField class
27803 * @cfg {string} fieldLabel - the label associated
27804 * @cfg {Number} labelWidth set the width of label (0-12)
27805 * @cfg {String} labelAlign (top|left)
27806 * @cfg {Boolean} dayAllowBlank (true|false) default false
27807 * @cfg {Boolean} monthAllowBlank (true|false) default false
27808 * @cfg {Boolean} yearAllowBlank (true|false) default false
27809 * @cfg {string} dayPlaceholder
27810 * @cfg {string} monthPlaceholder
27811 * @cfg {string} yearPlaceholder
27812 * @cfg {string} dayFormat default 'd'
27813 * @cfg {string} monthFormat default 'm'
27814 * @cfg {string} yearFormat default 'Y'
27818 * Create a new DateSplitField
27819 * @param {Object} config The config object
27822 Roo.bootstrap.DateSplitField = function(config){
27823 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27829 * getting the data of years
27830 * @param {Roo.bootstrap.DateSplitField} this
27831 * @param {Object} years
27836 * getting the data of days
27837 * @param {Roo.bootstrap.DateSplitField} this
27838 * @param {Object} days
27843 * Fires after the field has been marked as invalid.
27844 * @param {Roo.form.Field} this
27845 * @param {String} msg The validation message
27850 * Fires after the field has been validated with no errors.
27851 * @param {Roo.form.Field} this
27857 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
27860 labelAlign : 'top',
27862 dayAllowBlank : false,
27863 monthAllowBlank : false,
27864 yearAllowBlank : false,
27865 dayPlaceholder : '',
27866 monthPlaceholder : '',
27867 yearPlaceholder : '',
27871 isFormField : true,
27873 getAutoCreate : function()
27877 cls : 'row roo-date-split-field-group',
27882 cls : 'form-hidden-field roo-date-split-field-group-value',
27888 if(this.fieldLabel){
27891 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27895 html : this.fieldLabel
27901 Roo.each(['day', 'month', 'year'], function(t){
27904 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27911 inputEl: function ()
27913 return this.el.select('.roo-date-split-field-group-value', true).first();
27916 onRender : function(ct, position)
27920 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27922 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27924 this.dayField = new Roo.bootstrap.ComboBox({
27925 allowBlank : this.dayAllowBlank,
27926 alwaysQuery : true,
27927 displayField : 'value',
27930 forceSelection : true,
27932 placeholder : this.dayPlaceholder,
27933 selectOnFocus : true,
27934 tpl : '<div class="select2-result"><b>{value}</b></div>',
27935 triggerAction : 'all',
27937 valueField : 'value',
27938 store : new Roo.data.SimpleStore({
27939 data : (function() {
27941 _this.fireEvent('days', _this, days);
27944 fields : [ 'value' ]
27947 select : function (_self, record, index)
27949 _this.setValue(_this.getValue());
27954 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27956 this.monthField = new Roo.bootstrap.MonthField({
27957 after : '<i class=\"fa fa-calendar\"></i>',
27958 allowBlank : this.monthAllowBlank,
27959 placeholder : this.monthPlaceholder,
27962 render : function (_self)
27964 this.el.select('span.input-group-addon', true).first().on('click', function(e){
27965 e.preventDefault();
27969 select : function (_self, oldvalue, newvalue)
27971 _this.setValue(_this.getValue());
27976 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27978 this.yearField = new Roo.bootstrap.ComboBox({
27979 allowBlank : this.yearAllowBlank,
27980 alwaysQuery : true,
27981 displayField : 'value',
27984 forceSelection : true,
27986 placeholder : this.yearPlaceholder,
27987 selectOnFocus : true,
27988 tpl : '<div class="select2-result"><b>{value}</b></div>',
27989 triggerAction : 'all',
27991 valueField : 'value',
27992 store : new Roo.data.SimpleStore({
27993 data : (function() {
27995 _this.fireEvent('years', _this, years);
27998 fields : [ 'value' ]
28001 select : function (_self, record, index)
28003 _this.setValue(_this.getValue());
28008 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28011 setValue : function(v, format)
28013 this.inputEl.dom.value = v;
28015 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28017 var d = Date.parseDate(v, f);
28024 this.setDay(d.format(this.dayFormat));
28025 this.setMonth(d.format(this.monthFormat));
28026 this.setYear(d.format(this.yearFormat));
28033 setDay : function(v)
28035 this.dayField.setValue(v);
28036 this.inputEl.dom.value = this.getValue();
28041 setMonth : function(v)
28043 this.monthField.setValue(v, true);
28044 this.inputEl.dom.value = this.getValue();
28049 setYear : function(v)
28051 this.yearField.setValue(v);
28052 this.inputEl.dom.value = this.getValue();
28057 getDay : function()
28059 return this.dayField.getValue();
28062 getMonth : function()
28064 return this.monthField.getValue();
28067 getYear : function()
28069 return this.yearField.getValue();
28072 getValue : function()
28074 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28076 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28086 this.inputEl.dom.value = '';
28091 validate : function()
28093 var d = this.dayField.validate();
28094 var m = this.monthField.validate();
28095 var y = this.yearField.validate();
28100 (!this.dayAllowBlank && !d) ||
28101 (!this.monthAllowBlank && !m) ||
28102 (!this.yearAllowBlank && !y)
28107 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28116 this.markInvalid();
28121 markValid : function()
28124 var label = this.el.select('label', true).first();
28125 var icon = this.el.select('i.fa-star', true).first();
28131 this.fireEvent('valid', this);
28135 * Mark this field as invalid
28136 * @param {String} msg The validation message
28138 markInvalid : function(msg)
28141 var label = this.el.select('label', true).first();
28142 var icon = this.el.select('i.fa-star', true).first();
28144 if(label && !icon){
28145 this.el.select('.roo-date-split-field-label', true).createChild({
28147 cls : 'text-danger fa fa-lg fa-star',
28148 tooltip : 'This field is required',
28149 style : 'margin-right:5px;'
28153 this.fireEvent('invalid', this, msg);
28156 clearInvalid : function()
28158 var label = this.el.select('label', true).first();
28159 var icon = this.el.select('i.fa-star', true).first();
28165 this.fireEvent('valid', this);
28168 getName: function()
28178 * http://masonry.desandro.com
28180 * The idea is to render all the bricks based on vertical width...
28182 * The original code extends 'outlayer' - we might need to use that....
28188 * @class Roo.bootstrap.LayoutMasonry
28189 * @extends Roo.bootstrap.Component
28190 * Bootstrap Layout Masonry class
28193 * Create a new Element
28194 * @param {Object} config The config object
28197 Roo.bootstrap.LayoutMasonry = function(config){
28198 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28201 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
28204 * @cfg {Boolean} isFitWidth - resize the width..
28206 isFitWidth : false, // options..
28208 * @cfg {Boolean} isOriginLeft = left align?
28210 isOriginLeft : true,
28212 * @cfg {Boolean} isOriginTop = top align?
28214 isOriginTop : false,
28216 * @cfg {Boolean} isLayoutInstant = no animation?
28218 isLayoutInstant : false, // needed?
28220 * @cfg {Boolean} isResizingContainer = not sure if this is used..
28222 isResizingContainer : true,
28224 * @cfg {Number} columnWidth width of the columns
28229 * @cfg {Number} padHeight padding below box..
28235 * @cfg {Boolean} isAutoInitial defalut true
28238 isAutoInitial : true,
28244 initialColumnWidth : 0,
28245 currentSize : null,
28247 colYs : null, // array.
28254 bricks: null, //CompositeElement
28255 cols : 0, // array?
28256 // element : null, // wrapped now this.el
28257 _isLayoutInited : null,
28260 getAutoCreate : function(){
28264 cls: 'blog-masonary-wrapper ' + this.cls,
28266 cls : 'mas-boxes masonary'
28273 getChildContainer: function( )
28275 if (this.boxesEl) {
28276 return this.boxesEl;
28278 this.boxesEl = this.el.select('.mas-boxes').first();
28280 return this.boxesEl;
28284 initEvents : function()
28288 if(this.isAutoInitial){
28289 Roo.log('hook children rendered');
28290 this.on('childrenrendered', function() {
28291 Roo.log('children rendered');
28298 initial : function()
28300 this.reloadItems();
28302 this.currentSize = this.el.getBox(true);
28304 /// was window resize... - let's see if this works..
28305 Roo.EventManager.onWindowResize(this.resize, this);
28307 if(!this.isAutoInitial){
28312 this.layout.defer(500,this);
28315 reloadItems: function()
28317 this.bricks = this.el.select('.masonry-brick', true);
28319 this.bricks.each(function(b) {
28320 //Roo.log(b.getSize());
28321 if (!b.attr('originalwidth')) {
28322 b.attr('originalwidth', b.getSize().width);
28327 Roo.log(this.bricks.elements.length);
28331 resize : function()
28334 var cs = this.el.getBox(true);
28336 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
28337 Roo.log("no change in with or X");
28340 this.currentSize = cs;
28343 layout : function()
28346 this._resetLayout();
28347 //this._manageStamps();
28349 // don't animate first layout
28350 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
28351 this.layoutItems( isInstant );
28353 // flag for initalized
28354 this._isLayoutInited = true;
28357 layoutItems : function( isInstant )
28359 //var items = this._getItemsForLayout( this.items );
28360 // original code supports filtering layout items.. we just ignore it..
28362 this._layoutItems( this.bricks , isInstant );
28364 this._postLayout();
28366 _layoutItems : function ( items , isInstant)
28368 //this.fireEvent( 'layout', this, items );
28371 if ( !items || !items.elements.length ) {
28372 // no items, emit event with empty array
28377 items.each(function(item) {
28378 Roo.log("layout item");
28380 // get x/y object from method
28381 var position = this._getItemLayoutPosition( item );
28383 position.item = item;
28384 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
28385 queue.push( position );
28388 this._processLayoutQueue( queue );
28390 /** Sets position of item in DOM
28391 * @param {Element} item
28392 * @param {Number} x - horizontal position
28393 * @param {Number} y - vertical position
28394 * @param {Boolean} isInstant - disables transitions
28396 _processLayoutQueue : function( queue )
28398 for ( var i=0, len = queue.length; i < len; i++ ) {
28399 var obj = queue[i];
28400 obj.item.position('absolute');
28401 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
28407 * Any logic you want to do after each layout,
28408 * i.e. size the container
28410 _postLayout : function()
28412 this.resizeContainer();
28415 resizeContainer : function()
28417 if ( !this.isResizingContainer ) {
28420 var size = this._getContainerSize();
28422 this.el.setSize(size.width,size.height);
28423 this.boxesEl.setSize(size.width,size.height);
28429 _resetLayout : function()
28431 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
28432 this.colWidth = this.el.getWidth();
28433 //this.gutter = this.el.getWidth();
28435 this.measureColumns();
28441 this.colYs.push( 0 );
28447 measureColumns : function()
28449 this.getContainerWidth();
28450 // if columnWidth is 0, default to outerWidth of first item
28451 if ( !this.columnWidth ) {
28452 var firstItem = this.bricks.first();
28453 Roo.log(firstItem);
28454 this.columnWidth = this.containerWidth;
28455 if (firstItem && firstItem.attr('originalwidth') ) {
28456 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
28458 // columnWidth fall back to item of first element
28459 Roo.log("set column width?");
28460 this.initialColumnWidth = this.columnWidth ;
28462 // if first elem has no width, default to size of container
28467 if (this.initialColumnWidth) {
28468 this.columnWidth = this.initialColumnWidth;
28473 // column width is fixed at the top - however if container width get's smaller we should
28476 // this bit calcs how man columns..
28478 var columnWidth = this.columnWidth += this.gutter;
28480 // calculate columns
28481 var containerWidth = this.containerWidth + this.gutter;
28483 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
28484 // fix rounding errors, typically with gutters
28485 var excess = columnWidth - containerWidth % columnWidth;
28488 // if overshoot is less than a pixel, round up, otherwise floor it
28489 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
28490 cols = Math[ mathMethod ]( cols );
28491 this.cols = Math.max( cols, 1 );
28494 // padding positioning..
28495 var totalColWidth = this.cols * this.columnWidth;
28496 var padavail = this.containerWidth - totalColWidth;
28497 // so for 2 columns - we need 3 'pads'
28499 var padNeeded = (1+this.cols) * this.padWidth;
28501 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
28503 this.columnWidth += padExtra
28504 //this.padWidth = Math.floor(padavail / ( this.cols));
28506 // adjust colum width so that padding is fixed??
28508 // we have 3 columns ... total = width * 3
28509 // we have X left over... that should be used by
28511 //if (this.expandC) {
28519 getContainerWidth : function()
28521 /* // container is parent if fit width
28522 var container = this.isFitWidth ? this.element.parentNode : this.element;
28523 // check that this.size and size are there
28524 // IE8 triggers resize on body size change, so they might not be
28526 var size = getSize( container ); //FIXME
28527 this.containerWidth = size && size.innerWidth; //FIXME
28530 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
28534 _getItemLayoutPosition : function( item ) // what is item?
28536 // we resize the item to our columnWidth..
28538 item.setWidth(this.columnWidth);
28539 item.autoBoxAdjust = false;
28541 var sz = item.getSize();
28543 // how many columns does this brick span
28544 var remainder = this.containerWidth % this.columnWidth;
28546 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
28547 // round if off by 1 pixel, otherwise use ceil
28548 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
28549 colSpan = Math.min( colSpan, this.cols );
28551 // normally this should be '1' as we dont' currently allow multi width columns..
28553 var colGroup = this._getColGroup( colSpan );
28554 // get the minimum Y value from the columns
28555 var minimumY = Math.min.apply( Math, colGroup );
28556 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
28558 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
28560 // position the brick
28562 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
28563 y: this.currentSize.y + minimumY + this.padHeight
28567 // apply setHeight to necessary columns
28568 var setHeight = minimumY + sz.height + this.padHeight;
28569 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
28571 var setSpan = this.cols + 1 - colGroup.length;
28572 for ( var i = 0; i < setSpan; i++ ) {
28573 this.colYs[ shortColIndex + i ] = setHeight ;
28580 * @param {Number} colSpan - number of columns the element spans
28581 * @returns {Array} colGroup
28583 _getColGroup : function( colSpan )
28585 if ( colSpan < 2 ) {
28586 // if brick spans only one column, use all the column Ys
28591 // how many different places could this brick fit horizontally
28592 var groupCount = this.cols + 1 - colSpan;
28593 // for each group potential horizontal position
28594 for ( var i = 0; i < groupCount; i++ ) {
28595 // make an array of colY values for that one group
28596 var groupColYs = this.colYs.slice( i, i + colSpan );
28597 // and get the max value of the array
28598 colGroup[i] = Math.max.apply( Math, groupColYs );
28603 _manageStamp : function( stamp )
28605 var stampSize = stamp.getSize();
28606 var offset = stamp.getBox();
28607 // get the columns that this stamp affects
28608 var firstX = this.isOriginLeft ? offset.x : offset.right;
28609 var lastX = firstX + stampSize.width;
28610 var firstCol = Math.floor( firstX / this.columnWidth );
28611 firstCol = Math.max( 0, firstCol );
28613 var lastCol = Math.floor( lastX / this.columnWidth );
28614 // lastCol should not go over if multiple of columnWidth #425
28615 lastCol -= lastX % this.columnWidth ? 0 : 1;
28616 lastCol = Math.min( this.cols - 1, lastCol );
28618 // set colYs to bottom of the stamp
28619 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
28622 for ( var i = firstCol; i <= lastCol; i++ ) {
28623 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
28628 _getContainerSize : function()
28630 this.maxY = Math.max.apply( Math, this.colYs );
28635 if ( this.isFitWidth ) {
28636 size.width = this._getContainerFitWidth();
28642 _getContainerFitWidth : function()
28644 var unusedCols = 0;
28645 // count unused columns
28648 if ( this.colYs[i] !== 0 ) {
28653 // fit container to columns that have been used
28654 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
28657 needsResizeLayout : function()
28659 var previousWidth = this.containerWidth;
28660 this.getContainerWidth();
28661 return previousWidth !== this.containerWidth;