4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
23 * Do not use directly - it does not do anything..
24 * @param {Object} config The config object
29 Roo.bootstrap.Component = function(config){
30 Roo.bootstrap.Component.superclass.constructor.call(this, config);
34 * @event childrenrendered
35 * Fires when the children have been rendered..
36 * @param {Roo.bootstrap.Component} this
38 "childrenrendered" : true
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
50 allowDomMove : false, // to stop relocations in parent onRender...
60 * Initialize Events for the element
62 initEvents : function() { },
68 can_build_overlaid : true,
70 container_method : false,
77 // returns the parent component..
78 return Roo.ComponentMgr.get(this.parentId)
84 onRender : function(ct, position)
86 // Roo.log("Call onRender: " + this.xtype);
88 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
91 if (this.el.attr('xtype')) {
92 this.el.attr('xtypex', this.el.attr('xtype'));
93 this.el.dom.removeAttribute('xtype');
103 var cfg = Roo.apply({}, this.getAutoCreate());
104 cfg.id = this.id || Roo.id();
106 // fill in the extra attributes
107 if (this.xattr && typeof(this.xattr) =='object') {
108 for (var i in this.xattr) {
109 cfg[i] = this.xattr[i];
114 cfg.dataId = this.dataId;
118 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121 if (this.style) { // fixme needs to support more complex style data.
122 cfg.style = this.style;
126 cfg.name = this.name;
129 this.el = ct.createChild(cfg, position);
132 this.tooltipEl().attr('tooltip', this.tooltip);
135 if(this.tabIndex !== undefined){
136 this.el.dom.setAttribute('tabIndex', this.tabIndex);
143 * Fetch the element to add children to
144 * @return {Roo.Element} defaults to this.el
146 getChildContainer : function()
151 * Fetch the element to display the tooltip on.
152 * @return {Roo.Element} defaults to this.el
154 tooltipEl : function()
159 addxtype : function(tree,cntr)
163 cn = Roo.factory(tree);
165 cn.parentType = this.xtype; //??
166 cn.parentId = this.id;
168 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
169 if (typeof(cn.container_method) == 'string') {
170 cntr = cn.container_method;
174 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
176 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
178 var build_from_html = Roo.XComponent.build_from_html;
180 var is_body = (tree.xtype == 'Body') ;
182 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
184 var self_cntr_el = Roo.get(this[cntr](false));
186 // do not try and build conditional elements
187 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
191 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
192 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
193 return this.addxtypeChild(tree,cntr);
196 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202 Roo.log('skipping render');
208 if (!build_from_html) {
212 // this i think handles overlaying multiple children of the same type
213 // with the sam eelement.. - which might be buggy..
215 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
221 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
225 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
230 addxtypeChild : function (tree, cntr)
232 Roo.debug && Roo.log('addxtypeChild:' + cntr);
234 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
237 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
238 (typeof(tree['flexy:foreach']) != 'undefined');
242 skip_children = false;
243 // render the element if it's not BODY.
244 if (tree.xtype != 'Body') {
246 cn = Roo.factory(tree);
248 cn.parentType = this.xtype; //??
249 cn.parentId = this.id;
251 var build_from_html = Roo.XComponent.build_from_html;
254 // does the container contain child eleemnts with 'xtype' attributes.
255 // that match this xtype..
256 // note - when we render we create these as well..
257 // so we should check to see if body has xtype set.
258 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
260 var self_cntr_el = Roo.get(this[cntr](false));
261 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
263 //Roo.log(Roo.XComponent.build_from_html);
264 //Roo.log("got echild:");
267 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
268 // and are not displayed -this causes this to use up the wrong element when matching.
269 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
272 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
273 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
279 //echild.dom.removeAttribute('xtype');
281 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
282 Roo.debug && Roo.log(self_cntr_el);
283 Roo.debug && Roo.log(echild);
284 Roo.debug && Roo.log(cn);
290 // if object has flexy:if - then it may or may not be rendered.
291 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
292 // skip a flexy if element.
293 Roo.debug && Roo.log('skipping render');
294 Roo.debug && Roo.log(tree);
296 Roo.debug && Roo.log('skipping all children');
297 skip_children = true;
302 // actually if flexy:foreach is found, we really want to create
303 // multiple copies here...
305 //Roo.log(this[cntr]());
306 cn.render(this[cntr](true));
308 // then add the element..
316 if (typeof (tree.menu) != 'undefined') {
317 tree.menu.parentType = cn.xtype;
318 tree.menu.triggerEl = cn.el;
319 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
323 if (!tree.items || !tree.items.length) {
327 var items = tree.items;
330 //Roo.log(items.length);
332 if (!skip_children) {
333 for(var i =0;i < items.length;i++) {
334 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
340 this.fireEvent('childrenrendered', this);
345 * Show a component - removes 'hidden' class
350 this.el.removeClass('hidden');
354 * Hide a component - adds 'hidden' class
358 if (this.el && !this.el.hasClass('hidden')) {
359 this.el.addClass('hidden');
373 * @class Roo.bootstrap.Body
374 * @extends Roo.bootstrap.Component
375 * Bootstrap Body class
379 * @param {Object} config The config object
382 Roo.bootstrap.Body = function(config){
383 Roo.bootstrap.Body.superclass.constructor.call(this, config);
384 this.el = Roo.get(document.body);
385 if (this.cls && this.cls.length) {
386 Roo.get(document.body).addClass(this.cls);
390 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
395 onRender : function(ct, position)
397 /* Roo.log("Roo.bootstrap.Body - onRender");
398 if (this.cls && this.cls.length) {
399 Roo.get(document.body).addClass(this.cls);
419 * @class Roo.bootstrap.ButtonGroup
420 * @extends Roo.bootstrap.Component
421 * Bootstrap ButtonGroup class
422 * @cfg {String} size lg | sm | xs (default empty normal)
423 * @cfg {String} align vertical | justified (default none)
424 * @cfg {String} direction up | down (default down)
425 * @cfg {Boolean} toolbar false | true
426 * @cfg {Boolean} btn true | false
431 * @param {Object} config The config object
434 Roo.bootstrap.ButtonGroup = function(config){
435 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
438 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
446 getAutoCreate : function(){
452 cfg.html = this.html || cfg.html;
463 if (['vertical','justified'].indexOf(this.align)!==-1) {
464 cfg.cls = 'btn-group-' + this.align;
466 if (this.align == 'justified') {
467 console.log(this.items);
471 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
472 cfg.cls += ' btn-group-' + this.size;
475 if (this.direction == 'up') {
476 cfg.cls += ' dropup' ;
492 * @class Roo.bootstrap.Button
493 * @extends Roo.bootstrap.Component
494 * Bootstrap Button class
495 * @cfg {String} html The button content
496 * @cfg {String} weight ( primary | success | info | warning | danger | link ) default
497 * @cfg {String} size ( lg | sm | xs)
498 * @cfg {String} tag ( a | input | submit)
499 * @cfg {String} href empty or href
500 * @cfg {Boolean} disabled default false;
501 * @cfg {Boolean} isClose default false;
502 * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
503 * @cfg {String} badge text for badge
504 * @cfg {String} theme default
505 * @cfg {Boolean} inverse
506 * @cfg {Boolean} toggle
507 * @cfg {String} ontext text for on toggle state
508 * @cfg {String} offtext text for off toggle state
509 * @cfg {Boolean} defaulton
510 * @cfg {Boolean} preventDefault default true
511 * @cfg {Boolean} removeClass remove the standard class..
512 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
515 * Create a new button
516 * @param {Object} config The config object
520 Roo.bootstrap.Button = function(config){
521 Roo.bootstrap.Button.superclass.constructor.call(this, config);
526 * When a butotn is pressed
527 * @param {Roo.bootstrap.Button} this
528 * @param {Roo.EventObject} e
533 * After the button has been toggles
534 * @param {Roo.EventObject} e
535 * @param {boolean} pressed (also available as button.pressed)
541 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
559 preventDefault: true,
568 getAutoCreate : function(){
576 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
577 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
582 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
584 if (this.toggle == true) {
587 cls: 'slider-frame roo-button',
592 'data-off-text':'OFF',
593 cls: 'slider-button',
599 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
600 cfg.cls += ' '+this.weight;
609 cfg["aria-hidden"] = true;
611 cfg.html = "×";
617 if (this.theme==='default') {
618 cfg.cls = 'btn roo-button';
620 //if (this.parentType != 'Navbar') {
621 this.weight = this.weight.length ? this.weight : 'default';
623 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
625 cfg.cls += ' btn-' + this.weight;
627 } else if (this.theme==='glow') {
630 cfg.cls = 'btn-glow roo-button';
632 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
634 cfg.cls += ' ' + this.weight;
640 this.cls += ' inverse';
645 cfg.cls += ' active';
649 cfg.disabled = 'disabled';
653 Roo.log('changing to ul' );
655 this.glyphicon = 'caret';
658 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
660 //gsRoo.log(this.parentType);
661 if (this.parentType === 'Navbar' && !this.parent().bar) {
662 Roo.log('changing to li?');
671 href : this.href || '#'
674 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
675 cfg.cls += ' dropdown';
682 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
684 if (this.glyphicon) {
685 cfg.html = ' ' + cfg.html;
690 cls: 'glyphicon glyphicon-' + this.glyphicon
700 // cfg.cls='btn roo-button';
704 var value = cfg.html;
709 cls: 'glyphicon glyphicon-' + this.glyphicon,
728 cfg.cls += ' dropdown';
729 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
732 if (cfg.tag !== 'a' && this.href !== '') {
733 throw "Tag must be a to set href.";
734 } else if (this.href.length > 0) {
735 cfg.href = this.href;
738 if(this.removeClass){
743 cfg.target = this.target;
748 initEvents: function() {
749 // Roo.log('init events?');
750 // Roo.log(this.el.dom);
753 if (typeof (this.menu) != 'undefined') {
754 this.menu.parentType = this.xtype;
755 this.menu.triggerEl = this.el;
756 this.addxtype(Roo.apply({}, this.menu));
760 if (this.el.hasClass('roo-button')) {
761 this.el.on('click', this.onClick, this);
763 this.el.select('.roo-button').on('click', this.onClick, this);
766 if(this.removeClass){
767 this.el.on('click', this.onClick, this);
770 this.el.enableDisplayMode();
773 onClick : function(e)
780 Roo.log('button on click ');
781 if(this.preventDefault){
784 if (this.pressed === true || this.pressed === false) {
785 this.pressed = !this.pressed;
786 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
787 this.fireEvent('toggle', this, e, this.pressed);
791 this.fireEvent('click', this, e);
795 * Enables this button
799 this.disabled = false;
800 this.el.removeClass('disabled');
804 * Disable this button
808 this.disabled = true;
809 this.el.addClass('disabled');
812 * sets the active state on/off,
813 * @param {Boolean} state (optional) Force a particular state
815 setActive : function(v) {
817 this.el[v ? 'addClass' : 'removeClass']('active');
820 * toggles the current active state
822 toggleActive : function()
824 var active = this.el.hasClass('active');
825 this.setActive(!active);
829 setText : function(str)
831 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
835 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
858 * @class Roo.bootstrap.Column
859 * @extends Roo.bootstrap.Component
860 * Bootstrap Column class
861 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
862 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
863 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
864 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
865 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
866 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
867 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
868 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
871 * @cfg {Boolean} hidden (true|false) hide the element
872 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
873 * @cfg {String} fa (ban|check|...) font awesome icon
874 * @cfg {Number} fasize (1|2|....) font awsome size
876 * @cfg {String} icon (info-sign|check|...) glyphicon name
878 * @cfg {String} html content of column.
881 * Create a new Column
882 * @param {Object} config The config object
885 Roo.bootstrap.Column = function(config){
886 Roo.bootstrap.Column.superclass.constructor.call(this, config);
889 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
907 getAutoCreate : function(){
908 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
916 ['xs','sm','md','lg'].map(function(size){
917 //Roo.log( size + ':' + settings[size]);
919 if (settings[size+'off'] !== false) {
920 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
923 if (settings[size] === false) {
926 Roo.log(settings[size]);
927 if (!settings[size]) { // 0 = hidden
928 cfg.cls += ' hidden-' + size;
931 cfg.cls += ' col-' + size + '-' + settings[size];
936 cfg.cls += ' hidden';
939 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
940 cfg.cls +=' alert alert-' + this.alert;
944 if (this.html.length) {
945 cfg.html = this.html;
949 if (this.fasize > 1) {
950 fasize = ' fa-' + this.fasize + 'x';
952 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
957 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
976 * @class Roo.bootstrap.Container
977 * @extends Roo.bootstrap.Component
978 * Bootstrap Container class
979 * @cfg {Boolean} jumbotron is it a jumbotron element
980 * @cfg {String} html content of element
981 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
982 * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
983 * @cfg {String} header content of header (for panel)
984 * @cfg {String} footer content of footer (for panel)
985 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
986 * @cfg {String} tag (header|aside|section) type of HTML tag.
987 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
988 * @cfg {String} fa font awesome icon
989 * @cfg {String} icon (info-sign|check|...) glyphicon name
990 * @cfg {Boolean} hidden (true|false) hide the element
991 * @cfg {Boolean} expandable (true|false) default false
992 * @cfg {Boolean} expanded (true|false) default true
993 * @cfg {String} rheader contet on the right of header
997 * Create a new Container
998 * @param {Object} config The config object
1001 Roo.bootstrap.Container = function(config){
1002 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1008 * After the panel has been expand
1010 * @param {Roo.bootstrap.Container} this
1015 * After the panel has been collapsed
1017 * @param {Roo.bootstrap.Container} this
1023 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1040 getChildContainer : function() {
1046 if (this.panel.length) {
1047 return this.el.select('.panel-body',true).first();
1054 getAutoCreate : function(){
1057 tag : this.tag || 'div',
1061 if (this.jumbotron) {
1062 cfg.cls = 'jumbotron';
1067 // - this is applied by the parent..
1069 // cfg.cls = this.cls + '';
1072 if (this.sticky.length) {
1074 var bd = Roo.get(document.body);
1075 if (!bd.hasClass('bootstrap-sticky')) {
1076 bd.addClass('bootstrap-sticky');
1077 Roo.select('html',true).setStyle('height', '100%');
1080 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1084 if (this.well.length) {
1085 switch (this.well) {
1088 cfg.cls +=' well well-' +this.well;
1097 cfg.cls += ' hidden';
1101 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1102 cfg.cls +=' alert alert-' + this.alert;
1107 if (this.panel.length) {
1108 cfg.cls += ' panel panel-' + this.panel;
1110 if (this.header.length) {
1114 if(this.expandable){
1116 cfg.cls = cfg.cls + ' expandable';
1120 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1128 cls : 'panel-title',
1129 html : (this.expandable ? ' ' : '') + this.header
1133 cls: 'panel-header-right',
1139 cls : 'panel-heading',
1140 style : this.expandable ? 'cursor: pointer' : '',
1148 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1153 if (this.footer.length) {
1155 cls : 'panel-footer',
1164 body.html = this.html || cfg.html;
1165 // prefix with the icons..
1167 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1170 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1175 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1176 cfg.cls = 'container';
1182 initEvents: function()
1184 if(!this.expandable){
1188 var headerEl = this.headerEl();
1194 headerEl.on('click', this.onToggleClick, this);
1198 onToggleClick : function()
1200 var headerEl = this.headerEl();
1216 if(this.fireEvent('expand', this)) {
1218 this.expanded = true;
1220 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1222 this.el.select('.panel-body',true).first().removeClass('hide');
1224 var toggleEl = this.toggleEl();
1230 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1235 collapse : function()
1237 if(this.fireEvent('collapse', this)) {
1239 this.expanded = false;
1241 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1242 this.el.select('.panel-body',true).first().addClass('hide');
1244 var toggleEl = this.toggleEl();
1250 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1254 toggleEl : function()
1256 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1260 return this.el.select('.panel-heading .fa',true).first();
1263 headerEl : function()
1265 if(!this.el || !this.panel.length || !this.header.length){
1269 return this.el.select('.panel-heading',true).first()
1272 titleEl : function()
1274 if(!this.el || !this.panel.length || !this.header.length){
1278 return this.el.select('.panel-title',true).first();
1281 setTitle : function(v)
1283 var titleEl = this.titleEl();
1289 titleEl.dom.innerHTML = v;
1292 getTitle : function()
1295 var titleEl = this.titleEl();
1301 return titleEl.dom.innerHTML;
1304 setRightTitle : function(v)
1306 var t = this.el.select('.panel-header-right',true).first();
1312 t.dom.innerHTML = v;
1326 * @class Roo.bootstrap.Img
1327 * @extends Roo.bootstrap.Component
1328 * Bootstrap Img class
1329 * @cfg {Boolean} imgResponsive false | true
1330 * @cfg {String} border rounded | circle | thumbnail
1331 * @cfg {String} src image source
1332 * @cfg {String} alt image alternative text
1333 * @cfg {String} href a tag href
1334 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1335 * @cfg {String} xsUrl xs image source
1336 * @cfg {String} smUrl sm image source
1337 * @cfg {String} mdUrl md image source
1338 * @cfg {String} lgUrl lg image source
1341 * Create a new Input
1342 * @param {Object} config The config object
1345 Roo.bootstrap.Img = function(config){
1346 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1352 * The img click event for the img.
1353 * @param {Roo.EventObject} e
1359 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1361 imgResponsive: true,
1371 getAutoCreate : function()
1373 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1374 return this.createSingleImg();
1379 cls: 'roo-image-responsive-group',
1384 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1386 if(!_this[size + 'Url']){
1392 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1393 html: _this.html || cfg.html,
1394 src: _this[size + 'Url']
1397 img.cls += ' roo-image-responsive-' + size;
1399 var s = ['xs', 'sm', 'md', 'lg'];
1401 s.splice(s.indexOf(size), 1);
1403 Roo.each(s, function(ss){
1404 img.cls += ' hidden-' + ss;
1407 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1408 cfg.cls += ' img-' + _this.border;
1412 cfg.alt = _this.alt;
1425 a.target = _this.target;
1429 cfg.cn.push((_this.href) ? a : img);
1436 createSingleImg : function()
1440 cls: (this.imgResponsive) ? 'img-responsive' : '',
1444 cfg.html = this.html || cfg.html;
1446 cfg.src = this.src || cfg.src;
1448 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1449 cfg.cls += ' img-' + this.border;
1466 a.target = this.target;
1471 return (this.href) ? a : cfg;
1474 initEvents: function()
1477 this.el.on('click', this.onClick, this);
1482 onClick : function(e)
1484 Roo.log('img onclick');
1485 this.fireEvent('click', this, e);
1499 * @class Roo.bootstrap.Link
1500 * @extends Roo.bootstrap.Component
1501 * Bootstrap Link Class
1502 * @cfg {String} alt image alternative text
1503 * @cfg {String} href a tag href
1504 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1505 * @cfg {String} html the content of the link.
1506 * @cfg {String} anchor name for the anchor link
1508 * @cfg {Boolean} preventDefault (true | false) default false
1512 * Create a new Input
1513 * @param {Object} config The config object
1516 Roo.bootstrap.Link = function(config){
1517 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1523 * The img click event for the img.
1524 * @param {Roo.EventObject} e
1530 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1534 preventDefault: false,
1538 getAutoCreate : function()
1544 // anchor's do not require html/href...
1545 if (this.anchor === false) {
1546 cfg.html = this.html || '';
1547 cfg.href = this.href || '#';
1549 cfg.name = this.anchor;
1550 if (this.html !== false) {
1551 cfg.html = this.html;
1553 if (this.href !== false) {
1554 cfg.href = this.href;
1558 if(this.alt !== false){
1563 if(this.target !== false) {
1564 cfg.target = this.target;
1570 initEvents: function() {
1572 if(!this.href || this.preventDefault){
1573 this.el.on('click', this.onClick, this);
1577 onClick : function(e)
1579 if(this.preventDefault){
1582 //Roo.log('img onclick');
1583 this.fireEvent('click', this, e);
1596 * @class Roo.bootstrap.Header
1597 * @extends Roo.bootstrap.Component
1598 * Bootstrap Header class
1599 * @cfg {String} html content of header
1600 * @cfg {Number} level (1|2|3|4|5|6) default 1
1603 * Create a new Header
1604 * @param {Object} config The config object
1608 Roo.bootstrap.Header = function(config){
1609 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1612 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1620 getAutoCreate : function(){
1625 tag: 'h' + (1 *this.level),
1626 html: this.html || ''
1638 * Ext JS Library 1.1.1
1639 * Copyright(c) 2006-2007, Ext JS, LLC.
1641 * Originally Released Under LGPL - original licence link has changed is not relivant.
1644 * <script type="text/javascript">
1648 * @class Roo.bootstrap.MenuMgr
1649 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1652 Roo.bootstrap.MenuMgr = function(){
1653 var menus, active, groups = {}, attached = false, lastShow = new Date();
1655 // private - called when first menu is created
1658 active = new Roo.util.MixedCollection();
1659 Roo.get(document).addKeyListener(27, function(){
1660 if(active.length > 0){
1668 if(active && active.length > 0){
1669 var c = active.clone();
1679 if(active.length < 1){
1680 Roo.get(document).un("mouseup", onMouseDown);
1688 var last = active.last();
1689 lastShow = new Date();
1692 Roo.get(document).on("mouseup", onMouseDown);
1697 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1698 m.parentMenu.activeChild = m;
1699 }else if(last && last.isVisible()){
1700 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1705 function onBeforeHide(m){
1707 m.activeChild.hide();
1709 if(m.autoHideTimer){
1710 clearTimeout(m.autoHideTimer);
1711 delete m.autoHideTimer;
1716 function onBeforeShow(m){
1717 var pm = m.parentMenu;
1718 if(!pm && !m.allowOtherMenus){
1720 }else if(pm && pm.activeChild && active != m){
1721 pm.activeChild.hide();
1725 // private this should really trigger on mouseup..
1726 function onMouseDown(e){
1727 Roo.log("on Mouse Up");
1728 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1738 function onBeforeCheck(mi, state){
1740 var g = groups[mi.group];
1741 for(var i = 0, l = g.length; i < l; i++){
1743 g[i].setChecked(false);
1752 * Hides all menus that are currently visible
1754 hideAll : function(){
1759 register : function(menu){
1763 menus[menu.id] = menu;
1764 menu.on("beforehide", onBeforeHide);
1765 menu.on("hide", onHide);
1766 menu.on("beforeshow", onBeforeShow);
1767 menu.on("show", onShow);
1769 if(g && menu.events["checkchange"]){
1773 groups[g].push(menu);
1774 menu.on("checkchange", onCheck);
1779 * Returns a {@link Roo.menu.Menu} object
1780 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1781 * be used to generate and return a new Menu instance.
1783 get : function(menu){
1784 if(typeof menu == "string"){ // menu id
1786 }else if(menu.events){ // menu instance
1789 /*else if(typeof menu.length == 'number'){ // array of menu items?
1790 return new Roo.bootstrap.Menu({items:menu});
1791 }else{ // otherwise, must be a config
1792 return new Roo.bootstrap.Menu(menu);
1799 unregister : function(menu){
1800 delete menus[menu.id];
1801 menu.un("beforehide", onBeforeHide);
1802 menu.un("hide", onHide);
1803 menu.un("beforeshow", onBeforeShow);
1804 menu.un("show", onShow);
1806 if(g && menu.events["checkchange"]){
1807 groups[g].remove(menu);
1808 menu.un("checkchange", onCheck);
1813 registerCheckable : function(menuItem){
1814 var g = menuItem.group;
1819 groups[g].push(menuItem);
1820 menuItem.on("beforecheckchange", onBeforeCheck);
1825 unregisterCheckable : function(menuItem){
1826 var g = menuItem.group;
1828 groups[g].remove(menuItem);
1829 menuItem.un("beforecheckchange", onBeforeCheck);
1841 * @class Roo.bootstrap.Menu
1842 * @extends Roo.bootstrap.Component
1843 * Bootstrap Menu class - container for MenuItems
1844 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1848 * @param {Object} config The config object
1852 Roo.bootstrap.Menu = function(config){
1853 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1854 if (this.registerMenu) {
1855 Roo.bootstrap.MenuMgr.register(this);
1860 * Fires before this menu is displayed
1861 * @param {Roo.menu.Menu} this
1866 * Fires before this menu is hidden
1867 * @param {Roo.menu.Menu} this
1872 * Fires after this menu is displayed
1873 * @param {Roo.menu.Menu} this
1878 * Fires after this menu is hidden
1879 * @param {Roo.menu.Menu} this
1884 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1885 * @param {Roo.menu.Menu} this
1886 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1887 * @param {Roo.EventObject} e
1892 * Fires when the mouse is hovering over this menu
1893 * @param {Roo.menu.Menu} this
1894 * @param {Roo.EventObject} e
1895 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1900 * Fires when the mouse exits this menu
1901 * @param {Roo.menu.Menu} this
1902 * @param {Roo.EventObject} e
1903 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1908 * Fires when a menu item contained in this menu is clicked
1909 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1910 * @param {Roo.EventObject} e
1914 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1917 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1921 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1924 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1926 registerMenu : true,
1928 menuItems :false, // stores the menu items..
1934 getChildContainer : function() {
1938 getAutoCreate : function(){
1940 //if (['right'].indexOf(this.align)!==-1) {
1941 // cfg.cn[1].cls += ' pull-right'
1947 cls : 'dropdown-menu' ,
1948 style : 'z-index:1000'
1952 if (this.type === 'submenu') {
1953 cfg.cls = 'submenu active';
1955 if (this.type === 'treeview') {
1956 cfg.cls = 'treeview-menu';
1961 initEvents : function() {
1963 // Roo.log("ADD event");
1964 // Roo.log(this.triggerEl.dom);
1965 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1967 this.triggerEl.addClass('dropdown-toggle');
1973 this.el.on('touchstart' , this.onTouch, this);
1975 this.el.on('click' , this.onClick, this);
1977 this.el.on("mouseover", this.onMouseOver, this);
1978 this.el.on("mouseout", this.onMouseOut, this);
1982 findTargetItem : function(e){
1983 var t = e.getTarget(".dropdown-menu-item", this.el, true);
1987 //Roo.log(t); Roo.log(t.id);
1989 //Roo.log(this.menuitems);
1990 return this.menuitems.get(t.id);
1992 //return this.items.get(t.menuItemId);
1998 onTouch : function(e) {
2003 onClick : function(e){
2004 Roo.log("menu.onClick");
2005 var t = this.findTargetItem(e);
2006 if(!t || t.isContainer){
2011 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2012 if(t == this.activeItem && t.shouldDeactivate(e)){
2013 this.activeItem.deactivate();
2014 delete this.activeItem;
2018 this.setActiveItem(t, true);
2026 Roo.log('pass click event');
2030 this.fireEvent("click", this, t, e);
2034 onMouseOver : function(e){
2035 var t = this.findTargetItem(e);
2038 // if(t.canActivate && !t.disabled){
2039 // this.setActiveItem(t, true);
2043 this.fireEvent("mouseover", this, e, t);
2045 isVisible : function(){
2046 return !this.hidden;
2048 onMouseOut : function(e){
2049 var t = this.findTargetItem(e);
2052 // if(t == this.activeItem && t.shouldDeactivate(e)){
2053 // this.activeItem.deactivate();
2054 // delete this.activeItem;
2057 this.fireEvent("mouseout", this, e, t);
2062 * Displays this menu relative to another element
2063 * @param {String/HTMLElement/Roo.Element} element The element to align to
2064 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2065 * the element (defaults to this.defaultAlign)
2066 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2068 show : function(el, pos, parentMenu){
2069 this.parentMenu = parentMenu;
2073 this.fireEvent("beforeshow", this);
2074 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2077 * Displays this menu at a specific xy position
2078 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2079 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2081 showAt : function(xy, parentMenu, /* private: */_e){
2082 this.parentMenu = parentMenu;
2087 this.fireEvent("beforeshow", this);
2088 //xy = this.el.adjustForConstraints(xy);
2092 this.hideMenuItems();
2093 this.hidden = false;
2094 this.triggerEl.addClass('open');
2096 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2097 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2102 this.fireEvent("show", this);
2108 this.doFocus.defer(50, this);
2112 doFocus : function(){
2114 this.focusEl.focus();
2119 * Hides this menu and optionally all parent menus
2120 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2122 hide : function(deep){
2124 this.hideMenuItems();
2125 if(this.el && this.isVisible()){
2126 this.fireEvent("beforehide", this);
2127 if(this.activeItem){
2128 this.activeItem.deactivate();
2129 this.activeItem = null;
2131 this.triggerEl.removeClass('open');;
2133 this.fireEvent("hide", this);
2135 if(deep === true && this.parentMenu){
2136 this.parentMenu.hide(true);
2140 onTriggerPress : function(e)
2143 Roo.log('trigger press');
2144 //Roo.log(e.getTarget());
2145 // Roo.log(this.triggerEl.dom);
2146 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2150 if (this.isVisible()) {
2155 this.show(this.triggerEl, false, false);
2164 hideMenuItems : function()
2166 //$(backdrop).remove()
2167 Roo.select('.open',true).each(function(aa) {
2169 aa.removeClass('open');
2170 //var parent = getParent($(this))
2171 //var relatedTarget = { relatedTarget: this }
2173 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2174 //if (e.isDefaultPrevented()) return
2175 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2178 addxtypeChild : function (tree, cntr) {
2179 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2181 this.menuitems.add(comp);
2202 * @class Roo.bootstrap.MenuItem
2203 * @extends Roo.bootstrap.Component
2204 * Bootstrap MenuItem class
2205 * @cfg {String} html the menu label
2206 * @cfg {String} href the link
2207 * @cfg {Boolean} preventDefault (true | false) default true
2208 * @cfg {Boolean} isContainer (true | false) default false
2212 * Create a new MenuItem
2213 * @param {Object} config The config object
2217 Roo.bootstrap.MenuItem = function(config){
2218 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2223 * The raw click event for the entire grid.
2224 * @param {Roo.bootstrap.MenuItem} this
2225 * @param {Roo.EventObject} e
2231 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2235 preventDefault: true,
2236 isContainer : false,
2238 getAutoCreate : function(){
2240 if(this.isContainer){
2243 cls: 'dropdown-menu-item'
2249 cls: 'dropdown-menu-item',
2258 if (this.parent().type == 'treeview') {
2259 cfg.cls = 'treeview-menu';
2262 cfg.cn[0].href = this.href || cfg.cn[0].href ;
2263 cfg.cn[0].html = this.html || cfg.cn[0].html ;
2267 initEvents: function() {
2269 //this.el.select('a').on('click', this.onClick, this);
2272 onClick : function(e)
2274 Roo.log('item on click ');
2275 //if(this.preventDefault){
2276 // e.preventDefault();
2278 //this.parent().hideMenuItems();
2280 this.fireEvent('click', this, e);
2299 * @class Roo.bootstrap.MenuSeparator
2300 * @extends Roo.bootstrap.Component
2301 * Bootstrap MenuSeparator class
2304 * Create a new MenuItem
2305 * @param {Object} config The config object
2309 Roo.bootstrap.MenuSeparator = function(config){
2310 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2313 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2315 getAutoCreate : function(){
2334 * @class Roo.bootstrap.Modal
2335 * @extends Roo.bootstrap.Component
2336 * Bootstrap Modal class
2337 * @cfg {String} title Title of dialog
2338 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2339 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2340 * @cfg {Boolean} specificTitle default false
2341 * @cfg {Array} buttons Array of buttons or standard button set..
2342 * @cfg {String} buttonPosition (left|right|center) default right
2343 * @cfg {Boolean} animate default true
2344 * @cfg {Boolean} allow_close default true
2347 * Create a new Modal Dialog
2348 * @param {Object} config The config object
2351 Roo.bootstrap.Modal = function(config){
2352 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2357 * The raw btnclick event for the button
2358 * @param {Roo.EventObject} e
2362 this.buttons = this.buttons || [];
2365 this.tmpl = Roo.factory(this.tmpl);
2370 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2372 title : 'test dialog',
2382 specificTitle: false,
2384 buttonPosition: 'right',
2398 onRender : function(ct, position)
2400 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2403 var cfg = Roo.apply({}, this.getAutoCreate());
2406 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2408 //if (!cfg.name.length) {
2412 cfg.cls += ' ' + this.cls;
2415 cfg.style = this.style;
2417 this.el = Roo.get(document.body).createChild(cfg, position);
2419 //var type = this.el.dom.type;
2424 if(this.tabIndex !== undefined){
2425 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2429 this.bodyEl = this.el.select('.modal-body',true).first();
2430 this.closeEl = this.el.select('.modal-header .close', true).first();
2431 this.footerEl = this.el.select('.modal-footer',true).first();
2432 this.titleEl = this.el.select('.modal-title',true).first();
2436 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2437 this.maskEl.enableDisplayMode("block");
2439 //this.el.addClass("x-dlg-modal");
2441 if (this.buttons.length) {
2442 Roo.each(this.buttons, function(bb) {
2443 var b = Roo.apply({}, bb);
2444 b.xns = b.xns || Roo.bootstrap;
2445 b.xtype = b.xtype || 'Button';
2446 if (typeof(b.listeners) == 'undefined') {
2447 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2450 var btn = Roo.factory(b);
2452 btn.onRender(this.el.select('.modal-footer div').first());
2456 // render the children.
2459 if(typeof(this.items) != 'undefined'){
2460 var items = this.items;
2463 for(var i =0;i < items.length;i++) {
2464 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2468 this.items = nitems;
2470 // where are these used - they used to be body/close/footer
2474 //this.el.addClass([this.fieldClass, this.cls]);
2478 getAutoCreate : function(){
2483 html : this.html || ''
2488 cls : 'modal-title',
2492 if(this.specificTitle){
2498 if (this.allow_close) {
2509 style : 'display: none',
2512 cls: "modal-dialog",
2515 cls : "modal-content",
2518 cls : 'modal-header',
2523 cls : 'modal-footer',
2527 cls: 'btn-' + this.buttonPosition
2544 modal.cls += ' fade';
2550 getChildContainer : function() {
2555 getButtonContainer : function() {
2556 return this.el.select('.modal-footer div',true).first();
2559 initEvents : function()
2561 if (this.allow_close) {
2562 this.closeEl.on('click', this.hide, this);
2567 window.addEventListener("resize", function() { _this.resize(); } );
2573 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2578 if (!this.rendered) {
2582 this.el.setStyle('display', 'block');
2586 (function(){ _this.el.addClass('in'); }).defer(50);
2588 this.el.addClass('in');
2591 // not sure how we can show data in here..
2593 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2596 Roo.get(document.body).addClass("x-body-masked");
2597 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2599 this.el.setStyle('zIndex', '10001');
2601 this.fireEvent('show', this);
2608 Roo.get(document.body).removeClass("x-body-masked");
2609 this.el.removeClass('in');
2613 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2615 this.el.setStyle('display', 'none');
2618 this.fireEvent('hide', this);
2621 addButton : function(str, cb)
2625 var b = Roo.apply({}, { html : str } );
2626 b.xns = b.xns || Roo.bootstrap;
2627 b.xtype = b.xtype || 'Button';
2628 if (typeof(b.listeners) == 'undefined') {
2629 b.listeners = { click : cb.createDelegate(this) };
2632 var btn = Roo.factory(b);
2634 btn.onRender(this.el.select('.modal-footer div').first());
2640 setDefaultButton : function(btn)
2642 //this.el.select('.modal-footer').()
2644 resizeTo: function(w,h)
2648 setContentSize : function(w, h)
2652 onButtonClick: function(btn,e)
2655 this.fireEvent('btnclick', btn.name, e);
2658 * Set the title of the Dialog
2659 * @param {String} str new Title
2661 setTitle: function(str) {
2662 this.titleEl.dom.innerHTML = str;
2665 * Set the body of the Dialog
2666 * @param {String} str new Title
2668 setBody: function(str) {
2669 this.bodyEl.dom.innerHTML = str;
2672 * Set the body of the Dialog using the template
2673 * @param {Obj} data - apply this data to the template and replace the body contents.
2675 applyBody: function(obj)
2678 Roo.log("Error - using apply Body without a template");
2681 this.tmpl.overwrite(this.bodyEl, obj);
2687 Roo.apply(Roo.bootstrap.Modal, {
2689 * Button config that displays a single OK button
2698 * Button config that displays Yes and No buttons
2714 * Button config that displays OK and Cancel buttons
2729 * Button config that displays Yes, No and Cancel buttons
2752 * messagebox - can be used as a replace
2756 * @class Roo.MessageBox
2757 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2761 Roo.Msg.alert('Status', 'Changes saved successfully.');
2763 // Prompt for user data:
2764 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2766 // process text value...
2770 // Show a dialog using config options:
2772 title:'Save Changes?',
2773 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2774 buttons: Roo.Msg.YESNOCANCEL,
2781 Roo.bootstrap.MessageBox = function(){
2782 var dlg, opt, mask, waitTimer;
2783 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2784 var buttons, activeTextEl, bwidth;
2788 var handleButton = function(button){
2790 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2794 var handleHide = function(){
2796 dlg.el.removeClass(opt.cls);
2799 // Roo.TaskMgr.stop(waitTimer);
2800 // waitTimer = null;
2805 var updateButtons = function(b){
2808 buttons["ok"].hide();
2809 buttons["cancel"].hide();
2810 buttons["yes"].hide();
2811 buttons["no"].hide();
2812 //dlg.footer.dom.style.display = 'none';
2815 dlg.footerEl.dom.style.display = '';
2816 for(var k in buttons){
2817 if(typeof buttons[k] != "function"){
2820 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2821 width += buttons[k].el.getWidth()+15;
2831 var handleEsc = function(d, k, e){
2832 if(opt && opt.closable !== false){
2842 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2843 * @return {Roo.BasicDialog} The BasicDialog element
2845 getDialog : function(){
2847 dlg = new Roo.bootstrap.Modal( {
2850 //constraintoviewport:false,
2852 //collapsible : false,
2857 //buttonAlign:"center",
2858 closeClick : function(){
2859 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2862 handleButton("cancel");
2867 dlg.on("hide", handleHide);
2869 //dlg.addKeyListener(27, handleEsc);
2871 this.buttons = buttons;
2872 var bt = this.buttonText;
2873 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2874 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2875 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2876 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2878 bodyEl = dlg.bodyEl.createChild({
2880 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2881 '<textarea class="roo-mb-textarea"></textarea>' +
2882 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2884 msgEl = bodyEl.dom.firstChild;
2885 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2886 textboxEl.enableDisplayMode();
2887 textboxEl.addKeyListener([10,13], function(){
2888 if(dlg.isVisible() && opt && opt.buttons){
2891 }else if(opt.buttons.yes){
2892 handleButton("yes");
2896 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2897 textareaEl.enableDisplayMode();
2898 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2899 progressEl.enableDisplayMode();
2900 var pf = progressEl.dom.firstChild;
2902 pp = Roo.get(pf.firstChild);
2903 pp.setHeight(pf.offsetHeight);
2911 * Updates the message box body text
2912 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2913 * the XHTML-compliant non-breaking space character '&#160;')
2914 * @return {Roo.MessageBox} This message box
2916 updateText : function(text){
2917 if(!dlg.isVisible() && !opt.width){
2918 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2920 msgEl.innerHTML = text || ' ';
2922 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2923 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2925 Math.min(opt.width || cw , this.maxWidth),
2926 Math.max(opt.minWidth || this.minWidth, bwidth)
2929 activeTextEl.setWidth(w);
2931 if(dlg.isVisible()){
2932 dlg.fixedcenter = false;
2934 // to big, make it scroll. = But as usual stupid IE does not support
2937 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2938 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2939 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2941 bodyEl.dom.style.height = '';
2942 bodyEl.dom.style.overflowY = '';
2945 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2947 bodyEl.dom.style.overflowX = '';
2950 dlg.setContentSize(w, bodyEl.getHeight());
2951 if(dlg.isVisible()){
2952 dlg.fixedcenter = true;
2958 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
2959 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2960 * @param {Number} value Any number between 0 and 1 (e.g., .5)
2961 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2962 * @return {Roo.MessageBox} This message box
2964 updateProgress : function(value, text){
2966 this.updateText(text);
2968 if (pp) { // weird bug on my firefox - for some reason this is not defined
2969 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2975 * Returns true if the message box is currently displayed
2976 * @return {Boolean} True if the message box is visible, else false
2978 isVisible : function(){
2979 return dlg && dlg.isVisible();
2983 * Hides the message box if it is displayed
2986 if(this.isVisible()){
2992 * Displays a new message box, or reinitializes an existing message box, based on the config options
2993 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2994 * The following config object properties are supported:
2996 Property Type Description
2997 ---------- --------------- ------------------------------------------------------------------------------------
2998 animEl String/Element An id or Element from which the message box should animate as it opens and
2999 closes (defaults to undefined)
3000 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3001 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3002 closable Boolean False to hide the top-right close button (defaults to true). Note that
3003 progress and wait dialogs will ignore this property and always hide the
3004 close button as they can only be closed programmatically.
3005 cls String A custom CSS class to apply to the message box element
3006 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3007 displayed (defaults to 75)
3008 fn Function A callback function to execute after closing the dialog. The arguments to the
3009 function will be btn (the name of the button that was clicked, if applicable,
3010 e.g. "ok"), and text (the value of the active text field, if applicable).
3011 Progress and wait dialogs will ignore this option since they do not respond to
3012 user actions and can only be closed programmatically, so any required function
3013 should be called by the same code after it closes the dialog.
3014 icon String A CSS class that provides a background image to be used as an icon for
3015 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3016 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3017 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3018 modal Boolean False to allow user interaction with the page while the message box is
3019 displayed (defaults to true)
3020 msg String A string that will replace the existing message box body text (defaults
3021 to the XHTML-compliant non-breaking space character ' ')
3022 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3023 progress Boolean True to display a progress bar (defaults to false)
3024 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3025 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3026 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3027 title String The title text
3028 value String The string value to set into the active textbox element if displayed
3029 wait Boolean True to display a progress bar (defaults to false)
3030 width Number The width of the dialog in pixels
3037 msg: 'Please enter your address:',
3039 buttons: Roo.MessageBox.OKCANCEL,
3042 animEl: 'addAddressBtn'
3045 * @param {Object} config Configuration options
3046 * @return {Roo.MessageBox} This message box
3048 show : function(options)
3051 // this causes nightmares if you show one dialog after another
3052 // especially on callbacks..
3054 if(this.isVisible()){
3057 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3058 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3059 Roo.log("New Dialog Message:" + options.msg )
3060 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3061 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3064 var d = this.getDialog();
3066 d.setTitle(opt.title || " ");
3067 d.closeEl.setDisplayed(opt.closable !== false);
3068 activeTextEl = textboxEl;
3069 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3074 textareaEl.setHeight(typeof opt.multiline == "number" ?
3075 opt.multiline : this.defaultTextHeight);
3076 activeTextEl = textareaEl;
3085 progressEl.setDisplayed(opt.progress === true);
3086 this.updateProgress(0);
3087 activeTextEl.dom.value = opt.value || "";
3089 dlg.setDefaultButton(activeTextEl);
3091 var bs = opt.buttons;
3095 }else if(bs && bs.yes){
3096 db = buttons["yes"];
3098 dlg.setDefaultButton(db);
3100 bwidth = updateButtons(opt.buttons);
3101 this.updateText(opt.msg);
3103 d.el.addClass(opt.cls);
3105 d.proxyDrag = opt.proxyDrag === true;
3106 d.modal = opt.modal !== false;
3107 d.mask = opt.modal !== false ? mask : false;
3109 // force it to the end of the z-index stack so it gets a cursor in FF
3110 document.body.appendChild(dlg.el.dom);
3111 d.animateTarget = null;
3112 d.show(options.animEl);
3118 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3119 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3120 * and closing the message box when the process is complete.
3121 * @param {String} title The title bar text
3122 * @param {String} msg The message box body text
3123 * @return {Roo.MessageBox} This message box
3125 progress : function(title, msg){
3132 minWidth: this.minProgressWidth,
3139 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3140 * If a callback function is passed it will be called after the user clicks the button, and the
3141 * id of the button that was clicked will be passed as the only parameter to the callback
3142 * (could also be the top-right close button).
3143 * @param {String} title The title bar text
3144 * @param {String} msg The message box body text
3145 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3146 * @param {Object} scope (optional) The scope of the callback function
3147 * @return {Roo.MessageBox} This message box
3149 alert : function(title, msg, fn, scope){
3162 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3163 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3164 * You are responsible for closing the message box when the process is complete.
3165 * @param {String} msg The message box body text
3166 * @param {String} title (optional) The title bar text
3167 * @return {Roo.MessageBox} This message box
3169 wait : function(msg, title){
3180 waitTimer = Roo.TaskMgr.start({
3182 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3190 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3191 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3192 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3193 * @param {String} title The title bar text
3194 * @param {String} msg The message box body text
3195 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3196 * @param {Object} scope (optional) The scope of the callback function
3197 * @return {Roo.MessageBox} This message box
3199 confirm : function(title, msg, fn, scope){
3203 buttons: this.YESNO,
3212 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3213 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3214 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3215 * (could also be the top-right close button) and the text that was entered will be passed as the two
3216 * parameters to the callback.
3217 * @param {String} title The title bar text
3218 * @param {String} msg The message box body text
3219 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3220 * @param {Object} scope (optional) The scope of the callback function
3221 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3222 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3223 * @return {Roo.MessageBox} This message box
3225 prompt : function(title, msg, fn, scope, multiline){
3229 buttons: this.OKCANCEL,
3234 multiline: multiline,
3241 * Button config that displays a single OK button
3246 * Button config that displays Yes and No buttons
3249 YESNO : {yes:true, no:true},
3251 * Button config that displays OK and Cancel buttons
3254 OKCANCEL : {ok:true, cancel:true},
3256 * Button config that displays Yes, No and Cancel buttons
3259 YESNOCANCEL : {yes:true, no:true, cancel:true},
3262 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3265 defaultTextHeight : 75,
3267 * The maximum width in pixels of the message box (defaults to 600)
3272 * The minimum width in pixels of the message box (defaults to 100)
3277 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3278 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3281 minProgressWidth : 250,
3283 * An object containing the default button text strings that can be overriden for localized language support.
3284 * Supported properties are: ok, cancel, yes and no.
3285 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3298 * Shorthand for {@link Roo.MessageBox}
3300 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3301 Roo.Msg = Roo.Msg || Roo.MessageBox;
3310 * @class Roo.bootstrap.Navbar
3311 * @extends Roo.bootstrap.Component
3312 * Bootstrap Navbar class
3315 * Create a new Navbar
3316 * @param {Object} config The config object
3320 Roo.bootstrap.Navbar = function(config){
3321 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3325 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3334 getAutoCreate : function(){
3337 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3341 initEvents :function ()
3343 //Roo.log(this.el.select('.navbar-toggle',true));
3344 this.el.select('.navbar-toggle',true).on('click', function() {
3345 // Roo.log('click');
3346 this.el.select('.navbar-collapse',true).toggleClass('in');
3354 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3356 var size = this.el.getSize();
3357 this.maskEl.setSize(size.width, size.height);
3358 this.maskEl.enableDisplayMode("block");
3367 getChildContainer : function()
3369 if (this.el.select('.collapse').getCount()) {
3370 return this.el.select('.collapse',true).first();
3403 * @class Roo.bootstrap.NavSimplebar
3404 * @extends Roo.bootstrap.Navbar
3405 * Bootstrap Sidebar class
3407 * @cfg {Boolean} inverse is inverted color
3409 * @cfg {String} type (nav | pills | tabs)
3410 * @cfg {Boolean} arrangement stacked | justified
3411 * @cfg {String} align (left | right) alignment
3413 * @cfg {Boolean} main (true|false) main nav bar? default false
3414 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3416 * @cfg {String} tag (header|footer|nav|div) default is nav
3422 * Create a new Sidebar
3423 * @param {Object} config The config object
3427 Roo.bootstrap.NavSimplebar = function(config){
3428 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3431 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3447 getAutoCreate : function(){
3451 tag : this.tag || 'div',
3464 this.type = this.type || 'nav';
3465 if (['tabs','pills'].indexOf(this.type)!==-1) {
3466 cfg.cn[0].cls += ' nav-' + this.type
3470 if (this.type!=='nav') {
3471 Roo.log('nav type must be nav/tabs/pills')
3473 cfg.cn[0].cls += ' navbar-nav'
3479 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3480 cfg.cn[0].cls += ' nav-' + this.arrangement;
3484 if (this.align === 'right') {
3485 cfg.cn[0].cls += ' navbar-right';
3489 cfg.cls += ' navbar-inverse';
3516 * @class Roo.bootstrap.NavHeaderbar
3517 * @extends Roo.bootstrap.NavSimplebar
3518 * Bootstrap Sidebar class
3520 * @cfg {String} brand what is brand
3521 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3522 * @cfg {String} brand_href href of the brand
3523 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3524 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3525 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3526 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3529 * Create a new Sidebar
3530 * @param {Object} config The config object
3534 Roo.bootstrap.NavHeaderbar = function(config){
3535 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3539 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3546 desktopCenter : false,
3549 getAutoCreate : function(){
3552 tag: this.nav || 'nav',
3559 if (this.desktopCenter) {
3560 cn.push({cls : 'container', cn : []});
3567 cls: 'navbar-header',
3572 cls: 'navbar-toggle',
3573 'data-toggle': 'collapse',
3578 html: 'Toggle navigation'
3600 cls: 'collapse navbar-collapse',
3604 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3606 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3607 cfg.cls += ' navbar-' + this.position;
3609 // tag can override this..
3611 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3614 if (this.brand !== '') {
3617 href: this.brand_href ? this.brand_href : '#',
3618 cls: 'navbar-brand',
3626 cfg.cls += ' main-nav';
3634 getHeaderChildContainer : function()
3636 if (this.el.select('.navbar-header').getCount()) {
3637 return this.el.select('.navbar-header',true).first();
3640 return this.getChildContainer();
3644 initEvents : function()
3646 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3648 if (this.autohide) {
3653 Roo.get(document).on('scroll',function(e) {
3654 var ns = Roo.get(document).getScroll().top;
3655 var os = prevScroll;
3659 ft.removeClass('slideDown');
3660 ft.addClass('slideUp');
3663 ft.removeClass('slideUp');
3664 ft.addClass('slideDown');
3685 * @class Roo.bootstrap.NavSidebar
3686 * @extends Roo.bootstrap.Navbar
3687 * Bootstrap Sidebar class
3690 * Create a new Sidebar
3691 * @param {Object} config The config object
3695 Roo.bootstrap.NavSidebar = function(config){
3696 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3699 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3701 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3703 getAutoCreate : function(){
3708 cls: 'sidebar sidebar-nav'
3730 * @class Roo.bootstrap.NavGroup
3731 * @extends Roo.bootstrap.Component
3732 * Bootstrap NavGroup class
3733 * @cfg {String} align (left|right)
3734 * @cfg {Boolean} inverse
3735 * @cfg {String} type (nav|pills|tab) default nav
3736 * @cfg {String} navId - reference Id for navbar.
3740 * Create a new nav group
3741 * @param {Object} config The config object
3744 Roo.bootstrap.NavGroup = function(config){
3745 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3748 Roo.bootstrap.NavGroup.register(this);
3752 * Fires when the active item changes
3753 * @param {Roo.bootstrap.NavGroup} this
3754 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3755 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3762 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3773 getAutoCreate : function()
3775 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3782 if (['tabs','pills'].indexOf(this.type)!==-1) {
3783 cfg.cls += ' nav-' + this.type
3785 if (this.type!=='nav') {
3786 Roo.log('nav type must be nav/tabs/pills')
3788 cfg.cls += ' navbar-nav'
3791 if (this.parent().sidebar) {
3794 cls: 'dashboard-menu sidebar-menu'
3800 if (this.form === true) {
3806 if (this.align === 'right') {
3807 cfg.cls += ' navbar-right';
3809 cfg.cls += ' navbar-left';
3813 if (this.align === 'right') {
3814 cfg.cls += ' navbar-right';
3818 cfg.cls += ' navbar-inverse';
3826 * sets the active Navigation item
3827 * @param {Roo.bootstrap.NavItem} the new current navitem
3829 setActiveItem : function(item)
3832 Roo.each(this.navItems, function(v){
3837 v.setActive(false, true);
3844 item.setActive(true, true);
3845 this.fireEvent('changed', this, item, prev);
3850 * gets the active Navigation item
3851 * @return {Roo.bootstrap.NavItem} the current navitem
3853 getActive : function()
3857 Roo.each(this.navItems, function(v){
3868 indexOfNav : function()
3872 Roo.each(this.navItems, function(v,i){
3883 * adds a Navigation item
3884 * @param {Roo.bootstrap.NavItem} the navitem to add
3886 addItem : function(cfg)
3888 var cn = new Roo.bootstrap.NavItem(cfg);
3890 cn.parentId = this.id;
3891 cn.onRender(this.el, null);
3895 * register a Navigation item
3896 * @param {Roo.bootstrap.NavItem} the navitem to add
3898 register : function(item)
3900 this.navItems.push( item);
3901 item.navId = this.navId;
3906 * clear all the Navigation item
3909 clearAll : function()
3912 this.el.dom.innerHTML = '';
3915 getNavItem: function(tabId)
3918 Roo.each(this.navItems, function(e) {
3919 if (e.tabId == tabId) {
3929 setActiveNext : function()
3931 var i = this.indexOfNav(this.getActive());
3932 if (i > this.navItems.length) {
3935 this.setActiveItem(this.navItems[i+1]);
3937 setActivePrev : function()
3939 var i = this.indexOfNav(this.getActive());
3943 this.setActiveItem(this.navItems[i-1]);
3945 clearWasActive : function(except) {
3946 Roo.each(this.navItems, function(e) {
3947 if (e.tabId != except.tabId && e.was_active) {
3948 e.was_active = false;
3955 getWasActive : function ()
3958 Roo.each(this.navItems, function(e) {
3973 Roo.apply(Roo.bootstrap.NavGroup, {
3977 * register a Navigation Group
3978 * @param {Roo.bootstrap.NavGroup} the navgroup to add
3980 register : function(navgrp)
3982 this.groups[navgrp.navId] = navgrp;
3986 * fetch a Navigation Group based on the navigation ID
3987 * @param {string} the navgroup to add
3988 * @returns {Roo.bootstrap.NavGroup} the navgroup
3990 get: function(navId) {
3991 if (typeof(this.groups[navId]) == 'undefined') {
3993 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3995 return this.groups[navId] ;
4010 * @class Roo.bootstrap.NavItem
4011 * @extends Roo.bootstrap.Component
4012 * Bootstrap Navbar.NavItem class
4013 * @cfg {String} href link to
4014 * @cfg {String} html content of button
4015 * @cfg {String} badge text inside badge
4016 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4017 * @cfg {String} glyphicon name of glyphicon
4018 * @cfg {String} icon name of font awesome icon
4019 * @cfg {Boolean} active Is item active
4020 * @cfg {Boolean} disabled Is item disabled
4022 * @cfg {Boolean} preventDefault (true | false) default false
4023 * @cfg {String} tabId the tab that this item activates.
4024 * @cfg {String} tagtype (a|span) render as a href or span?
4025 * @cfg {Boolean} animateRef (true|false) link to element default false
4028 * Create a new Navbar Item
4029 * @param {Object} config The config object
4031 Roo.bootstrap.NavItem = function(config){
4032 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4037 * The raw click event for the entire grid.
4038 * @param {Roo.EventObject} e
4043 * Fires when the active item active state changes
4044 * @param {Roo.bootstrap.NavItem} this
4045 * @param {boolean} state the new state
4051 * Fires when scroll to element
4052 * @param {Roo.bootstrap.NavItem} this
4053 * @param {Object} options
4054 * @param {Roo.EventObject} e
4062 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4070 preventDefault : false,
4077 getAutoCreate : function(){
4086 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4088 if (this.disabled) {
4089 cfg.cls += ' disabled';
4092 if (this.href || this.html || this.glyphicon || this.icon) {
4096 href : this.href || "#",
4097 html: this.html || ''
4102 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4105 if(this.glyphicon) {
4106 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4111 cfg.cn[0].html += " <span class='caret'></span>";
4115 if (this.badge !== '') {
4117 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4125 initEvents: function()
4127 if (typeof (this.menu) != 'undefined') {
4128 this.menu.parentType = this.xtype;
4129 this.menu.triggerEl = this.el;
4130 this.menu = this.addxtype(Roo.apply({}, this.menu));
4133 this.el.select('a',true).on('click', this.onClick, this);
4135 if(this.tagtype == 'span'){
4136 this.el.select('span',true).on('click', this.onClick, this);
4139 // at this point parent should be available..
4140 this.parent().register(this);
4143 onClick : function(e)
4146 this.preventDefault ||
4153 if (this.disabled) {
4157 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4158 if (tg && tg.transition) {
4159 Roo.log("waiting for the transitionend");
4165 //Roo.log("fire event clicked");
4166 if(this.fireEvent('click', this, e) === false){
4170 if(this.tagtype == 'span'){
4174 //Roo.log(this.href);
4175 var ael = this.el.select('a',true).first();
4178 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4179 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4180 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4181 return; // ignore... - it's a 'hash' to another page.
4185 this.scrollToElement(e);
4189 var p = this.parent();
4191 if (['tabs','pills'].indexOf(p.type)!==-1) {
4192 if (typeof(p.setActiveItem) !== 'undefined') {
4193 p.setActiveItem(this);
4197 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4198 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4199 // remove the collapsed menu expand...
4200 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4204 isActive: function () {
4207 setActive : function(state, fire, is_was_active)
4209 if (this.active && !state && this.navId) {
4210 this.was_active = true;
4211 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4213 nv.clearWasActive(this);
4217 this.active = state;
4220 this.el.removeClass('active');
4221 } else if (!this.el.hasClass('active')) {
4222 this.el.addClass('active');
4225 this.fireEvent('changed', this, state);
4228 // show a panel if it's registered and related..
4230 if (!this.navId || !this.tabId || !state || is_was_active) {
4234 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4238 var pan = tg.getPanelByName(this.tabId);
4242 // if we can not flip to new panel - go back to old nav highlight..
4243 if (false == tg.showPanel(pan)) {
4244 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4246 var onav = nv.getWasActive();
4248 onav.setActive(true, false, true);
4257 // this should not be here...
4258 setDisabled : function(state)
4260 this.disabled = state;
4262 this.el.removeClass('disabled');
4263 } else if (!this.el.hasClass('disabled')) {
4264 this.el.addClass('disabled');
4270 * Fetch the element to display the tooltip on.
4271 * @return {Roo.Element} defaults to this.el
4273 tooltipEl : function()
4275 return this.el.select('' + this.tagtype + '', true).first();
4278 scrollToElement : function(e)
4280 var c = document.body;
4283 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4285 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4286 c = document.documentElement;
4289 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4295 var o = target.calcOffsetsTo(c);
4302 this.fireEvent('scrollto', this, options, e);
4304 Roo.get(c).scrollTo('top', options.value, true);
4317 * <span> icon </span>
4318 * <span> text </span>
4319 * <span>badge </span>
4323 * @class Roo.bootstrap.NavSidebarItem
4324 * @extends Roo.bootstrap.NavItem
4325 * Bootstrap Navbar.NavSidebarItem class
4326 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4328 * Create a new Navbar Button
4329 * @param {Object} config The config object
4331 Roo.bootstrap.NavSidebarItem = function(config){
4332 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4337 * The raw click event for the entire grid.
4338 * @param {Roo.EventObject} e
4343 * Fires when the active item active state changes
4344 * @param {Roo.bootstrap.NavSidebarItem} this
4345 * @param {boolean} state the new state
4353 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4355 badgeWeight : 'default',
4357 getAutoCreate : function(){
4362 href : this.href || '#',
4374 html : this.html || ''
4379 cfg.cls += ' active';
4382 if (this.disabled) {
4383 cfg.cls += ' disabled';
4387 if (this.glyphicon || this.icon) {
4388 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4389 a.cn.push({ tag : 'i', cls : c }) ;
4394 if (this.badge !== '') {
4396 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4400 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4401 a.cls += 'dropdown-toggle treeview' ;
4412 initEvents : function()
4414 this.el.on('click', this.onClick, this);
4417 if(this.badge !== ''){
4419 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4424 onClick : function(e)
4431 if(this.preventDefault){
4435 this.fireEvent('click', this);
4438 disable : function()
4440 this.setDisabled(true);
4445 this.setDisabled(false);
4448 setDisabled : function(state)
4450 if(this.disabled == state){
4454 this.disabled = state;
4457 this.el.addClass('disabled');
4461 this.el.removeClass('disabled');
4466 setActive : function(state)
4468 if(this.active == state){
4472 this.active = state;
4475 this.el.addClass('active');
4479 this.el.removeClass('active');
4484 isActive: function ()
4489 setBadge : function(str)
4495 this.badgeEl.dom.innerHTML = str;
4512 * @class Roo.bootstrap.Row
4513 * @extends Roo.bootstrap.Component
4514 * Bootstrap Row class (contains columns...)
4518 * @param {Object} config The config object
4521 Roo.bootstrap.Row = function(config){
4522 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4525 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4527 getAutoCreate : function(){
4546 * @class Roo.bootstrap.Element
4547 * @extends Roo.bootstrap.Component
4548 * Bootstrap Element class
4549 * @cfg {String} html contents of the element
4550 * @cfg {String} tag tag of the element
4551 * @cfg {String} cls class of the element
4552 * @cfg {Boolean} preventDefault (true|false) default false
4553 * @cfg {Boolean} clickable (true|false) default false
4556 * Create a new Element
4557 * @param {Object} config The config object
4560 Roo.bootstrap.Element = function(config){
4561 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4567 * When a element is chick
4568 * @param {Roo.bootstrap.Element} this
4569 * @param {Roo.EventObject} e
4575 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4580 preventDefault: false,
4583 getAutoCreate : function(){
4594 initEvents: function()
4596 Roo.bootstrap.Element.superclass.initEvents.call(this);
4599 this.el.on('click', this.onClick, this);
4604 onClick : function(e)
4606 if(this.preventDefault){
4610 this.fireEvent('click', this, e);
4613 getValue : function()
4615 return this.el.dom.innerHTML;
4618 setValue : function(value)
4620 this.el.dom.innerHTML = value;
4635 * @class Roo.bootstrap.Pagination
4636 * @extends Roo.bootstrap.Component
4637 * Bootstrap Pagination class
4638 * @cfg {String} size xs | sm | md | lg
4639 * @cfg {Boolean} inverse false | true
4642 * Create a new Pagination
4643 * @param {Object} config The config object
4646 Roo.bootstrap.Pagination = function(config){
4647 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4650 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4656 getAutoCreate : function(){
4662 cfg.cls += ' inverse';
4668 cfg.cls += " " + this.cls;
4686 * @class Roo.bootstrap.PaginationItem
4687 * @extends Roo.bootstrap.Component
4688 * Bootstrap PaginationItem class
4689 * @cfg {String} html text
4690 * @cfg {String} href the link
4691 * @cfg {Boolean} preventDefault (true | false) default true
4692 * @cfg {Boolean} active (true | false) default false
4693 * @cfg {Boolean} disabled default false
4697 * Create a new PaginationItem
4698 * @param {Object} config The config object
4702 Roo.bootstrap.PaginationItem = function(config){
4703 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4708 * The raw click event for the entire grid.
4709 * @param {Roo.EventObject} e
4715 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4719 preventDefault: true,
4724 getAutoCreate : function(){
4730 href : this.href ? this.href : '#',
4731 html : this.html ? this.html : ''
4741 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4745 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4751 initEvents: function() {
4753 this.el.on('click', this.onClick, this);
4756 onClick : function(e)
4758 Roo.log('PaginationItem on click ');
4759 if(this.preventDefault){
4767 this.fireEvent('click', this, e);
4783 * @class Roo.bootstrap.Slider
4784 * @extends Roo.bootstrap.Component
4785 * Bootstrap Slider class
4788 * Create a new Slider
4789 * @param {Object} config The config object
4792 Roo.bootstrap.Slider = function(config){
4793 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4796 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4798 getAutoCreate : function(){
4802 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4806 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4818 * Ext JS Library 1.1.1
4819 * Copyright(c) 2006-2007, Ext JS, LLC.
4821 * Originally Released Under LGPL - original licence link has changed is not relivant.
4824 * <script type="text/javascript">
4829 * @class Roo.grid.ColumnModel
4830 * @extends Roo.util.Observable
4831 * This is the default implementation of a ColumnModel used by the Grid. It defines
4832 * the columns in the grid.
4835 var colModel = new Roo.grid.ColumnModel([
4836 {header: "Ticker", width: 60, sortable: true, locked: true},
4837 {header: "Company Name", width: 150, sortable: true},
4838 {header: "Market Cap.", width: 100, sortable: true},
4839 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4840 {header: "Employees", width: 100, sortable: true, resizable: false}
4845 * The config options listed for this class are options which may appear in each
4846 * individual column definition.
4847 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4849 * @param {Object} config An Array of column config objects. See this class's
4850 * config objects for details.
4852 Roo.grid.ColumnModel = function(config){
4854 * The config passed into the constructor
4856 this.config = config;
4859 // if no id, create one
4860 // if the column does not have a dataIndex mapping,
4861 // map it to the order it is in the config
4862 for(var i = 0, len = config.length; i < len; i++){
4864 if(typeof c.dataIndex == "undefined"){
4867 if(typeof c.renderer == "string"){
4868 c.renderer = Roo.util.Format[c.renderer];
4870 if(typeof c.id == "undefined"){
4873 if(c.editor && c.editor.xtype){
4874 c.editor = Roo.factory(c.editor, Roo.grid);
4876 if(c.editor && c.editor.isFormField){
4877 c.editor = new Roo.grid.GridEditor(c.editor);
4879 this.lookup[c.id] = c;
4883 * The width of columns which have no width specified (defaults to 100)
4886 this.defaultWidth = 100;
4889 * Default sortable of columns which have no sortable specified (defaults to false)
4892 this.defaultSortable = false;
4896 * @event widthchange
4897 * Fires when the width of a column changes.
4898 * @param {ColumnModel} this
4899 * @param {Number} columnIndex The column index
4900 * @param {Number} newWidth The new width
4902 "widthchange": true,
4904 * @event headerchange
4905 * Fires when the text of a header changes.
4906 * @param {ColumnModel} this
4907 * @param {Number} columnIndex The column index
4908 * @param {Number} newText The new header text
4910 "headerchange": true,
4912 * @event hiddenchange
4913 * Fires when a column is hidden or "unhidden".
4914 * @param {ColumnModel} this
4915 * @param {Number} columnIndex The column index
4916 * @param {Boolean} hidden true if hidden, false otherwise
4918 "hiddenchange": true,
4920 * @event columnmoved
4921 * Fires when a column is moved.
4922 * @param {ColumnModel} this
4923 * @param {Number} oldIndex
4924 * @param {Number} newIndex
4926 "columnmoved" : true,
4928 * @event columlockchange
4929 * Fires when a column's locked state is changed
4930 * @param {ColumnModel} this
4931 * @param {Number} colIndex
4932 * @param {Boolean} locked true if locked
4934 "columnlockchange" : true
4936 Roo.grid.ColumnModel.superclass.constructor.call(this);
4938 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4940 * @cfg {String} header The header text to display in the Grid view.
4943 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4944 * {@link Roo.data.Record} definition from which to draw the column's value. If not
4945 * specified, the column's index is used as an index into the Record's data Array.
4948 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4949 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4952 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4953 * Defaults to the value of the {@link #defaultSortable} property.
4954 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4957 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
4960 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
4963 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4966 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4969 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4970 * given the cell's data value. See {@link #setRenderer}. If not specified, the
4971 * default renderer uses the raw data value. If an object is returned (bootstrap only)
4972 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4975 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
4978 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
4981 * @cfg {String} cursor (Optional)
4984 * @cfg {String} tooltip (Optional)
4987 * @cfg {Number} xs (Optional)
4990 * @cfg {Number} sm (Optional)
4993 * @cfg {Number} md (Optional)
4996 * @cfg {Number} lg (Optional)
4999 * Returns the id of the column at the specified index.
5000 * @param {Number} index The column index
5001 * @return {String} the id
5003 getColumnId : function(index){
5004 return this.config[index].id;
5008 * Returns the column for a specified id.
5009 * @param {String} id The column id
5010 * @return {Object} the column
5012 getColumnById : function(id){
5013 return this.lookup[id];
5018 * Returns the column for a specified dataIndex.
5019 * @param {String} dataIndex The column dataIndex
5020 * @return {Object|Boolean} the column or false if not found
5022 getColumnByDataIndex: function(dataIndex){
5023 var index = this.findColumnIndex(dataIndex);
5024 return index > -1 ? this.config[index] : false;
5028 * Returns the index for a specified column id.
5029 * @param {String} id The column id
5030 * @return {Number} the index, or -1 if not found
5032 getIndexById : function(id){
5033 for(var i = 0, len = this.config.length; i < len; i++){
5034 if(this.config[i].id == id){
5042 * Returns the index for a specified column dataIndex.
5043 * @param {String} dataIndex The column dataIndex
5044 * @return {Number} the index, or -1 if not found
5047 findColumnIndex : function(dataIndex){
5048 for(var i = 0, len = this.config.length; i < len; i++){
5049 if(this.config[i].dataIndex == dataIndex){
5057 moveColumn : function(oldIndex, newIndex){
5058 var c = this.config[oldIndex];
5059 this.config.splice(oldIndex, 1);
5060 this.config.splice(newIndex, 0, c);
5061 this.dataMap = null;
5062 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5065 isLocked : function(colIndex){
5066 return this.config[colIndex].locked === true;
5069 setLocked : function(colIndex, value, suppressEvent){
5070 if(this.isLocked(colIndex) == value){
5073 this.config[colIndex].locked = value;
5075 this.fireEvent("columnlockchange", this, colIndex, value);
5079 getTotalLockedWidth : function(){
5081 for(var i = 0; i < this.config.length; i++){
5082 if(this.isLocked(i) && !this.isHidden(i)){
5083 this.totalWidth += this.getColumnWidth(i);
5089 getLockedCount : function(){
5090 for(var i = 0, len = this.config.length; i < len; i++){
5091 if(!this.isLocked(i)){
5098 * Returns the number of columns.
5101 getColumnCount : function(visibleOnly){
5102 if(visibleOnly === true){
5104 for(var i = 0, len = this.config.length; i < len; i++){
5105 if(!this.isHidden(i)){
5111 return this.config.length;
5115 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5116 * @param {Function} fn
5117 * @param {Object} scope (optional)
5118 * @return {Array} result
5120 getColumnsBy : function(fn, scope){
5122 for(var i = 0, len = this.config.length; i < len; i++){
5123 var c = this.config[i];
5124 if(fn.call(scope||this, c, i) === true){
5132 * Returns true if the specified column is sortable.
5133 * @param {Number} col The column index
5136 isSortable : function(col){
5137 if(typeof this.config[col].sortable == "undefined"){
5138 return this.defaultSortable;
5140 return this.config[col].sortable;
5144 * Returns the rendering (formatting) function defined for the column.
5145 * @param {Number} col The column index.
5146 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5148 getRenderer : function(col){
5149 if(!this.config[col].renderer){
5150 return Roo.grid.ColumnModel.defaultRenderer;
5152 return this.config[col].renderer;
5156 * Sets the rendering (formatting) function for a column.
5157 * @param {Number} col The column index
5158 * @param {Function} fn The function to use to process the cell's raw data
5159 * to return HTML markup for the grid view. The render function is called with
5160 * the following parameters:<ul>
5161 * <li>Data value.</li>
5162 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5163 * <li>css A CSS style string to apply to the table cell.</li>
5164 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5165 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5166 * <li>Row index</li>
5167 * <li>Column index</li>
5168 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5170 setRenderer : function(col, fn){
5171 this.config[col].renderer = fn;
5175 * Returns the width for the specified column.
5176 * @param {Number} col The column index
5179 getColumnWidth : function(col){
5180 return this.config[col].width * 1 || this.defaultWidth;
5184 * Sets the width for a column.
5185 * @param {Number} col The column index
5186 * @param {Number} width The new width
5188 setColumnWidth : function(col, width, suppressEvent){
5189 this.config[col].width = width;
5190 this.totalWidth = null;
5192 this.fireEvent("widthchange", this, col, width);
5197 * Returns the total width of all columns.
5198 * @param {Boolean} includeHidden True to include hidden column widths
5201 getTotalWidth : function(includeHidden){
5202 if(!this.totalWidth){
5203 this.totalWidth = 0;
5204 for(var i = 0, len = this.config.length; i < len; i++){
5205 if(includeHidden || !this.isHidden(i)){
5206 this.totalWidth += this.getColumnWidth(i);
5210 return this.totalWidth;
5214 * Returns the header for the specified column.
5215 * @param {Number} col The column index
5218 getColumnHeader : function(col){
5219 return this.config[col].header;
5223 * Sets the header for a column.
5224 * @param {Number} col The column index
5225 * @param {String} header The new header
5227 setColumnHeader : function(col, header){
5228 this.config[col].header = header;
5229 this.fireEvent("headerchange", this, col, header);
5233 * Returns the tooltip for the specified column.
5234 * @param {Number} col The column index
5237 getColumnTooltip : function(col){
5238 return this.config[col].tooltip;
5241 * Sets the tooltip for a column.
5242 * @param {Number} col The column index
5243 * @param {String} tooltip The new tooltip
5245 setColumnTooltip : function(col, tooltip){
5246 this.config[col].tooltip = tooltip;
5250 * Returns the dataIndex for the specified column.
5251 * @param {Number} col The column index
5254 getDataIndex : function(col){
5255 return this.config[col].dataIndex;
5259 * Sets the dataIndex for a column.
5260 * @param {Number} col The column index
5261 * @param {Number} dataIndex The new dataIndex
5263 setDataIndex : function(col, dataIndex){
5264 this.config[col].dataIndex = dataIndex;
5270 * Returns true if the cell is editable.
5271 * @param {Number} colIndex The column index
5272 * @param {Number} rowIndex The row index
5275 isCellEditable : function(colIndex, rowIndex){
5276 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5280 * Returns the editor defined for the cell/column.
5281 * return false or null to disable editing.
5282 * @param {Number} colIndex The column index
5283 * @param {Number} rowIndex The row index
5286 getCellEditor : function(colIndex, rowIndex){
5287 return this.config[colIndex].editor;
5291 * Sets if a column is editable.
5292 * @param {Number} col The column index
5293 * @param {Boolean} editable True if the column is editable
5295 setEditable : function(col, editable){
5296 this.config[col].editable = editable;
5301 * Returns true if the column is hidden.
5302 * @param {Number} colIndex The column index
5305 isHidden : function(colIndex){
5306 return this.config[colIndex].hidden;
5311 * Returns true if the column width cannot be changed
5313 isFixed : function(colIndex){
5314 return this.config[colIndex].fixed;
5318 * Returns true if the column can be resized
5321 isResizable : function(colIndex){
5322 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5325 * Sets if a column is hidden.
5326 * @param {Number} colIndex The column index
5327 * @param {Boolean} hidden True if the column is hidden
5329 setHidden : function(colIndex, hidden){
5330 this.config[colIndex].hidden = hidden;
5331 this.totalWidth = null;
5332 this.fireEvent("hiddenchange", this, colIndex, hidden);
5336 * Sets the editor for a column.
5337 * @param {Number} col The column index
5338 * @param {Object} editor The editor object
5340 setEditor : function(col, editor){
5341 this.config[col].editor = editor;
5345 Roo.grid.ColumnModel.defaultRenderer = function(value){
5346 if(typeof value == "string" && value.length < 1){
5352 // Alias for backwards compatibility
5353 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5356 * Ext JS Library 1.1.1
5357 * Copyright(c) 2006-2007, Ext JS, LLC.
5359 * Originally Released Under LGPL - original licence link has changed is not relivant.
5362 * <script type="text/javascript">
5366 * @class Roo.LoadMask
5367 * A simple utility class for generically masking elements while loading data. If the element being masked has
5368 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5369 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5370 * element's UpdateManager load indicator and will be destroyed after the initial load.
5372 * Create a new LoadMask
5373 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5374 * @param {Object} config The config object
5376 Roo.LoadMask = function(el, config){
5377 this.el = Roo.get(el);
5378 Roo.apply(this, config);
5380 this.store.on('beforeload', this.onBeforeLoad, this);
5381 this.store.on('load', this.onLoad, this);
5382 this.store.on('loadexception', this.onLoadException, this);
5383 this.removeMask = false;
5385 var um = this.el.getUpdateManager();
5386 um.showLoadIndicator = false; // disable the default indicator
5387 um.on('beforeupdate', this.onBeforeLoad, this);
5388 um.on('update', this.onLoad, this);
5389 um.on('failure', this.onLoad, this);
5390 this.removeMask = true;
5394 Roo.LoadMask.prototype = {
5396 * @cfg {Boolean} removeMask
5397 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5398 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5402 * The text to display in a centered loading message box (defaults to 'Loading...')
5406 * @cfg {String} msgCls
5407 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5409 msgCls : 'x-mask-loading',
5412 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5418 * Disables the mask to prevent it from being displayed
5420 disable : function(){
5421 this.disabled = true;
5425 * Enables the mask so that it can be displayed
5427 enable : function(){
5428 this.disabled = false;
5431 onLoadException : function()
5435 if (typeof(arguments[3]) != 'undefined') {
5436 Roo.MessageBox.alert("Error loading",arguments[3]);
5440 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5441 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5450 this.el.unmask(this.removeMask);
5455 this.el.unmask(this.removeMask);
5459 onBeforeLoad : function(){
5461 this.el.mask(this.msg, this.msgCls);
5466 destroy : function(){
5468 this.store.un('beforeload', this.onBeforeLoad, this);
5469 this.store.un('load', this.onLoad, this);
5470 this.store.un('loadexception', this.onLoadException, this);
5472 var um = this.el.getUpdateManager();
5473 um.un('beforeupdate', this.onBeforeLoad, this);
5474 um.un('update', this.onLoad, this);
5475 um.un('failure', this.onLoad, this);
5486 * @class Roo.bootstrap.Table
5487 * @extends Roo.bootstrap.Component
5488 * Bootstrap Table class
5489 * @cfg {String} cls table class
5490 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5491 * @cfg {String} bgcolor Specifies the background color for a table
5492 * @cfg {Number} border Specifies whether the table cells should have borders or not
5493 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5494 * @cfg {Number} cellspacing Specifies the space between cells
5495 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5496 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5497 * @cfg {String} sortable Specifies that the table should be sortable
5498 * @cfg {String} summary Specifies a summary of the content of a table
5499 * @cfg {Number} width Specifies the width of a table
5500 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5502 * @cfg {boolean} striped Should the rows be alternative striped
5503 * @cfg {boolean} bordered Add borders to the table
5504 * @cfg {boolean} hover Add hover highlighting
5505 * @cfg {boolean} condensed Format condensed
5506 * @cfg {boolean} responsive Format condensed
5507 * @cfg {Boolean} loadMask (true|false) default false
5508 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5509 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5510 * @cfg {Boolean} rowSelection (true|false) default false
5511 * @cfg {Boolean} cellSelection (true|false) default false
5512 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5516 * Create a new Table
5517 * @param {Object} config The config object
5520 Roo.bootstrap.Table = function(config){
5521 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5524 this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5525 this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5526 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5527 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5531 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5532 this.sm = this.selModel;
5533 this.sm.xmodule = this.xmodule || false;
5535 if (this.cm && typeof(this.cm.config) == 'undefined') {
5536 this.colModel = new Roo.grid.ColumnModel(this.cm);
5537 this.cm = this.colModel;
5538 this.cm.xmodule = this.xmodule || false;
5541 this.store= Roo.factory(this.store, Roo.data);
5542 this.ds = this.store;
5543 this.ds.xmodule = this.xmodule || false;
5546 if (this.footer && this.store) {
5547 this.footer.dataSource = this.ds;
5548 this.footer = Roo.factory(this.footer);
5555 * Fires when a cell is clicked
5556 * @param {Roo.bootstrap.Table} this
5557 * @param {Roo.Element} el
5558 * @param {Number} rowIndex
5559 * @param {Number} columnIndex
5560 * @param {Roo.EventObject} e
5564 * @event celldblclick
5565 * Fires when a cell is double clicked
5566 * @param {Roo.bootstrap.Table} this
5567 * @param {Roo.Element} el
5568 * @param {Number} rowIndex
5569 * @param {Number} columnIndex
5570 * @param {Roo.EventObject} e
5572 "celldblclick" : true,
5575 * Fires when a row is clicked
5576 * @param {Roo.bootstrap.Table} this
5577 * @param {Roo.Element} el
5578 * @param {Number} rowIndex
5579 * @param {Roo.EventObject} e
5583 * @event rowdblclick
5584 * Fires when a row is double clicked
5585 * @param {Roo.bootstrap.Table} this
5586 * @param {Roo.Element} el
5587 * @param {Number} rowIndex
5588 * @param {Roo.EventObject} e
5590 "rowdblclick" : true,
5593 * Fires when a mouseover occur
5594 * @param {Roo.bootstrap.Table} this
5595 * @param {Roo.Element} el
5596 * @param {Number} rowIndex
5597 * @param {Number} columnIndex
5598 * @param {Roo.EventObject} e
5603 * Fires when a mouseout occur
5604 * @param {Roo.bootstrap.Table} this
5605 * @param {Roo.Element} el
5606 * @param {Number} rowIndex
5607 * @param {Number} columnIndex
5608 * @param {Roo.EventObject} e
5613 * Fires when a row is rendered, so you can change add a style to it.
5614 * @param {Roo.bootstrap.Table} this
5615 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5619 * @event rowsrendered
5620 * Fires when all the rows have been rendered
5621 * @param {Roo.bootstrap.Table} this
5623 'rowsrendered' : true
5628 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5653 rowSelection : false,
5654 cellSelection : false,
5657 // Roo.Element - the tbody
5660 getAutoCreate : function(){
5661 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5670 cfg.cls += ' table-striped';
5674 cfg.cls += ' table-hover';
5676 if (this.bordered) {
5677 cfg.cls += ' table-bordered';
5679 if (this.condensed) {
5680 cfg.cls += ' table-condensed';
5682 if (this.responsive) {
5683 cfg.cls += ' table-responsive';
5687 cfg.cls+= ' ' +this.cls;
5690 // this lot should be simplifed...
5693 cfg.align=this.align;
5696 cfg.bgcolor=this.bgcolor;
5699 cfg.border=this.border;
5701 if (this.cellpadding) {
5702 cfg.cellpadding=this.cellpadding;
5704 if (this.cellspacing) {
5705 cfg.cellspacing=this.cellspacing;
5708 cfg.frame=this.frame;
5711 cfg.rules=this.rules;
5713 if (this.sortable) {
5714 cfg.sortable=this.sortable;
5717 cfg.summary=this.summary;
5720 cfg.width=this.width;
5723 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5726 if(this.store || this.cm){
5727 if(this.headerShow){
5728 cfg.cn.push(this.renderHeader());
5731 cfg.cn.push(this.renderBody());
5733 if(this.footerShow){
5734 cfg.cn.push(this.renderFooter());
5737 cfg.cls+= ' TableGrid';
5740 return { cn : [ cfg ] };
5743 initEvents : function()
5745 if(!this.store || !this.cm){
5749 //Roo.log('initEvents with ds!!!!');
5751 this.mainBody = this.el.select('tbody', true).first();
5756 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5757 e.on('click', _this.sort, _this);
5760 this.el.on("click", this.onClick, this);
5761 this.el.on("dblclick", this.onDblClick, this);
5763 // why is this done????? = it breaks dialogs??
5764 //this.parent().el.setStyle('position', 'relative');
5768 this.footer.parentId = this.id;
5769 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5772 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5774 this.store.on('load', this.onLoad, this);
5775 this.store.on('beforeload', this.onBeforeLoad, this);
5776 this.store.on('update', this.onUpdate, this);
5777 this.store.on('add', this.onAdd, this);
5781 onMouseover : function(e, el)
5783 var cell = Roo.get(el);
5789 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5790 cell = cell.findParent('td', false, true);
5793 var row = cell.findParent('tr', false, true);
5794 var cellIndex = cell.dom.cellIndex;
5795 var rowIndex = row.dom.rowIndex - 1; // start from 0
5797 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5801 onMouseout : function(e, el)
5803 var cell = Roo.get(el);
5809 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5810 cell = cell.findParent('td', false, true);
5813 var row = cell.findParent('tr', false, true);
5814 var cellIndex = cell.dom.cellIndex;
5815 var rowIndex = row.dom.rowIndex - 1; // start from 0
5817 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5821 onClick : function(e, el)
5823 var cell = Roo.get(el);
5825 if(!cell || (!this.cellSelection && !this.rowSelection)){
5829 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5830 cell = cell.findParent('td', false, true);
5833 if(!cell || typeof(cell) == 'undefined'){
5837 var row = cell.findParent('tr', false, true);
5839 if(!row || typeof(row) == 'undefined'){
5843 var cellIndex = cell.dom.cellIndex;
5844 var rowIndex = this.getRowIndex(row);
5846 // why??? - should these not be based on SelectionModel?
5847 if(this.cellSelection){
5848 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5851 if(this.rowSelection){
5852 this.fireEvent('rowclick', this, row, rowIndex, e);
5858 onDblClick : function(e,el)
5860 var cell = Roo.get(el);
5862 if(!cell || (!this.CellSelection && !this.RowSelection)){
5866 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5867 cell = cell.findParent('td', false, true);
5870 if(!cell || typeof(cell) == 'undefined'){
5874 var row = cell.findParent('tr', false, true);
5876 if(!row || typeof(row) == 'undefined'){
5880 var cellIndex = cell.dom.cellIndex;
5881 var rowIndex = this.getRowIndex(row);
5883 if(this.CellSelection){
5884 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5887 if(this.RowSelection){
5888 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5892 sort : function(e,el)
5894 var col = Roo.get(el);
5896 if(!col.hasClass('sortable')){
5900 var sort = col.attr('sort');
5903 if(col.hasClass('glyphicon-arrow-up')){
5907 this.store.sortInfo = {field : sort, direction : dir};
5910 Roo.log("calling footer first");
5911 this.footer.onClick('first');
5914 this.store.load({ params : { start : 0 } });
5918 renderHeader : function()
5927 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5929 var config = cm.config[i];
5934 html: cm.getColumnHeader(i)
5939 if(typeof(config.lgHeader) != 'undefined'){
5940 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5943 if(typeof(config.mdHeader) != 'undefined'){
5944 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5947 if(typeof(config.smHeader) != 'undefined'){
5948 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5951 if(typeof(config.xsHeader) != 'undefined'){
5952 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5959 if(typeof(config.tooltip) != 'undefined'){
5960 c.tooltip = config.tooltip;
5963 if(typeof(config.colspan) != 'undefined'){
5964 c.colspan = config.colspan;
5967 if(typeof(config.hidden) != 'undefined' && config.hidden){
5968 c.style += ' display:none;';
5971 if(typeof(config.dataIndex) != 'undefined'){
5972 c.sort = config.dataIndex;
5975 if(typeof(config.sortable) != 'undefined' && config.sortable){
5979 if(typeof(config.align) != 'undefined' && config.align.length){
5980 c.style += ' text-align:' + config.align + ';';
5983 if(typeof(config.width) != 'undefined'){
5984 c.style += ' width:' + config.width + 'px;';
5987 if(typeof(config.cls) != 'undefined'){
5988 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5991 ['xs','sm','md','lg'].map(function(size){
5993 if(typeof(config[size]) == 'undefined'){
5997 if (!config[size]) { // 0 = hidden
5998 cfg.cls += ' hidden-' + size;
6002 cfg.cls += ' col-' + size + '-' + config[size];
6012 renderBody : function()
6022 colspan : this.cm.getColumnCount()
6032 renderFooter : function()
6042 colspan : this.cm.getColumnCount()
6056 Roo.log('ds onload');
6061 var ds = this.store;
6063 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6064 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6066 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6067 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6070 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6071 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6075 var tbody = this.mainBody;
6077 if(ds.getCount() > 0){
6078 ds.data.each(function(d,rowIndex){
6079 var row = this.renderRow(cm, ds, rowIndex);
6081 tbody.createChild(row);
6085 if(row.cellObjects.length){
6086 Roo.each(row.cellObjects, function(r){
6087 _this.renderCellObject(r);
6094 Roo.each(this.el.select('tbody td', true).elements, function(e){
6095 e.on('mouseover', _this.onMouseover, _this);
6098 Roo.each(this.el.select('tbody td', true).elements, function(e){
6099 e.on('mouseout', _this.onMouseout, _this);
6101 this.fireEvent('rowsrendered', this);
6102 //if(this.loadMask){
6103 // this.maskEl.hide();
6108 onUpdate : function(ds,record)
6110 this.refreshRow(record);
6113 onRemove : function(ds, record, index, isUpdate){
6114 if(isUpdate !== true){
6115 this.fireEvent("beforerowremoved", this, index, record);
6117 var bt = this.mainBody.dom;
6119 var rows = this.el.select('tbody > tr', true).elements;
6121 if(typeof(rows[index]) != 'undefined'){
6122 bt.removeChild(rows[index].dom);
6125 // if(bt.rows[index]){
6126 // bt.removeChild(bt.rows[index]);
6129 if(isUpdate !== true){
6130 //this.stripeRows(index);
6131 //this.syncRowHeights(index, index);
6133 this.fireEvent("rowremoved", this, index, record);
6137 onAdd : function(ds, records, rowIndex)
6139 //Roo.log('on Add called');
6140 // - note this does not handle multiple adding very well..
6141 var bt = this.mainBody.dom;
6142 for (var i =0 ; i < records.length;i++) {
6143 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6144 //Roo.log(records[i]);
6145 //Roo.log(this.store.getAt(rowIndex+i));
6146 this.insertRow(this.store, rowIndex + i, false);
6153 refreshRow : function(record){
6154 var ds = this.store, index;
6155 if(typeof record == 'number'){
6157 record = ds.getAt(index);
6159 index = ds.indexOf(record);
6161 this.insertRow(ds, index, true);
6162 this.onRemove(ds, record, index+1, true);
6163 //this.syncRowHeights(index, index);
6165 this.fireEvent("rowupdated", this, index, record);
6168 insertRow : function(dm, rowIndex, isUpdate){
6171 this.fireEvent("beforerowsinserted", this, rowIndex);
6173 //var s = this.getScrollState();
6174 var row = this.renderRow(this.cm, this.store, rowIndex);
6175 // insert before rowIndex..
6176 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6180 if(row.cellObjects.length){
6181 Roo.each(row.cellObjects, function(r){
6182 _this.renderCellObject(r);
6187 this.fireEvent("rowsinserted", this, rowIndex);
6188 //this.syncRowHeights(firstRow, lastRow);
6189 //this.stripeRows(firstRow);
6196 getRowDom : function(rowIndex)
6198 var rows = this.el.select('tbody > tr', true).elements;
6200 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6203 // returns the object tree for a tr..
6206 renderRow : function(cm, ds, rowIndex)
6209 var d = ds.getAt(rowIndex);
6216 var cellObjects = [];
6218 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6219 var config = cm.config[i];
6221 var renderer = cm.getRenderer(i);
6225 if(typeof(renderer) !== 'undefined'){
6226 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6228 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6229 // and are rendered into the cells after the row is rendered - using the id for the element.
6231 if(typeof(value) === 'object'){
6241 rowIndex : rowIndex,
6246 this.fireEvent('rowclass', this, rowcfg);
6250 cls : rowcfg.rowClass,
6252 html: (typeof(value) === 'object') ? '' : value
6259 if(typeof(config.colspan) != 'undefined'){
6260 td.colspan = config.colspan;
6263 if(typeof(config.hidden) != 'undefined' && config.hidden){
6264 td.style += ' display:none;';
6267 if(typeof(config.align) != 'undefined' && config.align.length){
6268 td.style += ' text-align:' + config.align + ';';
6271 if(typeof(config.width) != 'undefined'){
6272 td.style += ' width:' + config.width + 'px;';
6275 if(typeof(config.cursor) != 'undefined'){
6276 td.style += ' cursor:' + config.cursor + ';';
6279 if(typeof(config.cls) != 'undefined'){
6280 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6283 ['xs','sm','md','lg'].map(function(size){
6285 if(typeof(config[size]) == 'undefined'){
6289 if (!config[size]) { // 0 = hidden
6290 td.cls += ' hidden-' + size;
6294 td.cls += ' col-' + size + '-' + config[size];
6302 row.cellObjects = cellObjects;
6310 onBeforeLoad : function()
6312 //Roo.log('ds onBeforeLoad');
6316 //if(this.loadMask){
6317 // this.maskEl.show();
6325 this.el.select('tbody', true).first().dom.innerHTML = '';
6328 * Show or hide a row.
6329 * @param {Number} rowIndex to show or hide
6330 * @param {Boolean} state hide
6332 setRowVisibility : function(rowIndex, state)
6334 var bt = this.mainBody.dom;
6336 var rows = this.el.select('tbody > tr', true).elements;
6338 if(typeof(rows[rowIndex]) == 'undefined'){
6341 rows[rowIndex].dom.style.display = state ? '' : 'none';
6345 getSelectionModel : function(){
6347 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6349 return this.selModel;
6352 * Render the Roo.bootstrap object from renderder
6354 renderCellObject : function(r)
6358 var t = r.cfg.render(r.container);
6361 Roo.each(r.cfg.cn, function(c){
6363 container: t.getChildContainer(),
6366 _this.renderCellObject(child);
6371 getRowIndex : function(row)
6375 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6398 * @class Roo.bootstrap.TableCell
6399 * @extends Roo.bootstrap.Component
6400 * Bootstrap TableCell class
6401 * @cfg {String} html cell contain text
6402 * @cfg {String} cls cell class
6403 * @cfg {String} tag cell tag (td|th) default td
6404 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6405 * @cfg {String} align Aligns the content in a cell
6406 * @cfg {String} axis Categorizes cells
6407 * @cfg {String} bgcolor Specifies the background color of a cell
6408 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6409 * @cfg {Number} colspan Specifies the number of columns a cell should span
6410 * @cfg {String} headers Specifies one or more header cells a cell is related to
6411 * @cfg {Number} height Sets the height of a cell
6412 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6413 * @cfg {Number} rowspan Sets the number of rows a cell should span
6414 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6415 * @cfg {String} valign Vertical aligns the content in a cell
6416 * @cfg {Number} width Specifies the width of a cell
6419 * Create a new TableCell
6420 * @param {Object} config The config object
6423 Roo.bootstrap.TableCell = function(config){
6424 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6427 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6447 getAutoCreate : function(){
6448 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6468 cfg.align=this.align
6474 cfg.bgcolor=this.bgcolor
6477 cfg.charoff=this.charoff
6480 cfg.colspan=this.colspan
6483 cfg.headers=this.headers
6486 cfg.height=this.height
6489 cfg.nowrap=this.nowrap
6492 cfg.rowspan=this.rowspan
6495 cfg.scope=this.scope
6498 cfg.valign=this.valign
6501 cfg.width=this.width
6520 * @class Roo.bootstrap.TableRow
6521 * @extends Roo.bootstrap.Component
6522 * Bootstrap TableRow class
6523 * @cfg {String} cls row class
6524 * @cfg {String} align Aligns the content in a table row
6525 * @cfg {String} bgcolor Specifies a background color for a table row
6526 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6527 * @cfg {String} valign Vertical aligns the content in a table row
6530 * Create a new TableRow
6531 * @param {Object} config The config object
6534 Roo.bootstrap.TableRow = function(config){
6535 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6538 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6546 getAutoCreate : function(){
6547 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6557 cfg.align = this.align;
6560 cfg.bgcolor = this.bgcolor;
6563 cfg.charoff = this.charoff;
6566 cfg.valign = this.valign;
6584 * @class Roo.bootstrap.TableBody
6585 * @extends Roo.bootstrap.Component
6586 * Bootstrap TableBody class
6587 * @cfg {String} cls element class
6588 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6589 * @cfg {String} align Aligns the content inside the element
6590 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6591 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6594 * Create a new TableBody
6595 * @param {Object} config The config object
6598 Roo.bootstrap.TableBody = function(config){
6599 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6602 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6610 getAutoCreate : function(){
6611 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6625 cfg.align = this.align;
6628 cfg.charoff = this.charoff;
6631 cfg.valign = this.valign;
6638 // initEvents : function()
6645 // this.store = Roo.factory(this.store, Roo.data);
6646 // this.store.on('load', this.onLoad, this);
6648 // this.store.load();
6652 // onLoad: function ()
6654 // this.fireEvent('load', this);
6664 * Ext JS Library 1.1.1
6665 * Copyright(c) 2006-2007, Ext JS, LLC.
6667 * Originally Released Under LGPL - original licence link has changed is not relivant.
6670 * <script type="text/javascript">
6673 // as we use this in bootstrap.
6674 Roo.namespace('Roo.form');
6676 * @class Roo.form.Action
6677 * Internal Class used to handle form actions
6679 * @param {Roo.form.BasicForm} el The form element or its id
6680 * @param {Object} config Configuration options
6685 // define the action interface
6686 Roo.form.Action = function(form, options){
6688 this.options = options || {};
6691 * Client Validation Failed
6694 Roo.form.Action.CLIENT_INVALID = 'client';
6696 * Server Validation Failed
6699 Roo.form.Action.SERVER_INVALID = 'server';
6701 * Connect to Server Failed
6704 Roo.form.Action.CONNECT_FAILURE = 'connect';
6706 * Reading Data from Server Failed
6709 Roo.form.Action.LOAD_FAILURE = 'load';
6711 Roo.form.Action.prototype = {
6713 failureType : undefined,
6714 response : undefined,
6718 run : function(options){
6723 success : function(response){
6728 handleResponse : function(response){
6732 // default connection failure
6733 failure : function(response){
6735 this.response = response;
6736 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6737 this.form.afterAction(this, false);
6740 processResponse : function(response){
6741 this.response = response;
6742 if(!response.responseText){
6745 this.result = this.handleResponse(response);
6749 // utility functions used internally
6750 getUrl : function(appendParams){
6751 var url = this.options.url || this.form.url || this.form.el.dom.action;
6753 var p = this.getParams();
6755 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6761 getMethod : function(){
6762 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6765 getParams : function(){
6766 var bp = this.form.baseParams;
6767 var p = this.options.params;
6769 if(typeof p == "object"){
6770 p = Roo.urlEncode(Roo.applyIf(p, bp));
6771 }else if(typeof p == 'string' && bp){
6772 p += '&' + Roo.urlEncode(bp);
6775 p = Roo.urlEncode(bp);
6780 createCallback : function(){
6782 success: this.success,
6783 failure: this.failure,
6785 timeout: (this.form.timeout*1000),
6786 upload: this.form.fileUpload ? this.success : undefined
6791 Roo.form.Action.Submit = function(form, options){
6792 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6795 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6798 haveProgress : false,
6799 uploadComplete : false,
6801 // uploadProgress indicator.
6802 uploadProgress : function()
6804 if (!this.form.progressUrl) {
6808 if (!this.haveProgress) {
6809 Roo.MessageBox.progress("Uploading", "Uploading");
6811 if (this.uploadComplete) {
6812 Roo.MessageBox.hide();
6816 this.haveProgress = true;
6818 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6820 var c = new Roo.data.Connection();
6822 url : this.form.progressUrl,
6827 success : function(req){
6828 //console.log(data);
6832 rdata = Roo.decode(req.responseText)
6834 Roo.log("Invalid data from server..");
6838 if (!rdata || !rdata.success) {
6840 Roo.MessageBox.alert(Roo.encode(rdata));
6843 var data = rdata.data;
6845 if (this.uploadComplete) {
6846 Roo.MessageBox.hide();
6851 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6852 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6855 this.uploadProgress.defer(2000,this);
6858 failure: function(data) {
6859 Roo.log('progress url failed ');
6870 // run get Values on the form, so it syncs any secondary forms.
6871 this.form.getValues();
6873 var o = this.options;
6874 var method = this.getMethod();
6875 var isPost = method == 'POST';
6876 if(o.clientValidation === false || this.form.isValid()){
6878 if (this.form.progressUrl) {
6879 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6880 (new Date() * 1) + '' + Math.random());
6885 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6886 form:this.form.el.dom,
6887 url:this.getUrl(!isPost),
6889 params:isPost ? this.getParams() : null,
6890 isUpload: this.form.fileUpload
6893 this.uploadProgress();
6895 }else if (o.clientValidation !== false){ // client validation failed
6896 this.failureType = Roo.form.Action.CLIENT_INVALID;
6897 this.form.afterAction(this, false);
6901 success : function(response)
6903 this.uploadComplete= true;
6904 if (this.haveProgress) {
6905 Roo.MessageBox.hide();
6909 var result = this.processResponse(response);
6910 if(result === true || result.success){
6911 this.form.afterAction(this, true);
6915 this.form.markInvalid(result.errors);
6916 this.failureType = Roo.form.Action.SERVER_INVALID;
6918 this.form.afterAction(this, false);
6920 failure : function(response)
6922 this.uploadComplete= true;
6923 if (this.haveProgress) {
6924 Roo.MessageBox.hide();
6927 this.response = response;
6928 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6929 this.form.afterAction(this, false);
6932 handleResponse : function(response){
6933 if(this.form.errorReader){
6934 var rs = this.form.errorReader.read(response);
6937 for(var i = 0, len = rs.records.length; i < len; i++) {
6938 var r = rs.records[i];
6942 if(errors.length < 1){
6946 success : rs.success,
6952 ret = Roo.decode(response.responseText);
6956 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6966 Roo.form.Action.Load = function(form, options){
6967 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6968 this.reader = this.form.reader;
6971 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6976 Roo.Ajax.request(Roo.apply(
6977 this.createCallback(), {
6978 method:this.getMethod(),
6979 url:this.getUrl(false),
6980 params:this.getParams()
6984 success : function(response){
6986 var result = this.processResponse(response);
6987 if(result === true || !result.success || !result.data){
6988 this.failureType = Roo.form.Action.LOAD_FAILURE;
6989 this.form.afterAction(this, false);
6992 this.form.clearInvalid();
6993 this.form.setValues(result.data);
6994 this.form.afterAction(this, true);
6997 handleResponse : function(response){
6998 if(this.form.reader){
6999 var rs = this.form.reader.read(response);
7000 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7002 success : rs.success,
7006 return Roo.decode(response.responseText);
7010 Roo.form.Action.ACTION_TYPES = {
7011 'load' : Roo.form.Action.Load,
7012 'submit' : Roo.form.Action.Submit
7021 * @class Roo.bootstrap.Form
7022 * @extends Roo.bootstrap.Component
7023 * Bootstrap Form class
7024 * @cfg {String} method GET | POST (default POST)
7025 * @cfg {String} labelAlign top | left (default top)
7026 * @cfg {String} align left | right - for navbars
7027 * @cfg {Boolean} loadMask load mask when submit (default true)
7032 * @param {Object} config The config object
7036 Roo.bootstrap.Form = function(config){
7037 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7040 * @event clientvalidation
7041 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7042 * @param {Form} this
7043 * @param {Boolean} valid true if the form has passed client-side validation
7045 clientvalidation: true,
7047 * @event beforeaction
7048 * Fires before any action is performed. Return false to cancel the action.
7049 * @param {Form} this
7050 * @param {Action} action The action to be performed
7054 * @event actionfailed
7055 * Fires when an action fails.
7056 * @param {Form} this
7057 * @param {Action} action The action that failed
7059 actionfailed : true,
7061 * @event actioncomplete
7062 * Fires when an action is completed.
7063 * @param {Form} this
7064 * @param {Action} action The action that completed
7066 actioncomplete : true
7071 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7074 * @cfg {String} method
7075 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7080 * The URL to use for form actions if one isn't supplied in the action options.
7083 * @cfg {Boolean} fileUpload
7084 * Set to true if this form is a file upload.
7088 * @cfg {Object} baseParams
7089 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7093 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7097 * @cfg {Sting} align (left|right) for navbar forms
7102 activeAction : null,
7105 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7106 * element by passing it or its id or mask the form itself by passing in true.
7109 waitMsgTarget : false,
7113 getAutoCreate : function(){
7117 method : this.method || 'POST',
7118 id : this.id || Roo.id(),
7121 if (this.parent().xtype.match(/^Nav/)) {
7122 cfg.cls = 'navbar-form navbar-' + this.align;
7126 if (this.labelAlign == 'left' ) {
7127 cfg.cls += ' form-horizontal';
7133 initEvents : function()
7135 this.el.on('submit', this.onSubmit, this);
7136 // this was added as random key presses on the form where triggering form submit.
7137 this.el.on('keypress', function(e) {
7138 if (e.getCharCode() != 13) {
7141 // we might need to allow it for textareas.. and some other items.
7142 // check e.getTarget().
7144 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7148 Roo.log("keypress blocked");
7156 onSubmit : function(e){
7161 * Returns true if client-side validation on the form is successful.
7164 isValid : function(){
7165 var items = this.getItems();
7167 items.each(function(f){
7176 * Returns true if any fields in this form have changed since their original load.
7179 isDirty : function(){
7181 var items = this.getItems();
7182 items.each(function(f){
7192 * Performs a predefined action (submit or load) or custom actions you define on this form.
7193 * @param {String} actionName The name of the action type
7194 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7195 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7196 * accept other config options):
7198 Property Type Description
7199 ---------------- --------------- ----------------------------------------------------------------------------------
7200 url String The url for the action (defaults to the form's url)
7201 method String The form method to use (defaults to the form's method, or POST if not defined)
7202 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7203 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7204 validate the form on the client (defaults to false)
7206 * @return {BasicForm} this
7208 doAction : function(action, options){
7209 if(typeof action == 'string'){
7210 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7212 if(this.fireEvent('beforeaction', this, action) !== false){
7213 this.beforeAction(action);
7214 action.run.defer(100, action);
7220 beforeAction : function(action){
7221 var o = action.options;
7224 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7226 // not really supported yet.. ??
7228 //if(this.waitMsgTarget === true){
7229 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7230 //}else if(this.waitMsgTarget){
7231 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7232 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7234 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7240 afterAction : function(action, success){
7241 this.activeAction = null;
7242 var o = action.options;
7244 //if(this.waitMsgTarget === true){
7246 //}else if(this.waitMsgTarget){
7247 // this.waitMsgTarget.unmask();
7249 // Roo.MessageBox.updateProgress(1);
7250 // Roo.MessageBox.hide();
7257 Roo.callback(o.success, o.scope, [this, action]);
7258 this.fireEvent('actioncomplete', this, action);
7262 // failure condition..
7263 // we have a scenario where updates need confirming.
7264 // eg. if a locking scenario exists..
7265 // we look for { errors : { needs_confirm : true }} in the response.
7267 (typeof(action.result) != 'undefined') &&
7268 (typeof(action.result.errors) != 'undefined') &&
7269 (typeof(action.result.errors.needs_confirm) != 'undefined')
7272 Roo.log("not supported yet");
7275 Roo.MessageBox.confirm(
7276 "Change requires confirmation",
7277 action.result.errorMsg,
7282 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7292 Roo.callback(o.failure, o.scope, [this, action]);
7293 // show an error message if no failed handler is set..
7294 if (!this.hasListener('actionfailed')) {
7295 Roo.log("need to add dialog support");
7297 Roo.MessageBox.alert("Error",
7298 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7299 action.result.errorMsg :
7300 "Saving Failed, please check your entries or try again"
7305 this.fireEvent('actionfailed', this, action);
7310 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7311 * @param {String} id The value to search for
7314 findField : function(id){
7315 var items = this.getItems();
7316 var field = items.get(id);
7318 items.each(function(f){
7319 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7326 return field || null;
7329 * Mark fields in this form invalid in bulk.
7330 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7331 * @return {BasicForm} this
7333 markInvalid : function(errors){
7334 if(errors instanceof Array){
7335 for(var i = 0, len = errors.length; i < len; i++){
7336 var fieldError = errors[i];
7337 var f = this.findField(fieldError.id);
7339 f.markInvalid(fieldError.msg);
7345 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7346 field.markInvalid(errors[id]);
7350 //Roo.each(this.childForms || [], function (f) {
7351 // f.markInvalid(errors);
7358 * Set values for fields in this form in bulk.
7359 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7360 * @return {BasicForm} this
7362 setValues : function(values){
7363 if(values instanceof Array){ // array of objects
7364 for(var i = 0, len = values.length; i < len; i++){
7366 var f = this.findField(v.id);
7368 f.setValue(v.value);
7369 if(this.trackResetOnLoad){
7370 f.originalValue = f.getValue();
7374 }else{ // object hash
7377 if(typeof values[id] != 'function' && (field = this.findField(id))){
7379 if (field.setFromData &&
7381 field.displayField &&
7382 // combos' with local stores can
7383 // be queried via setValue()
7384 // to set their value..
7385 (field.store && !field.store.isLocal)
7389 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7390 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7391 field.setFromData(sd);
7394 field.setValue(values[id]);
7398 if(this.trackResetOnLoad){
7399 field.originalValue = field.getValue();
7405 //Roo.each(this.childForms || [], function (f) {
7406 // f.setValues(values);
7413 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7414 * they are returned as an array.
7415 * @param {Boolean} asString
7418 getValues : function(asString){
7419 //if (this.childForms) {
7420 // copy values from the child forms
7421 // Roo.each(this.childForms, function (f) {
7422 // this.setValues(f.getValues());
7428 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7429 if(asString === true){
7432 return Roo.urlDecode(fs);
7436 * Returns the fields in this form as an object with key/value pairs.
7437 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7440 getFieldValues : function(with_hidden)
7442 var items = this.getItems();
7444 items.each(function(f){
7448 var v = f.getValue();
7449 if (f.inputType =='radio') {
7450 if (typeof(ret[f.getName()]) == 'undefined') {
7451 ret[f.getName()] = ''; // empty..
7454 if (!f.el.dom.checked) {
7462 // not sure if this supported any more..
7463 if ((typeof(v) == 'object') && f.getRawValue) {
7464 v = f.getRawValue() ; // dates..
7466 // combo boxes where name != hiddenName...
7467 if (f.name != f.getName()) {
7468 ret[f.name] = f.getRawValue();
7470 ret[f.getName()] = v;
7477 * Clears all invalid messages in this form.
7478 * @return {BasicForm} this
7480 clearInvalid : function(){
7481 var items = this.getItems();
7483 items.each(function(f){
7494 * @return {BasicForm} this
7497 var items = this.getItems();
7498 items.each(function(f){
7502 Roo.each(this.childForms || [], function (f) {
7509 getItems : function()
7511 var r=new Roo.util.MixedCollection(false, function(o){
7512 return o.id || (o.id = Roo.id());
7514 var iter = function(el) {
7521 Roo.each(el.items,function(e) {
7541 * Ext JS Library 1.1.1
7542 * Copyright(c) 2006-2007, Ext JS, LLC.
7544 * Originally Released Under LGPL - original licence link has changed is not relivant.
7547 * <script type="text/javascript">
7550 * @class Roo.form.VTypes
7551 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7554 Roo.form.VTypes = function(){
7555 // closure these in so they are only created once.
7556 var alpha = /^[a-zA-Z_]+$/;
7557 var alphanum = /^[a-zA-Z0-9_]+$/;
7558 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7559 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7561 // All these messages and functions are configurable
7564 * The function used to validate email addresses
7565 * @param {String} value The email address
7567 'email' : function(v){
7568 return email.test(v);
7571 * The error text to display when the email validation function returns false
7574 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7576 * The keystroke filter mask to be applied on email input
7579 'emailMask' : /[a-z0-9_\.\-@]/i,
7582 * The function used to validate URLs
7583 * @param {String} value The URL
7585 'url' : function(v){
7589 * The error text to display when the url validation function returns false
7592 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7595 * The function used to validate alpha values
7596 * @param {String} value The value
7598 'alpha' : function(v){
7599 return alpha.test(v);
7602 * The error text to display when the alpha validation function returns false
7605 'alphaText' : 'This field should only contain letters and _',
7607 * The keystroke filter mask to be applied on alpha input
7610 'alphaMask' : /[a-z_]/i,
7613 * The function used to validate alphanumeric values
7614 * @param {String} value The value
7616 'alphanum' : function(v){
7617 return alphanum.test(v);
7620 * The error text to display when the alphanumeric validation function returns false
7623 'alphanumText' : 'This field should only contain letters, numbers and _',
7625 * The keystroke filter mask to be applied on alphanumeric input
7628 'alphanumMask' : /[a-z0-9_]/i
7638 * @class Roo.bootstrap.Input
7639 * @extends Roo.bootstrap.Component
7640 * Bootstrap Input class
7641 * @cfg {Boolean} disabled is it disabled
7642 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7643 * @cfg {String} name name of the input
7644 * @cfg {string} fieldLabel - the label associated
7645 * @cfg {string} placeholder - placeholder to put in text.
7646 * @cfg {string} before - input group add on before
7647 * @cfg {string} after - input group add on after
7648 * @cfg {string} size - (lg|sm) or leave empty..
7649 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7650 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7651 * @cfg {Number} md colspan out of 12 for computer-sized screens
7652 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7653 * @cfg {string} value default value of the input
7654 * @cfg {Number} labelWidth set the width of label (0-12)
7655 * @cfg {String} labelAlign (top|left)
7656 * @cfg {Boolean} readOnly Specifies that the field should be read-only
7657 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7659 * @cfg {String} align (left|center|right) Default left
7660 * @cfg {Boolean} forceFeedback (true|false) Default false
7666 * Create a new Input
7667 * @param {Object} config The config object
7670 Roo.bootstrap.Input = function(config){
7671 Roo.bootstrap.Input.superclass.constructor.call(this, config);
7676 * Fires when this field receives input focus.
7677 * @param {Roo.form.Field} this
7682 * Fires when this field loses input focus.
7683 * @param {Roo.form.Field} this
7688 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
7689 * {@link Roo.EventObject#getKey} to determine which key was pressed.
7690 * @param {Roo.form.Field} this
7691 * @param {Roo.EventObject} e The event object
7696 * Fires just before the field blurs if the field value has changed.
7697 * @param {Roo.form.Field} this
7698 * @param {Mixed} newValue The new value
7699 * @param {Mixed} oldValue The original value
7704 * Fires after the field has been marked as invalid.
7705 * @param {Roo.form.Field} this
7706 * @param {String} msg The validation message
7711 * Fires after the field has been validated with no errors.
7712 * @param {Roo.form.Field} this
7717 * Fires after the key up
7718 * @param {Roo.form.Field} this
7719 * @param {Roo.EventObject} e The event Object
7725 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
7727 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7728 automatic validation (defaults to "keyup").
7730 validationEvent : "keyup",
7732 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7734 validateOnBlur : true,
7736 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7738 validationDelay : 250,
7740 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7742 focusClass : "x-form-focus", // not needed???
7746 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7748 invalidClass : "has-warning",
7751 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7753 validClass : "has-success",
7756 * @cfg {Boolean} hasFeedback (true|false) default true
7761 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7763 invalidFeedbackClass : "glyphicon-warning-sign",
7766 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7768 validFeedbackClass : "glyphicon-ok",
7771 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7773 selectOnFocus : false,
7776 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7780 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7785 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7787 disableKeyFilter : false,
7790 * @cfg {Boolean} disabled True to disable the field (defaults to false).
7794 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7798 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7800 blankText : "This field is required",
7803 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7807 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7809 maxLength : Number.MAX_VALUE,
7811 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7813 minLengthText : "The minimum length for this field is {0}",
7815 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7817 maxLengthText : "The maximum length for this field is {0}",
7821 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7822 * If available, this function will be called only after the basic validators all return true, and will be passed the
7823 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7827 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7828 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7829 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
7833 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7837 autocomplete: false,
7856 formatedValue : false,
7857 forceFeedback : false,
7859 parentLabelAlign : function()
7862 while (parent.parent()) {
7863 parent = parent.parent();
7864 if (typeof(parent.labelAlign) !='undefined') {
7865 return parent.labelAlign;
7872 getAutoCreate : function(){
7874 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7880 if(this.inputType != 'hidden'){
7881 cfg.cls = 'form-group' //input-group
7887 type : this.inputType,
7889 cls : 'form-control',
7890 placeholder : this.placeholder || '',
7891 autocomplete : this.autocomplete || 'new-password'
7896 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7899 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7900 input.maxLength = this.maxLength;
7903 if (this.disabled) {
7904 input.disabled=true;
7907 if (this.readOnly) {
7908 input.readonly=true;
7912 input.name = this.name;
7915 input.cls += ' input-' + this.size;
7918 ['xs','sm','md','lg'].map(function(size){
7919 if (settings[size]) {
7920 cfg.cls += ' col-' + size + '-' + settings[size];
7924 var inputblock = input;
7928 cls: 'glyphicon form-control-feedback'
7931 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7934 cls : 'has-feedback',
7942 if (this.before || this.after) {
7945 cls : 'input-group',
7949 if (this.before && typeof(this.before) == 'string') {
7951 inputblock.cn.push({
7953 cls : 'roo-input-before input-group-addon',
7957 if (this.before && typeof(this.before) == 'object') {
7958 this.before = Roo.factory(this.before);
7959 Roo.log(this.before);
7960 inputblock.cn.push({
7962 cls : 'roo-input-before input-group-' +
7963 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7967 inputblock.cn.push(input);
7969 if (this.after && typeof(this.after) == 'string') {
7970 inputblock.cn.push({
7972 cls : 'roo-input-after input-group-addon',
7976 if (this.after && typeof(this.after) == 'object') {
7977 this.after = Roo.factory(this.after);
7978 Roo.log(this.after);
7979 inputblock.cn.push({
7981 cls : 'roo-input-after input-group-' +
7982 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7986 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7987 inputblock.cls += ' has-feedback';
7988 inputblock.cn.push(feedback);
7992 if (align ==='left' && this.fieldLabel.length) {
7993 Roo.log("left and has label");
7999 cls : 'control-label col-sm-' + this.labelWidth,
8000 html : this.fieldLabel
8004 cls : "col-sm-" + (12 - this.labelWidth),
8011 } else if ( this.fieldLabel.length) {
8017 //cls : 'input-group-addon',
8018 html : this.fieldLabel
8028 Roo.log(" no label && no align");
8037 Roo.log('input-parentType: ' + this.parentType);
8039 if (this.parentType === 'Navbar' && this.parent().bar) {
8040 cfg.cls += ' navbar-form';
8048 * return the real input element.
8050 inputEl: function ()
8052 return this.el.select('input.form-control',true).first();
8055 tooltipEl : function()
8057 return this.inputEl();
8060 setDisabled : function(v)
8062 var i = this.inputEl().dom;
8064 i.removeAttribute('disabled');
8068 i.setAttribute('disabled','true');
8070 initEvents : function()
8073 this.inputEl().on("keydown" , this.fireKey, this);
8074 this.inputEl().on("focus", this.onFocus, this);
8075 this.inputEl().on("blur", this.onBlur, this);
8077 this.inputEl().relayEvent('keyup', this);
8079 // reference to original value for reset
8080 this.originalValue = this.getValue();
8081 //Roo.form.TextField.superclass.initEvents.call(this);
8082 if(this.validationEvent == 'keyup'){
8083 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8084 this.inputEl().on('keyup', this.filterValidation, this);
8086 else if(this.validationEvent !== false){
8087 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8090 if(this.selectOnFocus){
8091 this.on("focus", this.preFocus, this);
8094 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8095 this.inputEl().on("keypress", this.filterKeys, this);
8098 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8099 this.el.on("click", this.autoSize, this);
8102 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8103 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8106 if (typeof(this.before) == 'object') {
8107 this.before.render(this.el.select('.roo-input-before',true).first());
8109 if (typeof(this.after) == 'object') {
8110 this.after.render(this.el.select('.roo-input-after',true).first());
8115 filterValidation : function(e){
8116 if(!e.isNavKeyPress()){
8117 this.validationTask.delay(this.validationDelay);
8121 * Validates the field value
8122 * @return {Boolean} True if the value is valid, else false
8124 validate : function(){
8125 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8126 if(this.disabled || this.validateValue(this.getRawValue())){
8137 * Validates a value according to the field's validation rules and marks the field as invalid
8138 * if the validation fails
8139 * @param {Mixed} value The value to validate
8140 * @return {Boolean} True if the value is valid, else false
8142 validateValue : function(value){
8143 if(value.length < 1) { // if it's blank
8144 if(this.allowBlank){
8150 if(value.length < this.minLength){
8153 if(value.length > this.maxLength){
8157 var vt = Roo.form.VTypes;
8158 if(!vt[this.vtype](value, this)){
8162 if(typeof this.validator == "function"){
8163 var msg = this.validator(value);
8169 if(this.regex && !this.regex.test(value)){
8179 fireKey : function(e){
8180 //Roo.log('field ' + e.getKey());
8181 if(e.isNavKeyPress()){
8182 this.fireEvent("specialkey", this, e);
8185 focus : function (selectText){
8187 this.inputEl().focus();
8188 if(selectText === true){
8189 this.inputEl().dom.select();
8195 onFocus : function(){
8196 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8197 // this.el.addClass(this.focusClass);
8200 this.hasFocus = true;
8201 this.startValue = this.getValue();
8202 this.fireEvent("focus", this);
8206 beforeBlur : Roo.emptyFn,
8210 onBlur : function(){
8212 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8213 //this.el.removeClass(this.focusClass);
8215 this.hasFocus = false;
8216 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8219 var v = this.getValue();
8220 if(String(v) !== String(this.startValue)){
8221 this.fireEvent('change', this, v, this.startValue);
8223 this.fireEvent("blur", this);
8227 * Resets the current field value to the originally loaded value and clears any validation messages
8230 this.setValue(this.originalValue);
8234 * Returns the name of the field
8235 * @return {Mixed} name The name field
8237 getName: function(){
8241 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8242 * @return {Mixed} value The field value
8244 getValue : function(){
8246 var v = this.inputEl().getValue();
8251 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8252 * @return {Mixed} value The field value
8254 getRawValue : function(){
8255 var v = this.inputEl().getValue();
8261 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8262 * @param {Mixed} value The value to set
8264 setRawValue : function(v){
8265 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8268 selectText : function(start, end){
8269 var v = this.getRawValue();
8271 start = start === undefined ? 0 : start;
8272 end = end === undefined ? v.length : end;
8273 var d = this.inputEl().dom;
8274 if(d.setSelectionRange){
8275 d.setSelectionRange(start, end);
8276 }else if(d.createTextRange){
8277 var range = d.createTextRange();
8278 range.moveStart("character", start);
8279 range.moveEnd("character", v.length-end);
8286 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8287 * @param {Mixed} value The value to set
8289 setValue : function(v){
8292 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8298 processValue : function(value){
8299 if(this.stripCharsRe){
8300 var newValue = value.replace(this.stripCharsRe, '');
8301 if(newValue !== value){
8302 this.setRawValue(newValue);
8309 preFocus : function(){
8311 if(this.selectOnFocus){
8312 this.inputEl().dom.select();
8315 filterKeys : function(e){
8317 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8320 var c = e.getCharCode(), cc = String.fromCharCode(c);
8321 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8324 if(!this.maskRe.test(cc)){
8329 * Clear any invalid styles/messages for this field
8331 clearInvalid : function(){
8333 if(!this.el || this.preventMark){ // not rendered
8336 this.el.removeClass(this.invalidClass);
8338 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8340 var feedback = this.el.select('.form-control-feedback', true).first();
8343 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8348 this.fireEvent('valid', this);
8352 * Mark this field as valid
8354 markValid : function()
8356 if(!this.el || this.preventMark){ // not rendered
8360 this.el.removeClass([this.invalidClass, this.validClass]);
8362 var feedback = this.el.select('.form-control-feedback', true).first();
8365 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8368 if(this.disabled || this.allowBlank){
8372 var formGroup = this.el.findParent('.form-group', false, true);
8376 var label = formGroup.select('label', true).first();
8377 var icon = formGroup.select('i.fa-star', true).first();
8384 this.el.addClass(this.validClass);
8386 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8388 var feedback = this.el.select('.form-control-feedback', true).first();
8391 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8392 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8397 this.fireEvent('valid', this);
8401 * Mark this field as invalid
8402 * @param {String} msg The validation message
8404 markInvalid : function(msg)
8406 if(!this.el || this.preventMark){ // not rendered
8410 this.el.removeClass([this.invalidClass, this.validClass]);
8412 var feedback = this.el.select('.form-control-feedback', true).first();
8415 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8418 if(this.disabled || this.allowBlank){
8422 var formGroup = this.el.findParent('.form-group', false, true);
8425 var label = formGroup.select('label', true).first();
8426 var icon = formGroup.select('i.fa-star', true).first();
8428 if(!this.getValue().length && label && !icon){
8429 this.el.findParent('.form-group', false, true).createChild({
8431 cls : 'text-danger fa fa-lg fa-star',
8432 tooltip : 'This field is required',
8433 style : 'margin-right:5px;'
8439 this.el.addClass(this.invalidClass);
8441 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8443 var feedback = this.el.select('.form-control-feedback', true).first();
8446 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8448 if(this.getValue().length || this.forceFeedback){
8449 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8456 this.fireEvent('invalid', this, msg);
8459 SafariOnKeyDown : function(event)
8461 // this is a workaround for a password hang bug on chrome/ webkit.
8463 var isSelectAll = false;
8465 if(this.inputEl().dom.selectionEnd > 0){
8466 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8468 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8469 event.preventDefault();
8474 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8476 event.preventDefault();
8477 // this is very hacky as keydown always get's upper case.
8479 var cc = String.fromCharCode(event.getCharCode());
8480 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8484 adjustWidth : function(tag, w){
8485 tag = tag.toLowerCase();
8486 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8487 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8491 if(tag == 'textarea'){
8494 }else if(Roo.isOpera){
8498 if(tag == 'textarea'){
8517 * @class Roo.bootstrap.TextArea
8518 * @extends Roo.bootstrap.Input
8519 * Bootstrap TextArea class
8520 * @cfg {Number} cols Specifies the visible width of a text area
8521 * @cfg {Number} rows Specifies the visible number of lines in a text area
8522 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8523 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8524 * @cfg {string} html text
8527 * Create a new TextArea
8528 * @param {Object} config The config object
8531 Roo.bootstrap.TextArea = function(config){
8532 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8536 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8546 getAutoCreate : function(){
8548 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8559 value : this.value || '',
8560 html: this.html || '',
8561 cls : 'form-control',
8562 placeholder : this.placeholder || ''
8566 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8567 input.maxLength = this.maxLength;
8571 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8575 input.cols = this.cols;
8578 if (this.readOnly) {
8579 input.readonly = true;
8583 input.name = this.name;
8587 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8591 ['xs','sm','md','lg'].map(function(size){
8592 if (settings[size]) {
8593 cfg.cls += ' col-' + size + '-' + settings[size];
8597 var inputblock = input;
8599 if(this.hasFeedback && !this.allowBlank){
8603 cls: 'glyphicon form-control-feedback'
8607 cls : 'has-feedback',
8616 if (this.before || this.after) {
8619 cls : 'input-group',
8623 inputblock.cn.push({
8625 cls : 'input-group-addon',
8630 inputblock.cn.push(input);
8632 if(this.hasFeedback && !this.allowBlank){
8633 inputblock.cls += ' has-feedback';
8634 inputblock.cn.push(feedback);
8638 inputblock.cn.push({
8640 cls : 'input-group-addon',
8647 if (align ==='left' && this.fieldLabel.length) {
8648 Roo.log("left and has label");
8654 cls : 'control-label col-sm-' + this.labelWidth,
8655 html : this.fieldLabel
8659 cls : "col-sm-" + (12 - this.labelWidth),
8666 } else if ( this.fieldLabel.length) {
8672 //cls : 'input-group-addon',
8673 html : this.fieldLabel
8683 Roo.log(" no label && no align");
8693 if (this.disabled) {
8694 input.disabled=true;
8701 * return the real textarea element.
8703 inputEl: function ()
8705 return this.el.select('textarea.form-control',true).first();
8713 * trigger field - base class for combo..
8718 * @class Roo.bootstrap.TriggerField
8719 * @extends Roo.bootstrap.Input
8720 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8721 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8722 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8723 * for which you can provide a custom implementation. For example:
8725 var trigger = new Roo.bootstrap.TriggerField();
8726 trigger.onTriggerClick = myTriggerFn;
8727 trigger.applyTo('my-field');
8730 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8731 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8732 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
8733 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8734 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8737 * Create a new TriggerField.
8738 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8739 * to the base TextField)
8741 Roo.bootstrap.TriggerField = function(config){
8742 this.mimicing = false;
8743 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8746 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
8748 * @cfg {String} triggerClass A CSS class to apply to the trigger
8751 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8756 * @cfg {Boolean} removable (true|false) special filter default false
8760 /** @cfg {Boolean} grow @hide */
8761 /** @cfg {Number} growMin @hide */
8762 /** @cfg {Number} growMax @hide */
8768 autoSize: Roo.emptyFn,
8775 actionMode : 'wrap',
8780 getAutoCreate : function(){
8782 var align = this.labelAlign || this.parentLabelAlign();
8787 cls: 'form-group' //input-group
8794 type : this.inputType,
8795 cls : 'form-control',
8796 autocomplete: 'new-password',
8797 placeholder : this.placeholder || ''
8801 input.name = this.name;
8804 input.cls += ' input-' + this.size;
8807 if (this.disabled) {
8808 input.disabled=true;
8811 var inputblock = input;
8813 if(this.hasFeedback && !this.allowBlank){
8817 cls: 'glyphicon form-control-feedback'
8820 if(this.removable && !this.editable && !this.tickable){
8822 cls : 'has-feedback',
8828 cls : 'roo-combo-removable-btn close'
8835 cls : 'has-feedback',
8844 if(this.removable && !this.editable && !this.tickable){
8846 cls : 'roo-removable',
8852 cls : 'roo-combo-removable-btn close'
8859 if (this.before || this.after) {
8862 cls : 'input-group',
8866 inputblock.cn.push({
8868 cls : 'input-group-addon',
8873 inputblock.cn.push(input);
8875 if(this.hasFeedback && !this.allowBlank){
8876 inputblock.cls += ' has-feedback';
8877 inputblock.cn.push(feedback);
8881 inputblock.cn.push({
8883 cls : 'input-group-addon',
8896 cls: 'form-hidden-field'
8904 Roo.log('multiple');
8912 cls: 'form-hidden-field'
8916 cls: 'select2-choices',
8920 cls: 'select2-search-field',
8933 cls: 'select2-container input-group',
8938 // cls: 'typeahead typeahead-long dropdown-menu',
8939 // style: 'display:none'
8944 if(!this.multiple && this.showToggleBtn){
8950 if (this.caret != false) {
8953 cls: 'fa fa-' + this.caret
8960 cls : 'input-group-addon btn dropdown-toggle',
8965 cls: 'combobox-clear',
8979 combobox.cls += ' select2-container-multi';
8982 if (align ==='left' && this.fieldLabel.length) {
8984 Roo.log("left and has label");
8990 cls : 'control-label col-sm-' + this.labelWidth,
8991 html : this.fieldLabel
8995 cls : "col-sm-" + (12 - this.labelWidth),
9002 } else if ( this.fieldLabel.length) {
9008 //cls : 'input-group-addon',
9009 html : this.fieldLabel
9019 Roo.log(" no label && no align");
9026 ['xs','sm','md','lg'].map(function(size){
9027 if (settings[size]) {
9028 cfg.cls += ' col-' + size + '-' + settings[size];
9039 onResize : function(w, h){
9040 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9041 // if(typeof w == 'number'){
9042 // var x = w - this.trigger.getWidth();
9043 // this.inputEl().setWidth(this.adjustWidth('input', x));
9044 // this.trigger.setStyle('left', x+'px');
9049 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9052 getResizeEl : function(){
9053 return this.inputEl();
9057 getPositionEl : function(){
9058 return this.inputEl();
9062 alignErrorIcon : function(){
9063 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9067 initEvents : function(){
9071 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9072 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9073 if(!this.multiple && this.showToggleBtn){
9074 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9075 if(this.hideTrigger){
9076 this.trigger.setDisplayed(false);
9078 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9082 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9085 if(this.removable && !this.editable && !this.tickable){
9086 var close = this.closeTriggerEl();
9089 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9090 close.on('click', this.removeBtnClick, this, close);
9094 //this.trigger.addClassOnOver('x-form-trigger-over');
9095 //this.trigger.addClassOnClick('x-form-trigger-click');
9098 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9102 closeTriggerEl : function()
9104 var close = this.el.select('.roo-combo-removable-btn', true).first();
9105 return close ? close : false;
9108 removeBtnClick : function(e, h, el)
9112 if(this.fireEvent("remove", this) !== false){
9117 createList : function()
9119 this.list = Roo.get(document.body).createChild({
9121 cls: 'typeahead typeahead-long dropdown-menu',
9122 style: 'display:none'
9125 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9130 initTrigger : function(){
9135 onDestroy : function(){
9137 this.trigger.removeAllListeners();
9138 // this.trigger.remove();
9141 // this.wrap.remove();
9143 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9147 onFocus : function(){
9148 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9151 this.wrap.addClass('x-trigger-wrap-focus');
9152 this.mimicing = true;
9153 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9154 if(this.monitorTab){
9155 this.el.on("keydown", this.checkTab, this);
9162 checkTab : function(e){
9163 if(e.getKey() == e.TAB){
9169 onBlur : function(){
9174 mimicBlur : function(e, t){
9176 if(!this.wrap.contains(t) && this.validateBlur()){
9183 triggerBlur : function(){
9184 this.mimicing = false;
9185 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9186 if(this.monitorTab){
9187 this.el.un("keydown", this.checkTab, this);
9189 //this.wrap.removeClass('x-trigger-wrap-focus');
9190 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9194 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9195 validateBlur : function(e, t){
9200 onDisable : function(){
9201 this.inputEl().dom.disabled = true;
9202 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9204 // this.wrap.addClass('x-item-disabled');
9209 onEnable : function(){
9210 this.inputEl().dom.disabled = false;
9211 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9213 // this.el.removeClass('x-item-disabled');
9218 onShow : function(){
9219 var ae = this.getActionEl();
9222 ae.dom.style.display = '';
9223 ae.dom.style.visibility = 'visible';
9229 onHide : function(){
9230 var ae = this.getActionEl();
9231 ae.dom.style.display = 'none';
9235 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9236 * by an implementing function.
9238 * @param {EventObject} e
9240 onTriggerClick : Roo.emptyFn
9244 * Ext JS Library 1.1.1
9245 * Copyright(c) 2006-2007, Ext JS, LLC.
9247 * Originally Released Under LGPL - original licence link has changed is not relivant.
9250 * <script type="text/javascript">
9255 * @class Roo.data.SortTypes
9257 * Defines the default sorting (casting?) comparison functions used when sorting data.
9259 Roo.data.SortTypes = {
9261 * Default sort that does nothing
9262 * @param {Mixed} s The value being converted
9263 * @return {Mixed} The comparison value
9270 * The regular expression used to strip tags
9274 stripTagsRE : /<\/?[^>]+>/gi,
9277 * Strips all HTML tags to sort on text only
9278 * @param {Mixed} s The value being converted
9279 * @return {String} The comparison value
9281 asText : function(s){
9282 return String(s).replace(this.stripTagsRE, "");
9286 * Strips all HTML tags to sort on text only - Case insensitive
9287 * @param {Mixed} s The value being converted
9288 * @return {String} The comparison value
9290 asUCText : function(s){
9291 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9295 * Case insensitive string
9296 * @param {Mixed} s The value being converted
9297 * @return {String} The comparison value
9299 asUCString : function(s) {
9300 return String(s).toUpperCase();
9305 * @param {Mixed} s The value being converted
9306 * @return {Number} The comparison value
9308 asDate : function(s) {
9312 if(s instanceof Date){
9315 return Date.parse(String(s));
9320 * @param {Mixed} s The value being converted
9321 * @return {Float} The comparison value
9323 asFloat : function(s) {
9324 var val = parseFloat(String(s).replace(/,/g, ""));
9325 if(isNaN(val)) val = 0;
9331 * @param {Mixed} s The value being converted
9332 * @return {Number} The comparison value
9334 asInt : function(s) {
9335 var val = parseInt(String(s).replace(/,/g, ""));
9336 if(isNaN(val)) val = 0;
9341 * Ext JS Library 1.1.1
9342 * Copyright(c) 2006-2007, Ext JS, LLC.
9344 * Originally Released Under LGPL - original licence link has changed is not relivant.
9347 * <script type="text/javascript">
9351 * @class Roo.data.Record
9352 * Instances of this class encapsulate both record <em>definition</em> information, and record
9353 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9354 * to access Records cached in an {@link Roo.data.Store} object.<br>
9356 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9357 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9360 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9362 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9363 * {@link #create}. The parameters are the same.
9364 * @param {Array} data An associative Array of data values keyed by the field name.
9365 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9366 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9367 * not specified an integer id is generated.
9369 Roo.data.Record = function(data, id){
9370 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9375 * Generate a constructor for a specific record layout.
9376 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9377 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9378 * Each field definition object may contain the following properties: <ul>
9379 * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
9380 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9381 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9382 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9383 * is being used, then this is a string containing the javascript expression to reference the data relative to
9384 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9385 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9386 * this may be omitted.</p></li>
9387 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9388 * <ul><li>auto (Default, implies no conversion)</li>
9393 * <li>date</li></ul></p></li>
9394 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9395 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9396 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9397 * by the Reader into an object that will be stored in the Record. It is passed the
9398 * following parameters:<ul>
9399 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9401 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9403 * <br>usage:<br><pre><code>
9404 var TopicRecord = Roo.data.Record.create(
9405 {name: 'title', mapping: 'topic_title'},
9406 {name: 'author', mapping: 'username'},
9407 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9408 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9409 {name: 'lastPoster', mapping: 'user2'},
9410 {name: 'excerpt', mapping: 'post_text'}
9413 var myNewRecord = new TopicRecord({
9414 title: 'Do my job please',
9417 lastPost: new Date(),
9418 lastPoster: 'Animal',
9419 excerpt: 'No way dude!'
9421 myStore.add(myNewRecord);
9426 Roo.data.Record.create = function(o){
9428 f.superclass.constructor.apply(this, arguments);
9430 Roo.extend(f, Roo.data.Record);
9431 var p = f.prototype;
9432 p.fields = new Roo.util.MixedCollection(false, function(field){
9435 for(var i = 0, len = o.length; i < len; i++){
9436 p.fields.add(new Roo.data.Field(o[i]));
9438 f.getField = function(name){
9439 return p.fields.get(name);
9444 Roo.data.Record.AUTO_ID = 1000;
9445 Roo.data.Record.EDIT = 'edit';
9446 Roo.data.Record.REJECT = 'reject';
9447 Roo.data.Record.COMMIT = 'commit';
9449 Roo.data.Record.prototype = {
9451 * Readonly flag - true if this record has been modified.
9460 join : function(store){
9465 * Set the named field to the specified value.
9466 * @param {String} name The name of the field to set.
9467 * @param {Object} value The value to set the field to.
9469 set : function(name, value){
9470 if(this.data[name] == value){
9477 if(typeof this.modified[name] == 'undefined'){
9478 this.modified[name] = this.data[name];
9480 this.data[name] = value;
9481 if(!this.editing && this.store){
9482 this.store.afterEdit(this);
9487 * Get the value of the named field.
9488 * @param {String} name The name of the field to get the value of.
9489 * @return {Object} The value of the field.
9491 get : function(name){
9492 return this.data[name];
9496 beginEdit : function(){
9497 this.editing = true;
9502 cancelEdit : function(){
9503 this.editing = false;
9504 delete this.modified;
9508 endEdit : function(){
9509 this.editing = false;
9510 if(this.dirty && this.store){
9511 this.store.afterEdit(this);
9516 * Usually called by the {@link Roo.data.Store} which owns the Record.
9517 * Rejects all changes made to the Record since either creation, or the last commit operation.
9518 * Modified fields are reverted to their original values.
9520 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9521 * of reject operations.
9523 reject : function(){
9524 var m = this.modified;
9526 if(typeof m[n] != "function"){
9527 this.data[n] = m[n];
9531 delete this.modified;
9532 this.editing = false;
9534 this.store.afterReject(this);
9539 * Usually called by the {@link Roo.data.Store} which owns the Record.
9540 * Commits all changes made to the Record since either creation, or the last commit operation.
9542 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9543 * of commit operations.
9545 commit : function(){
9547 delete this.modified;
9548 this.editing = false;
9550 this.store.afterCommit(this);
9555 hasError : function(){
9556 return this.error != null;
9560 clearError : function(){
9565 * Creates a copy of this record.
9566 * @param {String} id (optional) A new record id if you don't want to use this record's id
9569 copy : function(newId) {
9570 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9574 * Ext JS Library 1.1.1
9575 * Copyright(c) 2006-2007, Ext JS, LLC.
9577 * Originally Released Under LGPL - original licence link has changed is not relivant.
9580 * <script type="text/javascript">
9586 * @class Roo.data.Store
9587 * @extends Roo.util.Observable
9588 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9589 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9591 * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
9592 * has no knowledge of the format of the data returned by the Proxy.<br>
9594 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9595 * instances from the data object. These records are cached and made available through accessor functions.
9597 * Creates a new Store.
9598 * @param {Object} config A config object containing the objects needed for the Store to access data,
9599 * and read the data into Records.
9601 Roo.data.Store = function(config){
9602 this.data = new Roo.util.MixedCollection(false);
9603 this.data.getKey = function(o){
9606 this.baseParams = {};
9613 "multisort" : "_multisort"
9616 if(config && config.data){
9617 this.inlineData = config.data;
9621 Roo.apply(this, config);
9623 if(this.reader){ // reader passed
9624 this.reader = Roo.factory(this.reader, Roo.data);
9625 this.reader.xmodule = this.xmodule || false;
9626 if(!this.recordType){
9627 this.recordType = this.reader.recordType;
9629 if(this.reader.onMetaChange){
9630 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9634 if(this.recordType){
9635 this.fields = this.recordType.prototype.fields;
9641 * @event datachanged
9642 * Fires when the data cache has changed, and a widget which is using this Store
9643 * as a Record cache should refresh its view.
9644 * @param {Store} this
9649 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9650 * @param {Store} this
9651 * @param {Object} meta The JSON metadata
9656 * Fires when Records have been added to the Store
9657 * @param {Store} this
9658 * @param {Roo.data.Record[]} records The array of Records added
9659 * @param {Number} index The index at which the record(s) were added
9664 * Fires when a Record has been removed from the Store
9665 * @param {Store} this
9666 * @param {Roo.data.Record} record The Record that was removed
9667 * @param {Number} index The index at which the record was removed
9672 * Fires when a Record has been updated
9673 * @param {Store} this
9674 * @param {Roo.data.Record} record The Record that was updated
9675 * @param {String} operation The update operation being performed. Value may be one of:
9677 Roo.data.Record.EDIT
9678 Roo.data.Record.REJECT
9679 Roo.data.Record.COMMIT
9685 * Fires when the data cache has been cleared.
9686 * @param {Store} this
9691 * Fires before a request is made for a new data object. If the beforeload handler returns false
9692 * the load action will be canceled.
9693 * @param {Store} this
9694 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9698 * @event beforeloadadd
9699 * Fires after a new set of Records has been loaded.
9700 * @param {Store} this
9701 * @param {Roo.data.Record[]} records The Records that were loaded
9702 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9704 beforeloadadd : true,
9707 * Fires after a new set of Records has been loaded, before they are added to the store.
9708 * @param {Store} this
9709 * @param {Roo.data.Record[]} records The Records that were loaded
9710 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9711 * @params {Object} return from reader
9715 * @event loadexception
9716 * Fires if an exception occurs in the Proxy during loading.
9717 * Called with the signature of the Proxy's "loadexception" event.
9718 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9721 * @param {Object} return from JsonData.reader() - success, totalRecords, records
9722 * @param {Object} load options
9723 * @param {Object} jsonData from your request (normally this contains the Exception)
9725 loadexception : true
9729 this.proxy = Roo.factory(this.proxy, Roo.data);
9730 this.proxy.xmodule = this.xmodule || false;
9731 this.relayEvents(this.proxy, ["loadexception"]);
9733 this.sortToggle = {};
9734 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9736 Roo.data.Store.superclass.constructor.call(this);
9738 if(this.inlineData){
9739 this.loadData(this.inlineData);
9740 delete this.inlineData;
9744 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9746 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
9747 * without a remote query - used by combo/forms at present.
9751 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9754 * @cfg {Array} data Inline data to be loaded when the store is initialized.
9757 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9758 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9761 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9762 * on any HTTP request
9765 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9768 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9772 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9773 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9778 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9779 * loaded or when a record is removed. (defaults to false).
9781 pruneModifiedRecords : false,
9787 * Add Records to the Store and fires the add event.
9788 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9790 add : function(records){
9791 records = [].concat(records);
9792 for(var i = 0, len = records.length; i < len; i++){
9793 records[i].join(this);
9795 var index = this.data.length;
9796 this.data.addAll(records);
9797 this.fireEvent("add", this, records, index);
9801 * Remove a Record from the Store and fires the remove event.
9802 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9804 remove : function(record){
9805 var index = this.data.indexOf(record);
9806 this.data.removeAt(index);
9807 if(this.pruneModifiedRecords){
9808 this.modified.remove(record);
9810 this.fireEvent("remove", this, record, index);
9814 * Remove all Records from the Store and fires the clear event.
9816 removeAll : function(){
9818 if(this.pruneModifiedRecords){
9821 this.fireEvent("clear", this);
9825 * Inserts Records to the Store at the given index and fires the add event.
9826 * @param {Number} index The start index at which to insert the passed Records.
9827 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9829 insert : function(index, records){
9830 records = [].concat(records);
9831 for(var i = 0, len = records.length; i < len; i++){
9832 this.data.insert(index, records[i]);
9833 records[i].join(this);
9835 this.fireEvent("add", this, records, index);
9839 * Get the index within the cache of the passed Record.
9840 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9841 * @return {Number} The index of the passed Record. Returns -1 if not found.
9843 indexOf : function(record){
9844 return this.data.indexOf(record);
9848 * Get the index within the cache of the Record with the passed id.
9849 * @param {String} id The id of the Record to find.
9850 * @return {Number} The index of the Record. Returns -1 if not found.
9852 indexOfId : function(id){
9853 return this.data.indexOfKey(id);
9857 * Get the Record with the specified id.
9858 * @param {String} id The id of the Record to find.
9859 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9861 getById : function(id){
9862 return this.data.key(id);
9866 * Get the Record at the specified index.
9867 * @param {Number} index The index of the Record to find.
9868 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9870 getAt : function(index){
9871 return this.data.itemAt(index);
9875 * Returns a range of Records between specified indices.
9876 * @param {Number} startIndex (optional) The starting index (defaults to 0)
9877 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9878 * @return {Roo.data.Record[]} An array of Records
9880 getRange : function(start, end){
9881 return this.data.getRange(start, end);
9885 storeOptions : function(o){
9886 o = Roo.apply({}, o);
9889 this.lastOptions = o;
9893 * Loads the Record cache from the configured Proxy using the configured Reader.
9895 * If using remote paging, then the first load call must specify the <em>start</em>
9896 * and <em>limit</em> properties in the options.params property to establish the initial
9897 * position within the dataset, and the number of Records to cache on each read from the Proxy.
9899 * <strong>It is important to note that for remote data sources, loading is asynchronous,
9900 * and this call will return before the new data has been loaded. Perform any post-processing
9901 * in a callback function, or in a "load" event handler.</strong>
9903 * @param {Object} options An object containing properties which control loading options:<ul>
9904 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9905 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9906 * passed the following arguments:<ul>
9907 * <li>r : Roo.data.Record[]</li>
9908 * <li>options: Options object from the load call</li>
9909 * <li>success: Boolean success indicator</li></ul></li>
9910 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9911 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9914 load : function(options){
9915 options = options || {};
9916 if(this.fireEvent("beforeload", this, options) !== false){
9917 this.storeOptions(options);
9918 var p = Roo.apply(options.params || {}, this.baseParams);
9919 // if meta was not loaded from remote source.. try requesting it.
9920 if (!this.reader.metaFromRemote) {
9923 if(this.sortInfo && this.remoteSort){
9924 var pn = this.paramNames;
9925 p[pn["sort"]] = this.sortInfo.field;
9926 p[pn["dir"]] = this.sortInfo.direction;
9928 if (this.multiSort) {
9929 var pn = this.paramNames;
9930 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9933 this.proxy.load(p, this.reader, this.loadRecords, this, options);
9938 * Reloads the Record cache from the configured Proxy using the configured Reader and
9939 * the options from the last load operation performed.
9940 * @param {Object} options (optional) An object containing properties which may override the options
9941 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9942 * the most recently used options are reused).
9944 reload : function(options){
9945 this.load(Roo.applyIf(options||{}, this.lastOptions));
9949 // Called as a callback by the Reader during a load operation.
9950 loadRecords : function(o, options, success){
9951 if(!o || success === false){
9952 if(success !== false){
9953 this.fireEvent("load", this, [], options, o);
9955 if(options.callback){
9956 options.callback.call(options.scope || this, [], options, false);
9960 // if data returned failure - throw an exception.
9961 if (o.success === false) {
9962 // show a message if no listener is registered.
9963 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9964 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9966 // loadmask wil be hooked into this..
9967 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9970 var r = o.records, t = o.totalRecords || r.length;
9972 this.fireEvent("beforeloadadd", this, r, options, o);
9974 if(!options || options.add !== true){
9975 if(this.pruneModifiedRecords){
9978 for(var i = 0, len = r.length; i < len; i++){
9982 this.data = this.snapshot;
9983 delete this.snapshot;
9986 this.data.addAll(r);
9987 this.totalLength = t;
9989 this.fireEvent("datachanged", this);
9991 this.totalLength = Math.max(t, this.data.length+r.length);
9994 this.fireEvent("load", this, r, options, o);
9995 if(options.callback){
9996 options.callback.call(options.scope || this, r, options, true);
10002 * Loads data from a passed data block. A Reader which understands the format of the data
10003 * must have been configured in the constructor.
10004 * @param {Object} data The data block from which to read the Records. The format of the data expected
10005 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10006 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10008 loadData : function(o, append){
10009 var r = this.reader.readRecords(o);
10010 this.loadRecords(r, {add: append}, true);
10014 * Gets the number of cached records.
10016 * <em>If using paging, this may not be the total size of the dataset. If the data object
10017 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10018 * the data set size</em>
10020 getCount : function(){
10021 return this.data.length || 0;
10025 * Gets the total number of records in the dataset as returned by the server.
10027 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10028 * the dataset size</em>
10030 getTotalCount : function(){
10031 return this.totalLength || 0;
10035 * Returns the sort state of the Store as an object with two properties:
10037 field {String} The name of the field by which the Records are sorted
10038 direction {String} The sort order, "ASC" or "DESC"
10041 getSortState : function(){
10042 return this.sortInfo;
10046 applySort : function(){
10047 if(this.sortInfo && !this.remoteSort){
10048 var s = this.sortInfo, f = s.field;
10049 var st = this.fields.get(f).sortType;
10050 var fn = function(r1, r2){
10051 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10052 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10054 this.data.sort(s.direction, fn);
10055 if(this.snapshot && this.snapshot != this.data){
10056 this.snapshot.sort(s.direction, fn);
10062 * Sets the default sort column and order to be used by the next load operation.
10063 * @param {String} fieldName The name of the field to sort by.
10064 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10066 setDefaultSort : function(field, dir){
10067 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10071 * Sort the Records.
10072 * If remote sorting is used, the sort is performed on the server, and the cache is
10073 * reloaded. If local sorting is used, the cache is sorted internally.
10074 * @param {String} fieldName The name of the field to sort by.
10075 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10077 sort : function(fieldName, dir){
10078 var f = this.fields.get(fieldName);
10080 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10082 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10083 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10088 this.sortToggle[f.name] = dir;
10089 this.sortInfo = {field: f.name, direction: dir};
10090 if(!this.remoteSort){
10092 this.fireEvent("datachanged", this);
10094 this.load(this.lastOptions);
10099 * Calls the specified function for each of the Records in the cache.
10100 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10101 * Returning <em>false</em> aborts and exits the iteration.
10102 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10104 each : function(fn, scope){
10105 this.data.each(fn, scope);
10109 * Gets all records modified since the last commit. Modified records are persisted across load operations
10110 * (e.g., during paging).
10111 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10113 getModifiedRecords : function(){
10114 return this.modified;
10118 createFilterFn : function(property, value, anyMatch){
10119 if(!value.exec){ // not a regex
10120 value = String(value);
10121 if(value.length == 0){
10124 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10126 return function(r){
10127 return value.test(r.data[property]);
10132 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10133 * @param {String} property A field on your records
10134 * @param {Number} start The record index to start at (defaults to 0)
10135 * @param {Number} end The last record index to include (defaults to length - 1)
10136 * @return {Number} The sum
10138 sum : function(property, start, end){
10139 var rs = this.data.items, v = 0;
10140 start = start || 0;
10141 end = (end || end === 0) ? end : rs.length-1;
10143 for(var i = start; i <= end; i++){
10144 v += (rs[i].data[property] || 0);
10150 * Filter the records by a specified property.
10151 * @param {String} field A field on your records
10152 * @param {String/RegExp} value Either a string that the field
10153 * should start with or a RegExp to test against the field
10154 * @param {Boolean} anyMatch True to match any part not just the beginning
10156 filter : function(property, value, anyMatch){
10157 var fn = this.createFilterFn(property, value, anyMatch);
10158 return fn ? this.filterBy(fn) : this.clearFilter();
10162 * Filter by a function. The specified function will be called with each
10163 * record in this data source. If the function returns true the record is included,
10164 * otherwise it is filtered.
10165 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10166 * @param {Object} scope (optional) The scope of the function (defaults to this)
10168 filterBy : function(fn, scope){
10169 this.snapshot = this.snapshot || this.data;
10170 this.data = this.queryBy(fn, scope||this);
10171 this.fireEvent("datachanged", this);
10175 * Query the records by a specified property.
10176 * @param {String} field A field on your records
10177 * @param {String/RegExp} value Either a string that the field
10178 * should start with or a RegExp to test against the field
10179 * @param {Boolean} anyMatch True to match any part not just the beginning
10180 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10182 query : function(property, value, anyMatch){
10183 var fn = this.createFilterFn(property, value, anyMatch);
10184 return fn ? this.queryBy(fn) : this.data.clone();
10188 * Query by a function. The specified function will be called with each
10189 * record in this data source. If the function returns true the record is included
10191 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10192 * @param {Object} scope (optional) The scope of the function (defaults to this)
10193 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10195 queryBy : function(fn, scope){
10196 var data = this.snapshot || this.data;
10197 return data.filterBy(fn, scope||this);
10201 * Collects unique values for a particular dataIndex from this store.
10202 * @param {String} dataIndex The property to collect
10203 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10204 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10205 * @return {Array} An array of the unique values
10207 collect : function(dataIndex, allowNull, bypassFilter){
10208 var d = (bypassFilter === true && this.snapshot) ?
10209 this.snapshot.items : this.data.items;
10210 var v, sv, r = [], l = {};
10211 for(var i = 0, len = d.length; i < len; i++){
10212 v = d[i].data[dataIndex];
10214 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10223 * Revert to a view of the Record cache with no filtering applied.
10224 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10226 clearFilter : function(suppressEvent){
10227 if(this.snapshot && this.snapshot != this.data){
10228 this.data = this.snapshot;
10229 delete this.snapshot;
10230 if(suppressEvent !== true){
10231 this.fireEvent("datachanged", this);
10237 afterEdit : function(record){
10238 if(this.modified.indexOf(record) == -1){
10239 this.modified.push(record);
10241 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10245 afterReject : function(record){
10246 this.modified.remove(record);
10247 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10251 afterCommit : function(record){
10252 this.modified.remove(record);
10253 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10257 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10258 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10260 commitChanges : function(){
10261 var m = this.modified.slice(0);
10262 this.modified = [];
10263 for(var i = 0, len = m.length; i < len; i++){
10269 * Cancel outstanding changes on all changed records.
10271 rejectChanges : function(){
10272 var m = this.modified.slice(0);
10273 this.modified = [];
10274 for(var i = 0, len = m.length; i < len; i++){
10279 onMetaChange : function(meta, rtype, o){
10280 this.recordType = rtype;
10281 this.fields = rtype.prototype.fields;
10282 delete this.snapshot;
10283 this.sortInfo = meta.sortInfo || this.sortInfo;
10284 this.modified = [];
10285 this.fireEvent('metachange', this, this.reader.meta);
10288 moveIndex : function(data, type)
10290 var index = this.indexOf(data);
10292 var newIndex = index + type;
10296 this.insert(newIndex, data);
10301 * Ext JS Library 1.1.1
10302 * Copyright(c) 2006-2007, Ext JS, LLC.
10304 * Originally Released Under LGPL - original licence link has changed is not relivant.
10307 * <script type="text/javascript">
10311 * @class Roo.data.SimpleStore
10312 * @extends Roo.data.Store
10313 * Small helper class to make creating Stores from Array data easier.
10314 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10315 * @cfg {Array} fields An array of field definition objects, or field name strings.
10316 * @cfg {Array} data The multi-dimensional array of data
10318 * @param {Object} config
10320 Roo.data.SimpleStore = function(config){
10321 Roo.data.SimpleStore.superclass.constructor.call(this, {
10323 reader: new Roo.data.ArrayReader({
10326 Roo.data.Record.create(config.fields)
10328 proxy : new Roo.data.MemoryProxy(config.data)
10332 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10334 * Ext JS Library 1.1.1
10335 * Copyright(c) 2006-2007, Ext JS, LLC.
10337 * Originally Released Under LGPL - original licence link has changed is not relivant.
10340 * <script type="text/javascript">
10345 * @extends Roo.data.Store
10346 * @class Roo.data.JsonStore
10347 * Small helper class to make creating Stores for JSON data easier. <br/>
10349 var store = new Roo.data.JsonStore({
10350 url: 'get-images.php',
10352 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10355 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10356 * JsonReader and HttpProxy (unless inline data is provided).</b>
10357 * @cfg {Array} fields An array of field definition objects, or field name strings.
10359 * @param {Object} config
10361 Roo.data.JsonStore = function(c){
10362 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10363 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10364 reader: new Roo.data.JsonReader(c, c.fields)
10367 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10369 * Ext JS Library 1.1.1
10370 * Copyright(c) 2006-2007, Ext JS, LLC.
10372 * Originally Released Under LGPL - original licence link has changed is not relivant.
10375 * <script type="text/javascript">
10379 Roo.data.Field = function(config){
10380 if(typeof config == "string"){
10381 config = {name: config};
10383 Roo.apply(this, config);
10386 this.type = "auto";
10389 var st = Roo.data.SortTypes;
10390 // named sortTypes are supported, here we look them up
10391 if(typeof this.sortType == "string"){
10392 this.sortType = st[this.sortType];
10395 // set default sortType for strings and dates
10396 if(!this.sortType){
10399 this.sortType = st.asUCString;
10402 this.sortType = st.asDate;
10405 this.sortType = st.none;
10410 var stripRe = /[\$,%]/g;
10412 // prebuilt conversion function for this field, instead of
10413 // switching every time we're reading a value
10415 var cv, dateFormat = this.dateFormat;
10420 cv = function(v){ return v; };
10423 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10427 return v !== undefined && v !== null && v !== '' ?
10428 parseInt(String(v).replace(stripRe, ""), 10) : '';
10433 return v !== undefined && v !== null && v !== '' ?
10434 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10439 cv = function(v){ return v === true || v === "true" || v == 1; };
10446 if(v instanceof Date){
10450 if(dateFormat == "timestamp"){
10451 return new Date(v*1000);
10453 return Date.parseDate(v, dateFormat);
10455 var parsed = Date.parse(v);
10456 return parsed ? new Date(parsed) : null;
10465 Roo.data.Field.prototype = {
10473 * Ext JS Library 1.1.1
10474 * Copyright(c) 2006-2007, Ext JS, LLC.
10476 * Originally Released Under LGPL - original licence link has changed is not relivant.
10479 * <script type="text/javascript">
10482 // Base class for reading structured data from a data source. This class is intended to be
10483 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10486 * @class Roo.data.DataReader
10487 * Base class for reading structured data from a data source. This class is intended to be
10488 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10491 Roo.data.DataReader = function(meta, recordType){
10495 this.recordType = recordType instanceof Array ?
10496 Roo.data.Record.create(recordType) : recordType;
10499 Roo.data.DataReader.prototype = {
10501 * Create an empty record
10502 * @param {Object} data (optional) - overlay some values
10503 * @return {Roo.data.Record} record created.
10505 newRow : function(d) {
10507 this.recordType.prototype.fields.each(function(c) {
10509 case 'int' : da[c.name] = 0; break;
10510 case 'date' : da[c.name] = new Date(); break;
10511 case 'float' : da[c.name] = 0.0; break;
10512 case 'boolean' : da[c.name] = false; break;
10513 default : da[c.name] = ""; break;
10517 return new this.recordType(Roo.apply(da, d));
10522 * Ext JS Library 1.1.1
10523 * Copyright(c) 2006-2007, Ext JS, LLC.
10525 * Originally Released Under LGPL - original licence link has changed is not relivant.
10528 * <script type="text/javascript">
10532 * @class Roo.data.DataProxy
10533 * @extends Roo.data.Observable
10534 * This class is an abstract base class for implementations which provide retrieval of
10535 * unformatted data objects.<br>
10537 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10538 * (of the appropriate type which knows how to parse the data object) to provide a block of
10539 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10541 * Custom implementations must implement the load method as described in
10542 * {@link Roo.data.HttpProxy#load}.
10544 Roo.data.DataProxy = function(){
10547 * @event beforeload
10548 * Fires before a network request is made to retrieve a data object.
10549 * @param {Object} This DataProxy object.
10550 * @param {Object} params The params parameter to the load function.
10555 * Fires before the load method's callback is called.
10556 * @param {Object} This DataProxy object.
10557 * @param {Object} o The data object.
10558 * @param {Object} arg The callback argument object passed to the load function.
10562 * @event loadexception
10563 * Fires if an Exception occurs during data retrieval.
10564 * @param {Object} This DataProxy object.
10565 * @param {Object} o The data object.
10566 * @param {Object} arg The callback argument object passed to the load function.
10567 * @param {Object} e The Exception.
10569 loadexception : true
10571 Roo.data.DataProxy.superclass.constructor.call(this);
10574 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10577 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10581 * Ext JS Library 1.1.1
10582 * Copyright(c) 2006-2007, Ext JS, LLC.
10584 * Originally Released Under LGPL - original licence link has changed is not relivant.
10587 * <script type="text/javascript">
10590 * @class Roo.data.MemoryProxy
10591 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10592 * to the Reader when its load method is called.
10594 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10596 Roo.data.MemoryProxy = function(data){
10600 Roo.data.MemoryProxy.superclass.constructor.call(this);
10604 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10606 * Load data from the requested source (in this case an in-memory
10607 * data object passed to the constructor), read the data object into
10608 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10609 * process that block using the passed callback.
10610 * @param {Object} params This parameter is not used by the MemoryProxy class.
10611 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10612 * object into a block of Roo.data.Records.
10613 * @param {Function} callback The function into which to pass the block of Roo.data.records.
10614 * The function must be passed <ul>
10615 * <li>The Record block object</li>
10616 * <li>The "arg" argument from the load function</li>
10617 * <li>A boolean success indicator</li>
10619 * @param {Object} scope The scope in which to call the callback
10620 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10622 load : function(params, reader, callback, scope, arg){
10623 params = params || {};
10626 result = reader.readRecords(this.data);
10628 this.fireEvent("loadexception", this, arg, null, e);
10629 callback.call(scope, null, arg, false);
10632 callback.call(scope, result, arg, true);
10636 update : function(params, records){
10641 * Ext JS Library 1.1.1
10642 * Copyright(c) 2006-2007, Ext JS, LLC.
10644 * Originally Released Under LGPL - original licence link has changed is not relivant.
10647 * <script type="text/javascript">
10650 * @class Roo.data.HttpProxy
10651 * @extends Roo.data.DataProxy
10652 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10653 * configured to reference a certain URL.<br><br>
10655 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10656 * from which the running page was served.<br><br>
10658 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10660 * Be aware that to enable the browser to parse an XML document, the server must set
10661 * the Content-Type header in the HTTP response to "text/xml".
10663 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10664 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
10665 * will be used to make the request.
10667 Roo.data.HttpProxy = function(conn){
10668 Roo.data.HttpProxy.superclass.constructor.call(this);
10669 // is conn a conn config or a real conn?
10671 this.useAjax = !conn || !conn.events;
10675 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10676 // thse are take from connection...
10679 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10682 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10683 * extra parameters to each request made by this object. (defaults to undefined)
10686 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10687 * to each request made by this object. (defaults to undefined)
10690 * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
10693 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10696 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10702 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10706 * Return the {@link Roo.data.Connection} object being used by this Proxy.
10707 * @return {Connection} The Connection object. This object may be used to subscribe to events on
10708 * a finer-grained basis than the DataProxy events.
10710 getConnection : function(){
10711 return this.useAjax ? Roo.Ajax : this.conn;
10715 * Load data from the configured {@link Roo.data.Connection}, read the data object into
10716 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10717 * process that block using the passed callback.
10718 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10719 * for the request to the remote server.
10720 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10721 * object into a block of Roo.data.Records.
10722 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10723 * The function must be passed <ul>
10724 * <li>The Record block object</li>
10725 * <li>The "arg" argument from the load function</li>
10726 * <li>A boolean success indicator</li>
10728 * @param {Object} scope The scope in which to call the callback
10729 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10731 load : function(params, reader, callback, scope, arg){
10732 if(this.fireEvent("beforeload", this, params) !== false){
10734 params : params || {},
10736 callback : callback,
10741 callback : this.loadResponse,
10745 Roo.applyIf(o, this.conn);
10746 if(this.activeRequest){
10747 Roo.Ajax.abort(this.activeRequest);
10749 this.activeRequest = Roo.Ajax.request(o);
10751 this.conn.request(o);
10754 callback.call(scope||this, null, arg, false);
10759 loadResponse : function(o, success, response){
10760 delete this.activeRequest;
10762 this.fireEvent("loadexception", this, o, response);
10763 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10768 result = o.reader.read(response);
10770 this.fireEvent("loadexception", this, o, response, e);
10771 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10775 this.fireEvent("load", this, o, o.request.arg);
10776 o.request.callback.call(o.request.scope, result, o.request.arg, true);
10780 update : function(dataSet){
10785 updateResponse : function(dataSet){
10790 * Ext JS Library 1.1.1
10791 * Copyright(c) 2006-2007, Ext JS, LLC.
10793 * Originally Released Under LGPL - original licence link has changed is not relivant.
10796 * <script type="text/javascript">
10800 * @class Roo.data.ScriptTagProxy
10801 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10802 * other than the originating domain of the running page.<br><br>
10804 * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
10805 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10807 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10808 * source code that is used as the source inside a <script> tag.<br><br>
10810 * In order for the browser to process the returned data, the server must wrap the data object
10811 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10812 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10813 * depending on whether the callback name was passed:
10816 boolean scriptTag = false;
10817 String cb = request.getParameter("callback");
10820 response.setContentType("text/javascript");
10822 response.setContentType("application/x-json");
10824 Writer out = response.getWriter();
10826 out.write(cb + "(");
10828 out.print(dataBlock.toJsonString());
10835 * @param {Object} config A configuration object.
10837 Roo.data.ScriptTagProxy = function(config){
10838 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10839 Roo.apply(this, config);
10840 this.head = document.getElementsByTagName("head")[0];
10843 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10845 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10847 * @cfg {String} url The URL from which to request the data object.
10850 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10854 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10855 * the server the name of the callback function set up by the load call to process the returned data object.
10856 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10857 * javascript output which calls this named function passing the data object as its only parameter.
10859 callbackParam : "callback",
10861 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10862 * name to the request.
10867 * Load data from the configured URL, read the data object into
10868 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10869 * process that block using the passed callback.
10870 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10871 * for the request to the remote server.
10872 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10873 * object into a block of Roo.data.Records.
10874 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10875 * The function must be passed <ul>
10876 * <li>The Record block object</li>
10877 * <li>The "arg" argument from the load function</li>
10878 * <li>A boolean success indicator</li>
10880 * @param {Object} scope The scope in which to call the callback
10881 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10883 load : function(params, reader, callback, scope, arg){
10884 if(this.fireEvent("beforeload", this, params) !== false){
10886 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10888 var url = this.url;
10889 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10891 url += "&_dc=" + (new Date().getTime());
10893 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10896 cb : "stcCallback"+transId,
10897 scriptId : "stcScript"+transId,
10901 callback : callback,
10907 window[trans.cb] = function(o){
10908 conn.handleResponse(o, trans);
10911 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10913 if(this.autoAbort !== false){
10917 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10919 var script = document.createElement("script");
10920 script.setAttribute("src", url);
10921 script.setAttribute("type", "text/javascript");
10922 script.setAttribute("id", trans.scriptId);
10923 this.head.appendChild(script);
10925 this.trans = trans;
10927 callback.call(scope||this, null, arg, false);
10932 isLoading : function(){
10933 return this.trans ? true : false;
10937 * Abort the current server request.
10939 abort : function(){
10940 if(this.isLoading()){
10941 this.destroyTrans(this.trans);
10946 destroyTrans : function(trans, isLoaded){
10947 this.head.removeChild(document.getElementById(trans.scriptId));
10948 clearTimeout(trans.timeoutId);
10950 window[trans.cb] = undefined;
10952 delete window[trans.cb];
10955 // if hasn't been loaded, wait for load to remove it to prevent script error
10956 window[trans.cb] = function(){
10957 window[trans.cb] = undefined;
10959 delete window[trans.cb];
10966 handleResponse : function(o, trans){
10967 this.trans = false;
10968 this.destroyTrans(trans, true);
10971 result = trans.reader.readRecords(o);
10973 this.fireEvent("loadexception", this, o, trans.arg, e);
10974 trans.callback.call(trans.scope||window, null, trans.arg, false);
10977 this.fireEvent("load", this, o, trans.arg);
10978 trans.callback.call(trans.scope||window, result, trans.arg, true);
10982 handleFailure : function(trans){
10983 this.trans = false;
10984 this.destroyTrans(trans, false);
10985 this.fireEvent("loadexception", this, null, trans.arg);
10986 trans.callback.call(trans.scope||window, null, trans.arg, false);
10990 * Ext JS Library 1.1.1
10991 * Copyright(c) 2006-2007, Ext JS, LLC.
10993 * Originally Released Under LGPL - original licence link has changed is not relivant.
10996 * <script type="text/javascript">
11000 * @class Roo.data.JsonReader
11001 * @extends Roo.data.DataReader
11002 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11003 * based on mappings in a provided Roo.data.Record constructor.
11005 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11006 * in the reply previously.
11011 var RecordDef = Roo.data.Record.create([
11012 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11013 {name: 'occupation'} // This field will use "occupation" as the mapping.
11015 var myReader = new Roo.data.JsonReader({
11016 totalProperty: "results", // The property which contains the total dataset size (optional)
11017 root: "rows", // The property which contains an Array of row objects
11018 id: "id" // The property within each row object that provides an ID for the record (optional)
11022 * This would consume a JSON file like this:
11024 { 'results': 2, 'rows': [
11025 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11026 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11029 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11030 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11031 * paged from the remote server.
11032 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11033 * @cfg {String} root name of the property which contains the Array of row objects.
11034 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11035 * @cfg {Array} fields Array of field definition objects
11037 * Create a new JsonReader
11038 * @param {Object} meta Metadata configuration options
11039 * @param {Object} recordType Either an Array of field definition objects,
11040 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11042 Roo.data.JsonReader = function(meta, recordType){
11045 // set some defaults:
11046 Roo.applyIf(meta, {
11047 totalProperty: 'total',
11048 successProperty : 'success',
11053 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11055 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11058 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11059 * Used by Store query builder to append _requestMeta to params.
11062 metaFromRemote : false,
11064 * This method is only used by a DataProxy which has retrieved data from a remote server.
11065 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11066 * @return {Object} data A data block which is used by an Roo.data.Store object as
11067 * a cache of Roo.data.Records.
11069 read : function(response){
11070 var json = response.responseText;
11072 var o = /* eval:var:o */ eval("("+json+")");
11074 throw {message: "JsonReader.read: Json object not found"};
11080 this.metaFromRemote = true;
11081 this.meta = o.metaData;
11082 this.recordType = Roo.data.Record.create(o.metaData.fields);
11083 this.onMetaChange(this.meta, this.recordType, o);
11085 return this.readRecords(o);
11088 // private function a store will implement
11089 onMetaChange : function(meta, recordType, o){
11096 simpleAccess: function(obj, subsc) {
11103 getJsonAccessor: function(){
11105 return function(expr) {
11107 return(re.test(expr))
11108 ? new Function("obj", "return obj." + expr)
11113 return Roo.emptyFn;
11118 * Create a data block containing Roo.data.Records from an XML document.
11119 * @param {Object} o An object which contains an Array of row objects in the property specified
11120 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11121 * which contains the total size of the dataset.
11122 * @return {Object} data A data block which is used by an Roo.data.Store object as
11123 * a cache of Roo.data.Records.
11125 readRecords : function(o){
11127 * After any data loads, the raw JSON data is available for further custom processing.
11131 var s = this.meta, Record = this.recordType,
11132 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11134 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11136 if(s.totalProperty) {
11137 this.getTotal = this.getJsonAccessor(s.totalProperty);
11139 if(s.successProperty) {
11140 this.getSuccess = this.getJsonAccessor(s.successProperty);
11142 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11144 var g = this.getJsonAccessor(s.id);
11145 this.getId = function(rec) {
11147 return (r === undefined || r === "") ? null : r;
11150 this.getId = function(){return null;};
11153 for(var jj = 0; jj < fl; jj++){
11155 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11156 this.ef[jj] = this.getJsonAccessor(map);
11160 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11161 if(s.totalProperty){
11162 var vt = parseInt(this.getTotal(o), 10);
11167 if(s.successProperty){
11168 var vs = this.getSuccess(o);
11169 if(vs === false || vs === 'false'){
11174 for(var i = 0; i < c; i++){
11177 var id = this.getId(n);
11178 for(var j = 0; j < fl; j++){
11180 var v = this.ef[j](n);
11182 Roo.log('missing convert for ' + f.name);
11186 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11188 var record = new Record(values, id);
11190 records[i] = record;
11196 totalRecords : totalRecords
11201 * Ext JS Library 1.1.1
11202 * Copyright(c) 2006-2007, Ext JS, LLC.
11204 * Originally Released Under LGPL - original licence link has changed is not relivant.
11207 * <script type="text/javascript">
11211 * @class Roo.data.ArrayReader
11212 * @extends Roo.data.DataReader
11213 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11214 * Each element of that Array represents a row of data fields. The
11215 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11216 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11220 var RecordDef = Roo.data.Record.create([
11221 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11222 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11224 var myReader = new Roo.data.ArrayReader({
11225 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11229 * This would consume an Array like this:
11231 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11233 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11235 * Create a new JsonReader
11236 * @param {Object} meta Metadata configuration options.
11237 * @param {Object} recordType Either an Array of field definition objects
11238 * as specified to {@link Roo.data.Record#create},
11239 * or an {@link Roo.data.Record} object
11240 * created using {@link Roo.data.Record#create}.
11242 Roo.data.ArrayReader = function(meta, recordType){
11243 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11246 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11248 * Create a data block containing Roo.data.Records from an XML document.
11249 * @param {Object} o An Array of row objects which represents the dataset.
11250 * @return {Object} data A data block which is used by an Roo.data.Store object as
11251 * a cache of Roo.data.Records.
11253 readRecords : function(o){
11254 var sid = this.meta ? this.meta.id : null;
11255 var recordType = this.recordType, fields = recordType.prototype.fields;
11258 for(var i = 0; i < root.length; i++){
11261 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11262 for(var j = 0, jlen = fields.length; j < jlen; j++){
11263 var f = fields.items[j];
11264 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11265 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11267 values[f.name] = v;
11269 var record = new recordType(values, id);
11271 records[records.length] = record;
11275 totalRecords : records.length
11284 * @class Roo.bootstrap.ComboBox
11285 * @extends Roo.bootstrap.TriggerField
11286 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11287 * @cfg {Boolean} append (true|false) default false
11288 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11289 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11290 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11291 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11292 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11293 * @cfg {Boolean} animate default true
11294 * @cfg {Boolean} emptyResultText only for touch device
11295 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11297 * Create a new ComboBox.
11298 * @param {Object} config Configuration options
11300 Roo.bootstrap.ComboBox = function(config){
11301 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11305 * Fires when the dropdown list is expanded
11306 * @param {Roo.bootstrap.ComboBox} combo This combo box
11311 * Fires when the dropdown list is collapsed
11312 * @param {Roo.bootstrap.ComboBox} combo This combo box
11316 * @event beforeselect
11317 * Fires before a list item is selected. Return false to cancel the selection.
11318 * @param {Roo.bootstrap.ComboBox} combo This combo box
11319 * @param {Roo.data.Record} record The data record returned from the underlying store
11320 * @param {Number} index The index of the selected item in the dropdown list
11322 'beforeselect' : true,
11325 * Fires when a list item is selected
11326 * @param {Roo.bootstrap.ComboBox} combo This combo box
11327 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11328 * @param {Number} index The index of the selected item in the dropdown list
11332 * @event beforequery
11333 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11334 * The event object passed has these properties:
11335 * @param {Roo.bootstrap.ComboBox} combo This combo box
11336 * @param {String} query The query
11337 * @param {Boolean} forceAll true to force "all" query
11338 * @param {Boolean} cancel true to cancel the query
11339 * @param {Object} e The query event object
11341 'beforequery': true,
11344 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11345 * @param {Roo.bootstrap.ComboBox} combo This combo box
11350 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11351 * @param {Roo.bootstrap.ComboBox} combo This combo box
11352 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11357 * Fires when the remove value from the combobox array
11358 * @param {Roo.bootstrap.ComboBox} combo This combo box
11362 * @event specialfilter
11363 * Fires when specialfilter
11364 * @param {Roo.bootstrap.ComboBox} combo This combo box
11366 'specialfilter' : true,
11369 * Fires when tick the element
11370 * @param {Roo.bootstrap.ComboBox} combo This combo box
11377 this.tickItems = [];
11379 this.selectedIndex = -1;
11380 if(this.mode == 'local'){
11381 if(config.queryDelay === undefined){
11382 this.queryDelay = 10;
11384 if(config.minChars === undefined){
11390 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11393 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11394 * rendering into an Roo.Editor, defaults to false)
11397 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11398 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11401 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11404 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11405 * the dropdown list (defaults to undefined, with no header element)
11409 * @cfg {String/Roo.Template} tpl The template to use to render the output
11413 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11415 listWidth: undefined,
11417 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11418 * mode = 'remote' or 'text' if mode = 'local')
11420 displayField: undefined,
11423 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11424 * mode = 'remote' or 'value' if mode = 'local').
11425 * Note: use of a valueField requires the user make a selection
11426 * in order for a value to be mapped.
11428 valueField: undefined,
11432 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11433 * field's data value (defaults to the underlying DOM element's name)
11435 hiddenName: undefined,
11437 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11441 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11443 selectedClass: 'active',
11446 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11450 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11451 * anchor positions (defaults to 'tl-bl')
11453 listAlign: 'tl-bl?',
11455 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11459 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11460 * query specified by the allQuery config option (defaults to 'query')
11462 triggerAction: 'query',
11464 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11465 * (defaults to 4, does not apply if editable = false)
11469 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11470 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11474 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11475 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11479 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11480 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11484 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11485 * when editable = true (defaults to false)
11487 selectOnFocus:false,
11489 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11491 queryParam: 'query',
11493 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
11494 * when mode = 'remote' (defaults to 'Loading...')
11496 loadingText: 'Loading...',
11498 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11502 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11506 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11507 * traditional select (defaults to true)
11511 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11515 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11519 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11520 * listWidth has a higher value)
11524 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11525 * allow the user to set arbitrary text into the field (defaults to false)
11527 forceSelection:false,
11529 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11530 * if typeAhead = true (defaults to 250)
11532 typeAheadDelay : 250,
11534 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11535 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11537 valueNotFoundText : undefined,
11539 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11541 blockFocus : false,
11544 * @cfg {Boolean} disableClear Disable showing of clear button.
11546 disableClear : false,
11548 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
11550 alwaysQuery : false,
11553 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
11558 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11560 invalidClass : "has-warning",
11563 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11565 validClass : "has-success",
11568 * @cfg {Boolean} specialFilter (true|false) special filter default false
11570 specialFilter : false,
11573 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11575 mobileTouchView : true,
11587 btnPosition : 'right',
11588 triggerList : true,
11589 showToggleBtn : true,
11591 emptyResultText: 'Empty',
11592 triggerText : 'Select',
11594 // element that contains real text value.. (when hidden is used..)
11596 getAutoCreate : function()
11604 if(Roo.isTouch && this.mobileTouchView){
11605 cfg = this.getAutoCreateTouchView();
11612 if(!this.tickable){
11613 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11618 * ComboBox with tickable selections
11621 var align = this.labelAlign || this.parentLabelAlign();
11624 cls : 'form-group roo-combobox-tickable' //input-group
11629 cls : 'tickable-buttons',
11634 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11635 html : this.triggerText
11641 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11648 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11655 buttons.cn.unshift({
11657 cls: 'select2-search-field-input'
11663 Roo.each(buttons.cn, function(c){
11665 c.cls += ' btn-' + _this.size;
11668 if (_this.disabled) {
11679 cls: 'form-hidden-field'
11683 cls: 'select2-choices',
11687 cls: 'select2-search-field',
11699 cls: 'select2-container input-group select2-container-multi',
11704 // cls: 'typeahead typeahead-long dropdown-menu',
11705 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
11710 if(this.hasFeedback && !this.allowBlank){
11714 cls: 'glyphicon form-control-feedback'
11717 combobox.cn.push(feedback);
11720 if (align ==='left' && this.fieldLabel.length) {
11722 Roo.log("left and has label");
11728 cls : 'control-label col-sm-' + this.labelWidth,
11729 html : this.fieldLabel
11733 cls : "col-sm-" + (12 - this.labelWidth),
11740 } else if ( this.fieldLabel.length) {
11746 //cls : 'input-group-addon',
11747 html : this.fieldLabel
11757 Roo.log(" no label && no align");
11764 ['xs','sm','md','lg'].map(function(size){
11765 if (settings[size]) {
11766 cfg.cls += ' col-' + size + '-' + settings[size];
11774 _initEventsCalled : false,
11777 initEvents: function()
11780 if (this._initEventsCalled) { // as we call render... prevent looping...
11783 this._initEventsCalled = true;
11786 throw "can not find store for combo";
11789 this.store = Roo.factory(this.store, Roo.data);
11791 // if we are building from html. then this element is so complex, that we can not really
11792 // use the rendered HTML.
11793 // so we have to trash and replace the previous code.
11794 if (Roo.XComponent.build_from_html) {
11796 // remove this element....
11797 var e = this.el.dom, k=0;
11798 while (e ) { e = e.previousSibling; ++k;}
11803 this.rendered = false;
11805 this.render(this.parent().getChildContainer(true), k);
11816 if(Roo.isTouch && this.mobileTouchView){
11817 this.initTouchView();
11822 this.initTickableEvents();
11826 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11828 if(this.hiddenName){
11830 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11832 this.hiddenField.dom.value =
11833 this.hiddenValue !== undefined ? this.hiddenValue :
11834 this.value !== undefined ? this.value : '';
11836 // prevent input submission
11837 this.el.dom.removeAttribute('name');
11838 this.hiddenField.dom.setAttribute('name', this.hiddenName);
11843 // this.el.dom.setAttribute('autocomplete', 'off');
11846 var cls = 'x-combo-list';
11848 //this.list = new Roo.Layer({
11849 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11855 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11856 _this.list.setWidth(lw);
11859 this.list.on('mouseover', this.onViewOver, this);
11860 this.list.on('mousemove', this.onViewMove, this);
11862 this.list.on('scroll', this.onViewScroll, this);
11865 this.list.swallowEvent('mousewheel');
11866 this.assetHeight = 0;
11869 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11870 this.assetHeight += this.header.getHeight();
11873 this.innerList = this.list.createChild({cls:cls+'-inner'});
11874 this.innerList.on('mouseover', this.onViewOver, this);
11875 this.innerList.on('mousemove', this.onViewMove, this);
11876 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11878 if(this.allowBlank && !this.pageSize && !this.disableClear){
11879 this.footer = this.list.createChild({cls:cls+'-ft'});
11880 this.pageTb = new Roo.Toolbar(this.footer);
11884 this.footer = this.list.createChild({cls:cls+'-ft'});
11885 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11886 {pageSize: this.pageSize});
11890 if (this.pageTb && this.allowBlank && !this.disableClear) {
11892 this.pageTb.add(new Roo.Toolbar.Fill(), {
11893 cls: 'x-btn-icon x-btn-clear',
11895 handler: function()
11898 _this.clearValue();
11899 _this.onSelect(false, -1);
11904 this.assetHeight += this.footer.getHeight();
11909 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11912 this.view = new Roo.View(this.list, this.tpl, {
11913 singleSelect:true, store: this.store, selectedClass: this.selectedClass
11915 //this.view.wrapEl.setDisplayed(false);
11916 this.view.on('click', this.onViewClick, this);
11920 this.store.on('beforeload', this.onBeforeLoad, this);
11921 this.store.on('load', this.onLoad, this);
11922 this.store.on('loadexception', this.onLoadException, this);
11924 if(this.resizable){
11925 this.resizer = new Roo.Resizable(this.list, {
11926 pinned:true, handles:'se'
11928 this.resizer.on('resize', function(r, w, h){
11929 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11930 this.listWidth = w;
11931 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11932 this.restrictHeight();
11934 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11937 if(!this.editable){
11938 this.editable = true;
11939 this.setEditable(false);
11944 if (typeof(this.events.add.listeners) != 'undefined') {
11946 this.addicon = this.wrap.createChild(
11947 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
11949 this.addicon.on('click', function(e) {
11950 this.fireEvent('add', this);
11953 if (typeof(this.events.edit.listeners) != 'undefined') {
11955 this.editicon = this.wrap.createChild(
11956 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
11957 if (this.addicon) {
11958 this.editicon.setStyle('margin-left', '40px');
11960 this.editicon.on('click', function(e) {
11962 // we fire even if inothing is selected..
11963 this.fireEvent('edit', this, this.lastData );
11969 this.keyNav = new Roo.KeyNav(this.inputEl(), {
11970 "up" : function(e){
11971 this.inKeyMode = true;
11975 "down" : function(e){
11976 if(!this.isExpanded()){
11977 this.onTriggerClick();
11979 this.inKeyMode = true;
11984 "enter" : function(e){
11985 // this.onViewClick();
11989 if(this.fireEvent("specialkey", this, e)){
11990 this.onViewClick(false);
11996 "esc" : function(e){
12000 "tab" : function(e){
12003 if(this.fireEvent("specialkey", this, e)){
12004 this.onViewClick(false);
12012 doRelay : function(foo, bar, hname){
12013 if(hname == 'down' || this.scope.isExpanded()){
12014 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12023 this.queryDelay = Math.max(this.queryDelay || 10,
12024 this.mode == 'local' ? 10 : 250);
12027 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12029 if(this.typeAhead){
12030 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12032 if(this.editable !== false){
12033 this.inputEl().on("keyup", this.onKeyUp, this);
12035 if(this.forceSelection){
12036 this.inputEl().on('blur', this.doForce, this);
12040 this.choices = this.el.select('ul.select2-choices', true).first();
12041 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12045 initTickableEvents: function()
12049 if(this.hiddenName){
12051 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12053 this.hiddenField.dom.value =
12054 this.hiddenValue !== undefined ? this.hiddenValue :
12055 this.value !== undefined ? this.value : '';
12057 // prevent input submission
12058 this.el.dom.removeAttribute('name');
12059 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12064 // this.list = this.el.select('ul.dropdown-menu',true).first();
12066 this.choices = this.el.select('ul.select2-choices', true).first();
12067 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12068 if(this.triggerList){
12069 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12072 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12073 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12075 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12076 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12078 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12079 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12081 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12082 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12083 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12086 this.cancelBtn.hide();
12091 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12092 _this.list.setWidth(lw);
12095 this.list.on('mouseover', this.onViewOver, this);
12096 this.list.on('mousemove', this.onViewMove, this);
12098 this.list.on('scroll', this.onViewScroll, this);
12101 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>';
12104 this.view = new Roo.View(this.list, this.tpl, {
12105 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12108 //this.view.wrapEl.setDisplayed(false);
12109 this.view.on('click', this.onViewClick, this);
12113 this.store.on('beforeload', this.onBeforeLoad, this);
12114 this.store.on('load', this.onLoad, this);
12115 this.store.on('loadexception', this.onLoadException, this);
12118 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12119 "up" : function(e){
12120 this.inKeyMode = true;
12124 "down" : function(e){
12125 this.inKeyMode = true;
12129 "enter" : function(e){
12130 if(this.fireEvent("specialkey", this, e)){
12131 this.onViewClick(false);
12137 "esc" : function(e){
12138 this.onTickableFooterButtonClick(e, false, false);
12141 "tab" : function(e){
12142 this.fireEvent("specialkey", this, e);
12144 this.onTickableFooterButtonClick(e, false, false);
12151 doRelay : function(e, fn, key){
12152 if(this.scope.isExpanded()){
12153 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12162 this.queryDelay = Math.max(this.queryDelay || 10,
12163 this.mode == 'local' ? 10 : 250);
12166 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12168 if(this.typeAhead){
12169 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12172 if(this.editable !== false){
12173 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12178 onDestroy : function(){
12180 this.view.setStore(null);
12181 this.view.el.removeAllListeners();
12182 this.view.el.remove();
12183 this.view.purgeListeners();
12186 this.list.dom.innerHTML = '';
12190 this.store.un('beforeload', this.onBeforeLoad, this);
12191 this.store.un('load', this.onLoad, this);
12192 this.store.un('loadexception', this.onLoadException, this);
12194 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12198 fireKey : function(e){
12199 if(e.isNavKeyPress() && !this.list.isVisible()){
12200 this.fireEvent("specialkey", this, e);
12205 onResize: function(w, h){
12206 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12208 // if(typeof w != 'number'){
12209 // // we do not handle it!?!?
12212 // var tw = this.trigger.getWidth();
12213 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12214 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12216 // this.inputEl().setWidth( this.adjustWidth('input', x));
12218 // //this.trigger.setStyle('left', x+'px');
12220 // if(this.list && this.listWidth === undefined){
12221 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12222 // this.list.setWidth(lw);
12223 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12231 * Allow or prevent the user from directly editing the field text. If false is passed,
12232 * the user will only be able to select from the items defined in the dropdown list. This method
12233 * is the runtime equivalent of setting the 'editable' config option at config time.
12234 * @param {Boolean} value True to allow the user to directly edit the field text
12236 setEditable : function(value){
12237 if(value == this.editable){
12240 this.editable = value;
12242 this.inputEl().dom.setAttribute('readOnly', true);
12243 this.inputEl().on('mousedown', this.onTriggerClick, this);
12244 this.inputEl().addClass('x-combo-noedit');
12246 this.inputEl().dom.setAttribute('readOnly', false);
12247 this.inputEl().un('mousedown', this.onTriggerClick, this);
12248 this.inputEl().removeClass('x-combo-noedit');
12254 onBeforeLoad : function(combo,opts){
12255 if(!this.hasFocus){
12259 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12261 this.restrictHeight();
12262 this.selectedIndex = -1;
12266 onLoad : function(){
12268 this.hasQuery = false;
12270 if(!this.hasFocus){
12274 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12275 this.loading.hide();
12278 if(this.store.getCount() > 0){
12280 this.restrictHeight();
12281 if(this.lastQuery == this.allQuery){
12282 if(this.editable && !this.tickable){
12283 this.inputEl().dom.select();
12287 !this.selectByValue(this.value, true) &&
12290 !this.store.lastOptions ||
12291 typeof(this.store.lastOptions.add) == 'undefined' ||
12292 this.store.lastOptions.add != true
12295 this.select(0, true);
12298 if(this.autoFocus){
12301 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12302 this.taTask.delay(this.typeAheadDelay);
12306 this.onEmptyResults();
12312 onLoadException : function()
12314 this.hasQuery = false;
12316 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12317 this.loading.hide();
12320 if(this.tickable && this.editable){
12325 // only causes errors at present
12326 //Roo.log(this.store.reader.jsonData);
12327 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12329 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12335 onTypeAhead : function(){
12336 if(this.store.getCount() > 0){
12337 var r = this.store.getAt(0);
12338 var newValue = r.data[this.displayField];
12339 var len = newValue.length;
12340 var selStart = this.getRawValue().length;
12342 if(selStart != len){
12343 this.setRawValue(newValue);
12344 this.selectText(selStart, newValue.length);
12350 onSelect : function(record, index){
12352 if(this.fireEvent('beforeselect', this, record, index) !== false){
12354 this.setFromData(index > -1 ? record.data : false);
12357 this.fireEvent('select', this, record, index);
12362 * Returns the currently selected field value or empty string if no value is set.
12363 * @return {String} value The selected value
12365 getValue : function(){
12368 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12371 if(this.valueField){
12372 return typeof this.value != 'undefined' ? this.value : '';
12374 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12379 * Clears any text/value currently set in the field
12381 clearValue : function(){
12382 if(this.hiddenField){
12383 this.hiddenField.dom.value = '';
12386 this.setRawValue('');
12387 this.lastSelectionText = '';
12388 this.lastData = false;
12390 var close = this.closeTriggerEl();
12399 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12400 * will be displayed in the field. If the value does not match the data value of an existing item,
12401 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12402 * Otherwise the field will be blank (although the value will still be set).
12403 * @param {String} value The value to match
12405 setValue : function(v){
12412 if(this.valueField){
12413 var r = this.findRecord(this.valueField, v);
12415 text = r.data[this.displayField];
12416 }else if(this.valueNotFoundText !== undefined){
12417 text = this.valueNotFoundText;
12420 this.lastSelectionText = text;
12421 if(this.hiddenField){
12422 this.hiddenField.dom.value = v;
12424 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12427 var close = this.closeTriggerEl();
12430 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12434 * @property {Object} the last set data for the element
12439 * Sets the value of the field based on a object which is related to the record format for the store.
12440 * @param {Object} value the value to set as. or false on reset?
12442 setFromData : function(o){
12449 var dv = ''; // display value
12450 var vv = ''; // value value..
12452 if (this.displayField) {
12453 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12455 // this is an error condition!!!
12456 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12459 if(this.valueField){
12460 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12463 var close = this.closeTriggerEl();
12466 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12469 if(this.hiddenField){
12470 this.hiddenField.dom.value = vv;
12472 this.lastSelectionText = dv;
12473 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12477 // no hidden field.. - we store the value in 'value', but still display
12478 // display field!!!!
12479 this.lastSelectionText = dv;
12480 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12487 reset : function(){
12488 // overridden so that last data is reset..
12495 this.setValue(this.originalValue);
12496 this.clearInvalid();
12497 this.lastData = false;
12499 this.view.clearSelections();
12503 findRecord : function(prop, value){
12505 if(this.store.getCount() > 0){
12506 this.store.each(function(r){
12507 if(r.data[prop] == value){
12517 getName: function()
12519 // returns hidden if it's set..
12520 if (!this.rendered) {return ''};
12521 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
12525 onViewMove : function(e, t){
12526 this.inKeyMode = false;
12530 onViewOver : function(e, t){
12531 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12534 var item = this.view.findItemFromChild(t);
12537 var index = this.view.indexOf(item);
12538 this.select(index, false);
12543 onViewClick : function(view, doFocus, el, e)
12545 var index = this.view.getSelectedIndexes()[0];
12547 var r = this.store.getAt(index);
12551 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12558 Roo.each(this.tickItems, function(v,k){
12560 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12562 _this.tickItems.splice(k, 1);
12564 if(typeof(e) == 'undefined' && view == false){
12565 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12577 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
12578 this.tickItems.push(r.data);
12581 if(typeof(e) == 'undefined' && view == false){
12582 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12589 this.onSelect(r, index);
12591 if(doFocus !== false && !this.blockFocus){
12592 this.inputEl().focus();
12597 restrictHeight : function(){
12598 //this.innerList.dom.style.height = '';
12599 //var inner = this.innerList.dom;
12600 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12601 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12602 //this.list.beginUpdate();
12603 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12604 this.list.alignTo(this.inputEl(), this.listAlign);
12605 this.list.alignTo(this.inputEl(), this.listAlign);
12606 //this.list.endUpdate();
12610 onEmptyResults : function(){
12612 if(this.tickable && this.editable){
12613 this.restrictHeight();
12621 * Returns true if the dropdown list is expanded, else false.
12623 isExpanded : function(){
12624 return this.list.isVisible();
12628 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12629 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12630 * @param {String} value The data value of the item to select
12631 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12632 * selected item if it is not currently in view (defaults to true)
12633 * @return {Boolean} True if the value matched an item in the list, else false
12635 selectByValue : function(v, scrollIntoView){
12636 if(v !== undefined && v !== null){
12637 var r = this.findRecord(this.valueField || this.displayField, v);
12639 this.select(this.store.indexOf(r), scrollIntoView);
12647 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12648 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12649 * @param {Number} index The zero-based index of the list item to select
12650 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12651 * selected item if it is not currently in view (defaults to true)
12653 select : function(index, scrollIntoView){
12654 this.selectedIndex = index;
12655 this.view.select(index);
12656 if(scrollIntoView !== false){
12657 var el = this.view.getNode(index);
12659 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12662 this.list.scrollChildIntoView(el, false);
12668 selectNext : function(){
12669 var ct = this.store.getCount();
12671 if(this.selectedIndex == -1){
12673 }else if(this.selectedIndex < ct-1){
12674 this.select(this.selectedIndex+1);
12680 selectPrev : function(){
12681 var ct = this.store.getCount();
12683 if(this.selectedIndex == -1){
12685 }else if(this.selectedIndex != 0){
12686 this.select(this.selectedIndex-1);
12692 onKeyUp : function(e){
12693 if(this.editable !== false && !e.isSpecialKey()){
12694 this.lastKey = e.getKey();
12695 this.dqTask.delay(this.queryDelay);
12700 validateBlur : function(){
12701 return !this.list || !this.list.isVisible();
12705 initQuery : function(){
12707 var v = this.getRawValue();
12709 if(this.tickable && this.editable){
12710 v = this.tickableInputEl().getValue();
12717 doForce : function(){
12718 if(this.inputEl().dom.value.length > 0){
12719 this.inputEl().dom.value =
12720 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12726 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
12727 * query allowing the query action to be canceled if needed.
12728 * @param {String} query The SQL query to execute
12729 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12730 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
12731 * saved in the current store (defaults to false)
12733 doQuery : function(q, forceAll){
12735 if(q === undefined || q === null){
12740 forceAll: forceAll,
12744 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12749 forceAll = qe.forceAll;
12750 if(forceAll === true || (q.length >= this.minChars)){
12752 this.hasQuery = true;
12754 if(this.lastQuery != q || this.alwaysQuery){
12755 this.lastQuery = q;
12756 if(this.mode == 'local'){
12757 this.selectedIndex = -1;
12759 this.store.clearFilter();
12762 if(this.specialFilter){
12763 this.fireEvent('specialfilter', this);
12768 this.store.filter(this.displayField, q);
12771 this.store.fireEvent("datachanged", this.store);
12778 this.store.baseParams[this.queryParam] = q;
12780 var options = {params : this.getParams(q)};
12783 options.add = true;
12784 options.params.start = this.page * this.pageSize;
12787 this.store.load(options);
12790 * this code will make the page width larger, at the beginning, the list not align correctly,
12791 * we should expand the list on onLoad
12792 * so command out it
12797 this.selectedIndex = -1;
12802 this.loadNext = false;
12806 getParams : function(q){
12808 //p[this.queryParam] = q;
12812 p.limit = this.pageSize;
12818 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12820 collapse : function(){
12821 if(!this.isExpanded()){
12828 this.hasFocus = false;
12830 this.cancelBtn.hide();
12831 this.trigger.show();
12834 this.tickableInputEl().dom.value = '';
12835 this.tickableInputEl().blur();
12840 Roo.get(document).un('mousedown', this.collapseIf, this);
12841 Roo.get(document).un('mousewheel', this.collapseIf, this);
12842 if (!this.editable) {
12843 Roo.get(document).un('keydown', this.listKeyPress, this);
12845 this.fireEvent('collapse', this);
12849 collapseIf : function(e){
12850 var in_combo = e.within(this.el);
12851 var in_list = e.within(this.list);
12852 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12854 if (in_combo || in_list || is_list) {
12855 //e.stopPropagation();
12860 this.onTickableFooterButtonClick(e, false, false);
12868 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12870 expand : function(){
12872 if(this.isExpanded() || !this.hasFocus){
12876 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12877 this.list.setWidth(lw);
12884 this.restrictHeight();
12888 this.tickItems = Roo.apply([], this.item);
12891 this.cancelBtn.show();
12892 this.trigger.hide();
12895 this.tickableInputEl().focus();
12900 Roo.get(document).on('mousedown', this.collapseIf, this);
12901 Roo.get(document).on('mousewheel', this.collapseIf, this);
12902 if (!this.editable) {
12903 Roo.get(document).on('keydown', this.listKeyPress, this);
12906 this.fireEvent('expand', this);
12910 // Implements the default empty TriggerField.onTriggerClick function
12911 onTriggerClick : function(e)
12913 Roo.log('trigger click');
12915 if(this.disabled || !this.triggerList){
12920 this.loadNext = false;
12922 if(this.isExpanded()){
12924 if (!this.blockFocus) {
12925 this.inputEl().focus();
12929 this.hasFocus = true;
12930 if(this.triggerAction == 'all') {
12931 this.doQuery(this.allQuery, true);
12933 this.doQuery(this.getRawValue());
12935 if (!this.blockFocus) {
12936 this.inputEl().focus();
12941 onTickableTriggerClick : function(e)
12948 this.loadNext = false;
12949 this.hasFocus = true;
12951 if(this.triggerAction == 'all') {
12952 this.doQuery(this.allQuery, true);
12954 this.doQuery(this.getRawValue());
12958 onSearchFieldClick : function(e)
12960 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12961 this.onTickableFooterButtonClick(e, false, false);
12965 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12970 this.loadNext = false;
12971 this.hasFocus = true;
12973 if(this.triggerAction == 'all') {
12974 this.doQuery(this.allQuery, true);
12976 this.doQuery(this.getRawValue());
12980 listKeyPress : function(e)
12982 //Roo.log('listkeypress');
12983 // scroll to first matching element based on key pres..
12984 if (e.isSpecialKey()) {
12987 var k = String.fromCharCode(e.getKey()).toUpperCase();
12990 var csel = this.view.getSelectedNodes();
12991 var cselitem = false;
12993 var ix = this.view.indexOf(csel[0]);
12994 cselitem = this.store.getAt(ix);
12995 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13001 this.store.each(function(v) {
13003 // start at existing selection.
13004 if (cselitem.id == v.id) {
13010 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13011 match = this.store.indexOf(v);
13017 if (match === false) {
13018 return true; // no more action?
13021 this.view.select(match);
13022 var sn = Roo.get(this.view.getSelectedNodes()[0])
13023 sn.scrollIntoView(sn.dom.parentNode, false);
13026 onViewScroll : function(e, t){
13028 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){
13032 this.hasQuery = true;
13034 this.loading = this.list.select('.loading', true).first();
13036 if(this.loading === null){
13037 this.list.createChild({
13039 cls: 'loading select2-more-results select2-active',
13040 html: 'Loading more results...'
13043 this.loading = this.list.select('.loading', true).first();
13045 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13047 this.loading.hide();
13050 this.loading.show();
13055 this.loadNext = true;
13057 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13062 addItem : function(o)
13064 var dv = ''; // display value
13066 if (this.displayField) {
13067 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13069 // this is an error condition!!!
13070 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13077 var choice = this.choices.createChild({
13079 cls: 'select2-search-choice',
13088 cls: 'select2-search-choice-close',
13093 }, this.searchField);
13095 var close = choice.select('a.select2-search-choice-close', true).first()
13097 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13105 this.inputEl().dom.value = '';
13110 onRemoveItem : function(e, _self, o)
13112 e.preventDefault();
13114 this.lastItem = Roo.apply([], this.item);
13116 var index = this.item.indexOf(o.data) * 1;
13119 Roo.log('not this item?!');
13123 this.item.splice(index, 1);
13128 this.fireEvent('remove', this, e);
13134 syncValue : function()
13136 if(!this.item.length){
13143 Roo.each(this.item, function(i){
13144 if(_this.valueField){
13145 value.push(i[_this.valueField]);
13152 this.value = value.join(',');
13154 if(this.hiddenField){
13155 this.hiddenField.dom.value = this.value;
13158 this.store.fireEvent("datachanged", this.store);
13161 clearItem : function()
13163 if(!this.multiple){
13169 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13177 if(this.tickable && !Roo.isTouch){
13178 this.view.refresh();
13182 inputEl: function ()
13184 if(Roo.isTouch && this.mobileTouchView){
13185 return this.el.select('input.form-control',true).first();
13189 return this.searchField;
13192 return this.el.select('input.form-control',true).first();
13196 onTickableFooterButtonClick : function(e, btn, el)
13198 e.preventDefault();
13200 this.lastItem = Roo.apply([], this.item);
13202 if(btn && btn.name == 'cancel'){
13203 this.tickItems = Roo.apply([], this.item);
13212 Roo.each(this.tickItems, function(o){
13220 validate : function()
13222 var v = this.getRawValue();
13225 v = this.getValue();
13228 if(this.disabled || this.allowBlank || v.length){
13233 this.markInvalid();
13237 tickableInputEl : function()
13239 if(!this.tickable || !this.editable){
13240 return this.inputEl();
13243 return this.inputEl().select('.select2-search-field-input', true).first();
13247 getAutoCreateTouchView : function()
13252 cls: 'form-group' //input-group
13258 type : this.inputType,
13259 cls : 'form-control x-combo-noedit',
13260 autocomplete: 'new-password',
13261 placeholder : this.placeholder || '',
13266 input.name = this.name;
13270 input.cls += ' input-' + this.size;
13273 if (this.disabled) {
13274 input.disabled = true;
13285 inputblock.cls += ' input-group';
13287 inputblock.cn.unshift({
13289 cls : 'input-group-addon',
13294 if(this.removable && !this.multiple){
13295 inputblock.cls += ' roo-removable';
13297 inputblock.cn.push({
13300 cls : 'roo-combo-removable-btn close'
13304 if(this.hasFeedback && !this.allowBlank){
13306 inputblock.cls += ' has-feedback';
13308 inputblock.cn.push({
13310 cls: 'glyphicon form-control-feedback'
13317 inputblock.cls += (this.before) ? '' : ' input-group';
13319 inputblock.cn.push({
13321 cls : 'input-group-addon',
13332 cls: 'form-hidden-field'
13346 cls: 'form-hidden-field'
13350 cls: 'select2-choices',
13354 cls: 'select2-search-field',
13367 cls: 'select2-container input-group',
13374 combobox.cls += ' select2-container-multi';
13377 var align = this.labelAlign || this.parentLabelAlign();
13381 if(this.fieldLabel.length){
13383 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13384 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13389 cls : 'control-label ' + lw,
13390 html : this.fieldLabel
13402 var settings = this;
13404 ['xs','sm','md','lg'].map(function(size){
13405 if (settings[size]) {
13406 cfg.cls += ' col-' + size + '-' + settings[size];
13413 initTouchView : function()
13415 this.renderTouchView();
13417 this.touchViewEl.on('scroll', function(){
13418 this.el.dom.scrollTop = 0;
13421 this.inputEl().on("click", this.showTouchView, this);
13422 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13423 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13425 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13427 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13428 this.store.on('load', this.onTouchViewLoad, this);
13429 this.store.on('loadexception', this.onTouchViewLoadException, this);
13431 if(this.hiddenName){
13433 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13435 this.hiddenField.dom.value =
13436 this.hiddenValue !== undefined ? this.hiddenValue :
13437 this.value !== undefined ? this.value : '';
13439 this.el.dom.removeAttribute('name');
13440 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13444 this.choices = this.el.select('ul.select2-choices', true).first();
13445 this.searchField = this.el.select('ul li.select2-search-field', true).first();
13448 if(this.removable && !this.multiple){
13449 var close = this.closeTriggerEl();
13451 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13452 close.on('click', this.removeBtnClick, this, close);
13461 renderTouchView : function()
13463 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13464 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13466 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13467 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13469 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13470 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13471 this.touchViewBodyEl.setStyle('overflow', 'auto');
13473 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13474 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13476 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13477 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13481 showTouchView : function()
13483 this.touchViewHeaderEl.hide();
13485 if(this.fieldLabel.length){
13486 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13487 this.touchViewHeaderEl.show();
13490 this.touchViewEl.show();
13492 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13493 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13495 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13497 if(this.fieldLabel.length){
13498 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13501 this.touchViewBodyEl.setHeight(bodyHeight);
13505 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13507 this.touchViewEl.addClass('in');
13510 this.doTouchViewQuery();
13514 hideTouchView : function()
13516 this.touchViewEl.removeClass('in');
13520 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13522 this.touchViewEl.setStyle('display', 'none');
13527 setTouchViewValue : function()
13534 Roo.each(this.tickItems, function(o){
13539 this.hideTouchView();
13542 doTouchViewQuery : function()
13551 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13555 if(!this.alwaysQuery || this.mode == 'local'){
13556 this.onTouchViewLoad();
13563 onTouchViewBeforeLoad : function(combo,opts)
13569 onTouchViewLoad : function()
13571 if(this.store.getCount() < 1){
13572 this.onTouchViewEmptyResults();
13576 this.clearTouchView();
13578 var rawValue = this.getRawValue();
13580 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13582 this.tickItems = [];
13584 this.store.data.each(function(d, rowIndex){
13585 var row = this.touchViewListGroup.createChild(template);
13587 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13588 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13591 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13592 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13595 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13596 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13597 this.tickItems.push(d.data);
13600 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13604 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13606 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13608 if(this.fieldLabel.length){
13609 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13612 var listHeight = this.touchViewListGroup.getHeight();
13616 if(firstChecked && listHeight > bodyHeight){
13617 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13622 onTouchViewLoadException : function()
13624 this.hideTouchView();
13627 onTouchViewEmptyResults : function()
13629 this.clearTouchView();
13631 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13633 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13637 clearTouchView : function()
13639 this.touchViewListGroup.dom.innerHTML = '';
13642 onTouchViewClick : function(e, el, o)
13644 e.preventDefault();
13647 var rowIndex = o.rowIndex;
13649 var r = this.store.getAt(rowIndex);
13651 if(!this.multiple){
13652 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13653 c.dom.removeAttribute('checked');
13656 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13658 this.setFromData(r.data);
13660 var close = this.closeTriggerEl();
13666 this.hideTouchView();
13668 this.fireEvent('select', this, r, rowIndex);
13673 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13674 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13675 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13679 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13680 this.addItem(r.data);
13681 this.tickItems.push(r.data);
13687 * @cfg {Boolean} grow
13691 * @cfg {Number} growMin
13695 * @cfg {Number} growMax
13704 Roo.apply(Roo.bootstrap.ComboBox, {
13708 cls: 'modal-header',
13730 cls: 'list-group-item',
13734 cls: 'roo-combobox-list-group-item-value'
13738 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13752 listItemCheckbox : {
13754 cls: 'list-group-item',
13758 cls: 'roo-combobox-list-group-item-value'
13762 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13778 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13783 cls: 'modal-footer',
13791 cls: 'col-xs-6 text-left',
13794 cls: 'btn btn-danger roo-touch-view-cancel',
13800 cls: 'col-xs-6 text-right',
13803 cls: 'btn btn-success roo-touch-view-ok',
13814 Roo.apply(Roo.bootstrap.ComboBox, {
13816 touchViewTemplate : {
13818 cls: 'modal fade roo-combobox-touch-view',
13822 cls: 'modal-dialog',
13826 cls: 'modal-content',
13828 Roo.bootstrap.ComboBox.header,
13829 Roo.bootstrap.ComboBox.body,
13830 Roo.bootstrap.ComboBox.footer
13839 * Ext JS Library 1.1.1
13840 * Copyright(c) 2006-2007, Ext JS, LLC.
13842 * Originally Released Under LGPL - original licence link has changed is not relivant.
13845 * <script type="text/javascript">
13850 * @extends Roo.util.Observable
13851 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
13852 * This class also supports single and multi selection modes. <br>
13853 * Create a data model bound view:
13855 var store = new Roo.data.Store(...);
13857 var view = new Roo.View({
13859 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
13861 singleSelect: true,
13862 selectedClass: "ydataview-selected",
13866 // listen for node click?
13867 view.on("click", function(vw, index, node, e){
13868 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13872 dataModel.load("foobar.xml");
13874 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13876 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13877 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13879 * Note: old style constructor is still suported (container, template, config)
13882 * Create a new View
13883 * @param {Object} config The config object
13886 Roo.View = function(config, depreciated_tpl, depreciated_config){
13888 this.parent = false;
13890 if (typeof(depreciated_tpl) == 'undefined') {
13891 // new way.. - universal constructor.
13892 Roo.apply(this, config);
13893 this.el = Roo.get(this.el);
13896 this.el = Roo.get(config);
13897 this.tpl = depreciated_tpl;
13898 Roo.apply(this, depreciated_config);
13900 this.wrapEl = this.el.wrap().wrap();
13901 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13904 if(typeof(this.tpl) == "string"){
13905 this.tpl = new Roo.Template(this.tpl);
13907 // support xtype ctors..
13908 this.tpl = new Roo.factory(this.tpl, Roo);
13912 this.tpl.compile();
13917 * @event beforeclick
13918 * Fires before a click is processed. Returns false to cancel the default action.
13919 * @param {Roo.View} this
13920 * @param {Number} index The index of the target node
13921 * @param {HTMLElement} node The target node
13922 * @param {Roo.EventObject} e The raw event object
13924 "beforeclick" : true,
13927 * Fires when a template node is clicked.
13928 * @param {Roo.View} this
13929 * @param {Number} index The index of the target node
13930 * @param {HTMLElement} node The target node
13931 * @param {Roo.EventObject} e The raw event object
13936 * Fires when a template node is double clicked.
13937 * @param {Roo.View} this
13938 * @param {Number} index The index of the target node
13939 * @param {HTMLElement} node The target node
13940 * @param {Roo.EventObject} e The raw event object
13944 * @event contextmenu
13945 * Fires when a template node is right clicked.
13946 * @param {Roo.View} this
13947 * @param {Number} index The index of the target node
13948 * @param {HTMLElement} node The target node
13949 * @param {Roo.EventObject} e The raw event object
13951 "contextmenu" : true,
13953 * @event selectionchange
13954 * Fires when the selected nodes change.
13955 * @param {Roo.View} this
13956 * @param {Array} selections Array of the selected nodes
13958 "selectionchange" : true,
13961 * @event beforeselect
13962 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13963 * @param {Roo.View} this
13964 * @param {HTMLElement} node The node to be selected
13965 * @param {Array} selections Array of currently selected nodes
13967 "beforeselect" : true,
13969 * @event preparedata
13970 * Fires on every row to render, to allow you to change the data.
13971 * @param {Roo.View} this
13972 * @param {Object} data to be rendered (change this)
13974 "preparedata" : true
13982 "click": this.onClick,
13983 "dblclick": this.onDblClick,
13984 "contextmenu": this.onContextMenu,
13988 this.selections = [];
13990 this.cmp = new Roo.CompositeElementLite([]);
13992 this.store = Roo.factory(this.store, Roo.data);
13993 this.setStore(this.store, true);
13996 if ( this.footer && this.footer.xtype) {
13998 var fctr = this.wrapEl.appendChild(document.createElement("div"));
14000 this.footer.dataSource = this.store;
14001 this.footer.container = fctr;
14002 this.footer = Roo.factory(this.footer, Roo);
14003 fctr.insertFirst(this.el);
14005 // this is a bit insane - as the paging toolbar seems to detach the el..
14006 // dom.parentNode.parentNode.parentNode
14007 // they get detached?
14011 Roo.View.superclass.constructor.call(this);
14016 Roo.extend(Roo.View, Roo.util.Observable, {
14019 * @cfg {Roo.data.Store} store Data store to load data from.
14024 * @cfg {String|Roo.Element} el The container element.
14029 * @cfg {String|Roo.Template} tpl The template used by this View
14033 * @cfg {String} dataName the named area of the template to use as the data area
14034 * Works with domtemplates roo-name="name"
14038 * @cfg {String} selectedClass The css class to add to selected nodes
14040 selectedClass : "x-view-selected",
14042 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14047 * @cfg {String} text to display on mask (default Loading)
14051 * @cfg {Boolean} multiSelect Allow multiple selection
14053 multiSelect : false,
14055 * @cfg {Boolean} singleSelect Allow single selection
14057 singleSelect: false,
14060 * @cfg {Boolean} toggleSelect - selecting
14062 toggleSelect : false,
14065 * @cfg {Boolean} tickable - selecting
14070 * Returns the element this view is bound to.
14071 * @return {Roo.Element}
14073 getEl : function(){
14074 return this.wrapEl;
14080 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14082 refresh : function(){
14083 //Roo.log('refresh');
14086 // if we are using something like 'domtemplate', then
14087 // the what gets used is:
14088 // t.applySubtemplate(NAME, data, wrapping data..)
14089 // the outer template then get' applied with
14090 // the store 'extra data'
14091 // and the body get's added to the
14092 // roo-name="data" node?
14093 // <span class='roo-tpl-{name}'></span> ?????
14097 this.clearSelections();
14098 this.el.update("");
14100 var records = this.store.getRange();
14101 if(records.length < 1) {
14103 // is this valid?? = should it render a template??
14105 this.el.update(this.emptyText);
14109 if (this.dataName) {
14110 this.el.update(t.apply(this.store.meta)); //????
14111 el = this.el.child('.roo-tpl-' + this.dataName);
14114 for(var i = 0, len = records.length; i < len; i++){
14115 var data = this.prepareData(records[i].data, i, records[i]);
14116 this.fireEvent("preparedata", this, data, i, records[i]);
14118 var d = Roo.apply({}, data);
14121 Roo.apply(d, {'roo-id' : Roo.id()});
14125 Roo.each(this.parent.item, function(item){
14126 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14129 Roo.apply(d, {'roo-data-checked' : 'checked'});
14133 html[html.length] = Roo.util.Format.trim(
14135 t.applySubtemplate(this.dataName, d, this.store.meta) :
14142 el.update(html.join(""));
14143 this.nodes = el.dom.childNodes;
14144 this.updateIndexes(0);
14149 * Function to override to reformat the data that is sent to
14150 * the template for each node.
14151 * DEPRICATED - use the preparedata event handler.
14152 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14153 * a JSON object for an UpdateManager bound view).
14155 prepareData : function(data, index, record)
14157 this.fireEvent("preparedata", this, data, index, record);
14161 onUpdate : function(ds, record){
14162 // Roo.log('on update');
14163 this.clearSelections();
14164 var index = this.store.indexOf(record);
14165 var n = this.nodes[index];
14166 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14167 n.parentNode.removeChild(n);
14168 this.updateIndexes(index, index);
14174 onAdd : function(ds, records, index)
14176 //Roo.log(['on Add', ds, records, index] );
14177 this.clearSelections();
14178 if(this.nodes.length == 0){
14182 var n = this.nodes[index];
14183 for(var i = 0, len = records.length; i < len; i++){
14184 var d = this.prepareData(records[i].data, i, records[i]);
14186 this.tpl.insertBefore(n, d);
14189 this.tpl.append(this.el, d);
14192 this.updateIndexes(index);
14195 onRemove : function(ds, record, index){
14196 // Roo.log('onRemove');
14197 this.clearSelections();
14198 var el = this.dataName ?
14199 this.el.child('.roo-tpl-' + this.dataName) :
14202 el.dom.removeChild(this.nodes[index]);
14203 this.updateIndexes(index);
14207 * Refresh an individual node.
14208 * @param {Number} index
14210 refreshNode : function(index){
14211 this.onUpdate(this.store, this.store.getAt(index));
14214 updateIndexes : function(startIndex, endIndex){
14215 var ns = this.nodes;
14216 startIndex = startIndex || 0;
14217 endIndex = endIndex || ns.length - 1;
14218 for(var i = startIndex; i <= endIndex; i++){
14219 ns[i].nodeIndex = i;
14224 * Changes the data store this view uses and refresh the view.
14225 * @param {Store} store
14227 setStore : function(store, initial){
14228 if(!initial && this.store){
14229 this.store.un("datachanged", this.refresh);
14230 this.store.un("add", this.onAdd);
14231 this.store.un("remove", this.onRemove);
14232 this.store.un("update", this.onUpdate);
14233 this.store.un("clear", this.refresh);
14234 this.store.un("beforeload", this.onBeforeLoad);
14235 this.store.un("load", this.onLoad);
14236 this.store.un("loadexception", this.onLoad);
14240 store.on("datachanged", this.refresh, this);
14241 store.on("add", this.onAdd, this);
14242 store.on("remove", this.onRemove, this);
14243 store.on("update", this.onUpdate, this);
14244 store.on("clear", this.refresh, this);
14245 store.on("beforeload", this.onBeforeLoad, this);
14246 store.on("load", this.onLoad, this);
14247 store.on("loadexception", this.onLoad, this);
14255 * onbeforeLoad - masks the loading area.
14258 onBeforeLoad : function(store,opts)
14260 //Roo.log('onBeforeLoad');
14262 this.el.update("");
14264 this.el.mask(this.mask ? this.mask : "Loading" );
14266 onLoad : function ()
14273 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14274 * @param {HTMLElement} node
14275 * @return {HTMLElement} The template node
14277 findItemFromChild : function(node){
14278 var el = this.dataName ?
14279 this.el.child('.roo-tpl-' + this.dataName,true) :
14282 if(!node || node.parentNode == el){
14285 var p = node.parentNode;
14286 while(p && p != el){
14287 if(p.parentNode == el){
14296 onClick : function(e){
14297 var item = this.findItemFromChild(e.getTarget());
14299 var index = this.indexOf(item);
14300 if(this.onItemClick(item, index, e) !== false){
14301 this.fireEvent("click", this, index, item, e);
14304 this.clearSelections();
14309 onContextMenu : function(e){
14310 var item = this.findItemFromChild(e.getTarget());
14312 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14317 onDblClick : function(e){
14318 var item = this.findItemFromChild(e.getTarget());
14320 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14324 onItemClick : function(item, index, e)
14326 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14329 if (this.toggleSelect) {
14330 var m = this.isSelected(item) ? 'unselect' : 'select';
14333 _t[m](item, true, false);
14336 if(this.multiSelect || this.singleSelect){
14337 if(this.multiSelect && e.shiftKey && this.lastSelection){
14338 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14340 this.select(item, this.multiSelect && e.ctrlKey);
14341 this.lastSelection = item;
14344 if(!this.tickable){
14345 e.preventDefault();
14353 * Get the number of selected nodes.
14356 getSelectionCount : function(){
14357 return this.selections.length;
14361 * Get the currently selected nodes.
14362 * @return {Array} An array of HTMLElements
14364 getSelectedNodes : function(){
14365 return this.selections;
14369 * Get the indexes of the selected nodes.
14372 getSelectedIndexes : function(){
14373 var indexes = [], s = this.selections;
14374 for(var i = 0, len = s.length; i < len; i++){
14375 indexes.push(s[i].nodeIndex);
14381 * Clear all selections
14382 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14384 clearSelections : function(suppressEvent){
14385 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14386 this.cmp.elements = this.selections;
14387 this.cmp.removeClass(this.selectedClass);
14388 this.selections = [];
14389 if(!suppressEvent){
14390 this.fireEvent("selectionchange", this, this.selections);
14396 * Returns true if the passed node is selected
14397 * @param {HTMLElement/Number} node The node or node index
14398 * @return {Boolean}
14400 isSelected : function(node){
14401 var s = this.selections;
14405 node = this.getNode(node);
14406 return s.indexOf(node) !== -1;
14411 * @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
14412 * @param {Boolean} keepExisting (optional) true to keep existing selections
14413 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14415 select : function(nodeInfo, keepExisting, suppressEvent){
14416 if(nodeInfo instanceof Array){
14418 this.clearSelections(true);
14420 for(var i = 0, len = nodeInfo.length; i < len; i++){
14421 this.select(nodeInfo[i], true, true);
14425 var node = this.getNode(nodeInfo);
14426 if(!node || this.isSelected(node)){
14427 return; // already selected.
14430 this.clearSelections(true);
14433 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14434 Roo.fly(node).addClass(this.selectedClass);
14435 this.selections.push(node);
14436 if(!suppressEvent){
14437 this.fireEvent("selectionchange", this, this.selections);
14445 * @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
14446 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14447 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14449 unselect : function(nodeInfo, keepExisting, suppressEvent)
14451 if(nodeInfo instanceof Array){
14452 Roo.each(this.selections, function(s) {
14453 this.unselect(s, nodeInfo);
14457 var node = this.getNode(nodeInfo);
14458 if(!node || !this.isSelected(node)){
14459 //Roo.log("not selected");
14460 return; // not selected.
14464 Roo.each(this.selections, function(s) {
14466 Roo.fly(node).removeClass(this.selectedClass);
14473 this.selections= ns;
14474 this.fireEvent("selectionchange", this, this.selections);
14478 * Gets a template node.
14479 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14480 * @return {HTMLElement} The node or null if it wasn't found
14482 getNode : function(nodeInfo){
14483 if(typeof nodeInfo == "string"){
14484 return document.getElementById(nodeInfo);
14485 }else if(typeof nodeInfo == "number"){
14486 return this.nodes[nodeInfo];
14492 * Gets a range template nodes.
14493 * @param {Number} startIndex
14494 * @param {Number} endIndex
14495 * @return {Array} An array of nodes
14497 getNodes : function(start, end){
14498 var ns = this.nodes;
14499 start = start || 0;
14500 end = typeof end == "undefined" ? ns.length - 1 : end;
14503 for(var i = start; i <= end; i++){
14507 for(var i = start; i >= end; i--){
14515 * Finds the index of the passed node
14516 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14517 * @return {Number} The index of the node or -1
14519 indexOf : function(node){
14520 node = this.getNode(node);
14521 if(typeof node.nodeIndex == "number"){
14522 return node.nodeIndex;
14524 var ns = this.nodes;
14525 for(var i = 0, len = ns.length; i < len; i++){
14536 * based on jquery fullcalendar
14540 Roo.bootstrap = Roo.bootstrap || {};
14542 * @class Roo.bootstrap.Calendar
14543 * @extends Roo.bootstrap.Component
14544 * Bootstrap Calendar class
14545 * @cfg {Boolean} loadMask (true|false) default false
14546 * @cfg {Object} header generate the user specific header of the calendar, default false
14549 * Create a new Container
14550 * @param {Object} config The config object
14555 Roo.bootstrap.Calendar = function(config){
14556 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14560 * Fires when a date is selected
14561 * @param {DatePicker} this
14562 * @param {Date} date The selected date
14566 * @event monthchange
14567 * Fires when the displayed month changes
14568 * @param {DatePicker} this
14569 * @param {Date} date The selected month
14571 'monthchange': true,
14573 * @event evententer
14574 * Fires when mouse over an event
14575 * @param {Calendar} this
14576 * @param {event} Event
14578 'evententer': true,
14580 * @event eventleave
14581 * Fires when the mouse leaves an
14582 * @param {Calendar} this
14585 'eventleave': true,
14587 * @event eventclick
14588 * Fires when the mouse click an
14589 * @param {Calendar} this
14598 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
14601 * @cfg {Number} startDay
14602 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14610 getAutoCreate : function(){
14613 var fc_button = function(name, corner, style, content ) {
14614 return Roo.apply({},{
14616 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
14618 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14621 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14632 style : 'width:100%',
14639 cls : 'fc-header-left',
14641 fc_button('prev', 'left', 'arrow', '‹' ),
14642 fc_button('next', 'right', 'arrow', '›' ),
14643 { tag: 'span', cls: 'fc-header-space' },
14644 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
14652 cls : 'fc-header-center',
14656 cls: 'fc-header-title',
14659 html : 'month / year'
14667 cls : 'fc-header-right',
14669 /* fc_button('month', 'left', '', 'month' ),
14670 fc_button('week', '', '', 'week' ),
14671 fc_button('day', 'right', '', 'day' )
14683 header = this.header;
14686 var cal_heads = function() {
14688 // fixme - handle this.
14690 for (var i =0; i < Date.dayNames.length; i++) {
14691 var d = Date.dayNames[i];
14694 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14695 html : d.substring(0,3)
14699 ret[0].cls += ' fc-first';
14700 ret[6].cls += ' fc-last';
14703 var cal_cell = function(n) {
14706 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14711 cls: 'fc-day-number',
14715 cls: 'fc-day-content',
14719 style: 'position: relative;' // height: 17px;
14731 var cal_rows = function() {
14734 for (var r = 0; r < 6; r++) {
14741 for (var i =0; i < Date.dayNames.length; i++) {
14742 var d = Date.dayNames[i];
14743 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14746 row.cn[0].cls+=' fc-first';
14747 row.cn[0].cn[0].style = 'min-height:90px';
14748 row.cn[6].cls+=' fc-last';
14752 ret[0].cls += ' fc-first';
14753 ret[4].cls += ' fc-prev-last';
14754 ret[5].cls += ' fc-last';
14761 cls: 'fc-border-separate',
14762 style : 'width:100%',
14770 cls : 'fc-first fc-last',
14788 cls : 'fc-content',
14789 style : "position: relative;",
14792 cls : 'fc-view fc-view-month fc-grid',
14793 style : 'position: relative',
14794 unselectable : 'on',
14797 cls : 'fc-event-container',
14798 style : 'position:absolute;z-index:8;top:0;left:0;'
14816 initEvents : function()
14819 throw "can not find store for calendar";
14825 style: "text-align:center",
14829 style: "background-color:white;width:50%;margin:250 auto",
14833 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
14844 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14846 var size = this.el.select('.fc-content', true).first().getSize();
14847 this.maskEl.setSize(size.width, size.height);
14848 this.maskEl.enableDisplayMode("block");
14849 if(!this.loadMask){
14850 this.maskEl.hide();
14853 this.store = Roo.factory(this.store, Roo.data);
14854 this.store.on('load', this.onLoad, this);
14855 this.store.on('beforeload', this.onBeforeLoad, this);
14859 this.cells = this.el.select('.fc-day',true);
14860 //Roo.log(this.cells);
14861 this.textNodes = this.el.query('.fc-day-number');
14862 this.cells.addClassOnOver('fc-state-hover');
14864 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14865 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14866 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14867 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14869 this.on('monthchange', this.onMonthChange, this);
14871 this.update(new Date().clearTime());
14874 resize : function() {
14875 var sz = this.el.getSize();
14877 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14878 this.el.select('.fc-day-content div',true).setHeight(34);
14883 showPrevMonth : function(e){
14884 this.update(this.activeDate.add("mo", -1));
14886 showToday : function(e){
14887 this.update(new Date().clearTime());
14890 showNextMonth : function(e){
14891 this.update(this.activeDate.add("mo", 1));
14895 showPrevYear : function(){
14896 this.update(this.activeDate.add("y", -1));
14900 showNextYear : function(){
14901 this.update(this.activeDate.add("y", 1));
14906 update : function(date)
14908 var vd = this.activeDate;
14909 this.activeDate = date;
14910 // if(vd && this.el){
14911 // var t = date.getTime();
14912 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14913 // Roo.log('using add remove');
14915 // this.fireEvent('monthchange', this, date);
14917 // this.cells.removeClass("fc-state-highlight");
14918 // this.cells.each(function(c){
14919 // if(c.dateValue == t){
14920 // c.addClass("fc-state-highlight");
14921 // setTimeout(function(){
14922 // try{c.dom.firstChild.focus();}catch(e){}
14932 var days = date.getDaysInMonth();
14934 var firstOfMonth = date.getFirstDateOfMonth();
14935 var startingPos = firstOfMonth.getDay()-this.startDay;
14937 if(startingPos < this.startDay){
14941 var pm = date.add(Date.MONTH, -1);
14942 var prevStart = pm.getDaysInMonth()-startingPos;
14944 this.cells = this.el.select('.fc-day',true);
14945 this.textNodes = this.el.query('.fc-day-number');
14946 this.cells.addClassOnOver('fc-state-hover');
14948 var cells = this.cells.elements;
14949 var textEls = this.textNodes;
14951 Roo.each(cells, function(cell){
14952 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14955 days += startingPos;
14957 // convert everything to numbers so it's fast
14958 var day = 86400000;
14959 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14962 //Roo.log(prevStart);
14964 var today = new Date().clearTime().getTime();
14965 var sel = date.clearTime().getTime();
14966 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14967 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14968 var ddMatch = this.disabledDatesRE;
14969 var ddText = this.disabledDatesText;
14970 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14971 var ddaysText = this.disabledDaysText;
14972 var format = this.format;
14974 var setCellClass = function(cal, cell){
14978 //Roo.log('set Cell Class');
14980 var t = d.getTime();
14984 cell.dateValue = t;
14986 cell.className += " fc-today";
14987 cell.className += " fc-state-highlight";
14988 cell.title = cal.todayText;
14991 // disable highlight in other month..
14992 //cell.className += " fc-state-highlight";
14997 cell.className = " fc-state-disabled";
14998 cell.title = cal.minText;
15002 cell.className = " fc-state-disabled";
15003 cell.title = cal.maxText;
15007 if(ddays.indexOf(d.getDay()) != -1){
15008 cell.title = ddaysText;
15009 cell.className = " fc-state-disabled";
15012 if(ddMatch && format){
15013 var fvalue = d.dateFormat(format);
15014 if(ddMatch.test(fvalue)){
15015 cell.title = ddText.replace("%0", fvalue);
15016 cell.className = " fc-state-disabled";
15020 if (!cell.initialClassName) {
15021 cell.initialClassName = cell.dom.className;
15024 cell.dom.className = cell.initialClassName + ' ' + cell.className;
15029 for(; i < startingPos; i++) {
15030 textEls[i].innerHTML = (++prevStart);
15031 d.setDate(d.getDate()+1);
15033 cells[i].className = "fc-past fc-other-month";
15034 setCellClass(this, cells[i]);
15039 for(; i < days; i++){
15040 intDay = i - startingPos + 1;
15041 textEls[i].innerHTML = (intDay);
15042 d.setDate(d.getDate()+1);
15044 cells[i].className = ''; // "x-date-active";
15045 setCellClass(this, cells[i]);
15049 for(; i < 42; i++) {
15050 textEls[i].innerHTML = (++extraDays);
15051 d.setDate(d.getDate()+1);
15053 cells[i].className = "fc-future fc-other-month";
15054 setCellClass(this, cells[i]);
15057 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15059 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15061 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15062 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15064 if(totalRows != 6){
15065 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15066 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15069 this.fireEvent('monthchange', this, date);
15073 if(!this.internalRender){
15074 var main = this.el.dom.firstChild;
15075 var w = main.offsetWidth;
15076 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15077 Roo.fly(main).setWidth(w);
15078 this.internalRender = true;
15079 // opera does not respect the auto grow header center column
15080 // then, after it gets a width opera refuses to recalculate
15081 // without a second pass
15082 if(Roo.isOpera && !this.secondPass){
15083 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15084 this.secondPass = true;
15085 this.update.defer(10, this, [date]);
15092 findCell : function(dt) {
15093 dt = dt.clearTime().getTime();
15095 this.cells.each(function(c){
15096 //Roo.log("check " +c.dateValue + '?=' + dt);
15097 if(c.dateValue == dt){
15107 findCells : function(ev) {
15108 var s = ev.start.clone().clearTime().getTime();
15110 var e= ev.end.clone().clearTime().getTime();
15113 this.cells.each(function(c){
15114 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15116 if(c.dateValue > e){
15119 if(c.dateValue < s){
15128 // findBestRow: function(cells)
15132 // for (var i =0 ; i < cells.length;i++) {
15133 // ret = Math.max(cells[i].rows || 0,ret);
15140 addItem : function(ev)
15142 // look for vertical location slot in
15143 var cells = this.findCells(ev);
15145 // ev.row = this.findBestRow(cells);
15147 // work out the location.
15151 for(var i =0; i < cells.length; i++) {
15153 cells[i].row = cells[0].row;
15156 cells[i].row = cells[i].row + 1;
15166 if (crow.start.getY() == cells[i].getY()) {
15168 crow.end = cells[i];
15185 cells[0].events.push(ev);
15187 this.calevents.push(ev);
15190 clearEvents: function() {
15192 if(!this.calevents){
15196 Roo.each(this.cells.elements, function(c){
15202 Roo.each(this.calevents, function(e) {
15203 Roo.each(e.els, function(el) {
15204 el.un('mouseenter' ,this.onEventEnter, this);
15205 el.un('mouseleave' ,this.onEventLeave, this);
15210 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15216 renderEvents: function()
15220 this.cells.each(function(c) {
15229 if(c.row != c.events.length){
15230 r = 4 - (4 - (c.row - c.events.length));
15233 c.events = ev.slice(0, r);
15234 c.more = ev.slice(r);
15236 if(c.more.length && c.more.length == 1){
15237 c.events.push(c.more.pop());
15240 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15244 this.cells.each(function(c) {
15246 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15249 for (var e = 0; e < c.events.length; e++){
15250 var ev = c.events[e];
15251 var rows = ev.rows;
15253 for(var i = 0; i < rows.length; i++) {
15255 // how many rows should it span..
15258 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15259 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15261 unselectable : "on",
15264 cls: 'fc-event-inner',
15268 // cls: 'fc-event-time',
15269 // html : cells.length > 1 ? '' : ev.time
15273 cls: 'fc-event-title',
15274 html : String.format('{0}', ev.title)
15281 cls: 'ui-resizable-handle ui-resizable-e',
15282 html : '  '
15289 cfg.cls += ' fc-event-start';
15291 if ((i+1) == rows.length) {
15292 cfg.cls += ' fc-event-end';
15295 var ctr = _this.el.select('.fc-event-container',true).first();
15296 var cg = ctr.createChild(cfg);
15298 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15299 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15301 var r = (c.more.length) ? 1 : 0;
15302 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15303 cg.setWidth(ebox.right - sbox.x -2);
15305 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15306 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15307 cg.on('click', _this.onEventClick, _this, ev);
15318 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15319 style : 'position: absolute',
15320 unselectable : "on",
15323 cls: 'fc-event-inner',
15327 cls: 'fc-event-title',
15335 cls: 'ui-resizable-handle ui-resizable-e',
15336 html : '  '
15342 var ctr = _this.el.select('.fc-event-container',true).first();
15343 var cg = ctr.createChild(cfg);
15345 var sbox = c.select('.fc-day-content',true).first().getBox();
15346 var ebox = c.select('.fc-day-content',true).first().getBox();
15348 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15349 cg.setWidth(ebox.right - sbox.x -2);
15351 cg.on('click', _this.onMoreEventClick, _this, c.more);
15361 onEventEnter: function (e, el,event,d) {
15362 this.fireEvent('evententer', this, el, event);
15365 onEventLeave: function (e, el,event,d) {
15366 this.fireEvent('eventleave', this, el, event);
15369 onEventClick: function (e, el,event,d) {
15370 this.fireEvent('eventclick', this, el, event);
15373 onMonthChange: function () {
15377 onMoreEventClick: function(e, el, more)
15381 this.calpopover.placement = 'right';
15382 this.calpopover.setTitle('More');
15384 this.calpopover.setContent('');
15386 var ctr = this.calpopover.el.select('.popover-content', true).first();
15388 Roo.each(more, function(m){
15390 cls : 'fc-event-hori fc-event-draggable',
15393 var cg = ctr.createChild(cfg);
15395 cg.on('click', _this.onEventClick, _this, m);
15398 this.calpopover.show(el);
15403 onLoad: function ()
15405 this.calevents = [];
15408 if(this.store.getCount() > 0){
15409 this.store.data.each(function(d){
15412 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15413 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15414 time : d.data.start_time,
15415 title : d.data.title,
15416 description : d.data.description,
15417 venue : d.data.venue
15422 this.renderEvents();
15424 if(this.calevents.length && this.loadMask){
15425 this.maskEl.hide();
15429 onBeforeLoad: function()
15431 this.clearEvents();
15433 this.maskEl.show();
15447 * @class Roo.bootstrap.Popover
15448 * @extends Roo.bootstrap.Component
15449 * Bootstrap Popover class
15450 * @cfg {String} html contents of the popover (or false to use children..)
15451 * @cfg {String} title of popover (or false to hide)
15452 * @cfg {String} placement how it is placed
15453 * @cfg {String} trigger click || hover (or false to trigger manually)
15454 * @cfg {String} over what (parent or false to trigger manually.)
15455 * @cfg {Number} delay - delay before showing
15458 * Create a new Popover
15459 * @param {Object} config The config object
15462 Roo.bootstrap.Popover = function(config){
15463 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15466 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
15468 title: 'Fill in a title',
15471 placement : 'right',
15472 trigger : 'hover', // hover
15478 can_build_overlaid : false,
15480 getChildContainer : function()
15482 return this.el.select('.popover-content',true).first();
15485 getAutoCreate : function(){
15486 Roo.log('make popover?');
15488 cls : 'popover roo-dynamic',
15489 style: 'display:block',
15495 cls : 'popover-inner',
15499 cls: 'popover-title',
15503 cls : 'popover-content',
15514 setTitle: function(str)
15517 this.el.select('.popover-title',true).first().dom.innerHTML = str;
15519 setContent: function(str)
15522 this.el.select('.popover-content',true).first().dom.innerHTML = str;
15524 // as it get's added to the bottom of the page.
15525 onRender : function(ct, position)
15527 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15529 var cfg = Roo.apply({}, this.getAutoCreate());
15533 cfg.cls += ' ' + this.cls;
15536 cfg.style = this.style;
15538 Roo.log("adding to ")
15539 this.el = Roo.get(document.body).createChild(cfg, position);
15545 initEvents : function()
15547 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15548 this.el.enableDisplayMode('block');
15550 if (this.over === false) {
15553 if (this.triggers === false) {
15556 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15557 var triggers = this.trigger ? this.trigger.split(' ') : [];
15558 Roo.each(triggers, function(trigger) {
15560 if (trigger == 'click') {
15561 on_el.on('click', this.toggle, this);
15562 } else if (trigger != 'manual') {
15563 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
15564 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15566 on_el.on(eventIn ,this.enter, this);
15567 on_el.on(eventOut, this.leave, this);
15578 toggle : function () {
15579 this.hoverState == 'in' ? this.leave() : this.enter();
15582 enter : function () {
15585 clearTimeout(this.timeout);
15587 this.hoverState = 'in';
15589 if (!this.delay || !this.delay.show) {
15594 this.timeout = setTimeout(function () {
15595 if (_t.hoverState == 'in') {
15598 }, this.delay.show)
15600 leave : function() {
15601 clearTimeout(this.timeout);
15603 this.hoverState = 'out';
15605 if (!this.delay || !this.delay.hide) {
15610 this.timeout = setTimeout(function () {
15611 if (_t.hoverState == 'out') {
15614 }, this.delay.hide)
15617 show : function (on_el)
15620 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15623 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15624 if (this.html !== false) {
15625 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15627 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15628 if (!this.title.length) {
15629 this.el.select('.popover-title',true).hide();
15632 var placement = typeof this.placement == 'function' ?
15633 this.placement.call(this, this.el, on_el) :
15636 var autoToken = /\s?auto?\s?/i;
15637 var autoPlace = autoToken.test(placement);
15639 placement = placement.replace(autoToken, '') || 'top';
15643 //this.el.setXY([0,0]);
15645 this.el.dom.style.display='block';
15646 this.el.addClass(placement);
15648 //this.el.appendTo(on_el);
15650 var p = this.getPosition();
15651 var box = this.el.getBox();
15656 var align = Roo.bootstrap.Popover.alignment[placement];
15657 this.el.alignTo(on_el, align[0],align[1]);
15658 //var arrow = this.el.select('.arrow',true).first();
15659 //arrow.set(align[2],
15661 this.el.addClass('in');
15664 if (this.el.hasClass('fade')) {
15671 this.el.setXY([0,0]);
15672 this.el.removeClass('in');
15674 this.hoverState = null;
15680 Roo.bootstrap.Popover.alignment = {
15681 'left' : ['r-l', [-10,0], 'right'],
15682 'right' : ['l-r', [10,0], 'left'],
15683 'bottom' : ['t-b', [0,10], 'top'],
15684 'top' : [ 'b-t', [0,-10], 'bottom']
15695 * @class Roo.bootstrap.Progress
15696 * @extends Roo.bootstrap.Component
15697 * Bootstrap Progress class
15698 * @cfg {Boolean} striped striped of the progress bar
15699 * @cfg {Boolean} active animated of the progress bar
15703 * Create a new Progress
15704 * @param {Object} config The config object
15707 Roo.bootstrap.Progress = function(config){
15708 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15711 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
15716 getAutoCreate : function(){
15724 cfg.cls += ' progress-striped';
15728 cfg.cls += ' active';
15747 * @class Roo.bootstrap.ProgressBar
15748 * @extends Roo.bootstrap.Component
15749 * Bootstrap ProgressBar class
15750 * @cfg {Number} aria_valuenow aria-value now
15751 * @cfg {Number} aria_valuemin aria-value min
15752 * @cfg {Number} aria_valuemax aria-value max
15753 * @cfg {String} label label for the progress bar
15754 * @cfg {String} panel (success | info | warning | danger )
15755 * @cfg {String} role role of the progress bar
15756 * @cfg {String} sr_only text
15760 * Create a new ProgressBar
15761 * @param {Object} config The config object
15764 Roo.bootstrap.ProgressBar = function(config){
15765 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15768 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
15772 aria_valuemax : 100,
15778 getAutoCreate : function()
15783 cls: 'progress-bar',
15784 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15796 cfg.role = this.role;
15799 if(this.aria_valuenow){
15800 cfg['aria-valuenow'] = this.aria_valuenow;
15803 if(this.aria_valuemin){
15804 cfg['aria-valuemin'] = this.aria_valuemin;
15807 if(this.aria_valuemax){
15808 cfg['aria-valuemax'] = this.aria_valuemax;
15811 if(this.label && !this.sr_only){
15812 cfg.html = this.label;
15816 cfg.cls += ' progress-bar-' + this.panel;
15822 update : function(aria_valuenow)
15824 this.aria_valuenow = aria_valuenow;
15826 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15841 * @class Roo.bootstrap.TabGroup
15842 * @extends Roo.bootstrap.Column
15843 * Bootstrap Column class
15844 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15845 * @cfg {Boolean} carousel true to make the group behave like a carousel
15846 * @cfg {Number} bullets show the panel pointer.. default 0
15847 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15848 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15849 * @cfg {Number} timer auto slide timer .. default 0 millisecond
15852 * Create a new TabGroup
15853 * @param {Object} config The config object
15856 Roo.bootstrap.TabGroup = function(config){
15857 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15859 this.navId = Roo.id();
15862 Roo.bootstrap.TabGroup.register(this);
15866 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
15869 transition : false,
15874 slideOnTouch : false,
15876 getAutoCreate : function()
15878 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15880 cfg.cls += ' tab-content';
15882 Roo.log('get auto create...............');
15884 if (this.carousel) {
15885 cfg.cls += ' carousel slide';
15888 cls : 'carousel-inner'
15891 if(this.bullets > 0 && !Roo.isTouch){
15894 cls : 'carousel-bullets',
15898 if(this.bullets_cls){
15899 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15902 for (var i = 0; i < this.bullets; i++){
15904 cls : 'bullet bullet-' + i
15912 cfg.cn[0].cn = bullets;
15919 initEvents: function()
15921 Roo.log('-------- init events on tab group ---------');
15923 if(this.bullets > 0 && !Roo.isTouch){
15929 if(Roo.isTouch && this.slideOnTouch){
15930 this.el.on("touchstart", this.onTouchStart, this);
15933 if(this.autoslide){
15936 this.slideFn = window.setInterval(function() {
15937 _this.showPanelNext();
15943 onTouchStart : function(e, el, o)
15945 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15949 this.showPanelNext();
15952 getChildContainer : function()
15954 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15958 * register a Navigation item
15959 * @param {Roo.bootstrap.NavItem} the navitem to add
15961 register : function(item)
15963 this.tabs.push( item);
15964 item.navId = this.navId; // not really needed..
15968 getActivePanel : function()
15971 Roo.each(this.tabs, function(t) {
15981 getPanelByName : function(n)
15984 Roo.each(this.tabs, function(t) {
15985 if (t.tabId == n) {
15993 indexOfPanel : function(p)
15996 Roo.each(this.tabs, function(t,i) {
15997 if (t.tabId == p.tabId) {
16006 * show a specific panel
16007 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16008 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16010 showPanel : function (pan)
16012 if(this.transition){
16013 Roo.log("waiting for the transitionend");
16017 if (typeof(pan) == 'number') {
16018 pan = this.tabs[pan];
16020 if (typeof(pan) == 'string') {
16021 pan = this.getPanelByName(pan);
16023 if (pan.tabId == this.getActivePanel().tabId) {
16026 var cur = this.getActivePanel();
16028 if (false === cur.fireEvent('beforedeactivate')) {
16032 if(this.bullets > 0 && !Roo.isTouch){
16033 this.setActiveBullet(this.indexOfPanel(pan));
16036 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16038 this.transition = true;
16039 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16040 var lr = dir == 'next' ? 'left' : 'right';
16041 pan.el.addClass(dir); // or prev
16042 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16043 cur.el.addClass(lr); // or right
16044 pan.el.addClass(lr);
16047 cur.el.on('transitionend', function() {
16048 Roo.log("trans end?");
16050 pan.el.removeClass([lr,dir]);
16051 pan.setActive(true);
16053 cur.el.removeClass([lr]);
16054 cur.setActive(false);
16056 _this.transition = false;
16058 }, this, { single: true } );
16063 cur.setActive(false);
16064 pan.setActive(true);
16069 showPanelNext : function()
16071 var i = this.indexOfPanel(this.getActivePanel());
16073 if (i >= this.tabs.length - 1 && !this.autoslide) {
16077 if (i >= this.tabs.length - 1 && this.autoslide) {
16081 this.showPanel(this.tabs[i+1]);
16084 showPanelPrev : function()
16086 var i = this.indexOfPanel(this.getActivePanel());
16088 if (i < 1 && !this.autoslide) {
16092 if (i < 1 && this.autoslide) {
16093 i = this.tabs.length;
16096 this.showPanel(this.tabs[i-1]);
16099 initBullet : function()
16107 for (var i = 0; i < this.bullets; i++){
16108 var bullet = this.el.select('.bullet-' + i, true).first();
16114 bullet.on('click', (function(e, el, o, ii, t){
16116 e.preventDefault();
16118 _this.showPanel(ii);
16120 if(_this.autoslide && _this.slideFn){
16121 clearInterval(_this.slideFn);
16122 _this.slideFn = window.setInterval(function() {
16123 _this.showPanelNext();
16127 }).createDelegate(this, [i, bullet], true));
16131 setActiveBullet : function(i)
16137 Roo.each(this.el.select('.bullet', true).elements, function(el){
16138 el.removeClass('selected');
16141 var bullet = this.el.select('.bullet-' + i, true).first();
16147 bullet.addClass('selected');
16158 Roo.apply(Roo.bootstrap.TabGroup, {
16162 * register a Navigation Group
16163 * @param {Roo.bootstrap.NavGroup} the navgroup to add
16165 register : function(navgrp)
16167 this.groups[navgrp.navId] = navgrp;
16171 * fetch a Navigation Group based on the navigation ID
16172 * if one does not exist , it will get created.
16173 * @param {string} the navgroup to add
16174 * @returns {Roo.bootstrap.NavGroup} the navgroup
16176 get: function(navId) {
16177 if (typeof(this.groups[navId]) == 'undefined') {
16178 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16180 return this.groups[navId] ;
16195 * @class Roo.bootstrap.TabPanel
16196 * @extends Roo.bootstrap.Component
16197 * Bootstrap TabPanel class
16198 * @cfg {Boolean} active panel active
16199 * @cfg {String} html panel content
16200 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16201 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16205 * Create a new TabPanel
16206 * @param {Object} config The config object
16209 Roo.bootstrap.TabPanel = function(config){
16210 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16214 * Fires when the active status changes
16215 * @param {Roo.bootstrap.TabPanel} this
16216 * @param {Boolean} state the new state
16221 * @event beforedeactivate
16222 * Fires before a tab is de-activated - can be used to do validation on a form.
16223 * @param {Roo.bootstrap.TabPanel} this
16224 * @return {Boolean} false if there is an error
16227 'beforedeactivate': true
16230 this.tabId = this.tabId || Roo.id();
16234 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16241 getAutoCreate : function(){
16244 // item is needed for carousel - not sure if it has any effect otherwise
16245 cls: 'tab-pane item',
16246 html: this.html || ''
16250 cfg.cls += ' active';
16254 cfg.tabId = this.tabId;
16261 initEvents: function()
16263 Roo.log('-------- init events on tab panel ---------');
16265 var p = this.parent();
16266 this.navId = this.navId || p.navId;
16268 if (typeof(this.navId) != 'undefined') {
16269 // not really needed.. but just in case.. parent should be a NavGroup.
16270 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16271 Roo.log(['register', tg, this]);
16274 var i = tg.tabs.length - 1;
16276 if(this.active && tg.bullets > 0 && i < tg.bullets){
16277 tg.setActiveBullet(i);
16284 onRender : function(ct, position)
16286 // Roo.log("Call onRender: " + this.xtype);
16288 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16296 setActive: function(state)
16298 Roo.log("panel - set active " + this.tabId + "=" + state);
16300 this.active = state;
16302 this.el.removeClass('active');
16304 } else if (!this.el.hasClass('active')) {
16305 this.el.addClass('active');
16308 this.fireEvent('changed', this, state);
16325 * @class Roo.bootstrap.DateField
16326 * @extends Roo.bootstrap.Input
16327 * Bootstrap DateField class
16328 * @cfg {Number} weekStart default 0
16329 * @cfg {String} viewMode default empty, (months|years)
16330 * @cfg {String} minViewMode default empty, (months|years)
16331 * @cfg {Number} startDate default -Infinity
16332 * @cfg {Number} endDate default Infinity
16333 * @cfg {Boolean} todayHighlight default false
16334 * @cfg {Boolean} todayBtn default false
16335 * @cfg {Boolean} calendarWeeks default false
16336 * @cfg {Object} daysOfWeekDisabled default empty
16337 * @cfg {Boolean} singleMode default false (true | false)
16339 * @cfg {Boolean} keyboardNavigation default true
16340 * @cfg {String} language default en
16343 * Create a new DateField
16344 * @param {Object} config The config object
16347 Roo.bootstrap.DateField = function(config){
16348 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16352 * Fires when this field show.
16353 * @param {Roo.bootstrap.DateField} this
16354 * @param {Mixed} date The date value
16359 * Fires when this field hide.
16360 * @param {Roo.bootstrap.DateField} this
16361 * @param {Mixed} date The date value
16366 * Fires when select a date.
16367 * @param {Roo.bootstrap.DateField} this
16368 * @param {Mixed} date The date value
16372 * @event beforeselect
16373 * Fires when before select a date.
16374 * @param {Roo.bootstrap.DateField} this
16375 * @param {Mixed} date The date value
16377 beforeselect : true
16381 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16384 * @cfg {String} format
16385 * The default date format string which can be overriden for localization support. The format must be
16386 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16390 * @cfg {String} altFormats
16391 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16392 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16394 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16402 todayHighlight : false,
16408 keyboardNavigation: true,
16410 calendarWeeks: false,
16412 startDate: -Infinity,
16416 daysOfWeekDisabled: [],
16420 singleMode : false,
16422 UTCDate: function()
16424 return new Date(Date.UTC.apply(Date, arguments));
16427 UTCToday: function()
16429 var today = new Date();
16430 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16433 getDate: function() {
16434 var d = this.getUTCDate();
16435 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16438 getUTCDate: function() {
16442 setDate: function(d) {
16443 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16446 setUTCDate: function(d) {
16448 this.setValue(this.formatDate(this.date));
16451 onRender: function(ct, position)
16454 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16456 this.language = this.language || 'en';
16457 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16458 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16460 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16461 this.format = this.format || 'm/d/y';
16462 this.isInline = false;
16463 this.isInput = true;
16464 this.component = this.el.select('.add-on', true).first() || false;
16465 this.component = (this.component && this.component.length === 0) ? false : this.component;
16466 this.hasInput = this.component && this.inputEL().length;
16468 if (typeof(this.minViewMode === 'string')) {
16469 switch (this.minViewMode) {
16471 this.minViewMode = 1;
16474 this.minViewMode = 2;
16477 this.minViewMode = 0;
16482 if (typeof(this.viewMode === 'string')) {
16483 switch (this.viewMode) {
16496 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16498 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16500 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16502 this.picker().on('mousedown', this.onMousedown, this);
16503 this.picker().on('click', this.onClick, this);
16505 this.picker().addClass('datepicker-dropdown');
16507 this.startViewMode = this.viewMode;
16509 if(this.singleMode){
16510 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16511 v.setVisibilityMode(Roo.Element.DISPLAY)
16515 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16516 v.setStyle('width', '189px');
16520 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16521 if(!this.calendarWeeks){
16526 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16527 v.attr('colspan', function(i, val){
16528 return parseInt(val) + 1;
16533 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16535 this.setStartDate(this.startDate);
16536 this.setEndDate(this.endDate);
16538 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16545 if(this.isInline) {
16550 picker : function()
16552 return this.pickerEl;
16553 // return this.el.select('.datepicker', true).first();
16556 fillDow: function()
16558 var dowCnt = this.weekStart;
16567 if(this.calendarWeeks){
16575 while (dowCnt < this.weekStart + 7) {
16579 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16583 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16586 fillMonths: function()
16589 var months = this.picker().select('>.datepicker-months td', true).first();
16591 months.dom.innerHTML = '';
16597 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16600 months.createChild(month);
16607 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;
16609 if (this.date < this.startDate) {
16610 this.viewDate = new Date(this.startDate);
16611 } else if (this.date > this.endDate) {
16612 this.viewDate = new Date(this.endDate);
16614 this.viewDate = new Date(this.date);
16622 var d = new Date(this.viewDate),
16623 year = d.getUTCFullYear(),
16624 month = d.getUTCMonth(),
16625 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16626 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16627 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16628 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16629 currentDate = this.date && this.date.valueOf(),
16630 today = this.UTCToday();
16632 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16634 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16636 // this.picker.select('>tfoot th.today').
16637 // .text(dates[this.language].today)
16638 // .toggle(this.todayBtn !== false);
16640 this.updateNavArrows();
16643 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16645 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16647 prevMonth.setUTCDate(day);
16649 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16651 var nextMonth = new Date(prevMonth);
16653 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16655 nextMonth = nextMonth.valueOf();
16657 var fillMonths = false;
16659 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16661 while(prevMonth.valueOf() < nextMonth) {
16664 if (prevMonth.getUTCDay() === this.weekStart) {
16666 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16674 if(this.calendarWeeks){
16675 // ISO 8601: First week contains first thursday.
16676 // ISO also states week starts on Monday, but we can be more abstract here.
16678 // Start of current week: based on weekstart/current date
16679 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16680 // Thursday of this week
16681 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16682 // First Thursday of year, year from thursday
16683 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16684 // Calendar week: ms between thursdays, div ms per day, div 7 days
16685 calWeek = (th - yth) / 864e5 / 7 + 1;
16687 fillMonths.cn.push({
16695 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16697 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16700 if (this.todayHighlight &&
16701 prevMonth.getUTCFullYear() == today.getFullYear() &&
16702 prevMonth.getUTCMonth() == today.getMonth() &&
16703 prevMonth.getUTCDate() == today.getDate()) {
16704 clsName += ' today';
16707 if (currentDate && prevMonth.valueOf() === currentDate) {
16708 clsName += ' active';
16711 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16712 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16713 clsName += ' disabled';
16716 fillMonths.cn.push({
16718 cls: 'day ' + clsName,
16719 html: prevMonth.getDate()
16722 prevMonth.setDate(prevMonth.getDate()+1);
16725 var currentYear = this.date && this.date.getUTCFullYear();
16726 var currentMonth = this.date && this.date.getUTCMonth();
16728 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16730 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16731 v.removeClass('active');
16733 if(currentYear === year && k === currentMonth){
16734 v.addClass('active');
16737 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16738 v.addClass('disabled');
16744 year = parseInt(year/10, 10) * 10;
16746 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16748 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16751 for (var i = -1; i < 11; i++) {
16752 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16754 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16762 showMode: function(dir)
16765 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16768 Roo.each(this.picker().select('>div',true).elements, function(v){
16769 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16772 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16777 if(this.isInline) return;
16779 this.picker().removeClass(['bottom', 'top']);
16781 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16783 * place to the top of element!
16787 this.picker().addClass('top');
16788 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16793 this.picker().addClass('bottom');
16795 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16798 parseDate : function(value)
16800 if(!value || value instanceof Date){
16803 var v = Date.parseDate(value, this.format);
16804 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16805 v = Date.parseDate(value, 'Y-m-d');
16807 if(!v && this.altFormats){
16808 if(!this.altFormatsArray){
16809 this.altFormatsArray = this.altFormats.split("|");
16811 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16812 v = Date.parseDate(value, this.altFormatsArray[i]);
16818 formatDate : function(date, fmt)
16820 return (!date || !(date instanceof Date)) ?
16821 date : date.dateFormat(fmt || this.format);
16824 onFocus : function()
16826 Roo.bootstrap.DateField.superclass.onFocus.call(this);
16830 onBlur : function()
16832 Roo.bootstrap.DateField.superclass.onBlur.call(this);
16834 var d = this.inputEl().getValue();
16843 this.picker().show();
16847 this.fireEvent('show', this, this.date);
16852 if(this.isInline) return;
16853 this.picker().hide();
16854 this.viewMode = this.startViewMode;
16857 this.fireEvent('hide', this, this.date);
16861 onMousedown: function(e)
16863 e.stopPropagation();
16864 e.preventDefault();
16869 Roo.bootstrap.DateField.superclass.keyup.call(this);
16873 setValue: function(v)
16875 if(this.fireEvent('beforeselect', this, v) !== false){
16876 var d = new Date(this.parseDate(v) ).clearTime();
16878 if(isNaN(d.getTime())){
16879 this.date = this.viewDate = '';
16880 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16884 v = this.formatDate(d);
16886 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16888 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16892 this.fireEvent('select', this, this.date);
16896 getValue: function()
16898 return this.formatDate(this.date);
16901 fireKey: function(e)
16903 if (!this.picker().isVisible()){
16904 if (e.keyCode == 27) // allow escape to hide and re-show picker
16909 var dateChanged = false,
16911 newDate, newViewDate;
16916 e.preventDefault();
16920 if (!this.keyboardNavigation) break;
16921 dir = e.keyCode == 37 ? -1 : 1;
16924 newDate = this.moveYear(this.date, dir);
16925 newViewDate = this.moveYear(this.viewDate, dir);
16926 } else if (e.shiftKey){
16927 newDate = this.moveMonth(this.date, dir);
16928 newViewDate = this.moveMonth(this.viewDate, dir);
16930 newDate = new Date(this.date);
16931 newDate.setUTCDate(this.date.getUTCDate() + dir);
16932 newViewDate = new Date(this.viewDate);
16933 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16935 if (this.dateWithinRange(newDate)){
16936 this.date = newDate;
16937 this.viewDate = newViewDate;
16938 this.setValue(this.formatDate(this.date));
16940 e.preventDefault();
16941 dateChanged = true;
16946 if (!this.keyboardNavigation) break;
16947 dir = e.keyCode == 38 ? -1 : 1;
16949 newDate = this.moveYear(this.date, dir);
16950 newViewDate = this.moveYear(this.viewDate, dir);
16951 } else if (e.shiftKey){
16952 newDate = this.moveMonth(this.date, dir);
16953 newViewDate = this.moveMonth(this.viewDate, dir);
16955 newDate = new Date(this.date);
16956 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16957 newViewDate = new Date(this.viewDate);
16958 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16960 if (this.dateWithinRange(newDate)){
16961 this.date = newDate;
16962 this.viewDate = newViewDate;
16963 this.setValue(this.formatDate(this.date));
16965 e.preventDefault();
16966 dateChanged = true;
16970 this.setValue(this.formatDate(this.date));
16972 e.preventDefault();
16975 this.setValue(this.formatDate(this.date));
16989 onClick: function(e)
16991 e.stopPropagation();
16992 e.preventDefault();
16994 var target = e.getTarget();
16996 if(target.nodeName.toLowerCase() === 'i'){
16997 target = Roo.get(target).dom.parentNode;
17000 var nodeName = target.nodeName;
17001 var className = target.className;
17002 var html = target.innerHTML;
17003 //Roo.log(nodeName);
17005 switch(nodeName.toLowerCase()) {
17007 switch(className) {
17013 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17014 switch(this.viewMode){
17016 this.viewDate = this.moveMonth(this.viewDate, dir);
17020 this.viewDate = this.moveYear(this.viewDate, dir);
17026 var date = new Date();
17027 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17029 this.setValue(this.formatDate(this.date));
17036 if (className.indexOf('disabled') < 0) {
17037 this.viewDate.setUTCDate(1);
17038 if (className.indexOf('month') > -1) {
17039 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17041 var year = parseInt(html, 10) || 0;
17042 this.viewDate.setUTCFullYear(year);
17046 if(this.singleMode){
17047 this.setValue(this.formatDate(this.viewDate));
17058 //Roo.log(className);
17059 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17060 var day = parseInt(html, 10) || 1;
17061 var year = this.viewDate.getUTCFullYear(),
17062 month = this.viewDate.getUTCMonth();
17064 if (className.indexOf('old') > -1) {
17071 } else if (className.indexOf('new') > -1) {
17079 //Roo.log([year,month,day]);
17080 this.date = this.UTCDate(year, month, day,0,0,0,0);
17081 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17083 //Roo.log(this.formatDate(this.date));
17084 this.setValue(this.formatDate(this.date));
17091 setStartDate: function(startDate)
17093 this.startDate = startDate || -Infinity;
17094 if (this.startDate !== -Infinity) {
17095 this.startDate = this.parseDate(this.startDate);
17098 this.updateNavArrows();
17101 setEndDate: function(endDate)
17103 this.endDate = endDate || Infinity;
17104 if (this.endDate !== Infinity) {
17105 this.endDate = this.parseDate(this.endDate);
17108 this.updateNavArrows();
17111 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17113 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17114 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17115 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17117 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17118 return parseInt(d, 10);
17121 this.updateNavArrows();
17124 updateNavArrows: function()
17126 if(this.singleMode){
17130 var d = new Date(this.viewDate),
17131 year = d.getUTCFullYear(),
17132 month = d.getUTCMonth();
17134 Roo.each(this.picker().select('.prev', true).elements, function(v){
17136 switch (this.viewMode) {
17139 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17145 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17152 Roo.each(this.picker().select('.next', true).elements, function(v){
17154 switch (this.viewMode) {
17157 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17163 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17171 moveMonth: function(date, dir)
17173 if (!dir) return date;
17174 var new_date = new Date(date.valueOf()),
17175 day = new_date.getUTCDate(),
17176 month = new_date.getUTCMonth(),
17177 mag = Math.abs(dir),
17179 dir = dir > 0 ? 1 : -1;
17182 // If going back one month, make sure month is not current month
17183 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17185 return new_date.getUTCMonth() == month;
17187 // If going forward one month, make sure month is as expected
17188 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17190 return new_date.getUTCMonth() != new_month;
17192 new_month = month + dir;
17193 new_date.setUTCMonth(new_month);
17194 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17195 if (new_month < 0 || new_month > 11)
17196 new_month = (new_month + 12) % 12;
17198 // For magnitudes >1, move one month at a time...
17199 for (var i=0; i<mag; i++)
17200 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17201 new_date = this.moveMonth(new_date, dir);
17202 // ...then reset the day, keeping it in the new month
17203 new_month = new_date.getUTCMonth();
17204 new_date.setUTCDate(day);
17206 return new_month != new_date.getUTCMonth();
17209 // Common date-resetting loop -- if date is beyond end of month, make it
17212 new_date.setUTCDate(--day);
17213 new_date.setUTCMonth(new_month);
17218 moveYear: function(date, dir)
17220 return this.moveMonth(date, dir*12);
17223 dateWithinRange: function(date)
17225 return date >= this.startDate && date <= this.endDate;
17231 this.picker().remove();
17236 Roo.apply(Roo.bootstrap.DateField, {
17247 html: '<i class="fa fa-arrow-left"/>'
17257 html: '<i class="fa fa-arrow-right"/>'
17299 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17300 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17301 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17302 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17303 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17316 navFnc: 'FullYear',
17321 navFnc: 'FullYear',
17326 Roo.apply(Roo.bootstrap.DateField, {
17330 cls: 'datepicker dropdown-menu roo-dynamic',
17334 cls: 'datepicker-days',
17338 cls: 'table-condensed',
17340 Roo.bootstrap.DateField.head,
17344 Roo.bootstrap.DateField.footer
17351 cls: 'datepicker-months',
17355 cls: 'table-condensed',
17357 Roo.bootstrap.DateField.head,
17358 Roo.bootstrap.DateField.content,
17359 Roo.bootstrap.DateField.footer
17366 cls: 'datepicker-years',
17370 cls: 'table-condensed',
17372 Roo.bootstrap.DateField.head,
17373 Roo.bootstrap.DateField.content,
17374 Roo.bootstrap.DateField.footer
17393 * @class Roo.bootstrap.TimeField
17394 * @extends Roo.bootstrap.Input
17395 * Bootstrap DateField class
17399 * Create a new TimeField
17400 * @param {Object} config The config object
17403 Roo.bootstrap.TimeField = function(config){
17404 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17408 * Fires when this field show.
17409 * @param {Roo.bootstrap.DateField} thisthis
17410 * @param {Mixed} date The date value
17415 * Fires when this field hide.
17416 * @param {Roo.bootstrap.DateField} this
17417 * @param {Mixed} date The date value
17422 * Fires when select a date.
17423 * @param {Roo.bootstrap.DateField} this
17424 * @param {Mixed} date The date value
17430 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17433 * @cfg {String} format
17434 * The default time format string which can be overriden for localization support. The format must be
17435 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17439 onRender: function(ct, position)
17442 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17444 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17446 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17448 this.pop = this.picker().select('>.datepicker-time',true).first();
17449 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17451 this.picker().on('mousedown', this.onMousedown, this);
17452 this.picker().on('click', this.onClick, this);
17454 this.picker().addClass('datepicker-dropdown');
17459 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17460 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17461 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17462 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17463 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17464 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17468 fireKey: function(e){
17469 if (!this.picker().isVisible()){
17470 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17476 e.preventDefault();
17484 this.onTogglePeriod();
17487 this.onIncrementMinutes();
17490 this.onDecrementMinutes();
17499 onClick: function(e) {
17500 e.stopPropagation();
17501 e.preventDefault();
17504 picker : function()
17506 return this.el.select('.datepicker', true).first();
17509 fillTime: function()
17511 var time = this.pop.select('tbody', true).first();
17513 time.dom.innerHTML = '';
17528 cls: 'hours-up glyphicon glyphicon-chevron-up'
17548 cls: 'minutes-up glyphicon glyphicon-chevron-up'
17569 cls: 'timepicker-hour',
17584 cls: 'timepicker-minute',
17599 cls: 'btn btn-primary period',
17621 cls: 'hours-down glyphicon glyphicon-chevron-down'
17641 cls: 'minutes-down glyphicon glyphicon-chevron-down'
17659 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17666 var hours = this.time.getHours();
17667 var minutes = this.time.getMinutes();
17680 hours = hours - 12;
17684 hours = '0' + hours;
17688 minutes = '0' + minutes;
17691 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17692 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17693 this.pop.select('button', true).first().dom.innerHTML = period;
17699 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17701 var cls = ['bottom'];
17703 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17710 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17715 this.picker().addClass(cls.join('-'));
17719 Roo.each(cls, function(c){
17721 _this.picker().setTop(_this.inputEl().getHeight());
17725 _this.picker().setTop(0 - _this.picker().getHeight());
17730 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17734 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17741 onFocus : function()
17743 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17747 onBlur : function()
17749 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17755 this.picker().show();
17760 this.fireEvent('show', this, this.date);
17765 this.picker().hide();
17768 this.fireEvent('hide', this, this.date);
17771 setTime : function()
17774 this.setValue(this.time.format(this.format));
17776 this.fireEvent('select', this, this.date);
17781 onMousedown: function(e){
17782 e.stopPropagation();
17783 e.preventDefault();
17786 onIncrementHours: function()
17788 Roo.log('onIncrementHours');
17789 this.time = this.time.add(Date.HOUR, 1);
17794 onDecrementHours: function()
17796 Roo.log('onDecrementHours');
17797 this.time = this.time.add(Date.HOUR, -1);
17801 onIncrementMinutes: function()
17803 Roo.log('onIncrementMinutes');
17804 this.time = this.time.add(Date.MINUTE, 1);
17808 onDecrementMinutes: function()
17810 Roo.log('onDecrementMinutes');
17811 this.time = this.time.add(Date.MINUTE, -1);
17815 onTogglePeriod: function()
17817 Roo.log('onTogglePeriod');
17818 this.time = this.time.add(Date.HOUR, 12);
17825 Roo.apply(Roo.bootstrap.TimeField, {
17855 cls: 'btn btn-info ok',
17867 Roo.apply(Roo.bootstrap.TimeField, {
17871 cls: 'datepicker dropdown-menu',
17875 cls: 'datepicker-time',
17879 cls: 'table-condensed',
17881 Roo.bootstrap.TimeField.content,
17882 Roo.bootstrap.TimeField.footer
17901 * @class Roo.bootstrap.MonthField
17902 * @extends Roo.bootstrap.Input
17903 * Bootstrap MonthField class
17905 * @cfg {String} language default en
17908 * Create a new MonthField
17909 * @param {Object} config The config object
17912 Roo.bootstrap.MonthField = function(config){
17913 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17918 * Fires when this field show.
17919 * @param {Roo.bootstrap.MonthField} this
17920 * @param {Mixed} date The date value
17925 * Fires when this field hide.
17926 * @param {Roo.bootstrap.MonthField} this
17927 * @param {Mixed} date The date value
17932 * Fires when select a date.
17933 * @param {Roo.bootstrap.MonthField} this
17934 * @param {String} oldvalue The old value
17935 * @param {String} newvalue The new value
17941 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
17943 onRender: function(ct, position)
17946 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17948 this.language = this.language || 'en';
17949 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17950 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17952 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17953 this.isInline = false;
17954 this.isInput = true;
17955 this.component = this.el.select('.add-on', true).first() || false;
17956 this.component = (this.component && this.component.length === 0) ? false : this.component;
17957 this.hasInput = this.component && this.inputEL().length;
17959 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17961 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17963 this.picker().on('mousedown', this.onMousedown, this);
17964 this.picker().on('click', this.onClick, this);
17966 this.picker().addClass('datepicker-dropdown');
17968 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17969 v.setStyle('width', '189px');
17976 if(this.isInline) {
17982 setValue: function(v, suppressEvent)
17984 var o = this.getValue();
17986 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17990 if(suppressEvent !== true){
17991 this.fireEvent('select', this, o, v);
17996 getValue: function()
18001 onClick: function(e)
18003 e.stopPropagation();
18004 e.preventDefault();
18006 var target = e.getTarget();
18008 if(target.nodeName.toLowerCase() === 'i'){
18009 target = Roo.get(target).dom.parentNode;
18012 var nodeName = target.nodeName;
18013 var className = target.className;
18014 var html = target.innerHTML;
18016 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18020 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18022 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18028 picker : function()
18030 return this.pickerEl;
18033 fillMonths: function()
18036 var months = this.picker().select('>.datepicker-months td', true).first();
18038 months.dom.innerHTML = '';
18044 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18047 months.createChild(month);
18056 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18057 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18060 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18061 e.removeClass('active');
18063 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18064 e.addClass('active');
18071 if(this.isInline) return;
18073 this.picker().removeClass(['bottom', 'top']);
18075 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18077 * place to the top of element!
18081 this.picker().addClass('top');
18082 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18087 this.picker().addClass('bottom');
18089 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18092 onFocus : function()
18094 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18098 onBlur : function()
18100 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18102 var d = this.inputEl().getValue();
18111 this.picker().show();
18112 this.picker().select('>.datepicker-months', true).first().show();
18116 this.fireEvent('show', this, this.date);
18121 if(this.isInline) return;
18122 this.picker().hide();
18123 this.fireEvent('hide', this, this.date);
18127 onMousedown: function(e)
18129 e.stopPropagation();
18130 e.preventDefault();
18135 Roo.bootstrap.MonthField.superclass.keyup.call(this);
18139 fireKey: function(e)
18141 if (!this.picker().isVisible()){
18142 if (e.keyCode == 27) // allow escape to hide and re-show picker
18152 e.preventDefault();
18156 dir = e.keyCode == 37 ? -1 : 1;
18158 this.vIndex = this.vIndex + dir;
18160 if(this.vIndex < 0){
18164 if(this.vIndex > 11){
18168 if(isNaN(this.vIndex)){
18172 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18178 dir = e.keyCode == 38 ? -1 : 1;
18180 this.vIndex = this.vIndex + dir * 4;
18182 if(this.vIndex < 0){
18186 if(this.vIndex > 11){
18190 if(isNaN(this.vIndex)){
18194 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18199 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18200 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18204 e.preventDefault();
18207 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18208 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18224 this.picker().remove();
18229 Roo.apply(Roo.bootstrap.MonthField, {
18248 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18249 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18254 Roo.apply(Roo.bootstrap.MonthField, {
18258 cls: 'datepicker dropdown-menu roo-dynamic',
18262 cls: 'datepicker-months',
18266 cls: 'table-condensed',
18268 Roo.bootstrap.DateField.content
18288 * @class Roo.bootstrap.CheckBox
18289 * @extends Roo.bootstrap.Input
18290 * Bootstrap CheckBox class
18292 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18293 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18294 * @cfg {String} boxLabel The text that appears beside the checkbox
18295 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18296 * @cfg {Boolean} checked initnal the element
18297 * @cfg {Boolean} inline inline the element (default false)
18298 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18301 * Create a new CheckBox
18302 * @param {Object} config The config object
18305 Roo.bootstrap.CheckBox = function(config){
18306 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18311 * Fires when the element is checked or unchecked.
18312 * @param {Roo.bootstrap.CheckBox} this This input
18313 * @param {Boolean} checked The new checked value
18320 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18322 inputType: 'checkbox',
18330 getAutoCreate : function()
18332 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18338 cfg.cls = 'form-group ' + this.inputType; //input-group
18341 cfg.cls += ' ' + this.inputType + '-inline';
18347 type : this.inputType,
18348 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18349 cls : 'roo-' + this.inputType, //'form-box',
18350 placeholder : this.placeholder || ''
18354 if (this.weight) { // Validity check?
18355 cfg.cls += " " + this.inputType + "-" + this.weight;
18358 if (this.disabled) {
18359 input.disabled=true;
18363 input.checked = this.checked;
18367 input.name = this.name;
18371 input.cls += ' input-' + this.size;
18376 ['xs','sm','md','lg'].map(function(size){
18377 if (settings[size]) {
18378 cfg.cls += ' col-' + size + '-' + settings[size];
18382 var inputblock = input;
18384 if (this.before || this.after) {
18387 cls : 'input-group',
18392 inputblock.cn.push({
18394 cls : 'input-group-addon',
18399 inputblock.cn.push(input);
18402 inputblock.cn.push({
18404 cls : 'input-group-addon',
18411 if (align ==='left' && this.fieldLabel.length) {
18412 Roo.log("left and has label");
18418 cls : 'control-label col-md-' + this.labelWidth,
18419 html : this.fieldLabel
18423 cls : "col-md-" + (12 - this.labelWidth),
18430 } else if ( this.fieldLabel.length) {
18435 tag: this.boxLabel ? 'span' : 'label',
18437 cls: 'control-label box-input-label',
18438 //cls : 'input-group-addon',
18439 html : this.fieldLabel
18449 Roo.log(" no label && no align");
18450 cfg.cn = [ inputblock ] ;
18455 var boxLabelCfg = {
18457 //'for': id, // box label is handled by onclick - so no for...
18459 html: this.boxLabel
18463 boxLabelCfg.tooltip = this.tooltip;
18466 cfg.cn.push(boxLabelCfg);
18476 * return the real input element.
18478 inputEl: function ()
18480 return this.el.select('input.roo-' + this.inputType,true).first();
18483 labelEl: function()
18485 return this.el.select('label.control-label',true).first();
18487 /* depricated... */
18491 return this.labelEl();
18494 initEvents : function()
18496 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18498 this.inputEl().on('click', this.onClick, this);
18500 if (this.boxLabel) {
18501 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18504 this.startValue = this.getValue();
18507 Roo.bootstrap.CheckBox.register(this);
18511 onClick : function()
18513 this.setChecked(!this.checked);
18516 setChecked : function(state,suppressEvent)
18518 this.startValue = this.getValue();
18520 if(this.inputType == 'radio'){
18522 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18523 e.dom.checked = false;
18526 this.inputEl().dom.checked = true;
18528 this.inputEl().dom.value = this.inputValue;
18530 if(suppressEvent !== true){
18531 this.fireEvent('check', this, true);
18539 this.checked = state;
18541 this.inputEl().dom.checked = state;
18543 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18545 if(suppressEvent !== true){
18546 this.fireEvent('check', this, state);
18552 getValue : function()
18554 if(this.inputType == 'radio'){
18555 return this.getGroupValue();
18558 return this.inputEl().getValue();
18562 getGroupValue : function()
18564 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18568 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18571 setValue : function(v,suppressEvent)
18573 if(this.inputType == 'radio'){
18574 this.setGroupValue(v, suppressEvent);
18578 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18583 setGroupValue : function(v, suppressEvent)
18585 this.startValue = this.getValue();
18587 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18588 e.dom.checked = false;
18590 if(e.dom.value == v){
18591 e.dom.checked = true;
18595 if(suppressEvent !== true){
18596 this.fireEvent('check', this, true);
18604 validate : function()
18608 (this.inputType == 'radio' && this.validateRadio()) ||
18609 (this.inputType == 'checkbox' && this.validateCheckbox())
18615 this.markInvalid();
18619 validateRadio : function()
18623 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18624 if(!e.dom.checked){
18636 validateCheckbox : function()
18639 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18642 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18650 for(var i in group){
18655 r = (group[i].getValue() == group[i].inputValue) ? true : false;
18662 * Mark this field as valid
18664 markValid : function()
18666 if(this.allowBlank){
18672 this.fireEvent('valid', this);
18674 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18677 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18684 if(this.inputType == 'radio'){
18685 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18686 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18687 e.findParent('.form-group', false, true).addClass(_this.validClass);
18694 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18695 this.el.findParent('.form-group', false, true).addClass(this.validClass);
18699 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18705 for(var i in group){
18706 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18707 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18712 * Mark this field as invalid
18713 * @param {String} msg The validation message
18715 markInvalid : function(msg)
18717 if(this.allowBlank){
18723 this.fireEvent('invalid', this, msg);
18725 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18728 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18732 label.markInvalid();
18735 if(this.inputType == 'radio'){
18736 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18737 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18738 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18745 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18746 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18750 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18756 for(var i in group){
18757 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18758 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18765 Roo.apply(Roo.bootstrap.CheckBox, {
18770 * register a CheckBox Group
18771 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18773 register : function(checkbox)
18775 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18776 this.groups[checkbox.groupId] = {};
18779 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18783 this.groups[checkbox.groupId][checkbox.name] = checkbox;
18787 * fetch a CheckBox Group based on the group ID
18788 * @param {string} the group ID
18789 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18791 get: function(groupId) {
18792 if (typeof(this.groups[groupId]) == 'undefined') {
18796 return this.groups[groupId] ;
18808 *<div class="radio">
18810 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18811 Option one is this and that—be sure to include why it's great
18818 *<label class="radio-inline">fieldLabel</label>
18819 *<label class="radio-inline">
18820 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18828 * @class Roo.bootstrap.Radio
18829 * @extends Roo.bootstrap.CheckBox
18830 * Bootstrap Radio class
18833 * Create a new Radio
18834 * @param {Object} config The config object
18837 Roo.bootstrap.Radio = function(config){
18838 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18842 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
18844 inputType: 'radio',
18848 getAutoCreate : function()
18850 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18851 align = align || 'left'; // default...
18858 tag : this.inline ? 'span' : 'div',
18863 var inline = this.inline ? ' radio-inline' : '';
18867 // does not need for, as we wrap the input with it..
18869 cls : 'control-label box-label' + inline,
18872 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18876 //cls : 'control-label' + inline,
18877 html : this.fieldLabel,
18878 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18887 type : this.inputType,
18888 //value : (!this.checked) ? this.valueOff : this.inputValue,
18889 value : this.inputValue,
18891 placeholder : this.placeholder || '' // ?? needed????
18894 if (this.weight) { // Validity check?
18895 input.cls += " radio-" + this.weight;
18897 if (this.disabled) {
18898 input.disabled=true;
18902 input.checked = this.checked;
18906 input.name = this.name;
18910 input.cls += ' input-' + this.size;
18913 //?? can span's inline have a width??
18916 ['xs','sm','md','lg'].map(function(size){
18917 if (settings[size]) {
18918 cfg.cls += ' col-' + size + '-' + settings[size];
18922 var inputblock = input;
18924 if (this.before || this.after) {
18927 cls : 'input-group',
18932 inputblock.cn.push({
18934 cls : 'input-group-addon',
18938 inputblock.cn.push(input);
18940 inputblock.cn.push({
18942 cls : 'input-group-addon',
18950 if (this.fieldLabel && this.fieldLabel.length) {
18951 cfg.cn.push(fieldLabel);
18954 // normal bootstrap puts the input inside the label.
18955 // however with our styled version - it has to go after the input.
18957 //lbl.cn.push(inputblock);
18961 cls: 'radio' + inline,
18968 cfg.cn.push( lblwrap);
18973 html: this.boxLabel
18982 initEvents : function()
18984 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18986 this.inputEl().on('click', this.onClick, this);
18987 if (this.boxLabel) {
18988 Roo.log('find label')
18989 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
18994 inputEl: function ()
18996 return this.el.select('input.roo-radio',true).first();
18998 onClick : function()
19001 this.setChecked(true);
19004 setChecked : function(state,suppressEvent)
19007 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19008 v.dom.checked = false;
19011 Roo.log(this.inputEl().dom);
19012 this.checked = state;
19013 this.inputEl().dom.checked = state;
19015 if(suppressEvent !== true){
19016 this.fireEvent('check', this, state);
19019 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19023 getGroupValue : function()
19026 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19027 if(v.dom.checked == true){
19028 value = v.dom.value;
19036 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
19037 * @return {Mixed} value The field value
19039 getValue : function(){
19040 return this.getGroupValue();
19046 //<script type="text/javascript">
19049 * Based Ext JS Library 1.1.1
19050 * Copyright(c) 2006-2007, Ext JS, LLC.
19056 * @class Roo.HtmlEditorCore
19057 * @extends Roo.Component
19058 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19060 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19063 Roo.HtmlEditorCore = function(config){
19066 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19071 * @event initialize
19072 * Fires when the editor is fully initialized (including the iframe)
19073 * @param {Roo.HtmlEditorCore} this
19078 * Fires when the editor is first receives the focus. Any insertion must wait
19079 * until after this event.
19080 * @param {Roo.HtmlEditorCore} this
19084 * @event beforesync
19085 * Fires before the textarea is updated with content from the editor iframe. Return false
19086 * to cancel the sync.
19087 * @param {Roo.HtmlEditorCore} this
19088 * @param {String} html
19092 * @event beforepush
19093 * Fires before the iframe editor is updated with content from the textarea. Return false
19094 * to cancel the push.
19095 * @param {Roo.HtmlEditorCore} this
19096 * @param {String} html
19101 * Fires when the textarea is updated with content from the editor iframe.
19102 * @param {Roo.HtmlEditorCore} this
19103 * @param {String} html
19108 * Fires when the iframe editor is updated with content from the textarea.
19109 * @param {Roo.HtmlEditorCore} this
19110 * @param {String} html
19115 * @event editorevent
19116 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19117 * @param {Roo.HtmlEditorCore} this
19123 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19125 // defaults : white / black...
19126 this.applyBlacklists();
19133 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
19137 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
19143 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
19148 * @cfg {Number} height (in pixels)
19152 * @cfg {Number} width (in pixels)
19157 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19160 stylesheets: false,
19165 // private properties
19166 validationEvent : false,
19168 initialized : false,
19170 sourceEditMode : false,
19171 onFocus : Roo.emptyFn,
19173 hideMode:'offsets',
19177 // blacklist + whitelisted elements..
19184 * Protected method that will not generally be called directly. It
19185 * is called when the editor initializes the iframe with HTML contents. Override this method if you
19186 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19188 getDocMarkup : function(){
19192 // inherit styels from page...??
19193 if (this.stylesheets === false) {
19195 Roo.get(document.head).select('style').each(function(node) {
19196 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19199 Roo.get(document.head).select('link').each(function(node) {
19200 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19203 } else if (!this.stylesheets.length) {
19205 st = '<style type="text/css">' +
19206 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19212 st += '<style type="text/css">' +
19213 'IMG { cursor: pointer } ' +
19217 return '<html><head>' + st +
19218 //<style type="text/css">' +
19219 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19221 ' </head><body class="roo-htmleditor-body"></body></html>';
19225 onRender : function(ct, position)
19228 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19229 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19232 this.el.dom.style.border = '0 none';
19233 this.el.dom.setAttribute('tabIndex', -1);
19234 this.el.addClass('x-hidden hide');
19238 if(Roo.isIE){ // fix IE 1px bogus margin
19239 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19243 this.frameId = Roo.id();
19247 var iframe = this.owner.wrap.createChild({
19249 cls: 'form-control', // bootstrap..
19251 name: this.frameId,
19252 frameBorder : 'no',
19253 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19258 this.iframe = iframe.dom;
19260 this.assignDocWin();
19262 this.doc.designMode = 'on';
19265 this.doc.write(this.getDocMarkup());
19269 var task = { // must defer to wait for browser to be ready
19271 //console.log("run task?" + this.doc.readyState);
19272 this.assignDocWin();
19273 if(this.doc.body || this.doc.readyState == 'complete'){
19275 this.doc.designMode="on";
19279 Roo.TaskMgr.stop(task);
19280 this.initEditor.defer(10, this);
19287 Roo.TaskMgr.start(task);
19292 onResize : function(w, h)
19294 Roo.log('resize: ' +w + ',' + h );
19295 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19299 if(typeof w == 'number'){
19301 this.iframe.style.width = w + 'px';
19303 if(typeof h == 'number'){
19305 this.iframe.style.height = h + 'px';
19307 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19314 * Toggles the editor between standard and source edit mode.
19315 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19317 toggleSourceEdit : function(sourceEditMode){
19319 this.sourceEditMode = sourceEditMode === true;
19321 if(this.sourceEditMode){
19323 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19326 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19327 //this.iframe.className = '';
19330 //this.setSize(this.owner.wrap.getSize());
19331 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19338 * Protected method that will not generally be called directly. If you need/want
19339 * custom HTML cleanup, this is the method you should override.
19340 * @param {String} html The HTML to be cleaned
19341 * return {String} The cleaned HTML
19343 cleanHtml : function(html){
19344 html = String(html);
19345 if(html.length > 5){
19346 if(Roo.isSafari){ // strip safari nonsense
19347 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19350 if(html == ' '){
19357 * HTML Editor -> Textarea
19358 * Protected method that will not generally be called directly. Syncs the contents
19359 * of the editor iframe with the textarea.
19361 syncValue : function(){
19362 if(this.initialized){
19363 var bd = (this.doc.body || this.doc.documentElement);
19364 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19365 var html = bd.innerHTML;
19367 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19368 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19370 html = '<div style="'+m[0]+'">' + html + '</div>';
19373 html = this.cleanHtml(html);
19374 // fix up the special chars.. normaly like back quotes in word...
19375 // however we do not want to do this with chinese..
19376 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19377 var cc = b.charCodeAt();
19379 (cc >= 0x4E00 && cc < 0xA000 ) ||
19380 (cc >= 0x3400 && cc < 0x4E00 ) ||
19381 (cc >= 0xf900 && cc < 0xfb00 )
19387 if(this.owner.fireEvent('beforesync', this, html) !== false){
19388 this.el.dom.value = html;
19389 this.owner.fireEvent('sync', this, html);
19395 * Protected method that will not generally be called directly. Pushes the value of the textarea
19396 * into the iframe editor.
19398 pushValue : function(){
19399 if(this.initialized){
19400 var v = this.el.dom.value.trim();
19402 // if(v.length < 1){
19406 if(this.owner.fireEvent('beforepush', this, v) !== false){
19407 var d = (this.doc.body || this.doc.documentElement);
19409 this.cleanUpPaste();
19410 this.el.dom.value = d.innerHTML;
19411 this.owner.fireEvent('push', this, v);
19417 deferFocus : function(){
19418 this.focus.defer(10, this);
19422 focus : function(){
19423 if(this.win && !this.sourceEditMode){
19430 assignDocWin: function()
19432 var iframe = this.iframe;
19435 this.doc = iframe.contentWindow.document;
19436 this.win = iframe.contentWindow;
19438 // if (!Roo.get(this.frameId)) {
19441 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19442 // this.win = Roo.get(this.frameId).dom.contentWindow;
19444 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19448 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19449 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19454 initEditor : function(){
19455 //console.log("INIT EDITOR");
19456 this.assignDocWin();
19460 this.doc.designMode="on";
19462 this.doc.write(this.getDocMarkup());
19465 var dbody = (this.doc.body || this.doc.documentElement);
19466 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19467 // this copies styles from the containing element into thsi one..
19468 // not sure why we need all of this..
19469 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19471 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19472 //ss['background-attachment'] = 'fixed'; // w3c
19473 dbody.bgProperties = 'fixed'; // ie
19474 //Roo.DomHelper.applyStyles(dbody, ss);
19475 Roo.EventManager.on(this.doc, {
19476 //'mousedown': this.onEditorEvent,
19477 'mouseup': this.onEditorEvent,
19478 'dblclick': this.onEditorEvent,
19479 'click': this.onEditorEvent,
19480 'keyup': this.onEditorEvent,
19485 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19487 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19488 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19490 this.initialized = true;
19492 this.owner.fireEvent('initialize', this);
19497 onDestroy : function(){
19503 //for (var i =0; i < this.toolbars.length;i++) {
19504 // // fixme - ask toolbars for heights?
19505 // this.toolbars[i].onDestroy();
19508 //this.wrap.dom.innerHTML = '';
19509 //this.wrap.remove();
19514 onFirstFocus : function(){
19516 this.assignDocWin();
19519 this.activated = true;
19522 if(Roo.isGecko){ // prevent silly gecko errors
19524 var s = this.win.getSelection();
19525 if(!s.focusNode || s.focusNode.nodeType != 3){
19526 var r = s.getRangeAt(0);
19527 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19532 this.execCmd('useCSS', true);
19533 this.execCmd('styleWithCSS', false);
19536 this.owner.fireEvent('activate', this);
19540 adjustFont: function(btn){
19541 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19542 //if(Roo.isSafari){ // safari
19545 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19546 if(Roo.isSafari){ // safari
19547 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19548 v = (v < 10) ? 10 : v;
19549 v = (v > 48) ? 48 : v;
19550 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19555 v = Math.max(1, v+adjust);
19557 this.execCmd('FontSize', v );
19560 onEditorEvent : function(e)
19562 this.owner.fireEvent('editorevent', this, e);
19563 // this.updateToolbar();
19564 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19567 insertTag : function(tg)
19569 // could be a bit smarter... -> wrap the current selected tRoo..
19570 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19572 range = this.createRange(this.getSelection());
19573 var wrappingNode = this.doc.createElement(tg.toLowerCase());
19574 wrappingNode.appendChild(range.extractContents());
19575 range.insertNode(wrappingNode);
19582 this.execCmd("formatblock", tg);
19586 insertText : function(txt)
19590 var range = this.createRange();
19591 range.deleteContents();
19592 //alert(Sender.getAttribute('label'));
19594 range.insertNode(this.doc.createTextNode(txt));
19600 * Executes a Midas editor command on the editor document and performs necessary focus and
19601 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19602 * @param {String} cmd The Midas command
19603 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19605 relayCmd : function(cmd, value){
19607 this.execCmd(cmd, value);
19608 this.owner.fireEvent('editorevent', this);
19609 //this.updateToolbar();
19610 this.owner.deferFocus();
19614 * Executes a Midas editor command directly on the editor document.
19615 * For visual commands, you should use {@link #relayCmd} instead.
19616 * <b>This should only be called after the editor is initialized.</b>
19617 * @param {String} cmd The Midas command
19618 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19620 execCmd : function(cmd, value){
19621 this.doc.execCommand(cmd, false, value === undefined ? null : value);
19628 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19630 * @param {String} text | dom node..
19632 insertAtCursor : function(text)
19637 if(!this.activated){
19643 var r = this.doc.selection.createRange();
19654 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19658 // from jquery ui (MIT licenced)
19660 var win = this.win;
19662 if (win.getSelection && win.getSelection().getRangeAt) {
19663 range = win.getSelection().getRangeAt(0);
19664 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19665 range.insertNode(node);
19666 } else if (win.document.selection && win.document.selection.createRange) {
19667 // no firefox support
19668 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19669 win.document.selection.createRange().pasteHTML(txt);
19671 // no firefox support
19672 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19673 this.execCmd('InsertHTML', txt);
19682 mozKeyPress : function(e){
19684 var c = e.getCharCode(), cmd;
19687 c = String.fromCharCode(c).toLowerCase();
19701 this.cleanUpPaste.defer(100, this);
19709 e.preventDefault();
19717 fixKeys : function(){ // load time branching for fastest keydown performance
19719 return function(e){
19720 var k = e.getKey(), r;
19723 r = this.doc.selection.createRange();
19726 r.pasteHTML('    ');
19733 r = this.doc.selection.createRange();
19735 var target = r.parentElement();
19736 if(!target || target.tagName.toLowerCase() != 'li'){
19738 r.pasteHTML('<br />');
19744 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19745 this.cleanUpPaste.defer(100, this);
19751 }else if(Roo.isOpera){
19752 return function(e){
19753 var k = e.getKey();
19757 this.execCmd('InsertHTML','    ');
19760 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19761 this.cleanUpPaste.defer(100, this);
19766 }else if(Roo.isSafari){
19767 return function(e){
19768 var k = e.getKey();
19772 this.execCmd('InsertText','\t');
19776 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19777 this.cleanUpPaste.defer(100, this);
19785 getAllAncestors: function()
19787 var p = this.getSelectedNode();
19790 a.push(p); // push blank onto stack..
19791 p = this.getParentElement();
19795 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19799 a.push(this.doc.body);
19803 lastSelNode : false,
19806 getSelection : function()
19808 this.assignDocWin();
19809 return Roo.isIE ? this.doc.selection : this.win.getSelection();
19812 getSelectedNode: function()
19814 // this may only work on Gecko!!!
19816 // should we cache this!!!!
19821 var range = this.createRange(this.getSelection()).cloneRange();
19824 var parent = range.parentElement();
19826 var testRange = range.duplicate();
19827 testRange.moveToElementText(parent);
19828 if (testRange.inRange(range)) {
19831 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19834 parent = parent.parentElement;
19839 // is ancestor a text element.
19840 var ac = range.commonAncestorContainer;
19841 if (ac.nodeType == 3) {
19842 ac = ac.parentNode;
19845 var ar = ac.childNodes;
19848 var other_nodes = [];
19849 var has_other_nodes = false;
19850 for (var i=0;i<ar.length;i++) {
19851 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
19854 // fullly contained node.
19856 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19861 // probably selected..
19862 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19863 other_nodes.push(ar[i]);
19867 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
19872 has_other_nodes = true;
19874 if (!nodes.length && other_nodes.length) {
19875 nodes= other_nodes;
19877 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19883 createRange: function(sel)
19885 // this has strange effects when using with
19886 // top toolbar - not sure if it's a great idea.
19887 //this.editor.contentWindow.focus();
19888 if (typeof sel != "undefined") {
19890 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19892 return this.doc.createRange();
19895 return this.doc.createRange();
19898 getParentElement: function()
19901 this.assignDocWin();
19902 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19904 var range = this.createRange(sel);
19907 var p = range.commonAncestorContainer;
19908 while (p.nodeType == 3) { // text node
19919 * Range intersection.. the hard stuff...
19923 * [ -- selected range --- ]
19927 * if end is before start or hits it. fail.
19928 * if start is after end or hits it fail.
19930 * if either hits (but other is outside. - then it's not
19936 // @see http://www.thismuchiknow.co.uk/?p=64.
19937 rangeIntersectsNode : function(range, node)
19939 var nodeRange = node.ownerDocument.createRange();
19941 nodeRange.selectNode(node);
19943 nodeRange.selectNodeContents(node);
19946 var rangeStartRange = range.cloneRange();
19947 rangeStartRange.collapse(true);
19949 var rangeEndRange = range.cloneRange();
19950 rangeEndRange.collapse(false);
19952 var nodeStartRange = nodeRange.cloneRange();
19953 nodeStartRange.collapse(true);
19955 var nodeEndRange = nodeRange.cloneRange();
19956 nodeEndRange.collapse(false);
19958 return rangeStartRange.compareBoundaryPoints(
19959 Range.START_TO_START, nodeEndRange) == -1 &&
19960 rangeEndRange.compareBoundaryPoints(
19961 Range.START_TO_START, nodeStartRange) == 1;
19965 rangeCompareNode : function(range, node)
19967 var nodeRange = node.ownerDocument.createRange();
19969 nodeRange.selectNode(node);
19971 nodeRange.selectNodeContents(node);
19975 range.collapse(true);
19977 nodeRange.collapse(true);
19979 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19980 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
19982 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19984 var nodeIsBefore = ss == 1;
19985 var nodeIsAfter = ee == -1;
19987 if (nodeIsBefore && nodeIsAfter)
19989 if (!nodeIsBefore && nodeIsAfter)
19990 return 1; //right trailed.
19992 if (nodeIsBefore && !nodeIsAfter)
19993 return 2; // left trailed.
19998 // private? - in a new class?
19999 cleanUpPaste : function()
20001 // cleans up the whole document..
20002 Roo.log('cleanuppaste');
20004 this.cleanUpChildren(this.doc.body);
20005 var clean = this.cleanWordChars(this.doc.body.innerHTML);
20006 if (clean != this.doc.body.innerHTML) {
20007 this.doc.body.innerHTML = clean;
20012 cleanWordChars : function(input) {// change the chars to hex code
20013 var he = Roo.HtmlEditorCore;
20015 var output = input;
20016 Roo.each(he.swapCodes, function(sw) {
20017 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20019 output = output.replace(swapper, sw[1]);
20026 cleanUpChildren : function (n)
20028 if (!n.childNodes.length) {
20031 for (var i = n.childNodes.length-1; i > -1 ; i--) {
20032 this.cleanUpChild(n.childNodes[i]);
20039 cleanUpChild : function (node)
20042 //console.log(node);
20043 if (node.nodeName == "#text") {
20044 // clean up silly Windows -- stuff?
20047 if (node.nodeName == "#comment") {
20048 node.parentNode.removeChild(node);
20049 // clean up silly Windows -- stuff?
20052 var lcname = node.tagName.toLowerCase();
20053 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20054 // whitelist of tags..
20056 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20058 node.parentNode.removeChild(node);
20063 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20065 // remove <a name=....> as rendering on yahoo mailer is borked with this.
20066 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20068 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20069 // remove_keep_children = true;
20072 if (remove_keep_children) {
20073 this.cleanUpChildren(node);
20074 // inserts everything just before this node...
20075 while (node.childNodes.length) {
20076 var cn = node.childNodes[0];
20077 node.removeChild(cn);
20078 node.parentNode.insertBefore(cn, node);
20080 node.parentNode.removeChild(node);
20084 if (!node.attributes || !node.attributes.length) {
20085 this.cleanUpChildren(node);
20089 function cleanAttr(n,v)
20092 if (v.match(/^\./) || v.match(/^\//)) {
20095 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20098 if (v.match(/^#/)) {
20101 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20102 node.removeAttribute(n);
20106 var cwhite = this.cwhite;
20107 var cblack = this.cblack;
20109 function cleanStyle(n,v)
20111 if (v.match(/expression/)) { //XSS?? should we even bother..
20112 node.removeAttribute(n);
20116 var parts = v.split(/;/);
20119 Roo.each(parts, function(p) {
20120 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20124 var l = p.split(':').shift().replace(/\s+/g,'');
20125 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20127 if ( cwhite.length && cblack.indexOf(l) > -1) {
20128 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20129 //node.removeAttribute(n);
20133 // only allow 'c whitelisted system attributes'
20134 if ( cwhite.length && cwhite.indexOf(l) < 0) {
20135 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20136 //node.removeAttribute(n);
20146 if (clean.length) {
20147 node.setAttribute(n, clean.join(';'));
20149 node.removeAttribute(n);
20155 for (var i = node.attributes.length-1; i > -1 ; i--) {
20156 var a = node.attributes[i];
20159 if (a.name.toLowerCase().substr(0,2)=='on') {
20160 node.removeAttribute(a.name);
20163 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20164 node.removeAttribute(a.name);
20167 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20168 cleanAttr(a.name,a.value); // fixme..
20171 if (a.name == 'style') {
20172 cleanStyle(a.name,a.value);
20175 /// clean up MS crap..
20176 // tecnically this should be a list of valid class'es..
20179 if (a.name == 'class') {
20180 if (a.value.match(/^Mso/)) {
20181 node.className = '';
20184 if (a.value.match(/body/)) {
20185 node.className = '';
20196 this.cleanUpChildren(node);
20202 * Clean up MS wordisms...
20204 cleanWord : function(node)
20209 this.cleanWord(this.doc.body);
20212 if (node.nodeName == "#text") {
20213 // clean up silly Windows -- stuff?
20216 if (node.nodeName == "#comment") {
20217 node.parentNode.removeChild(node);
20218 // clean up silly Windows -- stuff?
20222 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20223 node.parentNode.removeChild(node);
20227 // remove - but keep children..
20228 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20229 while (node.childNodes.length) {
20230 var cn = node.childNodes[0];
20231 node.removeChild(cn);
20232 node.parentNode.insertBefore(cn, node);
20234 node.parentNode.removeChild(node);
20235 this.iterateChildren(node, this.cleanWord);
20239 if (node.className.length) {
20241 var cn = node.className.split(/\W+/);
20243 Roo.each(cn, function(cls) {
20244 if (cls.match(/Mso[a-zA-Z]+/)) {
20249 node.className = cna.length ? cna.join(' ') : '';
20251 node.removeAttribute("class");
20255 if (node.hasAttribute("lang")) {
20256 node.removeAttribute("lang");
20259 if (node.hasAttribute("style")) {
20261 var styles = node.getAttribute("style").split(";");
20263 Roo.each(styles, function(s) {
20264 if (!s.match(/:/)) {
20267 var kv = s.split(":");
20268 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20271 // what ever is left... we allow.
20274 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20275 if (!nstyle.length) {
20276 node.removeAttribute('style');
20279 this.iterateChildren(node, this.cleanWord);
20285 * iterateChildren of a Node, calling fn each time, using this as the scole..
20286 * @param {DomNode} node node to iterate children of.
20287 * @param {Function} fn method of this class to call on each item.
20289 iterateChildren : function(node, fn)
20291 if (!node.childNodes.length) {
20294 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20295 fn.call(this, node.childNodes[i])
20301 * cleanTableWidths.
20303 * Quite often pasting from word etc.. results in tables with column and widths.
20304 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20307 cleanTableWidths : function(node)
20312 this.cleanTableWidths(this.doc.body);
20317 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20320 Roo.log(node.tagName);
20321 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20322 this.iterateChildren(node, this.cleanTableWidths);
20325 if (node.hasAttribute('width')) {
20326 node.removeAttribute('width');
20330 if (node.hasAttribute("style")) {
20333 var styles = node.getAttribute("style").split(";");
20335 Roo.each(styles, function(s) {
20336 if (!s.match(/:/)) {
20339 var kv = s.split(":");
20340 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20343 // what ever is left... we allow.
20346 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20347 if (!nstyle.length) {
20348 node.removeAttribute('style');
20352 this.iterateChildren(node, this.cleanTableWidths);
20360 domToHTML : function(currentElement, depth, nopadtext) {
20362 depth = depth || 0;
20363 nopadtext = nopadtext || false;
20365 if (!currentElement) {
20366 return this.domToHTML(this.doc.body);
20369 //Roo.log(currentElement);
20371 var allText = false;
20372 var nodeName = currentElement.nodeName;
20373 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20375 if (nodeName == '#text') {
20377 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20382 if (nodeName != 'BODY') {
20385 // Prints the node tagName, such as <A>, <IMG>, etc
20388 for(i = 0; i < currentElement.attributes.length;i++) {
20390 var aname = currentElement.attributes.item(i).name;
20391 if (!currentElement.attributes.item(i).value.length) {
20394 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20397 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20406 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20409 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20414 // Traverse the tree
20416 var currentElementChild = currentElement.childNodes.item(i);
20417 var allText = true;
20418 var innerHTML = '';
20420 while (currentElementChild) {
20421 // Formatting code (indent the tree so it looks nice on the screen)
20422 var nopad = nopadtext;
20423 if (lastnode == 'SPAN') {
20427 if (currentElementChild.nodeName == '#text') {
20428 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20429 toadd = nopadtext ? toadd : toadd.trim();
20430 if (!nopad && toadd.length > 80) {
20431 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20433 innerHTML += toadd;
20436 currentElementChild = currentElement.childNodes.item(i);
20442 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20444 // Recursively traverse the tree structure of the child node
20445 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20446 lastnode = currentElementChild.nodeName;
20448 currentElementChild=currentElement.childNodes.item(i);
20454 // The remaining code is mostly for formatting the tree
20455 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20460 ret+= "</"+tagName+">";
20466 applyBlacklists : function()
20468 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20469 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20473 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20474 if (b.indexOf(tag) > -1) {
20477 this.white.push(tag);
20481 Roo.each(w, function(tag) {
20482 if (b.indexOf(tag) > -1) {
20485 if (this.white.indexOf(tag) > -1) {
20488 this.white.push(tag);
20493 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20494 if (w.indexOf(tag) > -1) {
20497 this.black.push(tag);
20501 Roo.each(b, function(tag) {
20502 if (w.indexOf(tag) > -1) {
20505 if (this.black.indexOf(tag) > -1) {
20508 this.black.push(tag);
20513 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20514 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20518 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20519 if (b.indexOf(tag) > -1) {
20522 this.cwhite.push(tag);
20526 Roo.each(w, function(tag) {
20527 if (b.indexOf(tag) > -1) {
20530 if (this.cwhite.indexOf(tag) > -1) {
20533 this.cwhite.push(tag);
20538 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20539 if (w.indexOf(tag) > -1) {
20542 this.cblack.push(tag);
20546 Roo.each(b, function(tag) {
20547 if (w.indexOf(tag) > -1) {
20550 if (this.cblack.indexOf(tag) > -1) {
20553 this.cblack.push(tag);
20558 setStylesheets : function(stylesheets)
20560 if(typeof(stylesheets) == 'string'){
20561 Roo.get(this.iframe.contentDocument.head).createChild({
20563 rel : 'stylesheet',
20572 Roo.each(stylesheets, function(s) {
20577 Roo.get(_this.iframe.contentDocument.head).createChild({
20579 rel : 'stylesheet',
20588 removeStylesheets : function()
20592 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20597 // hide stuff that is not compatible
20611 * @event specialkey
20615 * @cfg {String} fieldClass @hide
20618 * @cfg {String} focusClass @hide
20621 * @cfg {String} autoCreate @hide
20624 * @cfg {String} inputType @hide
20627 * @cfg {String} invalidClass @hide
20630 * @cfg {String} invalidText @hide
20633 * @cfg {String} msgFx @hide
20636 * @cfg {String} validateOnBlur @hide
20640 Roo.HtmlEditorCore.white = [
20641 'area', 'br', 'img', 'input', 'hr', 'wbr',
20643 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
20644 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
20645 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
20646 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
20647 'table', 'ul', 'xmp',
20649 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
20652 'dir', 'menu', 'ol', 'ul', 'dl',
20658 Roo.HtmlEditorCore.black = [
20659 // 'embed', 'object', // enable - backend responsiblity to clean thiese
20661 'base', 'basefont', 'bgsound', 'blink', 'body',
20662 'frame', 'frameset', 'head', 'html', 'ilayer',
20663 'iframe', 'layer', 'link', 'meta', 'object',
20664 'script', 'style' ,'title', 'xml' // clean later..
20666 Roo.HtmlEditorCore.clean = [
20667 'script', 'style', 'title', 'xml'
20669 Roo.HtmlEditorCore.remove = [
20674 Roo.HtmlEditorCore.ablack = [
20678 Roo.HtmlEditorCore.aclean = [
20679 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
20683 Roo.HtmlEditorCore.pwhite= [
20684 'http', 'https', 'mailto'
20687 // white listed style attributes.
20688 Roo.HtmlEditorCore.cwhite= [
20689 // 'text-align', /// default is to allow most things..
20695 // black listed style attributes.
20696 Roo.HtmlEditorCore.cblack= [
20697 // 'font-size' -- this can be set by the project
20701 Roo.HtmlEditorCore.swapCodes =[
20720 * @class Roo.bootstrap.HtmlEditor
20721 * @extends Roo.bootstrap.TextArea
20722 * Bootstrap HtmlEditor class
20725 * Create a new HtmlEditor
20726 * @param {Object} config The config object
20729 Roo.bootstrap.HtmlEditor = function(config){
20730 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20731 if (!this.toolbars) {
20732 this.toolbars = [];
20734 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20737 * @event initialize
20738 * Fires when the editor is fully initialized (including the iframe)
20739 * @param {HtmlEditor} this
20744 * Fires when the editor is first receives the focus. Any insertion must wait
20745 * until after this event.
20746 * @param {HtmlEditor} this
20750 * @event beforesync
20751 * Fires before the textarea is updated with content from the editor iframe. Return false
20752 * to cancel the sync.
20753 * @param {HtmlEditor} this
20754 * @param {String} html
20758 * @event beforepush
20759 * Fires before the iframe editor is updated with content from the textarea. Return false
20760 * to cancel the push.
20761 * @param {HtmlEditor} this
20762 * @param {String} html
20767 * Fires when the textarea is updated with content from the editor iframe.
20768 * @param {HtmlEditor} this
20769 * @param {String} html
20774 * Fires when the iframe editor is updated with content from the textarea.
20775 * @param {HtmlEditor} this
20776 * @param {String} html
20780 * @event editmodechange
20781 * Fires when the editor switches edit modes
20782 * @param {HtmlEditor} this
20783 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20785 editmodechange: true,
20787 * @event editorevent
20788 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20789 * @param {HtmlEditor} this
20793 * @event firstfocus
20794 * Fires when on first focus - needed by toolbars..
20795 * @param {HtmlEditor} this
20800 * Auto save the htmlEditor value as a file into Events
20801 * @param {HtmlEditor} this
20805 * @event savedpreview
20806 * preview the saved version of htmlEditor
20807 * @param {HtmlEditor} this
20814 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
20818 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20823 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20828 * @cfg {Number} height (in pixels)
20832 * @cfg {Number} width (in pixels)
20837 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20840 stylesheets: false,
20845 // private properties
20846 validationEvent : false,
20848 initialized : false,
20851 onFocus : Roo.emptyFn,
20853 hideMode:'offsets',
20856 tbContainer : false,
20858 toolbarContainer :function() {
20859 return this.wrap.select('.x-html-editor-tb',true).first();
20863 * Protected method that will not generally be called directly. It
20864 * is called when the editor creates its toolbar. Override this method if you need to
20865 * add custom toolbar buttons.
20866 * @param {HtmlEditor} editor
20868 createToolbar : function(){
20870 Roo.log("create toolbars");
20872 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20873 this.toolbars[0].render(this.toolbarContainer());
20877 // if (!editor.toolbars || !editor.toolbars.length) {
20878 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20881 // for (var i =0 ; i < editor.toolbars.length;i++) {
20882 // editor.toolbars[i] = Roo.factory(
20883 // typeof(editor.toolbars[i]) == 'string' ?
20884 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
20885 // Roo.bootstrap.HtmlEditor);
20886 // editor.toolbars[i].init(editor);
20892 onRender : function(ct, position)
20894 // Roo.log("Call onRender: " + this.xtype);
20896 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20898 this.wrap = this.inputEl().wrap({
20899 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20902 this.editorcore.onRender(ct, position);
20904 if (this.resizable) {
20905 this.resizeEl = new Roo.Resizable(this.wrap, {
20909 minHeight : this.height,
20910 height: this.height,
20911 handles : this.resizable,
20914 resize : function(r, w, h) {
20915 _t.onResize(w,h); // -something
20921 this.createToolbar(this);
20924 if(!this.width && this.resizable){
20925 this.setSize(this.wrap.getSize());
20927 if (this.resizeEl) {
20928 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20929 // should trigger onReize..
20935 onResize : function(w, h)
20937 Roo.log('resize: ' +w + ',' + h );
20938 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20942 if(this.inputEl() ){
20943 if(typeof w == 'number'){
20944 var aw = w - this.wrap.getFrameWidth('lr');
20945 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20948 if(typeof h == 'number'){
20949 var tbh = -11; // fixme it needs to tool bar size!
20950 for (var i =0; i < this.toolbars.length;i++) {
20951 // fixme - ask toolbars for heights?
20952 tbh += this.toolbars[i].el.getHeight();
20953 //if (this.toolbars[i].footer) {
20954 // tbh += this.toolbars[i].footer.el.getHeight();
20962 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20963 ah -= 5; // knock a few pixes off for look..
20964 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20968 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20969 this.editorcore.onResize(ew,eh);
20974 * Toggles the editor between standard and source edit mode.
20975 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20977 toggleSourceEdit : function(sourceEditMode)
20979 this.editorcore.toggleSourceEdit(sourceEditMode);
20981 if(this.editorcore.sourceEditMode){
20982 Roo.log('editor - showing textarea');
20985 // Roo.log(this.syncValue());
20987 this.inputEl().removeClass(['hide', 'x-hidden']);
20988 this.inputEl().dom.removeAttribute('tabIndex');
20989 this.inputEl().focus();
20991 Roo.log('editor - hiding textarea');
20993 // Roo.log(this.pushValue());
20996 this.inputEl().addClass(['hide', 'x-hidden']);
20997 this.inputEl().dom.setAttribute('tabIndex', -1);
20998 //this.deferFocus();
21001 if(this.resizable){
21002 this.setSize(this.wrap.getSize());
21005 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21008 // private (for BoxComponent)
21009 adjustSize : Roo.BoxComponent.prototype.adjustSize,
21011 // private (for BoxComponent)
21012 getResizeEl : function(){
21016 // private (for BoxComponent)
21017 getPositionEl : function(){
21022 initEvents : function(){
21023 this.originalValue = this.getValue();
21027 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21030 // markInvalid : Roo.emptyFn,
21032 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21035 // clearInvalid : Roo.emptyFn,
21037 setValue : function(v){
21038 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21039 this.editorcore.pushValue();
21044 deferFocus : function(){
21045 this.focus.defer(10, this);
21049 focus : function(){
21050 this.editorcore.focus();
21056 onDestroy : function(){
21062 for (var i =0; i < this.toolbars.length;i++) {
21063 // fixme - ask toolbars for heights?
21064 this.toolbars[i].onDestroy();
21067 this.wrap.dom.innerHTML = '';
21068 this.wrap.remove();
21073 onFirstFocus : function(){
21074 //Roo.log("onFirstFocus");
21075 this.editorcore.onFirstFocus();
21076 for (var i =0; i < this.toolbars.length;i++) {
21077 this.toolbars[i].onFirstFocus();
21083 syncValue : function()
21085 this.editorcore.syncValue();
21088 pushValue : function()
21090 this.editorcore.pushValue();
21094 // hide stuff that is not compatible
21108 * @event specialkey
21112 * @cfg {String} fieldClass @hide
21115 * @cfg {String} focusClass @hide
21118 * @cfg {String} autoCreate @hide
21121 * @cfg {String} inputType @hide
21124 * @cfg {String} invalidClass @hide
21127 * @cfg {String} invalidText @hide
21130 * @cfg {String} msgFx @hide
21133 * @cfg {String} validateOnBlur @hide
21142 Roo.namespace('Roo.bootstrap.htmleditor');
21144 * @class Roo.bootstrap.HtmlEditorToolbar1
21149 new Roo.bootstrap.HtmlEditor({
21152 new Roo.bootstrap.HtmlEditorToolbar1({
21153 disable : { fonts: 1 , format: 1, ..., ... , ...],
21159 * @cfg {Object} disable List of elements to disable..
21160 * @cfg {Array} btns List of additional buttons.
21164 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21167 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21170 Roo.apply(this, config);
21172 // default disabled, based on 'good practice'..
21173 this.disable = this.disable || {};
21174 Roo.applyIf(this.disable, {
21177 specialElements : true
21179 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21181 this.editor = config.editor;
21182 this.editorcore = config.editor.editorcore;
21184 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21186 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21187 // dont call parent... till later.
21189 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
21194 editorcore : false,
21199 "h1","h2","h3","h4","h5","h6",
21201 "abbr", "acronym", "address", "cite", "samp", "var",
21205 onRender : function(ct, position)
21207 // Roo.log("Call onRender: " + this.xtype);
21209 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21211 this.el.dom.style.marginBottom = '0';
21213 var editorcore = this.editorcore;
21214 var editor= this.editor;
21217 var btn = function(id,cmd , toggle, handler){
21219 var event = toggle ? 'toggle' : 'click';
21224 xns: Roo.bootstrap,
21227 enableToggle:toggle !== false,
21229 pressed : toggle ? false : null,
21232 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21233 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21242 xns: Roo.bootstrap,
21243 glyphicon : 'font',
21247 xns: Roo.bootstrap,
21251 Roo.each(this.formats, function(f) {
21252 style.menu.items.push({
21254 xns: Roo.bootstrap,
21255 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21260 editorcore.insertTag(this.tagname);
21267 children.push(style);
21270 btn('bold',false,true);
21271 btn('italic',false,true);
21272 btn('align-left', 'justifyleft',true);
21273 btn('align-center', 'justifycenter',true);
21274 btn('align-right' , 'justifyright',true);
21275 btn('link', false, false, function(btn) {
21276 //Roo.log("create link?");
21277 var url = prompt(this.createLinkText, this.defaultLinkValue);
21278 if(url && url != 'http:/'+'/'){
21279 this.editorcore.relayCmd('createlink', url);
21282 btn('list','insertunorderedlist',true);
21283 btn('pencil', false,true, function(btn){
21286 this.toggleSourceEdit(btn.pressed);
21292 xns: Roo.bootstrap,
21297 xns: Roo.bootstrap,
21302 cog.menu.items.push({
21304 xns: Roo.bootstrap,
21305 html : Clean styles,
21310 editorcore.insertTag(this.tagname);
21319 this.xtype = 'NavSimplebar';
21321 for(var i=0;i< children.length;i++) {
21323 this.buttons.add(this.addxtypeChild(children[i]));
21327 editor.on('editorevent', this.updateToolbar, this);
21329 onBtnClick : function(id)
21331 this.editorcore.relayCmd(id);
21332 this.editorcore.focus();
21336 * Protected method that will not generally be called directly. It triggers
21337 * a toolbar update by reading the markup state of the current selection in the editor.
21339 updateToolbar: function(){
21341 if(!this.editorcore.activated){
21342 this.editor.onFirstFocus(); // is this neeed?
21346 var btns = this.buttons;
21347 var doc = this.editorcore.doc;
21348 btns.get('bold').setActive(doc.queryCommandState('bold'));
21349 btns.get('italic').setActive(doc.queryCommandState('italic'));
21350 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21352 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21353 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21354 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21356 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21357 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21360 var ans = this.editorcore.getAllAncestors();
21361 if (this.formatCombo) {
21364 var store = this.formatCombo.store;
21365 this.formatCombo.setValue("");
21366 for (var i =0; i < ans.length;i++) {
21367 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21369 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21377 // hides menus... - so this cant be on a menu...
21378 Roo.bootstrap.MenuMgr.hideAll();
21380 Roo.bootstrap.MenuMgr.hideAll();
21381 //this.editorsyncValue();
21383 onFirstFocus: function() {
21384 this.buttons.each(function(item){
21388 toggleSourceEdit : function(sourceEditMode){
21391 if(sourceEditMode){
21392 Roo.log("disabling buttons");
21393 this.buttons.each( function(item){
21394 if(item.cmd != 'pencil'){
21400 Roo.log("enabling buttons");
21401 if(this.editorcore.initialized){
21402 this.buttons.each( function(item){
21408 Roo.log("calling toggole on editor");
21409 // tell the editor that it's been pressed..
21410 this.editor.toggleSourceEdit(sourceEditMode);
21420 * @class Roo.bootstrap.Table.AbstractSelectionModel
21421 * @extends Roo.util.Observable
21422 * Abstract base class for grid SelectionModels. It provides the interface that should be
21423 * implemented by descendant classes. This class should not be directly instantiated.
21426 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21427 this.locked = false;
21428 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21432 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21433 /** @ignore Called by the grid automatically. Do not call directly. */
21434 init : function(grid){
21440 * Locks the selections.
21443 this.locked = true;
21447 * Unlocks the selections.
21449 unlock : function(){
21450 this.locked = false;
21454 * Returns true if the selections are locked.
21455 * @return {Boolean}
21457 isLocked : function(){
21458 return this.locked;
21462 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21463 * @class Roo.bootstrap.Table.RowSelectionModel
21464 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21465 * It supports multiple selections and keyboard selection/navigation.
21467 * @param {Object} config
21470 Roo.bootstrap.Table.RowSelectionModel = function(config){
21471 Roo.apply(this, config);
21472 this.selections = new Roo.util.MixedCollection(false, function(o){
21477 this.lastActive = false;
21481 * @event selectionchange
21482 * Fires when the selection changes
21483 * @param {SelectionModel} this
21485 "selectionchange" : true,
21487 * @event afterselectionchange
21488 * Fires after the selection changes (eg. by key press or clicking)
21489 * @param {SelectionModel} this
21491 "afterselectionchange" : true,
21493 * @event beforerowselect
21494 * Fires when a row is selected being selected, return false to cancel.
21495 * @param {SelectionModel} this
21496 * @param {Number} rowIndex The selected index
21497 * @param {Boolean} keepExisting False if other selections will be cleared
21499 "beforerowselect" : true,
21502 * Fires when a row is selected.
21503 * @param {SelectionModel} this
21504 * @param {Number} rowIndex The selected index
21505 * @param {Roo.data.Record} r The record
21507 "rowselect" : true,
21509 * @event rowdeselect
21510 * Fires when a row is deselected.
21511 * @param {SelectionModel} this
21512 * @param {Number} rowIndex The selected index
21514 "rowdeselect" : true
21516 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21517 this.locked = false;
21520 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21522 * @cfg {Boolean} singleSelect
21523 * True to allow selection of only one row at a time (defaults to false)
21525 singleSelect : false,
21528 initEvents : function(){
21530 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21531 this.grid.on("mousedown", this.handleMouseDown, this);
21532 }else{ // allow click to work like normal
21533 this.grid.on("rowclick", this.handleDragableRowClick, this);
21536 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21537 "up" : function(e){
21539 this.selectPrevious(e.shiftKey);
21540 }else if(this.last !== false && this.lastActive !== false){
21541 var last = this.last;
21542 this.selectRange(this.last, this.lastActive-1);
21543 this.grid.getView().focusRow(this.lastActive);
21544 if(last !== false){
21548 this.selectFirstRow();
21550 this.fireEvent("afterselectionchange", this);
21552 "down" : function(e){
21554 this.selectNext(e.shiftKey);
21555 }else if(this.last !== false && this.lastActive !== false){
21556 var last = this.last;
21557 this.selectRange(this.last, this.lastActive+1);
21558 this.grid.getView().focusRow(this.lastActive);
21559 if(last !== false){
21563 this.selectFirstRow();
21565 this.fireEvent("afterselectionchange", this);
21570 var view = this.grid.view;
21571 view.on("refresh", this.onRefresh, this);
21572 view.on("rowupdated", this.onRowUpdated, this);
21573 view.on("rowremoved", this.onRemove, this);
21577 onRefresh : function(){
21578 var ds = this.grid.dataSource, i, v = this.grid.view;
21579 var s = this.selections;
21580 s.each(function(r){
21581 if((i = ds.indexOfId(r.id)) != -1){
21590 onRemove : function(v, index, r){
21591 this.selections.remove(r);
21595 onRowUpdated : function(v, index, r){
21596 if(this.isSelected(r)){
21597 v.onRowSelect(index);
21603 * @param {Array} records The records to select
21604 * @param {Boolean} keepExisting (optional) True to keep existing selections
21606 selectRecords : function(records, keepExisting){
21608 this.clearSelections();
21610 var ds = this.grid.dataSource;
21611 for(var i = 0, len = records.length; i < len; i++){
21612 this.selectRow(ds.indexOf(records[i]), true);
21617 * Gets the number of selected rows.
21620 getCount : function(){
21621 return this.selections.length;
21625 * Selects the first row in the grid.
21627 selectFirstRow : function(){
21632 * Select the last row.
21633 * @param {Boolean} keepExisting (optional) True to keep existing selections
21635 selectLastRow : function(keepExisting){
21636 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21640 * Selects the row immediately following the last selected row.
21641 * @param {Boolean} keepExisting (optional) True to keep existing selections
21643 selectNext : function(keepExisting){
21644 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21645 this.selectRow(this.last+1, keepExisting);
21646 this.grid.getView().focusRow(this.last);
21651 * Selects the row that precedes the last selected row.
21652 * @param {Boolean} keepExisting (optional) True to keep existing selections
21654 selectPrevious : function(keepExisting){
21656 this.selectRow(this.last-1, keepExisting);
21657 this.grid.getView().focusRow(this.last);
21662 * Returns the selected records
21663 * @return {Array} Array of selected records
21665 getSelections : function(){
21666 return [].concat(this.selections.items);
21670 * Returns the first selected record.
21673 getSelected : function(){
21674 return this.selections.itemAt(0);
21679 * Clears all selections.
21681 clearSelections : function(fast){
21682 if(this.locked) return;
21684 var ds = this.grid.dataSource;
21685 var s = this.selections;
21686 s.each(function(r){
21687 this.deselectRow(ds.indexOfId(r.id));
21691 this.selections.clear();
21698 * Selects all rows.
21700 selectAll : function(){
21701 if(this.locked) return;
21702 this.selections.clear();
21703 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21704 this.selectRow(i, true);
21709 * Returns True if there is a selection.
21710 * @return {Boolean}
21712 hasSelection : function(){
21713 return this.selections.length > 0;
21717 * Returns True if the specified row is selected.
21718 * @param {Number/Record} record The record or index of the record to check
21719 * @return {Boolean}
21721 isSelected : function(index){
21722 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21723 return (r && this.selections.key(r.id) ? true : false);
21727 * Returns True if the specified record id is selected.
21728 * @param {String} id The id of record to check
21729 * @return {Boolean}
21731 isIdSelected : function(id){
21732 return (this.selections.key(id) ? true : false);
21736 handleMouseDown : function(e, t){
21737 var view = this.grid.getView(), rowIndex;
21738 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21741 if(e.shiftKey && this.last !== false){
21742 var last = this.last;
21743 this.selectRange(last, rowIndex, e.ctrlKey);
21744 this.last = last; // reset the last
21745 view.focusRow(rowIndex);
21747 var isSelected = this.isSelected(rowIndex);
21748 if(e.button !== 0 && isSelected){
21749 view.focusRow(rowIndex);
21750 }else if(e.ctrlKey && isSelected){
21751 this.deselectRow(rowIndex);
21752 }else if(!isSelected){
21753 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21754 view.focusRow(rowIndex);
21757 this.fireEvent("afterselectionchange", this);
21760 handleDragableRowClick : function(grid, rowIndex, e)
21762 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21763 this.selectRow(rowIndex, false);
21764 grid.view.focusRow(rowIndex);
21765 this.fireEvent("afterselectionchange", this);
21770 * Selects multiple rows.
21771 * @param {Array} rows Array of the indexes of the row to select
21772 * @param {Boolean} keepExisting (optional) True to keep existing selections
21774 selectRows : function(rows, keepExisting){
21776 this.clearSelections();
21778 for(var i = 0, len = rows.length; i < len; i++){
21779 this.selectRow(rows[i], true);
21784 * Selects a range of rows. All rows in between startRow and endRow are also selected.
21785 * @param {Number} startRow The index of the first row in the range
21786 * @param {Number} endRow The index of the last row in the range
21787 * @param {Boolean} keepExisting (optional) True to retain existing selections
21789 selectRange : function(startRow, endRow, keepExisting){
21790 if(this.locked) return;
21792 this.clearSelections();
21794 if(startRow <= endRow){
21795 for(var i = startRow; i <= endRow; i++){
21796 this.selectRow(i, true);
21799 for(var i = startRow; i >= endRow; i--){
21800 this.selectRow(i, true);
21806 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21807 * @param {Number} startRow The index of the first row in the range
21808 * @param {Number} endRow The index of the last row in the range
21810 deselectRange : function(startRow, endRow, preventViewNotify){
21811 if(this.locked) return;
21812 for(var i = startRow; i <= endRow; i++){
21813 this.deselectRow(i, preventViewNotify);
21819 * @param {Number} row The index of the row to select
21820 * @param {Boolean} keepExisting (optional) True to keep existing selections
21822 selectRow : function(index, keepExisting, preventViewNotify){
21823 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21824 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21825 if(!keepExisting || this.singleSelect){
21826 this.clearSelections();
21828 var r = this.grid.dataSource.getAt(index);
21829 this.selections.add(r);
21830 this.last = this.lastActive = index;
21831 if(!preventViewNotify){
21832 this.grid.getView().onRowSelect(index);
21834 this.fireEvent("rowselect", this, index, r);
21835 this.fireEvent("selectionchange", this);
21841 * @param {Number} row The index of the row to deselect
21843 deselectRow : function(index, preventViewNotify){
21844 if(this.locked) return;
21845 if(this.last == index){
21848 if(this.lastActive == index){
21849 this.lastActive = false;
21851 var r = this.grid.dataSource.getAt(index);
21852 this.selections.remove(r);
21853 if(!preventViewNotify){
21854 this.grid.getView().onRowDeselect(index);
21856 this.fireEvent("rowdeselect", this, index);
21857 this.fireEvent("selectionchange", this);
21861 restoreLast : function(){
21863 this.last = this._last;
21868 acceptsNav : function(row, col, cm){
21869 return !cm.isHidden(col) && cm.isCellEditable(col, row);
21873 onEditorKey : function(field, e){
21874 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21879 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21881 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21883 }else if(k == e.ENTER && !e.ctrlKey){
21887 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21889 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21891 }else if(k == e.ESC){
21895 g.startEditing(newCell[0], newCell[1]);
21900 * Ext JS Library 1.1.1
21901 * Copyright(c) 2006-2007, Ext JS, LLC.
21903 * Originally Released Under LGPL - original licence link has changed is not relivant.
21906 * <script type="text/javascript">
21910 * @class Roo.bootstrap.PagingToolbar
21911 * @extends Roo.bootstrap.NavSimplebar
21912 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21914 * Create a new PagingToolbar
21915 * @param {Object} config The config object
21916 * @param {Roo.data.Store} store
21918 Roo.bootstrap.PagingToolbar = function(config)
21920 // old args format still supported... - xtype is prefered..
21921 // created from xtype...
21923 this.ds = config.dataSource;
21925 if (config.store && !this.ds) {
21926 this.store= Roo.factory(config.store, Roo.data);
21927 this.ds = this.store;
21928 this.ds.xmodule = this.xmodule || false;
21931 this.toolbarItems = [];
21932 if (config.items) {
21933 this.toolbarItems = config.items;
21936 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21941 this.bind(this.ds);
21944 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21948 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21950 * @cfg {Roo.data.Store} dataSource
21951 * The underlying data store providing the paged data
21954 * @cfg {String/HTMLElement/Element} container
21955 * container The id or element that will contain the toolbar
21958 * @cfg {Boolean} displayInfo
21959 * True to display the displayMsg (defaults to false)
21962 * @cfg {Number} pageSize
21963 * The number of records to display per page (defaults to 20)
21967 * @cfg {String} displayMsg
21968 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21970 displayMsg : 'Displaying {0} - {1} of {2}',
21972 * @cfg {String} emptyMsg
21973 * The message to display when no records are found (defaults to "No data to display")
21975 emptyMsg : 'No data to display',
21977 * Customizable piece of the default paging text (defaults to "Page")
21980 beforePageText : "Page",
21982 * Customizable piece of the default paging text (defaults to "of %0")
21985 afterPageText : "of {0}",
21987 * Customizable piece of the default paging text (defaults to "First Page")
21990 firstText : "First Page",
21992 * Customizable piece of the default paging text (defaults to "Previous Page")
21995 prevText : "Previous Page",
21997 * Customizable piece of the default paging text (defaults to "Next Page")
22000 nextText : "Next Page",
22002 * Customizable piece of the default paging text (defaults to "Last Page")
22005 lastText : "Last Page",
22007 * Customizable piece of the default paging text (defaults to "Refresh")
22010 refreshText : "Refresh",
22014 onRender : function(ct, position)
22016 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22017 this.navgroup.parentId = this.id;
22018 this.navgroup.onRender(this.el, null);
22019 // add the buttons to the navgroup
22021 if(this.displayInfo){
22022 Roo.log(this.el.select('ul.navbar-nav',true).first());
22023 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22024 this.displayEl = this.el.select('.x-paging-info', true).first();
22025 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22026 // this.displayEl = navel.el.select('span',true).first();
22032 Roo.each(_this.buttons, function(e){ // this might need to use render????
22033 Roo.factory(e).onRender(_this.el, null);
22037 Roo.each(_this.toolbarItems, function(e) {
22038 _this.navgroup.addItem(e);
22042 this.first = this.navgroup.addItem({
22043 tooltip: this.firstText,
22045 icon : 'fa fa-backward',
22047 preventDefault: true,
22048 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22051 this.prev = this.navgroup.addItem({
22052 tooltip: this.prevText,
22054 icon : 'fa fa-step-backward',
22056 preventDefault: true,
22057 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
22059 //this.addSeparator();
22062 var field = this.navgroup.addItem( {
22064 cls : 'x-paging-position',
22066 html : this.beforePageText +
22067 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22068 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
22071 this.field = field.el.select('input', true).first();
22072 this.field.on("keydown", this.onPagingKeydown, this);
22073 this.field.on("focus", function(){this.dom.select();});
22076 this.afterTextEl = field.el.select('.x-paging-after',true).first();
22077 //this.field.setHeight(18);
22078 //this.addSeparator();
22079 this.next = this.navgroup.addItem({
22080 tooltip: this.nextText,
22082 html : ' <i class="fa fa-step-forward">',
22084 preventDefault: true,
22085 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
22087 this.last = this.navgroup.addItem({
22088 tooltip: this.lastText,
22089 icon : 'fa fa-forward',
22092 preventDefault: true,
22093 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
22095 //this.addSeparator();
22096 this.loading = this.navgroup.addItem({
22097 tooltip: this.refreshText,
22098 icon: 'fa fa-refresh',
22099 preventDefault: true,
22100 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22106 updateInfo : function(){
22107 if(this.displayEl){
22108 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22109 var msg = count == 0 ?
22113 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
22115 this.displayEl.update(msg);
22120 onLoad : function(ds, r, o){
22121 this.cursor = o.params ? o.params.start : 0;
22122 var d = this.getPageData(),
22126 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22127 this.field.dom.value = ap;
22128 this.first.setDisabled(ap == 1);
22129 this.prev.setDisabled(ap == 1);
22130 this.next.setDisabled(ap == ps);
22131 this.last.setDisabled(ap == ps);
22132 this.loading.enable();
22137 getPageData : function(){
22138 var total = this.ds.getTotalCount();
22141 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22142 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22147 onLoadError : function(){
22148 this.loading.enable();
22152 onPagingKeydown : function(e){
22153 var k = e.getKey();
22154 var d = this.getPageData();
22156 var v = this.field.dom.value, pageNum;
22157 if(!v || isNaN(pageNum = parseInt(v, 10))){
22158 this.field.dom.value = d.activePage;
22161 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22162 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22165 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))
22167 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22168 this.field.dom.value = pageNum;
22169 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22172 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22174 var v = this.field.dom.value, pageNum;
22175 var increment = (e.shiftKey) ? 10 : 1;
22176 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22178 if(!v || isNaN(pageNum = parseInt(v, 10))) {
22179 this.field.dom.value = d.activePage;
22182 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22184 this.field.dom.value = parseInt(v, 10) + increment;
22185 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22186 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22193 beforeLoad : function(){
22195 this.loading.disable();
22200 onClick : function(which){
22209 ds.load({params:{start: 0, limit: this.pageSize}});
22212 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22215 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22218 var total = ds.getTotalCount();
22219 var extra = total % this.pageSize;
22220 var lastStart = extra ? (total - extra) : total-this.pageSize;
22221 ds.load({params:{start: lastStart, limit: this.pageSize}});
22224 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22230 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22231 * @param {Roo.data.Store} store The data store to unbind
22233 unbind : function(ds){
22234 ds.un("beforeload", this.beforeLoad, this);
22235 ds.un("load", this.onLoad, this);
22236 ds.un("loadexception", this.onLoadError, this);
22237 ds.un("remove", this.updateInfo, this);
22238 ds.un("add", this.updateInfo, this);
22239 this.ds = undefined;
22243 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22244 * @param {Roo.data.Store} store The data store to bind
22246 bind : function(ds){
22247 ds.on("beforeload", this.beforeLoad, this);
22248 ds.on("load", this.onLoad, this);
22249 ds.on("loadexception", this.onLoadError, this);
22250 ds.on("remove", this.updateInfo, this);
22251 ds.on("add", this.updateInfo, this);
22262 * @class Roo.bootstrap.MessageBar
22263 * @extends Roo.bootstrap.Component
22264 * Bootstrap MessageBar class
22265 * @cfg {String} html contents of the MessageBar
22266 * @cfg {String} weight (info | success | warning | danger) default info
22267 * @cfg {String} beforeClass insert the bar before the given class
22268 * @cfg {Boolean} closable (true | false) default false
22269 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22272 * Create a new Element
22273 * @param {Object} config The config object
22276 Roo.bootstrap.MessageBar = function(config){
22277 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22280 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22286 beforeClass: 'bootstrap-sticky-wrap',
22288 getAutoCreate : function(){
22292 cls: 'alert alert-dismissable alert-' + this.weight,
22297 html: this.html || ''
22303 cfg.cls += ' alert-messages-fixed';
22317 onRender : function(ct, position)
22319 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22322 var cfg = Roo.apply({}, this.getAutoCreate());
22326 cfg.cls += ' ' + this.cls;
22329 cfg.style = this.style;
22331 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22333 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22336 this.el.select('>button.close').on('click', this.hide, this);
22342 if (!this.rendered) {
22348 this.fireEvent('show', this);
22354 if (!this.rendered) {
22360 this.fireEvent('hide', this);
22363 update : function()
22365 // var e = this.el.dom.firstChild;
22367 // if(this.closable){
22368 // e = e.nextSibling;
22371 // e.data = this.html || '';
22373 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22389 * @class Roo.bootstrap.Graph
22390 * @extends Roo.bootstrap.Component
22391 * Bootstrap Graph class
22395 @cfg {String} graphtype bar | vbar | pie
22396 @cfg {number} g_x coodinator | centre x (pie)
22397 @cfg {number} g_y coodinator | centre y (pie)
22398 @cfg {number} g_r radius (pie)
22399 @cfg {number} g_height height of the chart (respected by all elements in the set)
22400 @cfg {number} g_width width of the chart (respected by all elements in the set)
22401 @cfg {Object} title The title of the chart
22404 -opts (object) options for the chart
22406 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22407 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22409 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.
22410 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22412 o stretch (boolean)
22414 -opts (object) options for the pie
22417 o startAngle (number)
22418 o endAngle (number)
22422 * Create a new Input
22423 * @param {Object} config The config object
22426 Roo.bootstrap.Graph = function(config){
22427 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22433 * The img click event for the img.
22434 * @param {Roo.EventObject} e
22440 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22451 //g_colors: this.colors,
22458 getAutoCreate : function(){
22469 onRender : function(ct,position){
22470 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22471 this.raphael = Raphael(this.el.dom);
22473 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22474 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22475 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22476 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22478 r.text(160, 10, "Single Series Chart").attr(txtattr);
22479 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22480 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22481 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22483 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22484 r.barchart(330, 10, 300, 220, data1);
22485 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22486 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22489 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22490 // r.barchart(30, 30, 560, 250, xdata, {
22491 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22492 // axis : "0 0 1 1",
22493 // axisxlabels : xdata
22494 // //yvalues : cols,
22497 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22499 // this.load(null,xdata,{
22500 // axis : "0 0 1 1",
22501 // axisxlabels : xdata
22506 load : function(graphtype,xdata,opts){
22507 this.raphael.clear();
22509 graphtype = this.graphtype;
22514 var r = this.raphael,
22515 fin = function () {
22516 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22518 fout = function () {
22519 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22521 pfin = function() {
22522 this.sector.stop();
22523 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22526 this.label[0].stop();
22527 this.label[0].attr({ r: 7.5 });
22528 this.label[1].attr({ "font-weight": 800 });
22531 pfout = function() {
22532 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22535 this.label[0].animate({ r: 5 }, 500, "bounce");
22536 this.label[1].attr({ "font-weight": 400 });
22542 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22545 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22548 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
22549 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22551 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22558 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22563 setTitle: function(o)
22568 initEvents: function() {
22571 this.el.on('click', this.onClick, this);
22575 onClick : function(e)
22577 Roo.log('img onclick');
22578 this.fireEvent('click', this, e);
22590 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22593 * @class Roo.bootstrap.dash.NumberBox
22594 * @extends Roo.bootstrap.Component
22595 * Bootstrap NumberBox class
22596 * @cfg {String} headline Box headline
22597 * @cfg {String} content Box content
22598 * @cfg {String} icon Box icon
22599 * @cfg {String} footer Footer text
22600 * @cfg {String} fhref Footer href
22603 * Create a new NumberBox
22604 * @param {Object} config The config object
22608 Roo.bootstrap.dash.NumberBox = function(config){
22609 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22613 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
22622 getAutoCreate : function(){
22626 cls : 'small-box ',
22634 cls : 'roo-headline',
22635 html : this.headline
22639 cls : 'roo-content',
22640 html : this.content
22654 cls : 'ion ' + this.icon
22663 cls : 'small-box-footer',
22664 href : this.fhref || '#',
22668 cfg.cn.push(footer);
22675 onRender : function(ct,position){
22676 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22683 setHeadline: function (value)
22685 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22688 setFooter: function (value, href)
22690 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22693 this.el.select('a.small-box-footer',true).first().attr('href', href);
22698 setContent: function (value)
22700 this.el.select('.roo-content',true).first().dom.innerHTML = value;
22703 initEvents: function()
22717 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22720 * @class Roo.bootstrap.dash.TabBox
22721 * @extends Roo.bootstrap.Component
22722 * Bootstrap TabBox class
22723 * @cfg {String} title Title of the TabBox
22724 * @cfg {String} icon Icon of the TabBox
22725 * @cfg {Boolean} showtabs (true|false) show the tabs default true
22726 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22729 * Create a new TabBox
22730 * @param {Object} config The config object
22734 Roo.bootstrap.dash.TabBox = function(config){
22735 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22740 * When a pane is added
22741 * @param {Roo.bootstrap.dash.TabPane} pane
22745 * @event activatepane
22746 * When a pane is activated
22747 * @param {Roo.bootstrap.dash.TabPane} pane
22749 "activatepane" : true
22757 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
22762 tabScrollable : false,
22764 getChildContainer : function()
22766 return this.el.select('.tab-content', true).first();
22769 getAutoCreate : function(){
22773 cls: 'pull-left header',
22781 cls: 'fa ' + this.icon
22787 cls: 'nav nav-tabs pull-right',
22793 if(this.tabScrollable){
22800 cls: 'nav nav-tabs pull-right',
22811 cls: 'nav-tabs-custom',
22816 cls: 'tab-content no-padding',
22824 initEvents : function()
22826 //Roo.log('add add pane handler');
22827 this.on('addpane', this.onAddPane, this);
22830 * Updates the box title
22831 * @param {String} html to set the title to.
22833 setTitle : function(value)
22835 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22837 onAddPane : function(pane)
22839 this.panes.push(pane);
22840 //Roo.log('addpane');
22842 // tabs are rendere left to right..
22843 if(!this.showtabs){
22847 var ctr = this.el.select('.nav-tabs', true).first();
22850 var existing = ctr.select('.nav-tab',true);
22851 var qty = existing.getCount();;
22854 var tab = ctr.createChild({
22856 cls : 'nav-tab' + (qty ? '' : ' active'),
22864 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22867 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22869 pane.el.addClass('active');
22874 onTabClick : function(ev,un,ob,pane)
22876 //Roo.log('tab - prev default');
22877 ev.preventDefault();
22880 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22881 pane.tab.addClass('active');
22882 //Roo.log(pane.title);
22883 this.getChildContainer().select('.tab-pane',true).removeClass('active');
22884 // technically we should have a deactivate event.. but maybe add later.
22885 // and it should not de-activate the selected tab...
22886 this.fireEvent('activatepane', pane);
22887 pane.el.addClass('active');
22888 pane.fireEvent('activate');
22893 getActivePane : function()
22896 Roo.each(this.panes, function(p) {
22897 if(p.el.hasClass('active')){
22918 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22920 * @class Roo.bootstrap.TabPane
22921 * @extends Roo.bootstrap.Component
22922 * Bootstrap TabPane class
22923 * @cfg {Boolean} active (false | true) Default false
22924 * @cfg {String} title title of panel
22928 * Create a new TabPane
22929 * @param {Object} config The config object
22932 Roo.bootstrap.dash.TabPane = function(config){
22933 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22939 * When a pane is activated
22940 * @param {Roo.bootstrap.dash.TabPane} pane
22947 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
22952 // the tabBox that this is attached to.
22955 getAutoCreate : function()
22963 cfg.cls += ' active';
22968 initEvents : function()
22970 //Roo.log('trigger add pane handler');
22971 this.parent().fireEvent('addpane', this)
22975 * Updates the tab title
22976 * @param {String} html to set the title to.
22978 setTitle: function(str)
22984 this.tab.select('a', true).first().dom.innerHTML = str;
23001 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23004 * @class Roo.bootstrap.menu.Menu
23005 * @extends Roo.bootstrap.Component
23006 * Bootstrap Menu class - container for Menu
23007 * @cfg {String} html Text of the menu
23008 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23009 * @cfg {String} icon Font awesome icon
23010 * @cfg {String} pos Menu align to (top | bottom) default bottom
23014 * Create a new Menu
23015 * @param {Object} config The config object
23019 Roo.bootstrap.menu.Menu = function(config){
23020 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23024 * @event beforeshow
23025 * Fires before this menu is displayed
23026 * @param {Roo.bootstrap.menu.Menu} this
23030 * @event beforehide
23031 * Fires before this menu is hidden
23032 * @param {Roo.bootstrap.menu.Menu} this
23037 * Fires after this menu is displayed
23038 * @param {Roo.bootstrap.menu.Menu} this
23043 * Fires after this menu is hidden
23044 * @param {Roo.bootstrap.menu.Menu} this
23049 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23050 * @param {Roo.bootstrap.menu.Menu} this
23051 * @param {Roo.EventObject} e
23058 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
23062 weight : 'default',
23067 getChildContainer : function() {
23068 if(this.isSubMenu){
23072 return this.el.select('ul.dropdown-menu', true).first();
23075 getAutoCreate : function()
23080 cls : 'roo-menu-text',
23088 cls : 'fa ' + this.icon
23099 cls : 'dropdown-button btn btn-' + this.weight,
23104 cls : 'dropdown-toggle btn btn-' + this.weight,
23114 cls : 'dropdown-menu'
23120 if(this.pos == 'top'){
23121 cfg.cls += ' dropup';
23124 if(this.isSubMenu){
23127 cls : 'dropdown-menu'
23134 onRender : function(ct, position)
23136 this.isSubMenu = ct.hasClass('dropdown-submenu');
23138 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23141 initEvents : function()
23143 if(this.isSubMenu){
23147 this.hidden = true;
23149 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23150 this.triggerEl.on('click', this.onTriggerPress, this);
23152 this.buttonEl = this.el.select('button.dropdown-button', true).first();
23153 this.buttonEl.on('click', this.onClick, this);
23159 if(this.isSubMenu){
23163 return this.el.select('ul.dropdown-menu', true).first();
23166 onClick : function(e)
23168 this.fireEvent("click", this, e);
23171 onTriggerPress : function(e)
23173 if (this.isVisible()) {
23180 isVisible : function(){
23181 return !this.hidden;
23186 this.fireEvent("beforeshow", this);
23188 this.hidden = false;
23189 this.el.addClass('open');
23191 Roo.get(document).on("mouseup", this.onMouseUp, this);
23193 this.fireEvent("show", this);
23200 this.fireEvent("beforehide", this);
23202 this.hidden = true;
23203 this.el.removeClass('open');
23205 Roo.get(document).un("mouseup", this.onMouseUp);
23207 this.fireEvent("hide", this);
23210 onMouseUp : function()
23224 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23227 * @class Roo.bootstrap.menu.Item
23228 * @extends Roo.bootstrap.Component
23229 * Bootstrap MenuItem class
23230 * @cfg {Boolean} submenu (true | false) default false
23231 * @cfg {String} html text of the item
23232 * @cfg {String} href the link
23233 * @cfg {Boolean} disable (true | false) default false
23234 * @cfg {Boolean} preventDefault (true | false) default true
23235 * @cfg {String} icon Font awesome icon
23236 * @cfg {String} pos Submenu align to (left | right) default right
23240 * Create a new Item
23241 * @param {Object} config The config object
23245 Roo.bootstrap.menu.Item = function(config){
23246 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23250 * Fires when the mouse is hovering over this menu
23251 * @param {Roo.bootstrap.menu.Item} this
23252 * @param {Roo.EventObject} e
23257 * Fires when the mouse exits this menu
23258 * @param {Roo.bootstrap.menu.Item} this
23259 * @param {Roo.EventObject} e
23265 * The raw click event for the entire grid.
23266 * @param {Roo.EventObject} e
23272 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23277 preventDefault: true,
23282 getAutoCreate : function()
23287 cls : 'roo-menu-item-text',
23295 cls : 'fa ' + this.icon
23304 href : this.href || '#',
23311 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23315 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23317 if(this.pos == 'left'){
23318 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23325 initEvents : function()
23327 this.el.on('mouseover', this.onMouseOver, this);
23328 this.el.on('mouseout', this.onMouseOut, this);
23330 this.el.select('a', true).first().on('click', this.onClick, this);
23334 onClick : function(e)
23336 if(this.preventDefault){
23337 e.preventDefault();
23340 this.fireEvent("click", this, e);
23343 onMouseOver : function(e)
23345 if(this.submenu && this.pos == 'left'){
23346 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23349 this.fireEvent("mouseover", this, e);
23352 onMouseOut : function(e)
23354 this.fireEvent("mouseout", this, e);
23366 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23369 * @class Roo.bootstrap.menu.Separator
23370 * @extends Roo.bootstrap.Component
23371 * Bootstrap Separator class
23374 * Create a new Separator
23375 * @param {Object} config The config object
23379 Roo.bootstrap.menu.Separator = function(config){
23380 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23383 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23385 getAutoCreate : function(){
23406 * @class Roo.bootstrap.Tooltip
23407 * Bootstrap Tooltip class
23408 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23409 * to determine which dom element triggers the tooltip.
23411 * It needs to add support for additional attributes like tooltip-position
23414 * Create a new Toolti
23415 * @param {Object} config The config object
23418 Roo.bootstrap.Tooltip = function(config){
23419 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23422 Roo.apply(Roo.bootstrap.Tooltip, {
23424 * @function init initialize tooltip monitoring.
23428 currentTip : false,
23429 currentRegion : false,
23435 Roo.get(document).on('mouseover', this.enter ,this);
23436 Roo.get(document).on('mouseout', this.leave, this);
23439 this.currentTip = new Roo.bootstrap.Tooltip();
23442 enter : function(ev)
23444 var dom = ev.getTarget();
23446 //Roo.log(['enter',dom]);
23447 var el = Roo.fly(dom);
23448 if (this.currentEl) {
23450 //Roo.log(this.currentEl);
23451 //Roo.log(this.currentEl.contains(dom));
23452 if (this.currentEl == el) {
23455 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23461 if (this.currentTip.el) {
23462 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23467 // you can not look for children, as if el is the body.. then everythign is the child..
23468 if (!el.attr('tooltip')) { //
23469 if (!el.select("[tooltip]").elements.length) {
23472 // is the mouse over this child...?
23473 bindEl = el.select("[tooltip]").first();
23474 var xy = ev.getXY();
23475 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23476 //Roo.log("not in region.");
23479 //Roo.log("child element over..");
23482 this.currentEl = bindEl;
23483 this.currentTip.bind(bindEl);
23484 this.currentRegion = Roo.lib.Region.getRegion(dom);
23485 this.currentTip.enter();
23488 leave : function(ev)
23490 var dom = ev.getTarget();
23491 //Roo.log(['leave',dom]);
23492 if (!this.currentEl) {
23497 if (dom != this.currentEl.dom) {
23500 var xy = ev.getXY();
23501 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23504 // only activate leave if mouse cursor is outside... bounding box..
23509 if (this.currentTip) {
23510 this.currentTip.leave();
23512 //Roo.log('clear currentEl');
23513 this.currentEl = false;
23518 'left' : ['r-l', [-2,0], 'right'],
23519 'right' : ['l-r', [2,0], 'left'],
23520 'bottom' : ['t-b', [0,2], 'top'],
23521 'top' : [ 'b-t', [0,-2], 'bottom']
23527 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
23532 delay : null, // can be { show : 300 , hide: 500}
23536 hoverState : null, //???
23538 placement : 'bottom',
23540 getAutoCreate : function(){
23547 cls : 'tooltip-arrow'
23550 cls : 'tooltip-inner'
23557 bind : function(el)
23563 enter : function () {
23565 if (this.timeout != null) {
23566 clearTimeout(this.timeout);
23569 this.hoverState = 'in';
23570 //Roo.log("enter - show");
23571 if (!this.delay || !this.delay.show) {
23576 this.timeout = setTimeout(function () {
23577 if (_t.hoverState == 'in') {
23580 }, this.delay.show);
23584 clearTimeout(this.timeout);
23586 this.hoverState = 'out';
23587 if (!this.delay || !this.delay.hide) {
23593 this.timeout = setTimeout(function () {
23594 //Roo.log("leave - timeout");
23596 if (_t.hoverState == 'out') {
23598 Roo.bootstrap.Tooltip.currentEl = false;
23606 this.render(document.body);
23609 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23611 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23613 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23615 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23617 var placement = typeof this.placement == 'function' ?
23618 this.placement.call(this, this.el, on_el) :
23621 var autoToken = /\s?auto?\s?/i;
23622 var autoPlace = autoToken.test(placement);
23624 placement = placement.replace(autoToken, '') || 'top';
23628 //this.el.setXY([0,0]);
23630 //this.el.dom.style.display='block';
23632 //this.el.appendTo(on_el);
23634 var p = this.getPosition();
23635 var box = this.el.getBox();
23641 var align = Roo.bootstrap.Tooltip.alignment[placement];
23643 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23645 if(placement == 'top' || placement == 'bottom'){
23647 placement = 'right';
23650 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23651 placement = 'left';
23655 align = Roo.bootstrap.Tooltip.alignment[placement];
23657 this.el.alignTo(this.bindEl, align[0],align[1]);
23658 //var arrow = this.el.select('.arrow',true).first();
23659 //arrow.set(align[2],
23661 this.el.addClass(placement);
23663 this.el.addClass('in fade');
23665 this.hoverState = null;
23667 if (this.el.hasClass('fade')) {
23678 //this.el.setXY([0,0]);
23679 this.el.removeClass('in');
23695 * @class Roo.bootstrap.LocationPicker
23696 * @extends Roo.bootstrap.Component
23697 * Bootstrap LocationPicker class
23698 * @cfg {Number} latitude Position when init default 0
23699 * @cfg {Number} longitude Position when init default 0
23700 * @cfg {Number} zoom default 15
23701 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23702 * @cfg {Boolean} mapTypeControl default false
23703 * @cfg {Boolean} disableDoubleClickZoom default false
23704 * @cfg {Boolean} scrollwheel default true
23705 * @cfg {Boolean} streetViewControl default false
23706 * @cfg {Number} radius default 0
23707 * @cfg {String} locationName
23708 * @cfg {Boolean} draggable default true
23709 * @cfg {Boolean} enableAutocomplete default false
23710 * @cfg {Boolean} enableReverseGeocode default true
23711 * @cfg {String} markerTitle
23714 * Create a new LocationPicker
23715 * @param {Object} config The config object
23719 Roo.bootstrap.LocationPicker = function(config){
23721 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23726 * Fires when the picker initialized.
23727 * @param {Roo.bootstrap.LocationPicker} this
23728 * @param {Google Location} location
23732 * @event positionchanged
23733 * Fires when the picker position changed.
23734 * @param {Roo.bootstrap.LocationPicker} this
23735 * @param {Google Location} location
23737 positionchanged : true,
23740 * Fires when the map resize.
23741 * @param {Roo.bootstrap.LocationPicker} this
23746 * Fires when the map show.
23747 * @param {Roo.bootstrap.LocationPicker} this
23752 * Fires when the map hide.
23753 * @param {Roo.bootstrap.LocationPicker} this
23758 * Fires when click the map.
23759 * @param {Roo.bootstrap.LocationPicker} this
23760 * @param {Map event} e
23764 * @event mapRightClick
23765 * Fires when right click the map.
23766 * @param {Roo.bootstrap.LocationPicker} this
23767 * @param {Map event} e
23769 mapRightClick : true,
23771 * @event markerClick
23772 * Fires when click the marker.
23773 * @param {Roo.bootstrap.LocationPicker} this
23774 * @param {Map event} e
23776 markerClick : true,
23778 * @event markerRightClick
23779 * Fires when right click the marker.
23780 * @param {Roo.bootstrap.LocationPicker} this
23781 * @param {Map event} e
23783 markerRightClick : true,
23785 * @event OverlayViewDraw
23786 * Fires when OverlayView Draw
23787 * @param {Roo.bootstrap.LocationPicker} this
23789 OverlayViewDraw : true,
23791 * @event OverlayViewOnAdd
23792 * Fires when OverlayView Draw
23793 * @param {Roo.bootstrap.LocationPicker} this
23795 OverlayViewOnAdd : true,
23797 * @event OverlayViewOnRemove
23798 * Fires when OverlayView Draw
23799 * @param {Roo.bootstrap.LocationPicker} this
23801 OverlayViewOnRemove : true,
23803 * @event OverlayViewShow
23804 * Fires when OverlayView Draw
23805 * @param {Roo.bootstrap.LocationPicker} this
23806 * @param {Pixel} cpx
23808 OverlayViewShow : true,
23810 * @event OverlayViewHide
23811 * Fires when OverlayView Draw
23812 * @param {Roo.bootstrap.LocationPicker} this
23814 OverlayViewHide : true,
23816 * @event loadexception
23817 * Fires when load google lib failed.
23818 * @param {Roo.bootstrap.LocationPicker} this
23820 loadexception : true
23825 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
23827 gMapContext: false,
23833 mapTypeControl: false,
23834 disableDoubleClickZoom: false,
23836 streetViewControl: false,
23840 enableAutocomplete: false,
23841 enableReverseGeocode: true,
23844 getAutoCreate: function()
23849 cls: 'roo-location-picker'
23855 initEvents: function(ct, position)
23857 if(!this.el.getWidth() || this.isApplied()){
23861 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23866 initial: function()
23868 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
23869 this.fireEvent('loadexception', this);
23873 if(!this.mapTypeId){
23874 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23877 this.gMapContext = this.GMapContext();
23879 this.initOverlayView();
23881 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23885 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23886 _this.setPosition(_this.gMapContext.marker.position);
23889 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23890 _this.fireEvent('mapClick', this, event);
23894 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23895 _this.fireEvent('mapRightClick', this, event);
23899 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23900 _this.fireEvent('markerClick', this, event);
23904 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23905 _this.fireEvent('markerRightClick', this, event);
23909 this.setPosition(this.gMapContext.location);
23911 this.fireEvent('initial', this, this.gMapContext.location);
23914 initOverlayView: function()
23918 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23922 _this.fireEvent('OverlayViewDraw', _this);
23927 _this.fireEvent('OverlayViewOnAdd', _this);
23930 onRemove: function()
23932 _this.fireEvent('OverlayViewOnRemove', _this);
23935 show: function(cpx)
23937 _this.fireEvent('OverlayViewShow', _this, cpx);
23942 _this.fireEvent('OverlayViewHide', _this);
23948 fromLatLngToContainerPixel: function(event)
23950 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23953 isApplied: function()
23955 return this.getGmapContext() == false ? false : true;
23958 getGmapContext: function()
23960 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
23963 GMapContext: function()
23965 var position = new google.maps.LatLng(this.latitude, this.longitude);
23967 var _map = new google.maps.Map(this.el.dom, {
23970 mapTypeId: this.mapTypeId,
23971 mapTypeControl: this.mapTypeControl,
23972 disableDoubleClickZoom: this.disableDoubleClickZoom,
23973 scrollwheel: this.scrollwheel,
23974 streetViewControl: this.streetViewControl,
23975 locationName: this.locationName,
23976 draggable: this.draggable,
23977 enableAutocomplete: this.enableAutocomplete,
23978 enableReverseGeocode: this.enableReverseGeocode
23981 var _marker = new google.maps.Marker({
23982 position: position,
23984 title: this.markerTitle,
23985 draggable: this.draggable
23992 location: position,
23993 radius: this.radius,
23994 locationName: this.locationName,
23995 addressComponents: {
23996 formatted_address: null,
23997 addressLine1: null,
23998 addressLine2: null,
24000 streetNumber: null,
24004 stateOrProvince: null
24007 domContainer: this.el.dom,
24008 geodecoder: new google.maps.Geocoder()
24012 drawCircle: function(center, radius, options)
24014 if (this.gMapContext.circle != null) {
24015 this.gMapContext.circle.setMap(null);
24019 options = Roo.apply({}, options, {
24020 strokeColor: "#0000FF",
24021 strokeOpacity: .35,
24023 fillColor: "#0000FF",
24027 options.map = this.gMapContext.map;
24028 options.radius = radius;
24029 options.center = center;
24030 this.gMapContext.circle = new google.maps.Circle(options);
24031 return this.gMapContext.circle;
24037 setPosition: function(location)
24039 this.gMapContext.location = location;
24040 this.gMapContext.marker.setPosition(location);
24041 this.gMapContext.map.panTo(location);
24042 this.drawCircle(location, this.gMapContext.radius, {});
24046 if (this.gMapContext.settings.enableReverseGeocode) {
24047 this.gMapContext.geodecoder.geocode({
24048 latLng: this.gMapContext.location
24049 }, function(results, status) {
24051 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24052 _this.gMapContext.locationName = results[0].formatted_address;
24053 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24055 _this.fireEvent('positionchanged', this, location);
24062 this.fireEvent('positionchanged', this, location);
24067 google.maps.event.trigger(this.gMapContext.map, "resize");
24069 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24071 this.fireEvent('resize', this);
24074 setPositionByLatLng: function(latitude, longitude)
24076 this.setPosition(new google.maps.LatLng(latitude, longitude));
24079 getCurrentPosition: function()
24082 latitude: this.gMapContext.location.lat(),
24083 longitude: this.gMapContext.location.lng()
24087 getAddressName: function()
24089 return this.gMapContext.locationName;
24092 getAddressComponents: function()
24094 return this.gMapContext.addressComponents;
24097 address_component_from_google_geocode: function(address_components)
24101 for (var i = 0; i < address_components.length; i++) {
24102 var component = address_components[i];
24103 if (component.types.indexOf("postal_code") >= 0) {
24104 result.postalCode = component.short_name;
24105 } else if (component.types.indexOf("street_number") >= 0) {
24106 result.streetNumber = component.short_name;
24107 } else if (component.types.indexOf("route") >= 0) {
24108 result.streetName = component.short_name;
24109 } else if (component.types.indexOf("neighborhood") >= 0) {
24110 result.city = component.short_name;
24111 } else if (component.types.indexOf("locality") >= 0) {
24112 result.city = component.short_name;
24113 } else if (component.types.indexOf("sublocality") >= 0) {
24114 result.district = component.short_name;
24115 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24116 result.stateOrProvince = component.short_name;
24117 } else if (component.types.indexOf("country") >= 0) {
24118 result.country = component.short_name;
24122 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24123 result.addressLine2 = "";
24127 setZoomLevel: function(zoom)
24129 this.gMapContext.map.setZoom(zoom);
24142 this.fireEvent('show', this);
24153 this.fireEvent('hide', this);
24158 Roo.apply(Roo.bootstrap.LocationPicker, {
24160 OverlayView : function(map, options)
24162 options = options || {};
24176 * @class Roo.bootstrap.Alert
24177 * @extends Roo.bootstrap.Component
24178 * Bootstrap Alert class
24179 * @cfg {String} title The title of alert
24180 * @cfg {String} html The content of alert
24181 * @cfg {String} weight ( success | info | warning | danger )
24182 * @cfg {String} faicon font-awesomeicon
24185 * Create a new alert
24186 * @param {Object} config The config object
24190 Roo.bootstrap.Alert = function(config){
24191 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24195 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
24202 getAutoCreate : function()
24211 cls : 'roo-alert-icon'
24216 cls : 'roo-alert-title',
24221 cls : 'roo-alert-text',
24228 cfg.cn[0].cls += ' fa ' + this.faicon;
24232 cfg.cls += ' alert-' + this.weight;
24238 initEvents: function()
24240 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24243 setTitle : function(str)
24245 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24248 setText : function(str)
24250 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24253 setWeight : function(weight)
24256 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24259 this.weight = weight;
24261 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24264 setIcon : function(icon)
24267 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24270 this.faicon = icon;
24272 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24293 * @class Roo.bootstrap.UploadCropbox
24294 * @extends Roo.bootstrap.Component
24295 * Bootstrap UploadCropbox class
24296 * @cfg {String} emptyText show when image has been loaded
24297 * @cfg {String} rotateNotify show when image too small to rotate
24298 * @cfg {Number} errorTimeout default 3000
24299 * @cfg {Number} minWidth default 300
24300 * @cfg {Number} minHeight default 300
24301 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24302 * @cfg {Boolean} isDocument (true|false) default false
24303 * @cfg {String} url action url
24304 * @cfg {String} paramName default 'imageUpload'
24305 * @cfg {String} method default POST
24306 * @cfg {Boolean} loadMask (true|false) default true
24307 * @cfg {Boolean} loadingText default 'Loading...'
24310 * Create a new UploadCropbox
24311 * @param {Object} config The config object
24314 Roo.bootstrap.UploadCropbox = function(config){
24315 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24319 * @event beforeselectfile
24320 * Fire before select file
24321 * @param {Roo.bootstrap.UploadCropbox} this
24323 "beforeselectfile" : true,
24326 * Fire after initEvent
24327 * @param {Roo.bootstrap.UploadCropbox} this
24332 * Fire after initEvent
24333 * @param {Roo.bootstrap.UploadCropbox} this
24334 * @param {String} data
24339 * Fire when preparing the file data
24340 * @param {Roo.bootstrap.UploadCropbox} this
24341 * @param {Object} file
24346 * Fire when get exception
24347 * @param {Roo.bootstrap.UploadCropbox} this
24348 * @param {XMLHttpRequest} xhr
24350 "exception" : true,
24352 * @event beforeloadcanvas
24353 * Fire before load the canvas
24354 * @param {Roo.bootstrap.UploadCropbox} this
24355 * @param {String} src
24357 "beforeloadcanvas" : true,
24360 * Fire when trash image
24361 * @param {Roo.bootstrap.UploadCropbox} this
24366 * Fire when download the image
24367 * @param {Roo.bootstrap.UploadCropbox} this
24371 * @event footerbuttonclick
24372 * Fire when footerbuttonclick
24373 * @param {Roo.bootstrap.UploadCropbox} this
24374 * @param {String} type
24376 "footerbuttonclick" : true,
24380 * @param {Roo.bootstrap.UploadCropbox} this
24385 * Fire when rotate the image
24386 * @param {Roo.bootstrap.UploadCropbox} this
24387 * @param {String} pos
24392 * Fire when inspect the file
24393 * @param {Roo.bootstrap.UploadCropbox} this
24394 * @param {Object} file
24399 * Fire when xhr upload the file
24400 * @param {Roo.bootstrap.UploadCropbox} this
24401 * @param {Object} data
24406 * Fire when arrange the file data
24407 * @param {Roo.bootstrap.UploadCropbox} this
24408 * @param {Object} formData
24413 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24416 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24418 emptyText : 'Click to upload image',
24419 rotateNotify : 'Image is too small to rotate',
24420 errorTimeout : 3000,
24434 cropType : 'image/jpeg',
24436 canvasLoaded : false,
24437 isDocument : false,
24439 paramName : 'imageUpload',
24441 loadingText : 'Loading...',
24444 getAutoCreate : function()
24448 cls : 'roo-upload-cropbox',
24452 cls : 'roo-upload-cropbox-selector',
24457 cls : 'roo-upload-cropbox-body',
24458 style : 'cursor:pointer',
24462 cls : 'roo-upload-cropbox-preview'
24466 cls : 'roo-upload-cropbox-thumb'
24470 cls : 'roo-upload-cropbox-empty-notify',
24471 html : this.emptyText
24475 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24476 html : this.rotateNotify
24482 cls : 'roo-upload-cropbox-footer',
24485 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24495 onRender : function(ct, position)
24497 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24499 if (this.buttons.length) {
24501 Roo.each(this.buttons, function(bb) {
24503 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24505 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24511 this.maskEl = this.el;
24515 initEvents : function()
24517 this.urlAPI = (window.createObjectURL && window) ||
24518 (window.URL && URL.revokeObjectURL && URL) ||
24519 (window.webkitURL && webkitURL);
24521 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24522 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24524 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24525 this.selectorEl.hide();
24527 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24528 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24530 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24531 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24532 this.thumbEl.hide();
24534 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24535 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24537 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24538 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24539 this.errorEl.hide();
24541 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24542 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24543 this.footerEl.hide();
24545 this.setThumbBoxSize();
24551 this.fireEvent('initial', this);
24558 window.addEventListener("resize", function() { _this.resize(); } );
24560 this.bodyEl.on('click', this.beforeSelectFile, this);
24563 this.bodyEl.on('touchstart', this.onTouchStart, this);
24564 this.bodyEl.on('touchmove', this.onTouchMove, this);
24565 this.bodyEl.on('touchend', this.onTouchEnd, this);
24569 this.bodyEl.on('mousedown', this.onMouseDown, this);
24570 this.bodyEl.on('mousemove', this.onMouseMove, this);
24571 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24572 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24573 Roo.get(document).on('mouseup', this.onMouseUp, this);
24576 this.selectorEl.on('change', this.onFileSelected, this);
24582 this.baseScale = 1;
24584 this.baseRotate = 1;
24585 this.dragable = false;
24586 this.pinching = false;
24589 this.cropData = false;
24590 this.notifyEl.dom.innerHTML = this.emptyText;
24592 this.selectorEl.dom.value = '';
24596 resize : function()
24598 if(this.fireEvent('resize', this) != false){
24599 this.setThumbBoxPosition();
24600 this.setCanvasPosition();
24604 onFooterButtonClick : function(e, el, o, type)
24607 case 'rotate-left' :
24608 this.onRotateLeft(e);
24610 case 'rotate-right' :
24611 this.onRotateRight(e);
24614 this.beforeSelectFile(e);
24629 this.fireEvent('footerbuttonclick', this, type);
24632 beforeSelectFile : function(e)
24634 e.preventDefault();
24636 if(this.fireEvent('beforeselectfile', this) != false){
24637 this.selectorEl.dom.click();
24641 onFileSelected : function(e)
24643 e.preventDefault();
24645 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24649 var file = this.selectorEl.dom.files[0];
24651 if(this.fireEvent('inspect', this, file) != false){
24652 this.prepare(file);
24657 trash : function(e)
24659 this.fireEvent('trash', this);
24662 download : function(e)
24664 this.fireEvent('download', this);
24667 loadCanvas : function(src)
24669 if(this.fireEvent('beforeloadcanvas', this, src) != false){
24673 this.imageEl = document.createElement('img');
24677 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24679 this.imageEl.src = src;
24683 onLoadCanvas : function()
24685 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24686 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24688 this.bodyEl.un('click', this.beforeSelectFile, this);
24690 this.notifyEl.hide();
24691 this.thumbEl.show();
24692 this.footerEl.show();
24694 this.baseRotateLevel();
24696 if(this.isDocument){
24697 this.setThumbBoxSize();
24700 this.setThumbBoxPosition();
24702 this.baseScaleLevel();
24708 this.canvasLoaded = true;
24711 this.maskEl.unmask();
24716 setCanvasPosition : function()
24718 if(!this.canvasEl){
24722 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24723 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24725 this.previewEl.setLeft(pw);
24726 this.previewEl.setTop(ph);
24730 onMouseDown : function(e)
24734 this.dragable = true;
24735 this.pinching = false;
24737 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24738 this.dragable = false;
24742 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24743 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24747 onMouseMove : function(e)
24751 if(!this.canvasLoaded){
24755 if (!this.dragable){
24759 var minX = Math.ceil(this.thumbEl.getLeft(true));
24760 var minY = Math.ceil(this.thumbEl.getTop(true));
24762 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24763 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24765 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24766 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24768 x = x - this.mouseX;
24769 y = y - this.mouseY;
24771 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24772 var bgY = Math.ceil(y + this.previewEl.getTop(true));
24774 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24775 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24777 this.previewEl.setLeft(bgX);
24778 this.previewEl.setTop(bgY);
24780 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24781 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24784 onMouseUp : function(e)
24788 this.dragable = false;
24791 onMouseWheel : function(e)
24795 this.startScale = this.scale;
24797 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24799 if(!this.zoomable()){
24800 this.scale = this.startScale;
24809 zoomable : function()
24811 var minScale = this.thumbEl.getWidth() / this.minWidth;
24813 if(this.minWidth < this.minHeight){
24814 minScale = this.thumbEl.getHeight() / this.minHeight;
24817 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
24818 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
24822 (this.rotate == 0 || this.rotate == 180) &&
24824 width > this.imageEl.OriginWidth ||
24825 height > this.imageEl.OriginHeight ||
24826 (width < this.minWidth && height < this.minHeight)
24834 (this.rotate == 90 || this.rotate == 270) &&
24836 width > this.imageEl.OriginWidth ||
24837 height > this.imageEl.OriginHeight ||
24838 (width < this.minHeight && height < this.minWidth)
24845 !this.isDocument &&
24846 (this.rotate == 0 || this.rotate == 180) &&
24848 width < this.minWidth ||
24849 width > this.imageEl.OriginWidth ||
24850 height < this.minHeight ||
24851 height > this.imageEl.OriginHeight
24858 !this.isDocument &&
24859 (this.rotate == 90 || this.rotate == 270) &&
24861 width < this.minHeight ||
24862 width > this.imageEl.OriginWidth ||
24863 height < this.minWidth ||
24864 height > this.imageEl.OriginHeight
24874 onRotateLeft : function(e)
24876 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24878 var minScale = this.thumbEl.getWidth() / this.minWidth;
24880 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24881 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24883 this.startScale = this.scale;
24885 while (this.getScaleLevel() < minScale){
24887 this.scale = this.scale + 1;
24889 if(!this.zoomable()){
24894 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24895 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24900 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24907 this.scale = this.startScale;
24909 this.onRotateFail();
24914 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24916 if(this.isDocument){
24917 this.setThumbBoxSize();
24918 this.setThumbBoxPosition();
24919 this.setCanvasPosition();
24924 this.fireEvent('rotate', this, 'left');
24928 onRotateRight : function(e)
24930 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24932 var minScale = this.thumbEl.getWidth() / this.minWidth;
24934 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24935 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24937 this.startScale = this.scale;
24939 while (this.getScaleLevel() < minScale){
24941 this.scale = this.scale + 1;
24943 if(!this.zoomable()){
24948 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24949 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24954 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24961 this.scale = this.startScale;
24963 this.onRotateFail();
24968 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24970 if(this.isDocument){
24971 this.setThumbBoxSize();
24972 this.setThumbBoxPosition();
24973 this.setCanvasPosition();
24978 this.fireEvent('rotate', this, 'right');
24981 onRotateFail : function()
24983 this.errorEl.show(true);
24987 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
24992 this.previewEl.dom.innerHTML = '';
24994 var canvasEl = document.createElement("canvas");
24996 var contextEl = canvasEl.getContext("2d");
24998 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24999 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25000 var center = this.imageEl.OriginWidth / 2;
25002 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25003 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25004 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25005 center = this.imageEl.OriginHeight / 2;
25008 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25010 contextEl.translate(center, center);
25011 contextEl.rotate(this.rotate * Math.PI / 180);
25013 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25015 this.canvasEl = document.createElement("canvas");
25017 this.contextEl = this.canvasEl.getContext("2d");
25019 switch (this.rotate) {
25022 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25023 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25025 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25030 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25031 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25033 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25034 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);
25038 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25043 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25044 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25046 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25047 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);
25051 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);
25056 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25057 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25059 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25060 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25064 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);
25071 this.previewEl.appendChild(this.canvasEl);
25073 this.setCanvasPosition();
25078 if(!this.canvasLoaded){
25082 var imageCanvas = document.createElement("canvas");
25084 var imageContext = imageCanvas.getContext("2d");
25086 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25087 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25089 var center = imageCanvas.width / 2;
25091 imageContext.translate(center, center);
25093 imageContext.rotate(this.rotate * Math.PI / 180);
25095 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25097 var canvas = document.createElement("canvas");
25099 var context = canvas.getContext("2d");
25101 canvas.width = this.minWidth;
25102 canvas.height = this.minHeight;
25104 switch (this.rotate) {
25107 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25108 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25110 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25111 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25113 var targetWidth = this.minWidth - 2 * x;
25114 var targetHeight = this.minHeight - 2 * y;
25118 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25119 scale = targetWidth / width;
25122 if(x > 0 && y == 0){
25123 scale = targetHeight / height;
25126 if(x > 0 && y > 0){
25127 scale = targetWidth / width;
25129 if(width < height){
25130 scale = targetHeight / height;
25134 context.scale(scale, scale);
25136 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25137 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25139 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25140 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25142 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25147 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25148 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25150 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25151 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25153 var targetWidth = this.minWidth - 2 * x;
25154 var targetHeight = this.minHeight - 2 * y;
25158 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25159 scale = targetWidth / width;
25162 if(x > 0 && y == 0){
25163 scale = targetHeight / height;
25166 if(x > 0 && y > 0){
25167 scale = targetWidth / width;
25169 if(width < height){
25170 scale = targetHeight / height;
25174 context.scale(scale, scale);
25176 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25177 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25179 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25180 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25182 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25184 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25189 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25190 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25192 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25193 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25195 var targetWidth = this.minWidth - 2 * x;
25196 var targetHeight = this.minHeight - 2 * y;
25200 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25201 scale = targetWidth / width;
25204 if(x > 0 && y == 0){
25205 scale = targetHeight / height;
25208 if(x > 0 && y > 0){
25209 scale = targetWidth / width;
25211 if(width < height){
25212 scale = targetHeight / height;
25216 context.scale(scale, scale);
25218 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25219 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25221 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25222 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25224 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25225 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25227 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25232 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25233 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25235 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25236 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25238 var targetWidth = this.minWidth - 2 * x;
25239 var targetHeight = this.minHeight - 2 * y;
25243 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25244 scale = targetWidth / width;
25247 if(x > 0 && y == 0){
25248 scale = targetHeight / height;
25251 if(x > 0 && y > 0){
25252 scale = targetWidth / width;
25254 if(width < height){
25255 scale = targetHeight / height;
25259 context.scale(scale, scale);
25261 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25262 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25264 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25265 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25267 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25269 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25276 this.cropData = canvas.toDataURL(this.cropType);
25278 if(this.fireEvent('crop', this, this.cropData) !== false){
25279 this.process(this.file, this.cropData);
25286 setThumbBoxSize : function()
25290 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25291 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25292 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25294 this.minWidth = width;
25295 this.minHeight = height;
25297 if(this.rotate == 90 || this.rotate == 270){
25298 this.minWidth = height;
25299 this.minHeight = width;
25304 width = Math.ceil(this.minWidth * height / this.minHeight);
25306 if(this.minWidth > this.minHeight){
25308 height = Math.ceil(this.minHeight * width / this.minWidth);
25311 this.thumbEl.setStyle({
25312 width : width + 'px',
25313 height : height + 'px'
25320 setThumbBoxPosition : function()
25322 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25323 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25325 this.thumbEl.setLeft(x);
25326 this.thumbEl.setTop(y);
25330 baseRotateLevel : function()
25332 this.baseRotate = 1;
25335 typeof(this.exif) != 'undefined' &&
25336 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25337 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25339 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25342 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25346 baseScaleLevel : function()
25350 if(this.isDocument){
25352 if(this.baseRotate == 6 || this.baseRotate == 8){
25354 height = this.thumbEl.getHeight();
25355 this.baseScale = height / this.imageEl.OriginWidth;
25357 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25358 width = this.thumbEl.getWidth();
25359 this.baseScale = width / this.imageEl.OriginHeight;
25365 height = this.thumbEl.getHeight();
25366 this.baseScale = height / this.imageEl.OriginHeight;
25368 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25369 width = this.thumbEl.getWidth();
25370 this.baseScale = width / this.imageEl.OriginWidth;
25376 if(this.baseRotate == 6 || this.baseRotate == 8){
25378 width = this.thumbEl.getHeight();
25379 this.baseScale = width / this.imageEl.OriginHeight;
25381 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25382 height = this.thumbEl.getWidth();
25383 this.baseScale = height / this.imageEl.OriginHeight;
25386 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25387 height = this.thumbEl.getWidth();
25388 this.baseScale = height / this.imageEl.OriginHeight;
25390 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25391 width = this.thumbEl.getHeight();
25392 this.baseScale = width / this.imageEl.OriginWidth;
25399 width = this.thumbEl.getWidth();
25400 this.baseScale = width / this.imageEl.OriginWidth;
25402 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25403 height = this.thumbEl.getHeight();
25404 this.baseScale = height / this.imageEl.OriginHeight;
25407 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25409 height = this.thumbEl.getHeight();
25410 this.baseScale = height / this.imageEl.OriginHeight;
25412 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25413 width = this.thumbEl.getWidth();
25414 this.baseScale = width / this.imageEl.OriginWidth;
25422 getScaleLevel : function()
25424 return this.baseScale * Math.pow(1.1, this.scale);
25427 onTouchStart : function(e)
25429 if(!this.canvasLoaded){
25430 this.beforeSelectFile(e);
25434 var touches = e.browserEvent.touches;
25440 if(touches.length == 1){
25441 this.onMouseDown(e);
25445 if(touches.length != 2){
25451 for(var i = 0, finger; finger = touches[i]; i++){
25452 coords.push(finger.pageX, finger.pageY);
25455 var x = Math.pow(coords[0] - coords[2], 2);
25456 var y = Math.pow(coords[1] - coords[3], 2);
25458 this.startDistance = Math.sqrt(x + y);
25460 this.startScale = this.scale;
25462 this.pinching = true;
25463 this.dragable = false;
25467 onTouchMove : function(e)
25469 if(!this.pinching && !this.dragable){
25473 var touches = e.browserEvent.touches;
25480 this.onMouseMove(e);
25486 for(var i = 0, finger; finger = touches[i]; i++){
25487 coords.push(finger.pageX, finger.pageY);
25490 var x = Math.pow(coords[0] - coords[2], 2);
25491 var y = Math.pow(coords[1] - coords[3], 2);
25493 this.endDistance = Math.sqrt(x + y);
25495 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25497 if(!this.zoomable()){
25498 this.scale = this.startScale;
25506 onTouchEnd : function(e)
25508 this.pinching = false;
25509 this.dragable = false;
25513 process : function(file, crop)
25516 this.maskEl.mask(this.loadingText);
25519 this.xhr = new XMLHttpRequest();
25521 file.xhr = this.xhr;
25523 this.xhr.open(this.method, this.url, true);
25526 "Accept": "application/json",
25527 "Cache-Control": "no-cache",
25528 "X-Requested-With": "XMLHttpRequest"
25531 for (var headerName in headers) {
25532 var headerValue = headers[headerName];
25534 this.xhr.setRequestHeader(headerName, headerValue);
25540 this.xhr.onload = function()
25542 _this.xhrOnLoad(_this.xhr);
25545 this.xhr.onerror = function()
25547 _this.xhrOnError(_this.xhr);
25550 var formData = new FormData();
25552 formData.append('returnHTML', 'NO');
25555 formData.append('crop', crop);
25558 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25559 formData.append(this.paramName, file, file.name);
25562 if(typeof(file.filename) != 'undefined'){
25563 formData.append('filename', file.filename);
25566 if(typeof(file.mimetype) != 'undefined'){
25567 formData.append('mimetype', file.mimetype);
25570 if(this.fireEvent('arrange', this, formData) != false){
25571 this.xhr.send(formData);
25575 xhrOnLoad : function(xhr)
25578 this.maskEl.unmask();
25581 if (xhr.readyState !== 4) {
25582 this.fireEvent('exception', this, xhr);
25586 var response = Roo.decode(xhr.responseText);
25588 if(!response.success){
25589 this.fireEvent('exception', this, xhr);
25593 var response = Roo.decode(xhr.responseText);
25595 this.fireEvent('upload', this, response);
25599 xhrOnError : function()
25602 this.maskEl.unmask();
25605 Roo.log('xhr on error');
25607 var response = Roo.decode(xhr.responseText);
25613 prepare : function(file)
25616 this.maskEl.mask(this.loadingText);
25622 if(typeof(file) === 'string'){
25623 this.loadCanvas(file);
25627 if(!file || !this.urlAPI){
25632 this.cropType = file.type;
25636 if(this.fireEvent('prepare', this, this.file) != false){
25638 var reader = new FileReader();
25640 reader.onload = function (e) {
25641 if (e.target.error) {
25642 Roo.log(e.target.error);
25646 var buffer = e.target.result,
25647 dataView = new DataView(buffer),
25649 maxOffset = dataView.byteLength - 4,
25653 if (dataView.getUint16(0) === 0xffd8) {
25654 while (offset < maxOffset) {
25655 markerBytes = dataView.getUint16(offset);
25657 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25658 markerLength = dataView.getUint16(offset + 2) + 2;
25659 if (offset + markerLength > dataView.byteLength) {
25660 Roo.log('Invalid meta data: Invalid segment size.');
25664 if(markerBytes == 0xffe1){
25665 _this.parseExifData(
25672 offset += markerLength;
25682 var url = _this.urlAPI.createObjectURL(_this.file);
25684 _this.loadCanvas(url);
25689 reader.readAsArrayBuffer(this.file);
25695 parseExifData : function(dataView, offset, length)
25697 var tiffOffset = offset + 10,
25701 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25702 // No Exif data, might be XMP data instead
25706 // Check for the ASCII code for "Exif" (0x45786966):
25707 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25708 // No Exif data, might be XMP data instead
25711 if (tiffOffset + 8 > dataView.byteLength) {
25712 Roo.log('Invalid Exif data: Invalid segment size.');
25715 // Check for the two null bytes:
25716 if (dataView.getUint16(offset + 8) !== 0x0000) {
25717 Roo.log('Invalid Exif data: Missing byte alignment offset.');
25720 // Check the byte alignment:
25721 switch (dataView.getUint16(tiffOffset)) {
25723 littleEndian = true;
25726 littleEndian = false;
25729 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25732 // Check for the TIFF tag marker (0x002A):
25733 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25734 Roo.log('Invalid Exif data: Missing TIFF marker.');
25737 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25738 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25740 this.parseExifTags(
25743 tiffOffset + dirOffset,
25748 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25753 if (dirOffset + 6 > dataView.byteLength) {
25754 Roo.log('Invalid Exif data: Invalid directory offset.');
25757 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25758 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25759 if (dirEndOffset + 4 > dataView.byteLength) {
25760 Roo.log('Invalid Exif data: Invalid directory size.');
25763 for (i = 0; i < tagsNumber; i += 1) {
25767 dirOffset + 2 + 12 * i, // tag offset
25771 // Return the offset to the next directory:
25772 return dataView.getUint32(dirEndOffset, littleEndian);
25775 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
25777 var tag = dataView.getUint16(offset, littleEndian);
25779 this.exif[tag] = this.getExifValue(
25783 dataView.getUint16(offset + 2, littleEndian), // tag type
25784 dataView.getUint32(offset + 4, littleEndian), // tag length
25789 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25791 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25800 Roo.log('Invalid Exif data: Invalid tag type.');
25804 tagSize = tagType.size * length;
25805 // Determine if the value is contained in the dataOffset bytes,
25806 // or if the value at the dataOffset is a pointer to the actual data:
25807 dataOffset = tagSize > 4 ?
25808 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25809 if (dataOffset + tagSize > dataView.byteLength) {
25810 Roo.log('Invalid Exif data: Invalid data offset.');
25813 if (length === 1) {
25814 return tagType.getValue(dataView, dataOffset, littleEndian);
25817 for (i = 0; i < length; i += 1) {
25818 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
25821 if (tagType.ascii) {
25823 // Concatenate the chars:
25824 for (i = 0; i < values.length; i += 1) {
25826 // Ignore the terminating NULL byte(s):
25827 if (c === '\u0000') {
25839 Roo.apply(Roo.bootstrap.UploadCropbox, {
25841 'Orientation': 0x0112
25845 1: 0, //'top-left',
25847 3: 180, //'bottom-right',
25848 // 4: 'bottom-left',
25850 6: 90, //'right-top',
25851 // 7: 'right-bottom',
25852 8: 270 //'left-bottom'
25856 // byte, 8-bit unsigned int:
25858 getValue: function (dataView, dataOffset) {
25859 return dataView.getUint8(dataOffset);
25863 // ascii, 8-bit byte:
25865 getValue: function (dataView, dataOffset) {
25866 return String.fromCharCode(dataView.getUint8(dataOffset));
25871 // short, 16 bit int:
25873 getValue: function (dataView, dataOffset, littleEndian) {
25874 return dataView.getUint16(dataOffset, littleEndian);
25878 // long, 32 bit int:
25880 getValue: function (dataView, dataOffset, littleEndian) {
25881 return dataView.getUint32(dataOffset, littleEndian);
25885 // rational = two long values, first is numerator, second is denominator:
25887 getValue: function (dataView, dataOffset, littleEndian) {
25888 return dataView.getUint32(dataOffset, littleEndian) /
25889 dataView.getUint32(dataOffset + 4, littleEndian);
25893 // slong, 32 bit signed int:
25895 getValue: function (dataView, dataOffset, littleEndian) {
25896 return dataView.getInt32(dataOffset, littleEndian);
25900 // srational, two slongs, first is numerator, second is denominator:
25902 getValue: function (dataView, dataOffset, littleEndian) {
25903 return dataView.getInt32(dataOffset, littleEndian) /
25904 dataView.getInt32(dataOffset + 4, littleEndian);
25914 cls : 'btn-group roo-upload-cropbox-rotate-left',
25915 action : 'rotate-left',
25919 cls : 'btn btn-default',
25920 html : '<i class="fa fa-undo"></i>'
25926 cls : 'btn-group roo-upload-cropbox-picture',
25927 action : 'picture',
25931 cls : 'btn btn-default',
25932 html : '<i class="fa fa-picture-o"></i>'
25938 cls : 'btn-group roo-upload-cropbox-rotate-right',
25939 action : 'rotate-right',
25943 cls : 'btn btn-default',
25944 html : '<i class="fa fa-repeat"></i>'
25952 cls : 'btn-group roo-upload-cropbox-rotate-left',
25953 action : 'rotate-left',
25957 cls : 'btn btn-default',
25958 html : '<i class="fa fa-undo"></i>'
25964 cls : 'btn-group roo-upload-cropbox-download',
25965 action : 'download',
25969 cls : 'btn btn-default',
25970 html : '<i class="fa fa-download"></i>'
25976 cls : 'btn-group roo-upload-cropbox-crop',
25981 cls : 'btn btn-default',
25982 html : '<i class="fa fa-crop"></i>'
25988 cls : 'btn-group roo-upload-cropbox-trash',
25993 cls : 'btn btn-default',
25994 html : '<i class="fa fa-trash"></i>'
26000 cls : 'btn-group roo-upload-cropbox-rotate-right',
26001 action : 'rotate-right',
26005 cls : 'btn btn-default',
26006 html : '<i class="fa fa-repeat"></i>'
26014 cls : 'btn-group roo-upload-cropbox-rotate-left',
26015 action : 'rotate-left',
26019 cls : 'btn btn-default',
26020 html : '<i class="fa fa-undo"></i>'
26026 cls : 'btn-group roo-upload-cropbox-rotate-right',
26027 action : 'rotate-right',
26031 cls : 'btn btn-default',
26032 html : '<i class="fa fa-repeat"></i>'
26045 * @class Roo.bootstrap.DocumentManager
26046 * @extends Roo.bootstrap.Component
26047 * Bootstrap DocumentManager class
26048 * @cfg {String} paramName default 'imageUpload'
26049 * @cfg {String} method default POST
26050 * @cfg {String} url action url
26051 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26052 * @cfg {Boolean} multiple multiple upload default true
26053 * @cfg {Number} thumbSize default 300
26054 * @cfg {String} fieldLabel
26055 * @cfg {Number} labelWidth default 4
26056 * @cfg {String} labelAlign (left|top) default left
26057 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26060 * Create a new DocumentManager
26061 * @param {Object} config The config object
26064 Roo.bootstrap.DocumentManager = function(config){
26065 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26070 * Fire when initial the DocumentManager
26071 * @param {Roo.bootstrap.DocumentManager} this
26076 * inspect selected file
26077 * @param {Roo.bootstrap.DocumentManager} this
26078 * @param {File} file
26083 * Fire when xhr load exception
26084 * @param {Roo.bootstrap.DocumentManager} this
26085 * @param {XMLHttpRequest} xhr
26087 "exception" : true,
26090 * prepare the form data
26091 * @param {Roo.bootstrap.DocumentManager} this
26092 * @param {Object} formData
26097 * Fire when remove the file
26098 * @param {Roo.bootstrap.DocumentManager} this
26099 * @param {Object} file
26104 * Fire after refresh the file
26105 * @param {Roo.bootstrap.DocumentManager} this
26110 * Fire after click the image
26111 * @param {Roo.bootstrap.DocumentManager} this
26112 * @param {Object} file
26117 * Fire when upload a image and editable set to true
26118 * @param {Roo.bootstrap.DocumentManager} this
26119 * @param {Object} file
26123 * @event beforeselectfile
26124 * Fire before select file
26125 * @param {Roo.bootstrap.DocumentManager} this
26127 "beforeselectfile" : true,
26130 * Fire before process file
26131 * @param {Roo.bootstrap.DocumentManager} this
26132 * @param {Object} file
26139 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
26148 paramName : 'imageUpload',
26151 labelAlign : 'left',
26158 getAutoCreate : function()
26160 var managerWidget = {
26162 cls : 'roo-document-manager',
26166 cls : 'roo-document-manager-selector',
26171 cls : 'roo-document-manager-uploader',
26175 cls : 'roo-document-manager-upload-btn',
26176 html : '<i class="fa fa-plus"></i>'
26187 cls : 'column col-md-12',
26192 if(this.fieldLabel.length){
26197 cls : 'column col-md-12',
26198 html : this.fieldLabel
26202 cls : 'column col-md-12',
26207 if(this.labelAlign == 'left'){
26211 cls : 'column col-md-' + this.labelWidth,
26212 html : this.fieldLabel
26216 cls : 'column col-md-' + (12 - this.labelWidth),
26226 cls : 'row clearfix',
26234 initEvents : function()
26236 this.managerEl = this.el.select('.roo-document-manager', true).first();
26237 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26239 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26240 this.selectorEl.hide();
26243 this.selectorEl.attr('multiple', 'multiple');
26246 this.selectorEl.on('change', this.onFileSelected, this);
26248 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26249 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26251 this.uploader.on('click', this.onUploaderClick, this);
26253 this.renderProgressDialog();
26257 window.addEventListener("resize", function() { _this.refresh(); } );
26259 this.fireEvent('initial', this);
26262 renderProgressDialog : function()
26266 this.progressDialog = new Roo.bootstrap.Modal({
26267 cls : 'roo-document-manager-progress-dialog',
26268 allow_close : false,
26278 btnclick : function() {
26279 _this.uploadCancel();
26285 this.progressDialog.render(Roo.get(document.body));
26287 this.progress = new Roo.bootstrap.Progress({
26288 cls : 'roo-document-manager-progress',
26293 this.progress.render(this.progressDialog.getChildContainer());
26295 this.progressBar = new Roo.bootstrap.ProgressBar({
26296 cls : 'roo-document-manager-progress-bar',
26299 aria_valuemax : 12,
26303 this.progressBar.render(this.progress.getChildContainer());
26306 onUploaderClick : function(e)
26308 e.preventDefault();
26310 if(this.fireEvent('beforeselectfile', this) != false){
26311 this.selectorEl.dom.click();
26316 onFileSelected : function(e)
26318 e.preventDefault();
26320 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26324 Roo.each(this.selectorEl.dom.files, function(file){
26325 if(this.fireEvent('inspect', this, file) != false){
26326 this.files.push(file);
26336 this.selectorEl.dom.value = '';
26338 if(!this.files.length){
26342 if(this.boxes > 0 && this.files.length > this.boxes){
26343 this.files = this.files.slice(0, this.boxes);
26346 this.uploader.show();
26348 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26349 this.uploader.hide();
26358 Roo.each(this.files, function(file){
26360 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26361 var f = this.renderPreview(file);
26366 if(file.type.indexOf('image') != -1){
26367 this.delegates.push(
26369 _this.process(file);
26370 }).createDelegate(this)
26378 _this.process(file);
26379 }).createDelegate(this)
26384 this.files = files;
26386 this.delegates = this.delegates.concat(docs);
26388 if(!this.delegates.length){
26393 this.progressBar.aria_valuemax = this.delegates.length;
26400 arrange : function()
26402 if(!this.delegates.length){
26403 this.progressDialog.hide();
26408 var delegate = this.delegates.shift();
26410 this.progressDialog.show();
26412 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26414 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26419 refresh : function()
26421 this.uploader.show();
26423 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26424 this.uploader.hide();
26427 Roo.isTouch ? this.closable(false) : this.closable(true);
26429 this.fireEvent('refresh', this);
26432 onRemove : function(e, el, o)
26434 e.preventDefault();
26436 this.fireEvent('remove', this, o);
26440 remove : function(o)
26444 Roo.each(this.files, function(file){
26445 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26454 this.files = files;
26461 Roo.each(this.files, function(file){
26466 file.target.remove();
26475 onClick : function(e, el, o)
26477 e.preventDefault();
26479 this.fireEvent('click', this, o);
26483 closable : function(closable)
26485 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26487 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26499 xhrOnLoad : function(xhr)
26501 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26505 if (xhr.readyState !== 4) {
26507 this.fireEvent('exception', this, xhr);
26511 var response = Roo.decode(xhr.responseText);
26513 if(!response.success){
26515 this.fireEvent('exception', this, xhr);
26519 var file = this.renderPreview(response.data);
26521 this.files.push(file);
26527 xhrOnError : function()
26529 Roo.log('xhr on error');
26531 var response = Roo.decode(xhr.responseText);
26538 process : function(file)
26540 if(this.fireEvent('process', this, file) !== false){
26541 if(this.editable && file.type.indexOf('image') != -1){
26542 this.fireEvent('edit', this, file);
26546 this.uploadStart(file, false);
26553 uploadStart : function(file, crop)
26555 this.xhr = new XMLHttpRequest();
26557 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26562 file.xhr = this.xhr;
26564 this.managerEl.createChild({
26566 cls : 'roo-document-manager-loading',
26570 tooltip : file.name,
26571 cls : 'roo-document-manager-thumb',
26572 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26578 this.xhr.open(this.method, this.url, true);
26581 "Accept": "application/json",
26582 "Cache-Control": "no-cache",
26583 "X-Requested-With": "XMLHttpRequest"
26586 for (var headerName in headers) {
26587 var headerValue = headers[headerName];
26589 this.xhr.setRequestHeader(headerName, headerValue);
26595 this.xhr.onload = function()
26597 _this.xhrOnLoad(_this.xhr);
26600 this.xhr.onerror = function()
26602 _this.xhrOnError(_this.xhr);
26605 var formData = new FormData();
26607 formData.append('returnHTML', 'NO');
26610 formData.append('crop', crop);
26613 formData.append(this.paramName, file, file.name);
26615 if(this.fireEvent('prepare', this, formData) != false){
26616 this.xhr.send(formData);
26620 uploadCancel : function()
26627 this.delegates = [];
26629 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26636 renderPreview : function(file)
26638 if(typeof(file.target) != 'undefined' && file.target){
26642 var previewEl = this.managerEl.createChild({
26644 cls : 'roo-document-manager-preview',
26648 tooltip : file.filename,
26649 cls : 'roo-document-manager-thumb',
26650 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26655 html : '<i class="fa fa-times-circle"></i>'
26660 var close = previewEl.select('button.close', true).first();
26662 close.on('click', this.onRemove, this, file);
26664 file.target = previewEl;
26666 var image = previewEl.select('img', true).first();
26670 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26672 image.on('click', this.onClick, this, file);
26678 onPreviewLoad : function(file, image)
26680 if(typeof(file.target) == 'undefined' || !file.target){
26684 var width = image.dom.naturalWidth || image.dom.width;
26685 var height = image.dom.naturalHeight || image.dom.height;
26687 if(width > height){
26688 file.target.addClass('wide');
26692 file.target.addClass('tall');
26697 uploadFromSource : function(file, crop)
26699 this.xhr = new XMLHttpRequest();
26701 this.managerEl.createChild({
26703 cls : 'roo-document-manager-loading',
26707 tooltip : file.name,
26708 cls : 'roo-document-manager-thumb',
26709 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26715 this.xhr.open(this.method, this.url, true);
26718 "Accept": "application/json",
26719 "Cache-Control": "no-cache",
26720 "X-Requested-With": "XMLHttpRequest"
26723 for (var headerName in headers) {
26724 var headerValue = headers[headerName];
26726 this.xhr.setRequestHeader(headerName, headerValue);
26732 this.xhr.onload = function()
26734 _this.xhrOnLoad(_this.xhr);
26737 this.xhr.onerror = function()
26739 _this.xhrOnError(_this.xhr);
26742 var formData = new FormData();
26744 formData.append('returnHTML', 'NO');
26746 formData.append('crop', crop);
26748 if(typeof(file.filename) != 'undefined'){
26749 formData.append('filename', file.filename);
26752 if(typeof(file.mimetype) != 'undefined'){
26753 formData.append('mimetype', file.mimetype);
26756 if(this.fireEvent('prepare', this, formData) != false){
26757 this.xhr.send(formData);
26767 * @class Roo.bootstrap.DocumentViewer
26768 * @extends Roo.bootstrap.Component
26769 * Bootstrap DocumentViewer class
26772 * Create a new DocumentViewer
26773 * @param {Object} config The config object
26776 Roo.bootstrap.DocumentViewer = function(config){
26777 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26782 * Fire after initEvent
26783 * @param {Roo.bootstrap.DocumentViewer} this
26789 * @param {Roo.bootstrap.DocumentViewer} this
26794 * Fire after trash button
26795 * @param {Roo.bootstrap.DocumentViewer} this
26802 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
26804 getAutoCreate : function()
26808 cls : 'roo-document-viewer',
26812 cls : 'roo-document-viewer-body',
26816 cls : 'roo-document-viewer-thumb',
26820 cls : 'roo-document-viewer-image'
26828 cls : 'roo-document-viewer-footer',
26831 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
26839 cls : 'btn btn-default roo-document-viewer-trash',
26840 html : '<i class="fa fa-trash"></i>'
26853 initEvents : function()
26856 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
26857 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26859 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
26860 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26862 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
26863 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26865 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
26866 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26868 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
26869 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26871 this.bodyEl.on('click', this.onClick, this);
26873 this.trashBtn.on('click', this.onTrash, this);
26877 initial : function()
26879 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
26882 this.fireEvent('initial', this);
26886 onClick : function(e)
26888 e.preventDefault();
26890 this.fireEvent('click', this);
26893 onTrash : function(e)
26895 e.preventDefault();
26897 this.fireEvent('trash', this);
26909 * @class Roo.bootstrap.NavProgressBar
26910 * @extends Roo.bootstrap.Component
26911 * Bootstrap NavProgressBar class
26914 * Create a new nav progress bar
26915 * @param {Object} config The config object
26918 Roo.bootstrap.NavProgressBar = function(config){
26919 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
26921 this.bullets = this.bullets || [];
26923 // Roo.bootstrap.NavProgressBar.register(this);
26927 * Fires when the active item changes
26928 * @param {Roo.bootstrap.NavProgressBar} this
26929 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
26930 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
26937 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
26942 getAutoCreate : function()
26944 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
26948 cls : 'roo-navigation-bar-group',
26952 cls : 'roo-navigation-top-bar'
26956 cls : 'roo-navigation-bullets-bar',
26960 cls : 'roo-navigation-bar'
26967 cls : 'roo-navigation-bottom-bar'
26977 initEvents: function()
26982 onRender : function(ct, position)
26984 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
26986 if(this.bullets.length){
26987 Roo.each(this.bullets, function(b){
26996 addItem : function(cfg)
26998 var item = new Roo.bootstrap.NavProgressItem(cfg);
27000 item.parentId = this.id;
27001 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27004 var top = new Roo.bootstrap.Element({
27006 cls : 'roo-navigation-bar-text'
27009 var bottom = new Roo.bootstrap.Element({
27011 cls : 'roo-navigation-bar-text'
27014 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27015 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27017 var topText = new Roo.bootstrap.Element({
27019 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27022 var bottomText = new Roo.bootstrap.Element({
27024 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27027 topText.onRender(top.el, null);
27028 bottomText.onRender(bottom.el, null);
27031 item.bottomEl = bottom;
27034 this.barItems.push(item);
27039 getActive : function()
27041 var active = false;
27043 Roo.each(this.barItems, function(v){
27045 if (!v.isActive()) {
27057 setActiveItem : function(item)
27061 Roo.each(this.barItems, function(v){
27062 if (v.rid == item.rid) {
27066 if (v.isActive()) {
27067 v.setActive(false);
27072 item.setActive(true);
27074 this.fireEvent('changed', this, item, prev);
27077 getBarItem: function(rid)
27081 Roo.each(this.barItems, function(e) {
27082 if (e.rid != rid) {
27093 indexOfItem : function(item)
27097 Roo.each(this.barItems, function(v, i){
27099 if (v.rid != item.rid) {
27110 setActiveNext : function()
27112 var i = this.indexOfItem(this.getActive());
27114 if (i > this.barItems.length) {
27118 this.setActiveItem(this.barItems[i+1]);
27121 setActivePrev : function()
27123 var i = this.indexOfItem(this.getActive());
27129 this.setActiveItem(this.barItems[i-1]);
27132 format : function()
27134 if(!this.barItems.length){
27138 var width = 100 / this.barItems.length;
27140 Roo.each(this.barItems, function(i){
27141 i.el.setStyle('width', width + '%');
27142 i.topEl.el.setStyle('width', width + '%');
27143 i.bottomEl.el.setStyle('width', width + '%');
27152 * Nav Progress Item
27157 * @class Roo.bootstrap.NavProgressItem
27158 * @extends Roo.bootstrap.Component
27159 * Bootstrap NavProgressItem class
27160 * @cfg {String} rid the reference id
27161 * @cfg {Boolean} active (true|false) Is item active default false
27162 * @cfg {Boolean} disabled (true|false) Is item active default false
27163 * @cfg {String} html
27164 * @cfg {String} position (top|bottom) text position default bottom
27165 * @cfg {String} icon show icon instead of number
27168 * Create a new NavProgressItem
27169 * @param {Object} config The config object
27171 Roo.bootstrap.NavProgressItem = function(config){
27172 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27177 * The raw click event for the entire grid.
27178 * @param {Roo.bootstrap.NavProgressItem} this
27179 * @param {Roo.EventObject} e
27186 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
27192 position : 'bottom',
27195 getAutoCreate : function()
27197 var iconCls = 'roo-navigation-bar-item-icon';
27199 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27203 cls: 'roo-navigation-bar-item',
27213 cfg.cls += ' active';
27216 cfg.cls += ' disabled';
27222 disable : function()
27224 this.setDisabled(true);
27227 enable : function()
27229 this.setDisabled(false);
27232 initEvents: function()
27234 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27236 this.iconEl.on('click', this.onClick, this);
27239 onClick : function(e)
27241 e.preventDefault();
27247 if(this.fireEvent('click', this, e) === false){
27251 this.parent().setActiveItem(this);
27254 isActive: function ()
27256 return this.active;
27259 setActive : function(state)
27261 if(this.active == state){
27265 this.active = state;
27268 this.el.addClass('active');
27272 this.el.removeClass('active');
27277 setDisabled : function(state)
27279 if(this.disabled == state){
27283 this.disabled = state;
27286 this.el.addClass('disabled');
27290 this.el.removeClass('disabled');
27293 tooltipEl : function()
27295 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27308 * @class Roo.bootstrap.FieldLabel
27309 * @extends Roo.bootstrap.Component
27310 * Bootstrap FieldLabel class
27311 * @cfg {String} html contents of the element
27312 * @cfg {String} tag tag of the element default label
27313 * @cfg {String} cls class of the element
27314 * @cfg {String} target label target
27315 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27316 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27317 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27318 * @cfg {String} iconTooltip default "This field is required"
27321 * Create a new FieldLabel
27322 * @param {Object} config The config object
27325 Roo.bootstrap.FieldLabel = function(config){
27326 Roo.bootstrap.Element.superclass.constructor.call(this, config);
27331 * Fires after the field has been marked as invalid.
27332 * @param {Roo.form.FieldLabel} this
27333 * @param {String} msg The validation message
27338 * Fires after the field has been validated with no errors.
27339 * @param {Roo.form.FieldLabel} this
27345 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
27352 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27353 validClass : 'text-success fa fa-lg fa-check',
27354 iconTooltip : 'This field is required',
27356 getAutoCreate : function(){
27360 cls : 'roo-bootstrap-field-label ' + this.cls,
27366 tooltip : this.iconTooltip
27378 initEvents: function()
27380 Roo.bootstrap.Element.superclass.initEvents.call(this);
27382 this.iconEl = this.el.select('i', true).first();
27384 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27386 Roo.bootstrap.FieldLabel.register(this);
27390 * Mark this field as valid
27392 markValid : function()
27394 this.iconEl.show();
27396 this.iconEl.removeClass(this.invalidClass);
27398 this.iconEl.addClass(this.validClass);
27400 this.fireEvent('valid', this);
27404 * Mark this field as invalid
27405 * @param {String} msg The validation message
27407 markInvalid : function(msg)
27409 this.iconEl.show();
27411 this.iconEl.removeClass(this.validClass);
27413 this.iconEl.addClass(this.invalidClass);
27415 this.fireEvent('invalid', this, msg);
27421 Roo.apply(Roo.bootstrap.FieldLabel, {
27426 * register a FieldLabel Group
27427 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27429 register : function(label)
27431 if(this.groups.hasOwnProperty(label.target)){
27435 this.groups[label.target] = label;
27439 * fetch a FieldLabel Group based on the target
27440 * @param {string} target
27441 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27443 get: function(target) {
27444 if (typeof(this.groups[target]) == 'undefined') {
27448 return this.groups[target] ;