4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
23 * Do not use directly - it does not do anything..
24 * @param {Object} config The config object
29 Roo.bootstrap.Component = function(config){
30 Roo.bootstrap.Component.superclass.constructor.call(this, config);
34 * @event childrenrendered
35 * Fires when the children have been rendered..
36 * @param {Roo.bootstrap.Component} this
38 "childrenrendered" : true
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
50 allowDomMove : false, // to stop relocations in parent onRender...
60 * Initialize Events for the element
62 initEvents : function() { },
68 can_build_overlaid : true,
70 container_method : false,
77 // returns the parent component..
78 return Roo.ComponentMgr.get(this.parentId)
84 onRender : function(ct, position)
86 // Roo.log("Call onRender: " + this.xtype);
88 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
91 if (this.el.attr('xtype')) {
92 this.el.attr('xtypex', this.el.attr('xtype'));
93 this.el.dom.removeAttribute('xtype');
103 var cfg = Roo.apply({}, this.getAutoCreate());
104 cfg.id = this.id || Roo.id();
106 // fill in the extra attributes
107 if (this.xattr && typeof(this.xattr) =='object') {
108 for (var i in this.xattr) {
109 cfg[i] = this.xattr[i];
114 cfg.dataId = this.dataId;
118 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121 if (this.style) { // fixme needs to support more complex style data.
122 cfg.style = this.style;
126 cfg.name = this.name;
129 this.el = ct.createChild(cfg, position);
132 this.tooltipEl().attr('tooltip', this.tooltip);
135 if(this.tabIndex !== undefined){
136 this.el.dom.setAttribute('tabIndex', this.tabIndex);
143 * Fetch the element to add children to
144 * @return {Roo.Element} defaults to this.el
146 getChildContainer : function()
151 * Fetch the element to display the tooltip on.
152 * @return {Roo.Element} defaults to this.el
154 tooltipEl : function()
159 addxtype : function(tree,cntr)
163 cn = Roo.factory(tree);
165 cn.parentType = this.xtype; //??
166 cn.parentId = this.id;
168 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
169 if (typeof(cn.container_method) == 'string') {
170 cntr = cn.container_method;
174 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
176 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
178 var build_from_html = Roo.XComponent.build_from_html;
180 var is_body = (tree.xtype == 'Body') ;
182 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
184 var self_cntr_el = Roo.get(this[cntr](false));
186 // do not try and build conditional elements
187 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
191 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
192 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
193 return this.addxtypeChild(tree,cntr);
196 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202 Roo.log('skipping render');
208 if (!build_from_html) {
212 // this i think handles overlaying multiple children of the same type
213 // with the sam eelement.. - which might be buggy..
215 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
221 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
225 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
230 addxtypeChild : function (tree, cntr)
232 Roo.debug && Roo.log('addxtypeChild:' + cntr);
234 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
237 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
238 (typeof(tree['flexy:foreach']) != 'undefined');
242 skip_children = false;
243 // render the element if it's not BODY.
244 if (tree.xtype != 'Body') {
246 cn = Roo.factory(tree);
248 cn.parentType = this.xtype; //??
249 cn.parentId = this.id;
251 var build_from_html = Roo.XComponent.build_from_html;
254 // does the container contain child eleemnts with 'xtype' attributes.
255 // that match this xtype..
256 // note - when we render we create these as well..
257 // so we should check to see if body has xtype set.
258 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
260 var self_cntr_el = Roo.get(this[cntr](false));
261 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
263 //Roo.log(Roo.XComponent.build_from_html);
264 //Roo.log("got echild:");
267 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
268 // and are not displayed -this causes this to use up the wrong element when matching.
269 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
272 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
273 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
279 //echild.dom.removeAttribute('xtype');
281 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
282 Roo.debug && Roo.log(self_cntr_el);
283 Roo.debug && Roo.log(echild);
284 Roo.debug && Roo.log(cn);
290 // if object has flexy:if - then it may or may not be rendered.
291 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
292 // skip a flexy if element.
293 Roo.debug && Roo.log('skipping render');
294 Roo.debug && Roo.log(tree);
296 Roo.debug && Roo.log('skipping all children');
297 skip_children = true;
302 // actually if flexy:foreach is found, we really want to create
303 // multiple copies here...
305 //Roo.log(this[cntr]());
306 cn.render(this[cntr](true));
308 // then add the element..
316 if (typeof (tree.menu) != 'undefined') {
317 tree.menu.parentType = cn.xtype;
318 tree.menu.triggerEl = cn.el;
319 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
323 if (!tree.items || !tree.items.length) {
327 var items = tree.items;
330 //Roo.log(items.length);
332 if (!skip_children) {
333 for(var i =0;i < items.length;i++) {
334 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
340 this.fireEvent('childrenrendered', this);
345 * Show a component - removes 'hidden' class
350 this.el.removeClass('hidden');
354 * Hide a component - adds 'hidden' class
358 if (this.el && !this.el.hasClass('hidden')) {
359 this.el.addClass('hidden');
373 * @class Roo.bootstrap.Body
374 * @extends Roo.bootstrap.Component
375 * Bootstrap Body class
379 * @param {Object} config The config object
382 Roo.bootstrap.Body = function(config){
383 Roo.bootstrap.Body.superclass.constructor.call(this, config);
384 this.el = Roo.get(document.body);
385 if (this.cls && this.cls.length) {
386 Roo.get(document.body).addClass(this.cls);
390 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
395 onRender : function(ct, position)
397 /* Roo.log("Roo.bootstrap.Body - onRender");
398 if (this.cls && this.cls.length) {
399 Roo.get(document.body).addClass(this.cls);
419 * @class Roo.bootstrap.ButtonGroup
420 * @extends Roo.bootstrap.Component
421 * Bootstrap ButtonGroup class
422 * @cfg {String} size lg | sm | xs (default empty normal)
423 * @cfg {String} align vertical | justified (default none)
424 * @cfg {String} direction up | down (default down)
425 * @cfg {Boolean} toolbar false | true
426 * @cfg {Boolean} btn true | false
431 * @param {Object} config The config object
434 Roo.bootstrap.ButtonGroup = function(config){
435 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
438 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
446 getAutoCreate : function(){
452 cfg.html = this.html || cfg.html;
463 if (['vertical','justified'].indexOf(this.align)!==-1) {
464 cfg.cls = 'btn-group-' + this.align;
466 if (this.align == 'justified') {
467 console.log(this.items);
471 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
472 cfg.cls += ' btn-group-' + this.size;
475 if (this.direction == 'up') {
476 cfg.cls += ' dropup' ;
492 * @class Roo.bootstrap.Button
493 * @extends Roo.bootstrap.Component
494 * Bootstrap Button class
495 * @cfg {String} html The button content
496 * @cfg {String} weight ( primary | success | info | warning | danger | link ) default
497 * @cfg {String} size ( lg | sm | xs)
498 * @cfg {String} tag ( a | input | submit)
499 * @cfg {String} href empty or href
500 * @cfg {Boolean} disabled default false;
501 * @cfg {Boolean} isClose default false;
502 * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
503 * @cfg {String} badge text for badge
504 * @cfg {String} theme default
505 * @cfg {Boolean} inverse
506 * @cfg {Boolean} toggle
507 * @cfg {String} ontext text for on toggle state
508 * @cfg {String} offtext text for off toggle state
509 * @cfg {Boolean} defaulton
510 * @cfg {Boolean} preventDefault default true
511 * @cfg {Boolean} removeClass remove the standard class..
512 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
515 * Create a new button
516 * @param {Object} config The config object
520 Roo.bootstrap.Button = function(config){
521 Roo.bootstrap.Button.superclass.constructor.call(this, config);
526 * When a butotn is pressed
527 * @param {Roo.bootstrap.Button} this
528 * @param {Roo.EventObject} e
533 * After the button has been toggles
534 * @param {Roo.EventObject} e
535 * @param {boolean} pressed (also available as button.pressed)
541 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
559 preventDefault: true,
568 getAutoCreate : function(){
576 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
577 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
582 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
584 if (this.toggle == true) {
587 cls: 'slider-frame roo-button',
592 'data-off-text':'OFF',
593 cls: 'slider-button',
599 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
600 cfg.cls += ' '+this.weight;
609 cfg["aria-hidden"] = true;
611 cfg.html = "×";
617 if (this.theme==='default') {
618 cfg.cls = 'btn roo-button';
620 //if (this.parentType != 'Navbar') {
621 this.weight = this.weight.length ? this.weight : 'default';
623 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
625 cfg.cls += ' btn-' + this.weight;
627 } else if (this.theme==='glow') {
630 cfg.cls = 'btn-glow roo-button';
632 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
634 cfg.cls += ' ' + this.weight;
640 this.cls += ' inverse';
645 cfg.cls += ' active';
649 cfg.disabled = 'disabled';
653 Roo.log('changing to ul' );
655 this.glyphicon = 'caret';
658 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
660 //gsRoo.log(this.parentType);
661 if (this.parentType === 'Navbar' && !this.parent().bar) {
662 Roo.log('changing to li?');
671 href : this.href || '#'
674 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
675 cfg.cls += ' dropdown';
682 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
684 if (this.glyphicon) {
685 cfg.html = ' ' + cfg.html;
690 cls: 'glyphicon glyphicon-' + this.glyphicon
700 // cfg.cls='btn roo-button';
704 var value = cfg.html;
709 cls: 'glyphicon glyphicon-' + this.glyphicon,
728 cfg.cls += ' dropdown';
729 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
732 if (cfg.tag !== 'a' && this.href !== '') {
733 throw "Tag must be a to set href.";
734 } else if (this.href.length > 0) {
735 cfg.href = this.href;
738 if(this.removeClass){
743 cfg.target = this.target;
748 initEvents: function() {
749 // Roo.log('init events?');
750 // Roo.log(this.el.dom);
753 if (typeof (this.menu) != 'undefined') {
754 this.menu.parentType = this.xtype;
755 this.menu.triggerEl = this.el;
756 this.addxtype(Roo.apply({}, this.menu));
760 if (this.el.hasClass('roo-button')) {
761 this.el.on('click', this.onClick, this);
763 this.el.select('.roo-button').on('click', this.onClick, this);
766 if(this.removeClass){
767 this.el.on('click', this.onClick, this);
770 this.el.enableDisplayMode();
773 onClick : function(e)
780 Roo.log('button on click ');
781 if(this.preventDefault){
784 if (this.pressed === true || this.pressed === false) {
785 this.pressed = !this.pressed;
786 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
787 this.fireEvent('toggle', this, e, this.pressed);
791 this.fireEvent('click', this, e);
795 * Enables this button
799 this.disabled = false;
800 this.el.removeClass('disabled');
804 * Disable this button
808 this.disabled = true;
809 this.el.addClass('disabled');
812 * sets the active state on/off,
813 * @param {Boolean} state (optional) Force a particular state
815 setActive : function(v) {
817 this.el[v ? 'addClass' : 'removeClass']('active');
820 * toggles the current active state
822 toggleActive : function()
824 var active = this.el.hasClass('active');
825 this.setActive(!active);
829 setText : function(str)
831 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
835 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
858 * @class Roo.bootstrap.Column
859 * @extends Roo.bootstrap.Component
860 * Bootstrap Column class
861 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
862 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
863 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
864 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
865 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
866 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
867 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
868 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
871 * @cfg {Boolean} hidden (true|false) hide the element
872 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
873 * @cfg {String} fa (ban|check|...) font awesome icon
874 * @cfg {Number} fasize (1|2|....) font awsome size
876 * @cfg {String} icon (info-sign|check|...) glyphicon name
878 * @cfg {String} html content of column.
881 * Create a new Column
882 * @param {Object} config The config object
885 Roo.bootstrap.Column = function(config){
886 Roo.bootstrap.Column.superclass.constructor.call(this, config);
889 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
907 getAutoCreate : function(){
908 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
916 ['xs','sm','md','lg'].map(function(size){
917 //Roo.log( size + ':' + settings[size]);
919 if (settings[size+'off'] !== false) {
920 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
923 if (settings[size] === false) {
926 Roo.log(settings[size]);
927 if (!settings[size]) { // 0 = hidden
928 cfg.cls += ' hidden-' + size;
931 cfg.cls += ' col-' + size + '-' + settings[size];
936 cfg.cls += ' hidden';
939 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
940 cfg.cls +=' alert alert-' + this.alert;
944 if (this.html.length) {
945 cfg.html = this.html;
949 if (this.fasize > 1) {
950 fasize = ' fa-' + this.fasize + 'x';
952 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
957 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
976 * @class Roo.bootstrap.Container
977 * @extends Roo.bootstrap.Component
978 * Bootstrap Container class
979 * @cfg {Boolean} jumbotron is it a jumbotron element
980 * @cfg {String} html content of element
981 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
982 * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
983 * @cfg {String} header content of header (for panel)
984 * @cfg {String} footer content of footer (for panel)
985 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
986 * @cfg {String} tag (header|aside|section) type of HTML tag.
987 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
988 * @cfg {String} fa (ban|check|...) font awesome icon
989 * @cfg {String} icon (info-sign|check|...) glyphicon name
990 * @cfg {Boolean} hidden (true|false) hide the element
991 * @cfg {Boolean} expandable (true|false) default false
992 * @cfg {Boolean} expanded (true|false) default true
993 * @cfg {String} rheader contet on the right of header
997 * Create a new Container
998 * @param {Object} config The config object
1001 Roo.bootstrap.Container = function(config){
1002 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1008 * After the panel has been expand
1010 * @param {Roo.bootstrap.Container} this
1015 * After the panel has been collapsed
1017 * @param {Roo.bootstrap.Container} this
1023 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1040 getChildContainer : function() {
1046 if (this.panel.length) {
1047 return this.el.select('.panel-body',true).first();
1054 getAutoCreate : function(){
1057 tag : this.tag || 'div',
1061 if (this.jumbotron) {
1062 cfg.cls = 'jumbotron';
1067 // - this is applied by the parent..
1069 // cfg.cls = this.cls + '';
1072 if (this.sticky.length) {
1074 var bd = Roo.get(document.body);
1075 if (!bd.hasClass('bootstrap-sticky')) {
1076 bd.addClass('bootstrap-sticky');
1077 Roo.select('html',true).setStyle('height', '100%');
1080 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1084 if (this.well.length) {
1085 switch (this.well) {
1088 cfg.cls +=' well well-' +this.well;
1097 cfg.cls += ' hidden';
1101 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1102 cfg.cls +=' alert alert-' + this.alert;
1107 if (this.panel.length) {
1108 cfg.cls += ' panel panel-' + this.panel;
1110 if (this.header.length) {
1114 if(this.expandable){
1116 cfg.cls = cfg.cls + ' expandable';
1120 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1128 cls : 'panel-title',
1129 html : (this.expandable ? ' ' : '') + this.header
1133 cls: 'panel-header-right',
1139 cls : 'panel-heading',
1140 style : this.expandable ? 'cursor: pointer' : '',
1148 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1153 if (this.footer.length) {
1155 cls : 'panel-footer',
1164 body.html = this.html || cfg.html;
1165 // prefix with the icons..
1167 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1170 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1175 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1176 cfg.cls = 'container';
1182 initEvents: function()
1184 if(!this.expandable){
1188 var headerEl = this.headerEl();
1194 headerEl.on('click', this.onToggleClick, this);
1198 onToggleClick : function()
1200 var headerEl = this.headerEl();
1216 if(this.fireEvent('expand', this)) {
1218 this.expanded = true;
1220 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1222 this.el.select('.panel-body',true).first().removeClass('hide');
1224 var toggleEl = this.toggleEl();
1230 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1235 collapse : function()
1237 if(this.fireEvent('collapse', this)) {
1239 this.expanded = false;
1241 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1242 this.el.select('.panel-body',true).first().addClass('hide');
1244 var toggleEl = this.toggleEl();
1250 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1254 toggleEl : function()
1256 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1260 return this.el.select('.panel-heading .fa',true).first();
1263 headerEl : function()
1265 if(!this.el || !this.panel.length || !this.header.length){
1269 return this.el.select('.panel-heading',true).first()
1272 titleEl : function()
1274 if(!this.el || !this.panel.length || !this.header.length){
1278 return this.el.select('.panel-title',true).first();
1281 setTitle : function(v)
1283 var titleEl = this.titleEl();
1289 titleEl.dom.innerHTML = v;
1292 getTitle : function()
1295 var titleEl = this.titleEl();
1301 return titleEl.dom.innerHTML;
1304 setRightTitle : function(v)
1306 var t = this.el.select('.panel-header-right',true).first();
1312 t.dom.innerHTML = v;
1326 * @class Roo.bootstrap.Img
1327 * @extends Roo.bootstrap.Component
1328 * Bootstrap Img class
1329 * @cfg {Boolean} imgResponsive false | true
1330 * @cfg {String} border rounded | circle | thumbnail
1331 * @cfg {String} src image source
1332 * @cfg {String} alt image alternative text
1333 * @cfg {String} href a tag href
1334 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1335 * @cfg {String} xsUrl xs image source
1336 * @cfg {String} smUrl sm image source
1337 * @cfg {String} mdUrl md image source
1338 * @cfg {String} lgUrl lg image source
1341 * Create a new Input
1342 * @param {Object} config The config object
1345 Roo.bootstrap.Img = function(config){
1346 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1352 * The img click event for the img.
1353 * @param {Roo.EventObject} e
1359 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1361 imgResponsive: true,
1371 getAutoCreate : function()
1373 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1374 return this.createSingleImg();
1379 cls: 'roo-image-responsive-group',
1384 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1386 if(!_this[size + 'Url']){
1392 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1393 html: _this.html || cfg.html,
1394 src: _this[size + 'Url']
1397 img.cls += ' roo-image-responsive-' + size;
1399 var s = ['xs', 'sm', 'md', 'lg'];
1401 s.splice(s.indexOf(size), 1);
1403 Roo.each(s, function(ss){
1404 img.cls += ' hidden-' + ss;
1407 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1408 cfg.cls += ' img-' + _this.border;
1412 cfg.alt = _this.alt;
1425 a.target = _this.target;
1429 cfg.cn.push((_this.href) ? a : img);
1436 createSingleImg : function()
1440 cls: (this.imgResponsive) ? 'img-responsive' : '',
1444 cfg.html = this.html || cfg.html;
1446 cfg.src = this.src || cfg.src;
1448 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1449 cfg.cls += ' img-' + this.border;
1466 a.target = this.target;
1471 return (this.href) ? a : cfg;
1474 initEvents: function()
1477 this.el.on('click', this.onClick, this);
1482 onClick : function(e)
1484 Roo.log('img onclick');
1485 this.fireEvent('click', this, e);
1499 * @class Roo.bootstrap.Link
1500 * @extends Roo.bootstrap.Component
1501 * Bootstrap Link Class
1502 * @cfg {String} alt image alternative text
1503 * @cfg {String} href a tag href
1504 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1505 * @cfg {String} html the content of the link.
1506 * @cfg {String} anchor name for the anchor link
1508 * @cfg {Boolean} preventDefault (true | false) default false
1512 * Create a new Input
1513 * @param {Object} config The config object
1516 Roo.bootstrap.Link = function(config){
1517 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1523 * The img click event for the img.
1524 * @param {Roo.EventObject} e
1530 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1534 preventDefault: false,
1538 getAutoCreate : function()
1544 // anchor's do not require html/href...
1545 if (this.anchor === false) {
1546 cfg.html = this.html || '';
1547 cfg.href = this.href || '#';
1549 cfg.name = this.anchor;
1550 if (this.html !== false) {
1551 cfg.html = this.html;
1553 if (this.href !== false) {
1554 cfg.href = this.href;
1558 if(this.alt !== false){
1563 if(this.target !== false) {
1564 cfg.target = this.target;
1570 initEvents: function() {
1572 if(!this.href || this.preventDefault){
1573 this.el.on('click', this.onClick, this);
1577 onClick : function(e)
1579 if(this.preventDefault){
1582 //Roo.log('img onclick');
1583 this.fireEvent('click', this, e);
1596 * @class Roo.bootstrap.Header
1597 * @extends Roo.bootstrap.Component
1598 * Bootstrap Header class
1599 * @cfg {String} html content of header
1600 * @cfg {Number} level (1|2|3|4|5|6) default 1
1603 * Create a new Header
1604 * @param {Object} config The config object
1608 Roo.bootstrap.Header = function(config){
1609 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1612 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1620 getAutoCreate : function(){
1625 tag: 'h' + (1 *this.level),
1626 html: this.html || ''
1638 * Ext JS Library 1.1.1
1639 * Copyright(c) 2006-2007, Ext JS, LLC.
1641 * Originally Released Under LGPL - original licence link has changed is not relivant.
1644 * <script type="text/javascript">
1648 * @class Roo.bootstrap.MenuMgr
1649 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1652 Roo.bootstrap.MenuMgr = function(){
1653 var menus, active, groups = {}, attached = false, lastShow = new Date();
1655 // private - called when first menu is created
1658 active = new Roo.util.MixedCollection();
1659 Roo.get(document).addKeyListener(27, function(){
1660 if(active.length > 0){
1668 if(active && active.length > 0){
1669 var c = active.clone();
1679 if(active.length < 1){
1680 Roo.get(document).un("mouseup", onMouseDown);
1688 var last = active.last();
1689 lastShow = new Date();
1692 Roo.get(document).on("mouseup", onMouseDown);
1697 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1698 m.parentMenu.activeChild = m;
1699 }else if(last && last.isVisible()){
1700 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1705 function onBeforeHide(m){
1707 m.activeChild.hide();
1709 if(m.autoHideTimer){
1710 clearTimeout(m.autoHideTimer);
1711 delete m.autoHideTimer;
1716 function onBeforeShow(m){
1717 var pm = m.parentMenu;
1718 if(!pm && !m.allowOtherMenus){
1720 }else if(pm && pm.activeChild && active != m){
1721 pm.activeChild.hide();
1725 // private this should really trigger on mouseup..
1726 function onMouseDown(e){
1727 Roo.log("on Mouse Up");
1728 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1738 function onBeforeCheck(mi, state){
1740 var g = groups[mi.group];
1741 for(var i = 0, l = g.length; i < l; i++){
1743 g[i].setChecked(false);
1752 * Hides all menus that are currently visible
1754 hideAll : function(){
1759 register : function(menu){
1763 menus[menu.id] = menu;
1764 menu.on("beforehide", onBeforeHide);
1765 menu.on("hide", onHide);
1766 menu.on("beforeshow", onBeforeShow);
1767 menu.on("show", onShow);
1769 if(g && menu.events["checkchange"]){
1773 groups[g].push(menu);
1774 menu.on("checkchange", onCheck);
1779 * Returns a {@link Roo.menu.Menu} object
1780 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1781 * be used to generate and return a new Menu instance.
1783 get : function(menu){
1784 if(typeof menu == "string"){ // menu id
1786 }else if(menu.events){ // menu instance
1789 /*else if(typeof menu.length == 'number'){ // array of menu items?
1790 return new Roo.bootstrap.Menu({items:menu});
1791 }else{ // otherwise, must be a config
1792 return new Roo.bootstrap.Menu(menu);
1799 unregister : function(menu){
1800 delete menus[menu.id];
1801 menu.un("beforehide", onBeforeHide);
1802 menu.un("hide", onHide);
1803 menu.un("beforeshow", onBeforeShow);
1804 menu.un("show", onShow);
1806 if(g && menu.events["checkchange"]){
1807 groups[g].remove(menu);
1808 menu.un("checkchange", onCheck);
1813 registerCheckable : function(menuItem){
1814 var g = menuItem.group;
1819 groups[g].push(menuItem);
1820 menuItem.on("beforecheckchange", onBeforeCheck);
1825 unregisterCheckable : function(menuItem){
1826 var g = menuItem.group;
1828 groups[g].remove(menuItem);
1829 menuItem.un("beforecheckchange", onBeforeCheck);
1841 * @class Roo.bootstrap.Menu
1842 * @extends Roo.bootstrap.Component
1843 * Bootstrap Menu class - container for MenuItems
1844 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1848 * @param {Object} config The config object
1852 Roo.bootstrap.Menu = function(config){
1853 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1854 if (this.registerMenu) {
1855 Roo.bootstrap.MenuMgr.register(this);
1860 * Fires before this menu is displayed
1861 * @param {Roo.menu.Menu} this
1866 * Fires before this menu is hidden
1867 * @param {Roo.menu.Menu} this
1872 * Fires after this menu is displayed
1873 * @param {Roo.menu.Menu} this
1878 * Fires after this menu is hidden
1879 * @param {Roo.menu.Menu} this
1884 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1885 * @param {Roo.menu.Menu} this
1886 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1887 * @param {Roo.EventObject} e
1892 * Fires when the mouse is hovering over this menu
1893 * @param {Roo.menu.Menu} this
1894 * @param {Roo.EventObject} e
1895 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1900 * Fires when the mouse exits this menu
1901 * @param {Roo.menu.Menu} this
1902 * @param {Roo.EventObject} e
1903 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1908 * Fires when a menu item contained in this menu is clicked
1909 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1910 * @param {Roo.EventObject} e
1914 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1917 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1921 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1924 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1926 registerMenu : true,
1928 menuItems :false, // stores the menu items..
1934 getChildContainer : function() {
1938 getAutoCreate : function(){
1940 //if (['right'].indexOf(this.align)!==-1) {
1941 // cfg.cn[1].cls += ' pull-right'
1947 cls : 'dropdown-menu' ,
1948 style : 'z-index:1000'
1952 if (this.type === 'submenu') {
1953 cfg.cls = 'submenu active';
1955 if (this.type === 'treeview') {
1956 cfg.cls = 'treeview-menu';
1961 initEvents : function() {
1963 // Roo.log("ADD event");
1964 // Roo.log(this.triggerEl.dom);
1965 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1967 this.triggerEl.addClass('dropdown-toggle');
1973 this.el.on('touchstart' , this.onTouch, this);
1975 this.el.on('click' , this.onClick, this);
1977 this.el.on("mouseover", this.onMouseOver, this);
1978 this.el.on("mouseout", this.onMouseOut, this);
1982 findTargetItem : function(e){
1983 var t = e.getTarget(".dropdown-menu-item", this.el, true);
1987 //Roo.log(t); Roo.log(t.id);
1989 //Roo.log(this.menuitems);
1990 return this.menuitems.get(t.id);
1992 //return this.items.get(t.menuItemId);
1998 onTouch : function(e) {
2003 onClick : function(e){
2004 Roo.log("menu.onClick");
2005 var t = this.findTargetItem(e);
2006 if(!t || t.isContainer){
2011 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2012 if(t == this.activeItem && t.shouldDeactivate(e)){
2013 this.activeItem.deactivate();
2014 delete this.activeItem;
2018 this.setActiveItem(t, true);
2026 Roo.log('pass click event');
2030 this.fireEvent("click", this, t, e);
2034 onMouseOver : function(e){
2035 var t = this.findTargetItem(e);
2038 // if(t.canActivate && !t.disabled){
2039 // this.setActiveItem(t, true);
2043 this.fireEvent("mouseover", this, e, t);
2045 isVisible : function(){
2046 return !this.hidden;
2048 onMouseOut : function(e){
2049 var t = this.findTargetItem(e);
2052 // if(t == this.activeItem && t.shouldDeactivate(e)){
2053 // this.activeItem.deactivate();
2054 // delete this.activeItem;
2057 this.fireEvent("mouseout", this, e, t);
2062 * Displays this menu relative to another element
2063 * @param {String/HTMLElement/Roo.Element} element The element to align to
2064 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2065 * the element (defaults to this.defaultAlign)
2066 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2068 show : function(el, pos, parentMenu){
2069 this.parentMenu = parentMenu;
2073 this.fireEvent("beforeshow", this);
2074 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2077 * Displays this menu at a specific xy position
2078 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2079 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2081 showAt : function(xy, parentMenu, /* private: */_e){
2082 this.parentMenu = parentMenu;
2087 this.fireEvent("beforeshow", this);
2088 //xy = this.el.adjustForConstraints(xy);
2092 this.hideMenuItems();
2093 this.hidden = false;
2094 this.triggerEl.addClass('open');
2096 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2097 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2102 this.fireEvent("show", this);
2108 this.doFocus.defer(50, this);
2112 doFocus : function(){
2114 this.focusEl.focus();
2119 * Hides this menu and optionally all parent menus
2120 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2122 hide : function(deep){
2124 this.hideMenuItems();
2125 if(this.el && this.isVisible()){
2126 this.fireEvent("beforehide", this);
2127 if(this.activeItem){
2128 this.activeItem.deactivate();
2129 this.activeItem = null;
2131 this.triggerEl.removeClass('open');;
2133 this.fireEvent("hide", this);
2135 if(deep === true && this.parentMenu){
2136 this.parentMenu.hide(true);
2140 onTriggerPress : function(e)
2143 Roo.log('trigger press');
2144 //Roo.log(e.getTarget());
2145 // Roo.log(this.triggerEl.dom);
2146 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2150 if (this.isVisible()) {
2155 this.show(this.triggerEl, false, false);
2164 hideMenuItems : function()
2166 //$(backdrop).remove()
2167 Roo.select('.open',true).each(function(aa) {
2169 aa.removeClass('open');
2170 //var parent = getParent($(this))
2171 //var relatedTarget = { relatedTarget: this }
2173 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2174 //if (e.isDefaultPrevented()) return
2175 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2178 addxtypeChild : function (tree, cntr) {
2179 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2181 this.menuitems.add(comp);
2202 * @class Roo.bootstrap.MenuItem
2203 * @extends Roo.bootstrap.Component
2204 * Bootstrap MenuItem class
2205 * @cfg {String} html the menu label
2206 * @cfg {String} href the link
2207 * @cfg {Boolean} preventDefault (true | false) default true
2208 * @cfg {Boolean} isContainer (true | false) default false
2212 * Create a new MenuItem
2213 * @param {Object} config The config object
2217 Roo.bootstrap.MenuItem = function(config){
2218 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2223 * The raw click event for the entire grid.
2224 * @param {Roo.bootstrap.MenuItem} this
2225 * @param {Roo.EventObject} e
2231 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2235 preventDefault: true,
2236 isContainer : false,
2238 getAutoCreate : function(){
2240 if(this.isContainer){
2243 cls: 'dropdown-menu-item'
2249 cls: 'dropdown-menu-item',
2258 if (this.parent().type == 'treeview') {
2259 cfg.cls = 'treeview-menu';
2262 cfg.cn[0].href = this.href || cfg.cn[0].href ;
2263 cfg.cn[0].html = this.html || cfg.cn[0].html ;
2267 initEvents: function() {
2269 //this.el.select('a').on('click', this.onClick, this);
2272 onClick : function(e)
2274 Roo.log('item on click ');
2275 //if(this.preventDefault){
2276 // e.preventDefault();
2278 //this.parent().hideMenuItems();
2280 this.fireEvent('click', this, e);
2299 * @class Roo.bootstrap.MenuSeparator
2300 * @extends Roo.bootstrap.Component
2301 * Bootstrap MenuSeparator class
2304 * Create a new MenuItem
2305 * @param {Object} config The config object
2309 Roo.bootstrap.MenuSeparator = function(config){
2310 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2313 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2315 getAutoCreate : function(){
2334 * @class Roo.bootstrap.Modal
2335 * @extends Roo.bootstrap.Component
2336 * Bootstrap Modal class
2337 * @cfg {String} title Title of dialog
2338 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2339 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2340 * @cfg {Boolean} specificTitle default false
2341 * @cfg {Array} buttons Array of buttons or standard button set..
2342 * @cfg {String} buttonPosition (left|right|center) default right
2343 * @cfg {Boolean} animate default true
2344 * @cfg {Boolean} allow_close default true
2347 * Create a new Modal Dialog
2348 * @param {Object} config The config object
2351 Roo.bootstrap.Modal = function(config){
2352 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2357 * The raw btnclick event for the button
2358 * @param {Roo.EventObject} e
2362 this.buttons = this.buttons || [];
2365 this.tmpl = Roo.factory(this.tmpl);
2370 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2372 title : 'test dialog',
2382 specificTitle: false,
2384 buttonPosition: 'right',
2398 onRender : function(ct, position)
2400 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2403 var cfg = Roo.apply({}, this.getAutoCreate());
2406 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2408 //if (!cfg.name.length) {
2412 cfg.cls += ' ' + this.cls;
2415 cfg.style = this.style;
2417 this.el = Roo.get(document.body).createChild(cfg, position);
2419 //var type = this.el.dom.type;
2424 if(this.tabIndex !== undefined){
2425 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2429 this.bodyEl = this.el.select('.modal-body',true).first();
2430 this.closeEl = this.el.select('.modal-header .close', true).first();
2431 this.footerEl = this.el.select('.modal-footer',true).first();
2432 this.titleEl = this.el.select('.modal-title',true).first();
2436 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2437 this.maskEl.enableDisplayMode("block");
2439 //this.el.addClass("x-dlg-modal");
2441 if (this.buttons.length) {
2442 Roo.each(this.buttons, function(bb) {
2443 var b = Roo.apply({}, bb);
2444 b.xns = b.xns || Roo.bootstrap;
2445 b.xtype = b.xtype || 'Button';
2446 if (typeof(b.listeners) == 'undefined') {
2447 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2450 var btn = Roo.factory(b);
2452 btn.onRender(this.el.select('.modal-footer div').first());
2456 // render the children.
2459 if(typeof(this.items) != 'undefined'){
2460 var items = this.items;
2463 for(var i =0;i < items.length;i++) {
2464 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2468 this.items = nitems;
2470 // where are these used - they used to be body/close/footer
2474 //this.el.addClass([this.fieldClass, this.cls]);
2478 getAutoCreate : function(){
2483 html : this.html || ''
2488 cls : 'modal-title',
2492 if(this.specificTitle){
2498 if (this.allow_close) {
2509 style : 'display: none',
2512 cls: "modal-dialog",
2515 cls : "modal-content",
2518 cls : 'modal-header',
2523 cls : 'modal-footer',
2527 cls: 'btn-' + this.buttonPosition
2544 modal.cls += ' fade';
2550 getChildContainer : function() {
2555 getButtonContainer : function() {
2556 return this.el.select('.modal-footer div',true).first();
2559 initEvents : function()
2561 if (this.allow_close) {
2562 this.closeEl.on('click', this.hide, this);
2567 window.addEventListener("resize", function() { _this.resize(); } );
2573 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2578 if (!this.rendered) {
2582 this.el.setStyle('display', 'block');
2586 (function(){ _this.el.addClass('in'); }).defer(50);
2588 this.el.addClass('in');
2591 // not sure how we can show data in here..
2593 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2596 Roo.get(document.body).addClass("x-body-masked");
2597 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2599 this.el.setStyle('zIndex', '10001');
2601 this.fireEvent('show', this);
2608 Roo.get(document.body).removeClass("x-body-masked");
2609 this.el.removeClass('in');
2613 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2615 this.el.setStyle('display', 'none');
2618 this.fireEvent('hide', this);
2621 addButton : function(str, cb)
2625 var b = Roo.apply({}, { html : str } );
2626 b.xns = b.xns || Roo.bootstrap;
2627 b.xtype = b.xtype || 'Button';
2628 if (typeof(b.listeners) == 'undefined') {
2629 b.listeners = { click : cb.createDelegate(this) };
2632 var btn = Roo.factory(b);
2634 btn.onRender(this.el.select('.modal-footer div').first());
2640 setDefaultButton : function(btn)
2642 //this.el.select('.modal-footer').()
2644 resizeTo: function(w,h)
2648 setContentSize : function(w, h)
2652 onButtonClick: function(btn,e)
2655 this.fireEvent('btnclick', btn.name, e);
2658 * Set the title of the Dialog
2659 * @param {String} str new Title
2661 setTitle: function(str) {
2662 this.titleEl.dom.innerHTML = str;
2665 * Set the body of the Dialog
2666 * @param {String} str new Title
2668 setBody: function(str) {
2669 this.bodyEl.dom.innerHTML = str;
2672 * Set the body of the Dialog using the template
2673 * @param {Obj} data - apply this data to the template and replace the body contents.
2675 applyBody: function(obj)
2678 Roo.log("Error - using apply Body without a template");
2681 this.tmpl.overwrite(this.bodyEl, obj);
2687 Roo.apply(Roo.bootstrap.Modal, {
2689 * Button config that displays a single OK button
2698 * Button config that displays Yes and No buttons
2714 * Button config that displays OK and Cancel buttons
2729 * Button config that displays Yes, No and Cancel buttons
2752 * messagebox - can be used as a replace
2756 * @class Roo.MessageBox
2757 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2761 Roo.Msg.alert('Status', 'Changes saved successfully.');
2763 // Prompt for user data:
2764 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2766 // process text value...
2770 // Show a dialog using config options:
2772 title:'Save Changes?',
2773 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2774 buttons: Roo.Msg.YESNOCANCEL,
2781 Roo.bootstrap.MessageBox = function(){
2782 var dlg, opt, mask, waitTimer;
2783 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2784 var buttons, activeTextEl, bwidth;
2788 var handleButton = function(button){
2790 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2794 var handleHide = function(){
2796 dlg.el.removeClass(opt.cls);
2799 // Roo.TaskMgr.stop(waitTimer);
2800 // waitTimer = null;
2805 var updateButtons = function(b){
2808 buttons["ok"].hide();
2809 buttons["cancel"].hide();
2810 buttons["yes"].hide();
2811 buttons["no"].hide();
2812 //dlg.footer.dom.style.display = 'none';
2815 dlg.footerEl.dom.style.display = '';
2816 for(var k in buttons){
2817 if(typeof buttons[k] != "function"){
2820 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2821 width += buttons[k].el.getWidth()+15;
2831 var handleEsc = function(d, k, e){
2832 if(opt && opt.closable !== false){
2842 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2843 * @return {Roo.BasicDialog} The BasicDialog element
2845 getDialog : function(){
2847 dlg = new Roo.bootstrap.Modal( {
2850 //constraintoviewport:false,
2852 //collapsible : false,
2857 //buttonAlign:"center",
2858 closeClick : function(){
2859 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2862 handleButton("cancel");
2867 dlg.on("hide", handleHide);
2869 //dlg.addKeyListener(27, handleEsc);
2871 this.buttons = buttons;
2872 var bt = this.buttonText;
2873 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2874 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2875 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2876 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2878 bodyEl = dlg.bodyEl.createChild({
2880 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2881 '<textarea class="roo-mb-textarea"></textarea>' +
2882 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2884 msgEl = bodyEl.dom.firstChild;
2885 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2886 textboxEl.enableDisplayMode();
2887 textboxEl.addKeyListener([10,13], function(){
2888 if(dlg.isVisible() && opt && opt.buttons){
2891 }else if(opt.buttons.yes){
2892 handleButton("yes");
2896 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2897 textareaEl.enableDisplayMode();
2898 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2899 progressEl.enableDisplayMode();
2900 var pf = progressEl.dom.firstChild;
2902 pp = Roo.get(pf.firstChild);
2903 pp.setHeight(pf.offsetHeight);
2911 * Updates the message box body text
2912 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2913 * the XHTML-compliant non-breaking space character '&#160;')
2914 * @return {Roo.MessageBox} This message box
2916 updateText : function(text){
2917 if(!dlg.isVisible() && !opt.width){
2918 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2920 msgEl.innerHTML = text || ' ';
2922 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2923 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2925 Math.min(opt.width || cw , this.maxWidth),
2926 Math.max(opt.minWidth || this.minWidth, bwidth)
2929 activeTextEl.setWidth(w);
2931 if(dlg.isVisible()){
2932 dlg.fixedcenter = false;
2934 // to big, make it scroll. = But as usual stupid IE does not support
2937 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2938 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2939 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2941 bodyEl.dom.style.height = '';
2942 bodyEl.dom.style.overflowY = '';
2945 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2947 bodyEl.dom.style.overflowX = '';
2950 dlg.setContentSize(w, bodyEl.getHeight());
2951 if(dlg.isVisible()){
2952 dlg.fixedcenter = true;
2958 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
2959 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2960 * @param {Number} value Any number between 0 and 1 (e.g., .5)
2961 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2962 * @return {Roo.MessageBox} This message box
2964 updateProgress : function(value, text){
2966 this.updateText(text);
2968 if (pp) { // weird bug on my firefox - for some reason this is not defined
2969 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2975 * Returns true if the message box is currently displayed
2976 * @return {Boolean} True if the message box is visible, else false
2978 isVisible : function(){
2979 return dlg && dlg.isVisible();
2983 * Hides the message box if it is displayed
2986 if(this.isVisible()){
2992 * Displays a new message box, or reinitializes an existing message box, based on the config options
2993 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2994 * The following config object properties are supported:
2996 Property Type Description
2997 ---------- --------------- ------------------------------------------------------------------------------------
2998 animEl String/Element An id or Element from which the message box should animate as it opens and
2999 closes (defaults to undefined)
3000 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3001 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3002 closable Boolean False to hide the top-right close button (defaults to true). Note that
3003 progress and wait dialogs will ignore this property and always hide the
3004 close button as they can only be closed programmatically.
3005 cls String A custom CSS class to apply to the message box element
3006 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3007 displayed (defaults to 75)
3008 fn Function A callback function to execute after closing the dialog. The arguments to the
3009 function will be btn (the name of the button that was clicked, if applicable,
3010 e.g. "ok"), and text (the value of the active text field, if applicable).
3011 Progress and wait dialogs will ignore this option since they do not respond to
3012 user actions and can only be closed programmatically, so any required function
3013 should be called by the same code after it closes the dialog.
3014 icon String A CSS class that provides a background image to be used as an icon for
3015 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3016 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3017 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3018 modal Boolean False to allow user interaction with the page while the message box is
3019 displayed (defaults to true)
3020 msg String A string that will replace the existing message box body text (defaults
3021 to the XHTML-compliant non-breaking space character ' ')
3022 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3023 progress Boolean True to display a progress bar (defaults to false)
3024 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3025 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3026 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3027 title String The title text
3028 value String The string value to set into the active textbox element if displayed
3029 wait Boolean True to display a progress bar (defaults to false)
3030 width Number The width of the dialog in pixels
3037 msg: 'Please enter your address:',
3039 buttons: Roo.MessageBox.OKCANCEL,
3042 animEl: 'addAddressBtn'
3045 * @param {Object} config Configuration options
3046 * @return {Roo.MessageBox} This message box
3048 show : function(options)
3051 // this causes nightmares if you show one dialog after another
3052 // especially on callbacks..
3054 if(this.isVisible()){
3057 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3058 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3059 Roo.log("New Dialog Message:" + options.msg )
3060 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3061 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3064 var d = this.getDialog();
3066 d.setTitle(opt.title || " ");
3067 d.closeEl.setDisplayed(opt.closable !== false);
3068 activeTextEl = textboxEl;
3069 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3074 textareaEl.setHeight(typeof opt.multiline == "number" ?
3075 opt.multiline : this.defaultTextHeight);
3076 activeTextEl = textareaEl;
3085 progressEl.setDisplayed(opt.progress === true);
3086 this.updateProgress(0);
3087 activeTextEl.dom.value = opt.value || "";
3089 dlg.setDefaultButton(activeTextEl);
3091 var bs = opt.buttons;
3095 }else if(bs && bs.yes){
3096 db = buttons["yes"];
3098 dlg.setDefaultButton(db);
3100 bwidth = updateButtons(opt.buttons);
3101 this.updateText(opt.msg);
3103 d.el.addClass(opt.cls);
3105 d.proxyDrag = opt.proxyDrag === true;
3106 d.modal = opt.modal !== false;
3107 d.mask = opt.modal !== false ? mask : false;
3109 // force it to the end of the z-index stack so it gets a cursor in FF
3110 document.body.appendChild(dlg.el.dom);
3111 d.animateTarget = null;
3112 d.show(options.animEl);
3118 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3119 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3120 * and closing the message box when the process is complete.
3121 * @param {String} title The title bar text
3122 * @param {String} msg The message box body text
3123 * @return {Roo.MessageBox} This message box
3125 progress : function(title, msg){
3132 minWidth: this.minProgressWidth,
3139 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3140 * If a callback function is passed it will be called after the user clicks the button, and the
3141 * id of the button that was clicked will be passed as the only parameter to the callback
3142 * (could also be the top-right close button).
3143 * @param {String} title The title bar text
3144 * @param {String} msg The message box body text
3145 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3146 * @param {Object} scope (optional) The scope of the callback function
3147 * @return {Roo.MessageBox} This message box
3149 alert : function(title, msg, fn, scope){
3162 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3163 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3164 * You are responsible for closing the message box when the process is complete.
3165 * @param {String} msg The message box body text
3166 * @param {String} title (optional) The title bar text
3167 * @return {Roo.MessageBox} This message box
3169 wait : function(msg, title){
3180 waitTimer = Roo.TaskMgr.start({
3182 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3190 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3191 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3192 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3193 * @param {String} title The title bar text
3194 * @param {String} msg The message box body text
3195 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3196 * @param {Object} scope (optional) The scope of the callback function
3197 * @return {Roo.MessageBox} This message box
3199 confirm : function(title, msg, fn, scope){
3203 buttons: this.YESNO,
3212 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3213 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3214 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3215 * (could also be the top-right close button) and the text that was entered will be passed as the two
3216 * parameters to the callback.
3217 * @param {String} title The title bar text
3218 * @param {String} msg The message box body text
3219 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3220 * @param {Object} scope (optional) The scope of the callback function
3221 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3222 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3223 * @return {Roo.MessageBox} This message box
3225 prompt : function(title, msg, fn, scope, multiline){
3229 buttons: this.OKCANCEL,
3234 multiline: multiline,
3241 * Button config that displays a single OK button
3246 * Button config that displays Yes and No buttons
3249 YESNO : {yes:true, no:true},
3251 * Button config that displays OK and Cancel buttons
3254 OKCANCEL : {ok:true, cancel:true},
3256 * Button config that displays Yes, No and Cancel buttons
3259 YESNOCANCEL : {yes:true, no:true, cancel:true},
3262 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3265 defaultTextHeight : 75,
3267 * The maximum width in pixels of the message box (defaults to 600)
3272 * The minimum width in pixels of the message box (defaults to 100)
3277 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3278 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3281 minProgressWidth : 250,
3283 * An object containing the default button text strings that can be overriden for localized language support.
3284 * Supported properties are: ok, cancel, yes and no.
3285 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3298 * Shorthand for {@link Roo.MessageBox}
3300 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3301 Roo.Msg = Roo.Msg || Roo.MessageBox;
3310 * @class Roo.bootstrap.Navbar
3311 * @extends Roo.bootstrap.Component
3312 * Bootstrap Navbar class
3315 * Create a new Navbar
3316 * @param {Object} config The config object
3320 Roo.bootstrap.Navbar = function(config){
3321 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3325 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3334 getAutoCreate : function(){
3337 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3341 initEvents :function ()
3343 //Roo.log(this.el.select('.navbar-toggle',true));
3344 this.el.select('.navbar-toggle',true).on('click', function() {
3345 // Roo.log('click');
3346 this.el.select('.navbar-collapse',true).toggleClass('in');
3354 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3356 var size = this.el.getSize();
3357 this.maskEl.setSize(size.width, size.height);
3358 this.maskEl.enableDisplayMode("block");
3367 getChildContainer : function()
3369 if (this.el.select('.collapse').getCount()) {
3370 return this.el.select('.collapse',true).first();
3403 * @class Roo.bootstrap.NavSimplebar
3404 * @extends Roo.bootstrap.Navbar
3405 * Bootstrap Sidebar class
3407 * @cfg {Boolean} inverse is inverted color
3409 * @cfg {String} type (nav | pills | tabs)
3410 * @cfg {Boolean} arrangement stacked | justified
3411 * @cfg {String} align (left | right) alignment
3413 * @cfg {Boolean} main (true|false) main nav bar? default false
3414 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3416 * @cfg {String} tag (header|footer|nav|div) default is nav
3422 * Create a new Sidebar
3423 * @param {Object} config The config object
3427 Roo.bootstrap.NavSimplebar = function(config){
3428 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3431 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3447 getAutoCreate : function(){
3451 tag : this.tag || 'div',
3464 this.type = this.type || 'nav';
3465 if (['tabs','pills'].indexOf(this.type)!==-1) {
3466 cfg.cn[0].cls += ' nav-' + this.type
3470 if (this.type!=='nav') {
3471 Roo.log('nav type must be nav/tabs/pills')
3473 cfg.cn[0].cls += ' navbar-nav'
3479 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3480 cfg.cn[0].cls += ' nav-' + this.arrangement;
3484 if (this.align === 'right') {
3485 cfg.cn[0].cls += ' navbar-right';
3489 cfg.cls += ' navbar-inverse';
3516 * @class Roo.bootstrap.NavHeaderbar
3517 * @extends Roo.bootstrap.NavSimplebar
3518 * Bootstrap Sidebar class
3520 * @cfg {String} brand what is brand
3521 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3522 * @cfg {String} brand_href href of the brand
3523 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3524 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3525 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3526 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3529 * Create a new Sidebar
3530 * @param {Object} config The config object
3534 Roo.bootstrap.NavHeaderbar = function(config){
3535 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3539 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3546 desktopCenter : false,
3549 getAutoCreate : function(){
3552 tag: this.nav || 'nav',
3559 if (this.desktopCenter) {
3560 cn.push({cls : 'container', cn : []});
3567 cls: 'navbar-header',
3572 cls: 'navbar-toggle',
3573 'data-toggle': 'collapse',
3578 html: 'Toggle navigation'
3600 cls: 'collapse navbar-collapse',
3604 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3606 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3607 cfg.cls += ' navbar-' + this.position;
3609 // tag can override this..
3611 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3614 if (this.brand !== '') {
3617 href: this.brand_href ? this.brand_href : '#',
3618 cls: 'navbar-brand',
3626 cfg.cls += ' main-nav';
3634 getHeaderChildContainer : function()
3636 if (this.el.select('.navbar-header').getCount()) {
3637 return this.el.select('.navbar-header',true).first();
3640 return this.getChildContainer();
3644 initEvents : function()
3646 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3648 if (this.autohide) {
3653 Roo.get(document).on('scroll',function(e) {
3654 var ns = Roo.get(document).getScroll().top;
3655 var os = prevScroll;
3659 ft.removeClass('slideDown');
3660 ft.addClass('slideUp');
3663 ft.removeClass('slideUp');
3664 ft.addClass('slideDown');
3685 * @class Roo.bootstrap.NavSidebar
3686 * @extends Roo.bootstrap.Navbar
3687 * Bootstrap Sidebar class
3690 * Create a new Sidebar
3691 * @param {Object} config The config object
3695 Roo.bootstrap.NavSidebar = function(config){
3696 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3699 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3701 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3703 getAutoCreate : function(){
3708 cls: 'sidebar sidebar-nav'
3730 * @class Roo.bootstrap.NavGroup
3731 * @extends Roo.bootstrap.Component
3732 * Bootstrap NavGroup class
3733 * @cfg {String} align (left|right)
3734 * @cfg {Boolean} inverse
3735 * @cfg {String} type (nav|pills|tab) default nav
3736 * @cfg {String} navId - reference Id for navbar.
3740 * Create a new nav group
3741 * @param {Object} config The config object
3744 Roo.bootstrap.NavGroup = function(config){
3745 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3748 Roo.bootstrap.NavGroup.register(this);
3752 * Fires when the active item changes
3753 * @param {Roo.bootstrap.NavGroup} this
3754 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3755 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3762 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3773 getAutoCreate : function()
3775 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3782 if (['tabs','pills'].indexOf(this.type)!==-1) {
3783 cfg.cls += ' nav-' + this.type
3785 if (this.type!=='nav') {
3786 Roo.log('nav type must be nav/tabs/pills')
3788 cfg.cls += ' navbar-nav'
3791 if (this.parent().sidebar) {
3794 cls: 'dashboard-menu sidebar-menu'
3800 if (this.form === true) {
3806 if (this.align === 'right') {
3807 cfg.cls += ' navbar-right';
3809 cfg.cls += ' navbar-left';
3813 if (this.align === 'right') {
3814 cfg.cls += ' navbar-right';
3818 cfg.cls += ' navbar-inverse';
3826 * sets the active Navigation item
3827 * @param {Roo.bootstrap.NavItem} the new current navitem
3829 setActiveItem : function(item)
3832 Roo.each(this.navItems, function(v){
3837 v.setActive(false, true);
3844 item.setActive(true, true);
3845 this.fireEvent('changed', this, item, prev);
3850 * gets the active Navigation item
3851 * @return {Roo.bootstrap.NavItem} the current navitem
3853 getActive : function()
3857 Roo.each(this.navItems, function(v){
3868 indexOfNav : function()
3872 Roo.each(this.navItems, function(v,i){
3883 * adds a Navigation item
3884 * @param {Roo.bootstrap.NavItem} the navitem to add
3886 addItem : function(cfg)
3888 var cn = new Roo.bootstrap.NavItem(cfg);
3890 cn.parentId = this.id;
3891 cn.onRender(this.el, null);
3895 * register a Navigation item
3896 * @param {Roo.bootstrap.NavItem} the navitem to add
3898 register : function(item)
3900 this.navItems.push( item);
3901 item.navId = this.navId;
3906 * clear all the Navigation item
3909 clearAll : function()
3912 this.el.dom.innerHTML = '';
3915 getNavItem: function(tabId)
3918 Roo.each(this.navItems, function(e) {
3919 if (e.tabId == tabId) {
3929 setActiveNext : function()
3931 var i = this.indexOfNav(this.getActive());
3932 if (i > this.navItems.length) {
3935 this.setActiveItem(this.navItems[i+1]);
3937 setActivePrev : function()
3939 var i = this.indexOfNav(this.getActive());
3943 this.setActiveItem(this.navItems[i-1]);
3945 clearWasActive : function(except) {
3946 Roo.each(this.navItems, function(e) {
3947 if (e.tabId != except.tabId && e.was_active) {
3948 e.was_active = false;
3955 getWasActive : function ()
3958 Roo.each(this.navItems, function(e) {
3973 Roo.apply(Roo.bootstrap.NavGroup, {
3977 * register a Navigation Group
3978 * @param {Roo.bootstrap.NavGroup} the navgroup to add
3980 register : function(navgrp)
3982 this.groups[navgrp.navId] = navgrp;
3986 * fetch a Navigation Group based on the navigation ID
3987 * @param {string} the navgroup to add
3988 * @returns {Roo.bootstrap.NavGroup} the navgroup
3990 get: function(navId) {
3991 if (typeof(this.groups[navId]) == 'undefined') {
3993 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3995 return this.groups[navId] ;
4010 * @class Roo.bootstrap.NavItem
4011 * @extends Roo.bootstrap.Component
4012 * Bootstrap Navbar.NavItem class
4013 * @cfg {String} href link to
4014 * @cfg {String} html content of button
4015 * @cfg {String} badge text inside badge
4016 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4017 * @cfg {String} glyphicon name of glyphicon
4018 * @cfg {String} icon name of font awesome icon
4019 * @cfg {Boolean} active Is item active
4020 * @cfg {Boolean} disabled Is item disabled
4022 * @cfg {Boolean} preventDefault (true | false) default false
4023 * @cfg {String} tabId the tab that this item activates.
4024 * @cfg {String} tagtype (a|span) render as a href or span?
4025 * @cfg {Boolean} animateRef (true|false) link to element default false
4028 * Create a new Navbar Item
4029 * @param {Object} config The config object
4031 Roo.bootstrap.NavItem = function(config){
4032 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4037 * The raw click event for the entire grid.
4038 * @param {Roo.EventObject} e
4043 * Fires when the active item active state changes
4044 * @param {Roo.bootstrap.NavItem} this
4045 * @param {boolean} state the new state
4051 * Fires when scroll to element
4052 * @param {Roo.bootstrap.NavItem} this
4053 * @param {Object} options
4054 * @param {Roo.EventObject} e
4062 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4070 preventDefault : false,
4077 getAutoCreate : function(){
4086 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4088 if (this.disabled) {
4089 cfg.cls += ' disabled';
4092 if (this.href || this.html || this.glyphicon || this.icon) {
4096 href : this.href || "#",
4097 html: this.html || ''
4102 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4105 if(this.glyphicon) {
4106 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4111 cfg.cn[0].html += " <span class='caret'></span>";
4115 if (this.badge !== '') {
4117 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4125 initEvents: function()
4127 if (typeof (this.menu) != 'undefined') {
4128 this.menu.parentType = this.xtype;
4129 this.menu.triggerEl = this.el;
4130 this.menu = this.addxtype(Roo.apply({}, this.menu));
4133 this.el.select('a',true).on('click', this.onClick, this);
4135 if(this.tagtype == 'span'){
4136 this.el.select('span',true).on('click', this.onClick, this);
4139 // at this point parent should be available..
4140 this.parent().register(this);
4143 onClick : function(e)
4146 this.preventDefault ||
4153 if (this.disabled) {
4157 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4158 if (tg && tg.transition) {
4159 Roo.log("waiting for the transitionend");
4165 //Roo.log("fire event clicked");
4166 if(this.fireEvent('click', this, e) === false){
4170 if(this.tagtype == 'span'){
4174 //Roo.log(this.href);
4175 var ael = this.el.select('a',true).first();
4178 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4179 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4180 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4181 return; // ignore... - it's a 'hash' to another page.
4185 this.scrollToElement(e);
4189 var p = this.parent();
4191 if (['tabs','pills'].indexOf(p.type)!==-1) {
4192 if (typeof(p.setActiveItem) !== 'undefined') {
4193 p.setActiveItem(this);
4197 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4198 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4199 // remove the collapsed menu expand...
4200 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4204 isActive: function () {
4207 setActive : function(state, fire, is_was_active)
4209 if (this.active && !state && this.navId) {
4210 this.was_active = true;
4211 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4213 nv.clearWasActive(this);
4217 this.active = state;
4220 this.el.removeClass('active');
4221 } else if (!this.el.hasClass('active')) {
4222 this.el.addClass('active');
4225 this.fireEvent('changed', this, state);
4228 // show a panel if it's registered and related..
4230 if (!this.navId || !this.tabId || !state || is_was_active) {
4234 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4238 var pan = tg.getPanelByName(this.tabId);
4242 // if we can not flip to new panel - go back to old nav highlight..
4243 if (false == tg.showPanel(pan)) {
4244 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4246 var onav = nv.getWasActive();
4248 onav.setActive(true, false, true);
4257 // this should not be here...
4258 setDisabled : function(state)
4260 this.disabled = state;
4262 this.el.removeClass('disabled');
4263 } else if (!this.el.hasClass('disabled')) {
4264 this.el.addClass('disabled');
4270 * Fetch the element to display the tooltip on.
4271 * @return {Roo.Element} defaults to this.el
4273 tooltipEl : function()
4275 return this.el.select('' + this.tagtype + '', true).first();
4278 scrollToElement : function(e)
4280 var c = document.body;
4283 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4285 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4286 c = document.documentElement;
4289 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4295 var o = target.calcOffsetsTo(c);
4302 this.fireEvent('scrollto', this, options, e);
4304 Roo.get(c).scrollTo('top', options.value, true);
4317 * <span> icon </span>
4318 * <span> text </span>
4319 * <span>badge </span>
4323 * @class Roo.bootstrap.NavSidebarItem
4324 * @extends Roo.bootstrap.NavItem
4325 * Bootstrap Navbar.NavSidebarItem class
4326 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4328 * Create a new Navbar Button
4329 * @param {Object} config The config object
4331 Roo.bootstrap.NavSidebarItem = function(config){
4332 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4337 * The raw click event for the entire grid.
4338 * @param {Roo.EventObject} e
4343 * Fires when the active item active state changes
4344 * @param {Roo.bootstrap.NavSidebarItem} this
4345 * @param {boolean} state the new state
4353 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4355 badgeWeight : 'default',
4357 getAutoCreate : function(){
4362 href : this.href || '#',
4374 html : this.html || ''
4379 cfg.cls += ' active';
4382 if (this.disabled) {
4383 cfg.cls += ' disabled';
4387 if (this.glyphicon || this.icon) {
4388 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4389 a.cn.push({ tag : 'i', cls : c }) ;
4394 if (this.badge !== '') {
4396 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4400 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4401 a.cls += 'dropdown-toggle treeview' ;
4412 initEvents : function()
4414 this.el.on('click', this.onClick, this);
4417 if(this.badge !== ''){
4419 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4424 onClick : function(e)
4431 if(this.preventDefault){
4435 this.fireEvent('click', this);
4438 disable : function()
4440 this.setDisabled(true);
4445 this.setDisabled(false);
4448 setDisabled : function(state)
4450 if(this.disabled == state){
4454 this.disabled = state;
4457 this.el.addClass('disabled');
4461 this.el.removeClass('disabled');
4466 setActive : function(state)
4468 if(this.active == state){
4472 this.active = state;
4475 this.el.addClass('active');
4479 this.el.removeClass('active');
4484 isActive: function ()
4489 setBadge : function(str)
4495 this.badgeEl.dom.innerHTML = str;
4512 * @class Roo.bootstrap.Row
4513 * @extends Roo.bootstrap.Component
4514 * Bootstrap Row class (contains columns...)
4518 * @param {Object} config The config object
4521 Roo.bootstrap.Row = function(config){
4522 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4525 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4527 getAutoCreate : function(){
4546 * @class Roo.bootstrap.Element
4547 * @extends Roo.bootstrap.Component
4548 * Bootstrap Element class
4549 * @cfg {String} html contents of the element
4550 * @cfg {String} tag tag of the element
4551 * @cfg {String} cls class of the element
4552 * @cfg {Boolean} preventDefault (true|false) default false
4553 * @cfg {Boolean} clickable (true|false) default false
4556 * Create a new Element
4557 * @param {Object} config The config object
4560 Roo.bootstrap.Element = function(config){
4561 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4567 * When a element is chick
4568 * @param {Roo.bootstrap.Element} this
4569 * @param {Roo.EventObject} e
4575 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4580 preventDefault: false,
4583 getAutoCreate : function(){
4594 initEvents: function()
4596 Roo.bootstrap.Element.superclass.initEvents.call(this);
4599 this.el.on('click', this.onClick, this);
4604 onClick : function(e)
4606 if(this.preventDefault){
4610 this.fireEvent('click', this, e);
4613 getValue : function()
4615 return this.el.dom.innerHTML;
4618 setValue : function(value)
4620 this.el.dom.innerHTML = value;
4635 * @class Roo.bootstrap.Pagination
4636 * @extends Roo.bootstrap.Component
4637 * Bootstrap Pagination class
4638 * @cfg {String} size xs | sm | md | lg
4639 * @cfg {Boolean} inverse false | true
4642 * Create a new Pagination
4643 * @param {Object} config The config object
4646 Roo.bootstrap.Pagination = function(config){
4647 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4650 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4656 getAutoCreate : function(){
4662 cfg.cls += ' inverse';
4668 cfg.cls += " " + this.cls;
4686 * @class Roo.bootstrap.PaginationItem
4687 * @extends Roo.bootstrap.Component
4688 * Bootstrap PaginationItem class
4689 * @cfg {String} html text
4690 * @cfg {String} href the link
4691 * @cfg {Boolean} preventDefault (true | false) default true
4692 * @cfg {Boolean} active (true | false) default false
4693 * @cfg {Boolean} disabled default false
4697 * Create a new PaginationItem
4698 * @param {Object} config The config object
4702 Roo.bootstrap.PaginationItem = function(config){
4703 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4708 * The raw click event for the entire grid.
4709 * @param {Roo.EventObject} e
4715 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4719 preventDefault: true,
4724 getAutoCreate : function(){
4730 href : this.href ? this.href : '#',
4731 html : this.html ? this.html : ''
4741 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4745 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4751 initEvents: function() {
4753 this.el.on('click', this.onClick, this);
4756 onClick : function(e)
4758 Roo.log('PaginationItem on click ');
4759 if(this.preventDefault){
4767 this.fireEvent('click', this, e);
4783 * @class Roo.bootstrap.Slider
4784 * @extends Roo.bootstrap.Component
4785 * Bootstrap Slider class
4788 * Create a new Slider
4789 * @param {Object} config The config object
4792 Roo.bootstrap.Slider = function(config){
4793 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4796 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4798 getAutoCreate : function(){
4802 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4806 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4818 * Ext JS Library 1.1.1
4819 * Copyright(c) 2006-2007, Ext JS, LLC.
4821 * Originally Released Under LGPL - original licence link has changed is not relivant.
4824 * <script type="text/javascript">
4829 * @class Roo.grid.ColumnModel
4830 * @extends Roo.util.Observable
4831 * This is the default implementation of a ColumnModel used by the Grid. It defines
4832 * the columns in the grid.
4835 var colModel = new Roo.grid.ColumnModel([
4836 {header: "Ticker", width: 60, sortable: true, locked: true},
4837 {header: "Company Name", width: 150, sortable: true},
4838 {header: "Market Cap.", width: 100, sortable: true},
4839 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4840 {header: "Employees", width: 100, sortable: true, resizable: false}
4845 * The config options listed for this class are options which may appear in each
4846 * individual column definition.
4847 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4849 * @param {Object} config An Array of column config objects. See this class's
4850 * config objects for details.
4852 Roo.grid.ColumnModel = function(config){
4854 * The config passed into the constructor
4856 this.config = config;
4859 // if no id, create one
4860 // if the column does not have a dataIndex mapping,
4861 // map it to the order it is in the config
4862 for(var i = 0, len = config.length; i < len; i++){
4864 if(typeof c.dataIndex == "undefined"){
4867 if(typeof c.renderer == "string"){
4868 c.renderer = Roo.util.Format[c.renderer];
4870 if(typeof c.id == "undefined"){
4873 if(c.editor && c.editor.xtype){
4874 c.editor = Roo.factory(c.editor, Roo.grid);
4876 if(c.editor && c.editor.isFormField){
4877 c.editor = new Roo.grid.GridEditor(c.editor);
4879 this.lookup[c.id] = c;
4883 * The width of columns which have no width specified (defaults to 100)
4886 this.defaultWidth = 100;
4889 * Default sortable of columns which have no sortable specified (defaults to false)
4892 this.defaultSortable = false;
4896 * @event widthchange
4897 * Fires when the width of a column changes.
4898 * @param {ColumnModel} this
4899 * @param {Number} columnIndex The column index
4900 * @param {Number} newWidth The new width
4902 "widthchange": true,
4904 * @event headerchange
4905 * Fires when the text of a header changes.
4906 * @param {ColumnModel} this
4907 * @param {Number} columnIndex The column index
4908 * @param {Number} newText The new header text
4910 "headerchange": true,
4912 * @event hiddenchange
4913 * Fires when a column is hidden or "unhidden".
4914 * @param {ColumnModel} this
4915 * @param {Number} columnIndex The column index
4916 * @param {Boolean} hidden true if hidden, false otherwise
4918 "hiddenchange": true,
4920 * @event columnmoved
4921 * Fires when a column is moved.
4922 * @param {ColumnModel} this
4923 * @param {Number} oldIndex
4924 * @param {Number} newIndex
4926 "columnmoved" : true,
4928 * @event columlockchange
4929 * Fires when a column's locked state is changed
4930 * @param {ColumnModel} this
4931 * @param {Number} colIndex
4932 * @param {Boolean} locked true if locked
4934 "columnlockchange" : true
4936 Roo.grid.ColumnModel.superclass.constructor.call(this);
4938 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4940 * @cfg {String} header The header text to display in the Grid view.
4943 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4944 * {@link Roo.data.Record} definition from which to draw the column's value. If not
4945 * specified, the column's index is used as an index into the Record's data Array.
4948 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4949 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4952 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4953 * Defaults to the value of the {@link #defaultSortable} property.
4954 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4957 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
4960 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
4963 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4966 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4969 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4970 * given the cell's data value. See {@link #setRenderer}. If not specified, the
4971 * default renderer uses the raw data value. If an object is returned (bootstrap only)
4972 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4975 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
4978 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
4981 * @cfg {String} cursor (Optional)
4984 * @cfg {String} tooltip (Optional)
4987 * @cfg {Number} xs (Optional)
4990 * @cfg {Number} sm (Optional)
4993 * @cfg {Number} md (Optional)
4996 * @cfg {Number} lg (Optional)
4999 * Returns the id of the column at the specified index.
5000 * @param {Number} index The column index
5001 * @return {String} the id
5003 getColumnId : function(index){
5004 return this.config[index].id;
5008 * Returns the column for a specified id.
5009 * @param {String} id The column id
5010 * @return {Object} the column
5012 getColumnById : function(id){
5013 return this.lookup[id];
5018 * Returns the column for a specified dataIndex.
5019 * @param {String} dataIndex The column dataIndex
5020 * @return {Object|Boolean} the column or false if not found
5022 getColumnByDataIndex: function(dataIndex){
5023 var index = this.findColumnIndex(dataIndex);
5024 return index > -1 ? this.config[index] : false;
5028 * Returns the index for a specified column id.
5029 * @param {String} id The column id
5030 * @return {Number} the index, or -1 if not found
5032 getIndexById : function(id){
5033 for(var i = 0, len = this.config.length; i < len; i++){
5034 if(this.config[i].id == id){
5042 * Returns the index for a specified column dataIndex.
5043 * @param {String} dataIndex The column dataIndex
5044 * @return {Number} the index, or -1 if not found
5047 findColumnIndex : function(dataIndex){
5048 for(var i = 0, len = this.config.length; i < len; i++){
5049 if(this.config[i].dataIndex == dataIndex){
5057 moveColumn : function(oldIndex, newIndex){
5058 var c = this.config[oldIndex];
5059 this.config.splice(oldIndex, 1);
5060 this.config.splice(newIndex, 0, c);
5061 this.dataMap = null;
5062 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5065 isLocked : function(colIndex){
5066 return this.config[colIndex].locked === true;
5069 setLocked : function(colIndex, value, suppressEvent){
5070 if(this.isLocked(colIndex) == value){
5073 this.config[colIndex].locked = value;
5075 this.fireEvent("columnlockchange", this, colIndex, value);
5079 getTotalLockedWidth : function(){
5081 for(var i = 0; i < this.config.length; i++){
5082 if(this.isLocked(i) && !this.isHidden(i)){
5083 this.totalWidth += this.getColumnWidth(i);
5089 getLockedCount : function(){
5090 for(var i = 0, len = this.config.length; i < len; i++){
5091 if(!this.isLocked(i)){
5098 * Returns the number of columns.
5101 getColumnCount : function(visibleOnly){
5102 if(visibleOnly === true){
5104 for(var i = 0, len = this.config.length; i < len; i++){
5105 if(!this.isHidden(i)){
5111 return this.config.length;
5115 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5116 * @param {Function} fn
5117 * @param {Object} scope (optional)
5118 * @return {Array} result
5120 getColumnsBy : function(fn, scope){
5122 for(var i = 0, len = this.config.length; i < len; i++){
5123 var c = this.config[i];
5124 if(fn.call(scope||this, c, i) === true){
5132 * Returns true if the specified column is sortable.
5133 * @param {Number} col The column index
5136 isSortable : function(col){
5137 if(typeof this.config[col].sortable == "undefined"){
5138 return this.defaultSortable;
5140 return this.config[col].sortable;
5144 * Returns the rendering (formatting) function defined for the column.
5145 * @param {Number} col The column index.
5146 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5148 getRenderer : function(col){
5149 if(!this.config[col].renderer){
5150 return Roo.grid.ColumnModel.defaultRenderer;
5152 return this.config[col].renderer;
5156 * Sets the rendering (formatting) function for a column.
5157 * @param {Number} col The column index
5158 * @param {Function} fn The function to use to process the cell's raw data
5159 * to return HTML markup for the grid view. The render function is called with
5160 * the following parameters:<ul>
5161 * <li>Data value.</li>
5162 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5163 * <li>css A CSS style string to apply to the table cell.</li>
5164 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5165 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5166 * <li>Row index</li>
5167 * <li>Column index</li>
5168 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5170 setRenderer : function(col, fn){
5171 this.config[col].renderer = fn;
5175 * Returns the width for the specified column.
5176 * @param {Number} col The column index
5179 getColumnWidth : function(col){
5180 return this.config[col].width * 1 || this.defaultWidth;
5184 * Sets the width for a column.
5185 * @param {Number} col The column index
5186 * @param {Number} width The new width
5188 setColumnWidth : function(col, width, suppressEvent){
5189 this.config[col].width = width;
5190 this.totalWidth = null;
5192 this.fireEvent("widthchange", this, col, width);
5197 * Returns the total width of all columns.
5198 * @param {Boolean} includeHidden True to include hidden column widths
5201 getTotalWidth : function(includeHidden){
5202 if(!this.totalWidth){
5203 this.totalWidth = 0;
5204 for(var i = 0, len = this.config.length; i < len; i++){
5205 if(includeHidden || !this.isHidden(i)){
5206 this.totalWidth += this.getColumnWidth(i);
5210 return this.totalWidth;
5214 * Returns the header for the specified column.
5215 * @param {Number} col The column index
5218 getColumnHeader : function(col){
5219 return this.config[col].header;
5223 * Sets the header for a column.
5224 * @param {Number} col The column index
5225 * @param {String} header The new header
5227 setColumnHeader : function(col, header){
5228 this.config[col].header = header;
5229 this.fireEvent("headerchange", this, col, header);
5233 * Returns the tooltip for the specified column.
5234 * @param {Number} col The column index
5237 getColumnTooltip : function(col){
5238 return this.config[col].tooltip;
5241 * Sets the tooltip for a column.
5242 * @param {Number} col The column index
5243 * @param {String} tooltip The new tooltip
5245 setColumnTooltip : function(col, tooltip){
5246 this.config[col].tooltip = tooltip;
5250 * Returns the dataIndex for the specified column.
5251 * @param {Number} col The column index
5254 getDataIndex : function(col){
5255 return this.config[col].dataIndex;
5259 * Sets the dataIndex for a column.
5260 * @param {Number} col The column index
5261 * @param {Number} dataIndex The new dataIndex
5263 setDataIndex : function(col, dataIndex){
5264 this.config[col].dataIndex = dataIndex;
5270 * Returns true if the cell is editable.
5271 * @param {Number} colIndex The column index
5272 * @param {Number} rowIndex The row index
5275 isCellEditable : function(colIndex, rowIndex){
5276 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5280 * Returns the editor defined for the cell/column.
5281 * return false or null to disable editing.
5282 * @param {Number} colIndex The column index
5283 * @param {Number} rowIndex The row index
5286 getCellEditor : function(colIndex, rowIndex){
5287 return this.config[colIndex].editor;
5291 * Sets if a column is editable.
5292 * @param {Number} col The column index
5293 * @param {Boolean} editable True if the column is editable
5295 setEditable : function(col, editable){
5296 this.config[col].editable = editable;
5301 * Returns true if the column is hidden.
5302 * @param {Number} colIndex The column index
5305 isHidden : function(colIndex){
5306 return this.config[colIndex].hidden;
5311 * Returns true if the column width cannot be changed
5313 isFixed : function(colIndex){
5314 return this.config[colIndex].fixed;
5318 * Returns true if the column can be resized
5321 isResizable : function(colIndex){
5322 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5325 * Sets if a column is hidden.
5326 * @param {Number} colIndex The column index
5327 * @param {Boolean} hidden True if the column is hidden
5329 setHidden : function(colIndex, hidden){
5330 this.config[colIndex].hidden = hidden;
5331 this.totalWidth = null;
5332 this.fireEvent("hiddenchange", this, colIndex, hidden);
5336 * Sets the editor for a column.
5337 * @param {Number} col The column index
5338 * @param {Object} editor The editor object
5340 setEditor : function(col, editor){
5341 this.config[col].editor = editor;
5345 Roo.grid.ColumnModel.defaultRenderer = function(value){
5346 if(typeof value == "string" && value.length < 1){
5352 // Alias for backwards compatibility
5353 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5356 * Ext JS Library 1.1.1
5357 * Copyright(c) 2006-2007, Ext JS, LLC.
5359 * Originally Released Under LGPL - original licence link has changed is not relivant.
5362 * <script type="text/javascript">
5366 * @class Roo.LoadMask
5367 * A simple utility class for generically masking elements while loading data. If the element being masked has
5368 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5369 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5370 * element's UpdateManager load indicator and will be destroyed after the initial load.
5372 * Create a new LoadMask
5373 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5374 * @param {Object} config The config object
5376 Roo.LoadMask = function(el, config){
5377 this.el = Roo.get(el);
5378 Roo.apply(this, config);
5380 this.store.on('beforeload', this.onBeforeLoad, this);
5381 this.store.on('load', this.onLoad, this);
5382 this.store.on('loadexception', this.onLoadException, this);
5383 this.removeMask = false;
5385 var um = this.el.getUpdateManager();
5386 um.showLoadIndicator = false; // disable the default indicator
5387 um.on('beforeupdate', this.onBeforeLoad, this);
5388 um.on('update', this.onLoad, this);
5389 um.on('failure', this.onLoad, this);
5390 this.removeMask = true;
5394 Roo.LoadMask.prototype = {
5396 * @cfg {Boolean} removeMask
5397 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5398 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5402 * The text to display in a centered loading message box (defaults to 'Loading...')
5406 * @cfg {String} msgCls
5407 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5409 msgCls : 'x-mask-loading',
5412 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5418 * Disables the mask to prevent it from being displayed
5420 disable : function(){
5421 this.disabled = true;
5425 * Enables the mask so that it can be displayed
5427 enable : function(){
5428 this.disabled = false;
5431 onLoadException : function()
5435 if (typeof(arguments[3]) != 'undefined') {
5436 Roo.MessageBox.alert("Error loading",arguments[3]);
5440 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5441 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5450 this.el.unmask(this.removeMask);
5455 this.el.unmask(this.removeMask);
5459 onBeforeLoad : function(){
5461 this.el.mask(this.msg, this.msgCls);
5466 destroy : function(){
5468 this.store.un('beforeload', this.onBeforeLoad, this);
5469 this.store.un('load', this.onLoad, this);
5470 this.store.un('loadexception', this.onLoadException, this);
5472 var um = this.el.getUpdateManager();
5473 um.un('beforeupdate', this.onBeforeLoad, this);
5474 um.un('update', this.onLoad, this);
5475 um.un('failure', this.onLoad, this);
5486 * @class Roo.bootstrap.Table
5487 * @extends Roo.bootstrap.Component
5488 * Bootstrap Table class
5489 * @cfg {String} cls table class
5490 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5491 * @cfg {String} bgcolor Specifies the background color for a table
5492 * @cfg {Number} border Specifies whether the table cells should have borders or not
5493 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5494 * @cfg {Number} cellspacing Specifies the space between cells
5495 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5496 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5497 * @cfg {String} sortable Specifies that the table should be sortable
5498 * @cfg {String} summary Specifies a summary of the content of a table
5499 * @cfg {Number} width Specifies the width of a table
5500 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5502 * @cfg {boolean} striped Should the rows be alternative striped
5503 * @cfg {boolean} bordered Add borders to the table
5504 * @cfg {boolean} hover Add hover highlighting
5505 * @cfg {boolean} condensed Format condensed
5506 * @cfg {boolean} responsive Format condensed
5507 * @cfg {Boolean} loadMask (true|false) default false
5508 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5509 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5510 * @cfg {Boolean} rowSelection (true|false) default false
5511 * @cfg {Boolean} cellSelection (true|false) default false
5512 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5516 * Create a new Table
5517 * @param {Object} config The config object
5520 Roo.bootstrap.Table = function(config){
5521 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5524 this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5525 this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5526 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5527 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5531 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5532 this.sm = this.selModel;
5533 this.sm.xmodule = this.xmodule || false;
5535 if (this.cm && typeof(this.cm.config) == 'undefined') {
5536 this.colModel = new Roo.grid.ColumnModel(this.cm);
5537 this.cm = this.colModel;
5538 this.cm.xmodule = this.xmodule || false;
5541 this.store= Roo.factory(this.store, Roo.data);
5542 this.ds = this.store;
5543 this.ds.xmodule = this.xmodule || false;
5546 if (this.footer && this.store) {
5547 this.footer.dataSource = this.ds;
5548 this.footer = Roo.factory(this.footer);
5555 * Fires when a cell is clicked
5556 * @param {Roo.bootstrap.Table} this
5557 * @param {Roo.Element} el
5558 * @param {Number} rowIndex
5559 * @param {Number} columnIndex
5560 * @param {Roo.EventObject} e
5564 * @event celldblclick
5565 * Fires when a cell is double clicked
5566 * @param {Roo.bootstrap.Table} this
5567 * @param {Roo.Element} el
5568 * @param {Number} rowIndex
5569 * @param {Number} columnIndex
5570 * @param {Roo.EventObject} e
5572 "celldblclick" : true,
5575 * Fires when a row is clicked
5576 * @param {Roo.bootstrap.Table} this
5577 * @param {Roo.Element} el
5578 * @param {Number} rowIndex
5579 * @param {Roo.EventObject} e
5583 * @event rowdblclick
5584 * Fires when a row is double clicked
5585 * @param {Roo.bootstrap.Table} this
5586 * @param {Roo.Element} el
5587 * @param {Number} rowIndex
5588 * @param {Roo.EventObject} e
5590 "rowdblclick" : true,
5593 * Fires when a mouseover occur
5594 * @param {Roo.bootstrap.Table} this
5595 * @param {Roo.Element} el
5596 * @param {Number} rowIndex
5597 * @param {Number} columnIndex
5598 * @param {Roo.EventObject} e
5603 * Fires when a mouseout occur
5604 * @param {Roo.bootstrap.Table} this
5605 * @param {Roo.Element} el
5606 * @param {Number} rowIndex
5607 * @param {Number} columnIndex
5608 * @param {Roo.EventObject} e
5613 * Fires when a row is rendered, so you can change add a style to it.
5614 * @param {Roo.bootstrap.Table} this
5615 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5619 * @event rowsrendered
5620 * Fires when all the rows have been rendered
5621 * @param {Roo.bootstrap.Table} this
5623 'rowsrendered' : true
5628 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5653 rowSelection : false,
5654 cellSelection : false,
5657 // Roo.Element - the tbody
5660 getAutoCreate : function(){
5661 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5670 cfg.cls += ' table-striped';
5674 cfg.cls += ' table-hover';
5676 if (this.bordered) {
5677 cfg.cls += ' table-bordered';
5679 if (this.condensed) {
5680 cfg.cls += ' table-condensed';
5682 if (this.responsive) {
5683 cfg.cls += ' table-responsive';
5687 cfg.cls+= ' ' +this.cls;
5690 // this lot should be simplifed...
5693 cfg.align=this.align;
5696 cfg.bgcolor=this.bgcolor;
5699 cfg.border=this.border;
5701 if (this.cellpadding) {
5702 cfg.cellpadding=this.cellpadding;
5704 if (this.cellspacing) {
5705 cfg.cellspacing=this.cellspacing;
5708 cfg.frame=this.frame;
5711 cfg.rules=this.rules;
5713 if (this.sortable) {
5714 cfg.sortable=this.sortable;
5717 cfg.summary=this.summary;
5720 cfg.width=this.width;
5723 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5726 if(this.store || this.cm){
5727 if(this.headerShow){
5728 cfg.cn.push(this.renderHeader());
5731 cfg.cn.push(this.renderBody());
5733 if(this.footerShow){
5734 cfg.cn.push(this.renderFooter());
5737 cfg.cls+= ' TableGrid';
5740 return { cn : [ cfg ] };
5743 initEvents : function()
5745 if(!this.store || !this.cm){
5749 //Roo.log('initEvents with ds!!!!');
5751 this.mainBody = this.el.select('tbody', true).first();
5756 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5757 e.on('click', _this.sort, _this);
5760 this.el.on("click", this.onClick, this);
5761 this.el.on("dblclick", this.onDblClick, this);
5763 // why is this done????? = it breaks dialogs??
5764 //this.parent().el.setStyle('position', 'relative');
5768 this.footer.parentId = this.id;
5769 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5772 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5774 this.store.on('load', this.onLoad, this);
5775 this.store.on('beforeload', this.onBeforeLoad, this);
5776 this.store.on('update', this.onUpdate, this);
5777 this.store.on('add', this.onAdd, this);
5781 onMouseover : function(e, el)
5783 var cell = Roo.get(el);
5789 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5790 cell = cell.findParent('td', false, true);
5793 var row = cell.findParent('tr', false, true);
5794 var cellIndex = cell.dom.cellIndex;
5795 var rowIndex = row.dom.rowIndex - 1; // start from 0
5797 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5801 onMouseout : function(e, el)
5803 var cell = Roo.get(el);
5809 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5810 cell = cell.findParent('td', false, true);
5813 var row = cell.findParent('tr', false, true);
5814 var cellIndex = cell.dom.cellIndex;
5815 var rowIndex = row.dom.rowIndex - 1; // start from 0
5817 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5821 onClick : function(e, el)
5823 var cell = Roo.get(el);
5825 if(!cell || (!this.cellSelection && !this.rowSelection)){
5829 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5830 cell = cell.findParent('td', false, true);
5833 if(!cell || typeof(cell) == 'undefined'){
5837 var row = cell.findParent('tr', false, true);
5839 if(!row || typeof(row) == 'undefined'){
5843 var cellIndex = cell.dom.cellIndex;
5844 var rowIndex = this.getRowIndex(row);
5846 // why??? - should these not be based on SelectionModel?
5847 if(this.cellSelection){
5848 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5851 if(this.rowSelection){
5852 this.fireEvent('rowclick', this, row, rowIndex, e);
5858 onDblClick : function(e,el)
5860 var cell = Roo.get(el);
5862 if(!cell || (!this.CellSelection && !this.RowSelection)){
5866 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5867 cell = cell.findParent('td', false, true);
5870 if(!cell || typeof(cell) == 'undefined'){
5874 var row = cell.findParent('tr', false, true);
5876 if(!row || typeof(row) == 'undefined'){
5880 var cellIndex = cell.dom.cellIndex;
5881 var rowIndex = this.getRowIndex(row);
5883 if(this.CellSelection){
5884 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5887 if(this.RowSelection){
5888 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5892 sort : function(e,el)
5894 var col = Roo.get(el);
5896 if(!col.hasClass('sortable')){
5900 var sort = col.attr('sort');
5903 if(col.hasClass('glyphicon-arrow-up')){
5907 this.store.sortInfo = {field : sort, direction : dir};
5910 Roo.log("calling footer first");
5911 this.footer.onClick('first');
5914 this.store.load({ params : { start : 0 } });
5918 renderHeader : function()
5927 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5929 var config = cm.config[i];
5934 html: cm.getColumnHeader(i)
5939 if(typeof(config.lgHeader) != 'undefined'){
5940 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5943 if(typeof(config.mdHeader) != 'undefined'){
5944 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5947 if(typeof(config.smHeader) != 'undefined'){
5948 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5951 if(typeof(config.xsHeader) != 'undefined'){
5952 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5959 if(typeof(config.tooltip) != 'undefined'){
5960 c.tooltip = config.tooltip;
5963 if(typeof(config.colspan) != 'undefined'){
5964 c.colspan = config.colspan;
5967 if(typeof(config.hidden) != 'undefined' && config.hidden){
5968 c.style += ' display:none;';
5971 if(typeof(config.dataIndex) != 'undefined'){
5972 c.sort = config.dataIndex;
5975 if(typeof(config.sortable) != 'undefined' && config.sortable){
5979 if(typeof(config.align) != 'undefined' && config.align.length){
5980 c.style += ' text-align:' + config.align + ';';
5983 if(typeof(config.width) != 'undefined'){
5984 c.style += ' width:' + config.width + 'px;';
5987 if(typeof(config.cls) != 'undefined'){
5988 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5991 ['xs','sm','md','lg'].map(function(size){
5993 if(typeof(config[size]) == 'undefined'){
5997 if (!config[size]) { // 0 = hidden
5998 cfg.cls += ' hidden-' + size;
6002 cfg.cls += ' col-' + size + '-' + config[size];
6012 renderBody : function()
6022 colspan : this.cm.getColumnCount()
6032 renderFooter : function()
6042 colspan : this.cm.getColumnCount()
6056 Roo.log('ds onload');
6061 var ds = this.store;
6063 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6064 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6066 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6067 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6070 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6071 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6075 var tbody = this.mainBody;
6077 if(ds.getCount() > 0){
6078 ds.data.each(function(d,rowIndex){
6079 var row = this.renderRow(cm, ds, rowIndex);
6081 tbody.createChild(row);
6085 if(row.cellObjects.length){
6086 Roo.each(row.cellObjects, function(r){
6087 _this.renderCellObject(r);
6094 Roo.each(this.el.select('tbody td', true).elements, function(e){
6095 e.on('mouseover', _this.onMouseover, _this);
6098 Roo.each(this.el.select('tbody td', true).elements, function(e){
6099 e.on('mouseout', _this.onMouseout, _this);
6101 this.fireEvent('rowsrendered', this);
6102 //if(this.loadMask){
6103 // this.maskEl.hide();
6108 onUpdate : function(ds,record)
6110 this.refreshRow(record);
6113 onRemove : function(ds, record, index, isUpdate){
6114 if(isUpdate !== true){
6115 this.fireEvent("beforerowremoved", this, index, record);
6117 var bt = this.mainBody.dom;
6119 var rows = this.el.select('tbody > tr', true).elements;
6121 if(typeof(rows[index]) != 'undefined'){
6122 bt.removeChild(rows[index].dom);
6125 // if(bt.rows[index]){
6126 // bt.removeChild(bt.rows[index]);
6129 if(isUpdate !== true){
6130 //this.stripeRows(index);
6131 //this.syncRowHeights(index, index);
6133 this.fireEvent("rowremoved", this, index, record);
6137 onAdd : function(ds, records, rowIndex)
6139 //Roo.log('on Add called');
6140 // - note this does not handle multiple adding very well..
6141 var bt = this.mainBody.dom;
6142 for (var i =0 ; i < records.length;i++) {
6143 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6144 //Roo.log(records[i]);
6145 //Roo.log(this.store.getAt(rowIndex+i));
6146 this.insertRow(this.store, rowIndex + i, false);
6153 refreshRow : function(record){
6154 var ds = this.store, index;
6155 if(typeof record == 'number'){
6157 record = ds.getAt(index);
6159 index = ds.indexOf(record);
6161 this.insertRow(ds, index, true);
6162 this.onRemove(ds, record, index+1, true);
6163 //this.syncRowHeights(index, index);
6165 this.fireEvent("rowupdated", this, index, record);
6168 insertRow : function(dm, rowIndex, isUpdate){
6171 this.fireEvent("beforerowsinserted", this, rowIndex);
6173 //var s = this.getScrollState();
6174 var row = this.renderRow(this.cm, this.store, rowIndex);
6175 // insert before rowIndex..
6176 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6180 if(row.cellObjects.length){
6181 Roo.each(row.cellObjects, function(r){
6182 _this.renderCellObject(r);
6187 this.fireEvent("rowsinserted", this, rowIndex);
6188 //this.syncRowHeights(firstRow, lastRow);
6189 //this.stripeRows(firstRow);
6196 getRowDom : function(rowIndex)
6198 var rows = this.el.select('tbody > tr', true).elements;
6200 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6203 // returns the object tree for a tr..
6206 renderRow : function(cm, ds, rowIndex)
6209 var d = ds.getAt(rowIndex);
6216 var cellObjects = [];
6218 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6219 var config = cm.config[i];
6221 var renderer = cm.getRenderer(i);
6225 if(typeof(renderer) !== 'undefined'){
6226 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6228 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6229 // and are rendered into the cells after the row is rendered - using the id for the element.
6231 if(typeof(value) === 'object'){
6241 rowIndex : rowIndex,
6246 this.fireEvent('rowclass', this, rowcfg);
6250 cls : rowcfg.rowClass,
6252 html: (typeof(value) === 'object') ? '' : value
6259 if(typeof(config.colspan) != 'undefined'){
6260 td.colspan = config.colspan;
6263 if(typeof(config.hidden) != 'undefined' && config.hidden){
6264 td.style += ' display:none;';
6267 if(typeof(config.align) != 'undefined' && config.align.length){
6268 td.style += ' text-align:' + config.align + ';';
6271 if(typeof(config.width) != 'undefined'){
6272 td.style += ' width:' + config.width + 'px;';
6275 if(typeof(config.cursor) != 'undefined'){
6276 td.style += ' cursor:' + config.cursor + ';';
6279 if(typeof(config.cls) != 'undefined'){
6280 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6283 ['xs','sm','md','lg'].map(function(size){
6285 if(typeof(config[size]) == 'undefined'){
6289 if (!config[size]) { // 0 = hidden
6290 td.cls += ' hidden-' + size;
6294 td.cls += ' col-' + size + '-' + config[size];
6302 row.cellObjects = cellObjects;
6310 onBeforeLoad : function()
6312 //Roo.log('ds onBeforeLoad');
6316 //if(this.loadMask){
6317 // this.maskEl.show();
6325 this.el.select('tbody', true).first().dom.innerHTML = '';
6328 * Show or hide a row.
6329 * @param {Number} rowIndex to show or hide
6330 * @param {Boolean} state hide
6332 setRowVisibility : function(rowIndex, state)
6334 var bt = this.mainBody.dom;
6336 var rows = this.el.select('tbody > tr', true).elements;
6338 if(typeof(rows[rowIndex]) == 'undefined'){
6341 rows[rowIndex].dom.style.display = state ? '' : 'none';
6345 getSelectionModel : function(){
6347 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6349 return this.selModel;
6352 * Render the Roo.bootstrap object from renderder
6354 renderCellObject : function(r)
6358 var t = r.cfg.render(r.container);
6361 Roo.each(r.cfg.cn, function(c){
6363 container: t.getChildContainer(),
6366 _this.renderCellObject(child);
6371 getRowIndex : function(row)
6375 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6398 * @class Roo.bootstrap.TableCell
6399 * @extends Roo.bootstrap.Component
6400 * Bootstrap TableCell class
6401 * @cfg {String} html cell contain text
6402 * @cfg {String} cls cell class
6403 * @cfg {String} tag cell tag (td|th) default td
6404 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6405 * @cfg {String} align Aligns the content in a cell
6406 * @cfg {String} axis Categorizes cells
6407 * @cfg {String} bgcolor Specifies the background color of a cell
6408 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6409 * @cfg {Number} colspan Specifies the number of columns a cell should span
6410 * @cfg {String} headers Specifies one or more header cells a cell is related to
6411 * @cfg {Number} height Sets the height of a cell
6412 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6413 * @cfg {Number} rowspan Sets the number of rows a cell should span
6414 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6415 * @cfg {String} valign Vertical aligns the content in a cell
6416 * @cfg {Number} width Specifies the width of a cell
6419 * Create a new TableCell
6420 * @param {Object} config The config object
6423 Roo.bootstrap.TableCell = function(config){
6424 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6427 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6447 getAutoCreate : function(){
6448 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6468 cfg.align=this.align
6474 cfg.bgcolor=this.bgcolor
6477 cfg.charoff=this.charoff
6480 cfg.colspan=this.colspan
6483 cfg.headers=this.headers
6486 cfg.height=this.height
6489 cfg.nowrap=this.nowrap
6492 cfg.rowspan=this.rowspan
6495 cfg.scope=this.scope
6498 cfg.valign=this.valign
6501 cfg.width=this.width
6520 * @class Roo.bootstrap.TableRow
6521 * @extends Roo.bootstrap.Component
6522 * Bootstrap TableRow class
6523 * @cfg {String} cls row class
6524 * @cfg {String} align Aligns the content in a table row
6525 * @cfg {String} bgcolor Specifies a background color for a table row
6526 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6527 * @cfg {String} valign Vertical aligns the content in a table row
6530 * Create a new TableRow
6531 * @param {Object} config The config object
6534 Roo.bootstrap.TableRow = function(config){
6535 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6538 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6546 getAutoCreate : function(){
6547 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6557 cfg.align = this.align;
6560 cfg.bgcolor = this.bgcolor;
6563 cfg.charoff = this.charoff;
6566 cfg.valign = this.valign;
6584 * @class Roo.bootstrap.TableBody
6585 * @extends Roo.bootstrap.Component
6586 * Bootstrap TableBody class
6587 * @cfg {String} cls element class
6588 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6589 * @cfg {String} align Aligns the content inside the element
6590 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6591 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6594 * Create a new TableBody
6595 * @param {Object} config The config object
6598 Roo.bootstrap.TableBody = function(config){
6599 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6602 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6610 getAutoCreate : function(){
6611 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6625 cfg.align = this.align;
6628 cfg.charoff = this.charoff;
6631 cfg.valign = this.valign;
6638 // initEvents : function()
6645 // this.store = Roo.factory(this.store, Roo.data);
6646 // this.store.on('load', this.onLoad, this);
6648 // this.store.load();
6652 // onLoad: function ()
6654 // this.fireEvent('load', this);
6664 * Ext JS Library 1.1.1
6665 * Copyright(c) 2006-2007, Ext JS, LLC.
6667 * Originally Released Under LGPL - original licence link has changed is not relivant.
6670 * <script type="text/javascript">
6673 // as we use this in bootstrap.
6674 Roo.namespace('Roo.form');
6676 * @class Roo.form.Action
6677 * Internal Class used to handle form actions
6679 * @param {Roo.form.BasicForm} el The form element or its id
6680 * @param {Object} config Configuration options
6685 // define the action interface
6686 Roo.form.Action = function(form, options){
6688 this.options = options || {};
6691 * Client Validation Failed
6694 Roo.form.Action.CLIENT_INVALID = 'client';
6696 * Server Validation Failed
6699 Roo.form.Action.SERVER_INVALID = 'server';
6701 * Connect to Server Failed
6704 Roo.form.Action.CONNECT_FAILURE = 'connect';
6706 * Reading Data from Server Failed
6709 Roo.form.Action.LOAD_FAILURE = 'load';
6711 Roo.form.Action.prototype = {
6713 failureType : undefined,
6714 response : undefined,
6718 run : function(options){
6723 success : function(response){
6728 handleResponse : function(response){
6732 // default connection failure
6733 failure : function(response){
6735 this.response = response;
6736 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6737 this.form.afterAction(this, false);
6740 processResponse : function(response){
6741 this.response = response;
6742 if(!response.responseText){
6745 this.result = this.handleResponse(response);
6749 // utility functions used internally
6750 getUrl : function(appendParams){
6751 var url = this.options.url || this.form.url || this.form.el.dom.action;
6753 var p = this.getParams();
6755 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6761 getMethod : function(){
6762 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6765 getParams : function(){
6766 var bp = this.form.baseParams;
6767 var p = this.options.params;
6769 if(typeof p == "object"){
6770 p = Roo.urlEncode(Roo.applyIf(p, bp));
6771 }else if(typeof p == 'string' && bp){
6772 p += '&' + Roo.urlEncode(bp);
6775 p = Roo.urlEncode(bp);
6780 createCallback : function(){
6782 success: this.success,
6783 failure: this.failure,
6785 timeout: (this.form.timeout*1000),
6786 upload: this.form.fileUpload ? this.success : undefined
6791 Roo.form.Action.Submit = function(form, options){
6792 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6795 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6798 haveProgress : false,
6799 uploadComplete : false,
6801 // uploadProgress indicator.
6802 uploadProgress : function()
6804 if (!this.form.progressUrl) {
6808 if (!this.haveProgress) {
6809 Roo.MessageBox.progress("Uploading", "Uploading");
6811 if (this.uploadComplete) {
6812 Roo.MessageBox.hide();
6816 this.haveProgress = true;
6818 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6820 var c = new Roo.data.Connection();
6822 url : this.form.progressUrl,
6827 success : function(req){
6828 //console.log(data);
6832 rdata = Roo.decode(req.responseText)
6834 Roo.log("Invalid data from server..");
6838 if (!rdata || !rdata.success) {
6840 Roo.MessageBox.alert(Roo.encode(rdata));
6843 var data = rdata.data;
6845 if (this.uploadComplete) {
6846 Roo.MessageBox.hide();
6851 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6852 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6855 this.uploadProgress.defer(2000,this);
6858 failure: function(data) {
6859 Roo.log('progress url failed ');
6870 // run get Values on the form, so it syncs any secondary forms.
6871 this.form.getValues();
6873 var o = this.options;
6874 var method = this.getMethod();
6875 var isPost = method == 'POST';
6876 if(o.clientValidation === false || this.form.isValid()){
6878 if (this.form.progressUrl) {
6879 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6880 (new Date() * 1) + '' + Math.random());
6885 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6886 form:this.form.el.dom,
6887 url:this.getUrl(!isPost),
6889 params:isPost ? this.getParams() : null,
6890 isUpload: this.form.fileUpload
6893 this.uploadProgress();
6895 }else if (o.clientValidation !== false){ // client validation failed
6896 this.failureType = Roo.form.Action.CLIENT_INVALID;
6897 this.form.afterAction(this, false);
6901 success : function(response)
6903 this.uploadComplete= true;
6904 if (this.haveProgress) {
6905 Roo.MessageBox.hide();
6909 var result = this.processResponse(response);
6910 if(result === true || result.success){
6911 this.form.afterAction(this, true);
6915 this.form.markInvalid(result.errors);
6916 this.failureType = Roo.form.Action.SERVER_INVALID;
6918 this.form.afterAction(this, false);
6920 failure : function(response)
6922 this.uploadComplete= true;
6923 if (this.haveProgress) {
6924 Roo.MessageBox.hide();
6927 this.response = response;
6928 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6929 this.form.afterAction(this, false);
6932 handleResponse : function(response){
6933 if(this.form.errorReader){
6934 var rs = this.form.errorReader.read(response);
6937 for(var i = 0, len = rs.records.length; i < len; i++) {
6938 var r = rs.records[i];
6942 if(errors.length < 1){
6946 success : rs.success,
6952 ret = Roo.decode(response.responseText);
6956 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6966 Roo.form.Action.Load = function(form, options){
6967 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6968 this.reader = this.form.reader;
6971 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6976 Roo.Ajax.request(Roo.apply(
6977 this.createCallback(), {
6978 method:this.getMethod(),
6979 url:this.getUrl(false),
6980 params:this.getParams()
6984 success : function(response){
6986 var result = this.processResponse(response);
6987 if(result === true || !result.success || !result.data){
6988 this.failureType = Roo.form.Action.LOAD_FAILURE;
6989 this.form.afterAction(this, false);
6992 this.form.clearInvalid();
6993 this.form.setValues(result.data);
6994 this.form.afterAction(this, true);
6997 handleResponse : function(response){
6998 if(this.form.reader){
6999 var rs = this.form.reader.read(response);
7000 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7002 success : rs.success,
7006 return Roo.decode(response.responseText);
7010 Roo.form.Action.ACTION_TYPES = {
7011 'load' : Roo.form.Action.Load,
7012 'submit' : Roo.form.Action.Submit
7021 * @class Roo.bootstrap.Form
7022 * @extends Roo.bootstrap.Component
7023 * Bootstrap Form class
7024 * @cfg {String} method GET | POST (default POST)
7025 * @cfg {String} labelAlign top | left (default top)
7026 * @cfg {String} align left | right - for navbars
7027 * @cfg {Boolean} loadMask load mask when submit (default true)
7032 * @param {Object} config The config object
7036 Roo.bootstrap.Form = function(config){
7037 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7040 * @event clientvalidation
7041 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7042 * @param {Form} this
7043 * @param {Boolean} valid true if the form has passed client-side validation
7045 clientvalidation: true,
7047 * @event beforeaction
7048 * Fires before any action is performed. Return false to cancel the action.
7049 * @param {Form} this
7050 * @param {Action} action The action to be performed
7054 * @event actionfailed
7055 * Fires when an action fails.
7056 * @param {Form} this
7057 * @param {Action} action The action that failed
7059 actionfailed : true,
7061 * @event actioncomplete
7062 * Fires when an action is completed.
7063 * @param {Form} this
7064 * @param {Action} action The action that completed
7066 actioncomplete : true
7071 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7074 * @cfg {String} method
7075 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7080 * The URL to use for form actions if one isn't supplied in the action options.
7083 * @cfg {Boolean} fileUpload
7084 * Set to true if this form is a file upload.
7088 * @cfg {Object} baseParams
7089 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7093 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7097 * @cfg {Sting} align (left|right) for navbar forms
7102 activeAction : null,
7105 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7106 * element by passing it or its id or mask the form itself by passing in true.
7109 waitMsgTarget : false,
7113 getAutoCreate : function(){
7117 method : this.method || 'POST',
7118 id : this.id || Roo.id(),
7121 if (this.parent().xtype.match(/^Nav/)) {
7122 cfg.cls = 'navbar-form navbar-' + this.align;
7126 if (this.labelAlign == 'left' ) {
7127 cfg.cls += ' form-horizontal';
7133 initEvents : function()
7135 this.el.on('submit', this.onSubmit, this);
7136 // this was added as random key presses on the form where triggering form submit.
7137 this.el.on('keypress', function(e) {
7138 if (e.getCharCode() != 13) {
7141 // we might need to allow it for textareas.. and some other items.
7142 // check e.getTarget().
7144 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7148 Roo.log("keypress blocked");
7156 onSubmit : function(e){
7161 * Returns true if client-side validation on the form is successful.
7164 isValid : function(){
7165 var items = this.getItems();
7167 items.each(function(f){
7176 * Returns true if any fields in this form have changed since their original load.
7179 isDirty : function(){
7181 var items = this.getItems();
7182 items.each(function(f){
7192 * Performs a predefined action (submit or load) or custom actions you define on this form.
7193 * @param {String} actionName The name of the action type
7194 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7195 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7196 * accept other config options):
7198 Property Type Description
7199 ---------------- --------------- ----------------------------------------------------------------------------------
7200 url String The url for the action (defaults to the form's url)
7201 method String The form method to use (defaults to the form's method, or POST if not defined)
7202 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7203 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7204 validate the form on the client (defaults to false)
7206 * @return {BasicForm} this
7208 doAction : function(action, options){
7209 if(typeof action == 'string'){
7210 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7212 if(this.fireEvent('beforeaction', this, action) !== false){
7213 this.beforeAction(action);
7214 action.run.defer(100, action);
7220 beforeAction : function(action){
7221 var o = action.options;
7224 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7226 // not really supported yet.. ??
7228 //if(this.waitMsgTarget === true){
7229 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7230 //}else if(this.waitMsgTarget){
7231 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7232 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7234 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7240 afterAction : function(action, success){
7241 this.activeAction = null;
7242 var o = action.options;
7244 //if(this.waitMsgTarget === true){
7246 //}else if(this.waitMsgTarget){
7247 // this.waitMsgTarget.unmask();
7249 // Roo.MessageBox.updateProgress(1);
7250 // Roo.MessageBox.hide();
7257 Roo.callback(o.success, o.scope, [this, action]);
7258 this.fireEvent('actioncomplete', this, action);
7262 // failure condition..
7263 // we have a scenario where updates need confirming.
7264 // eg. if a locking scenario exists..
7265 // we look for { errors : { needs_confirm : true }} in the response.
7267 (typeof(action.result) != 'undefined') &&
7268 (typeof(action.result.errors) != 'undefined') &&
7269 (typeof(action.result.errors.needs_confirm) != 'undefined')
7272 Roo.log("not supported yet");
7275 Roo.MessageBox.confirm(
7276 "Change requires confirmation",
7277 action.result.errorMsg,
7282 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7292 Roo.callback(o.failure, o.scope, [this, action]);
7293 // show an error message if no failed handler is set..
7294 if (!this.hasListener('actionfailed')) {
7295 Roo.log("need to add dialog support");
7297 Roo.MessageBox.alert("Error",
7298 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7299 action.result.errorMsg :
7300 "Saving Failed, please check your entries or try again"
7305 this.fireEvent('actionfailed', this, action);
7310 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7311 * @param {String} id The value to search for
7314 findField : function(id){
7315 var items = this.getItems();
7316 var field = items.get(id);
7318 items.each(function(f){
7319 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7326 return field || null;
7329 * Mark fields in this form invalid in bulk.
7330 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7331 * @return {BasicForm} this
7333 markInvalid : function(errors){
7334 if(errors instanceof Array){
7335 for(var i = 0, len = errors.length; i < len; i++){
7336 var fieldError = errors[i];
7337 var f = this.findField(fieldError.id);
7339 f.markInvalid(fieldError.msg);
7345 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7346 field.markInvalid(errors[id]);
7350 //Roo.each(this.childForms || [], function (f) {
7351 // f.markInvalid(errors);
7358 * Set values for fields in this form in bulk.
7359 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7360 * @return {BasicForm} this
7362 setValues : function(values){
7363 if(values instanceof Array){ // array of objects
7364 for(var i = 0, len = values.length; i < len; i++){
7366 var f = this.findField(v.id);
7368 f.setValue(v.value);
7369 if(this.trackResetOnLoad){
7370 f.originalValue = f.getValue();
7374 }else{ // object hash
7377 if(typeof values[id] != 'function' && (field = this.findField(id))){
7379 if (field.setFromData &&
7381 field.displayField &&
7382 // combos' with local stores can
7383 // be queried via setValue()
7384 // to set their value..
7385 (field.store && !field.store.isLocal)
7389 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7390 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7391 field.setFromData(sd);
7394 field.setValue(values[id]);
7398 if(this.trackResetOnLoad){
7399 field.originalValue = field.getValue();
7405 //Roo.each(this.childForms || [], function (f) {
7406 // f.setValues(values);
7413 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7414 * they are returned as an array.
7415 * @param {Boolean} asString
7418 getValues : function(asString){
7419 //if (this.childForms) {
7420 // copy values from the child forms
7421 // Roo.each(this.childForms, function (f) {
7422 // this.setValues(f.getValues());
7428 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7429 if(asString === true){
7432 return Roo.urlDecode(fs);
7436 * Returns the fields in this form as an object with key/value pairs.
7437 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7440 getFieldValues : function(with_hidden)
7442 var items = this.getItems();
7444 items.each(function(f){
7448 var v = f.getValue();
7449 if (f.inputType =='radio') {
7450 if (typeof(ret[f.getName()]) == 'undefined') {
7451 ret[f.getName()] = ''; // empty..
7454 if (!f.el.dom.checked) {
7462 // not sure if this supported any more..
7463 if ((typeof(v) == 'object') && f.getRawValue) {
7464 v = f.getRawValue() ; // dates..
7466 // combo boxes where name != hiddenName...
7467 if (f.name != f.getName()) {
7468 ret[f.name] = f.getRawValue();
7470 ret[f.getName()] = v;
7477 * Clears all invalid messages in this form.
7478 * @return {BasicForm} this
7480 clearInvalid : function(){
7481 var items = this.getItems();
7483 items.each(function(f){
7494 * @return {BasicForm} this
7497 var items = this.getItems();
7498 items.each(function(f){
7502 Roo.each(this.childForms || [], function (f) {
7509 getItems : function()
7511 var r=new Roo.util.MixedCollection(false, function(o){
7512 return o.id || (o.id = Roo.id());
7514 var iter = function(el) {
7521 Roo.each(el.items,function(e) {
7541 * Ext JS Library 1.1.1
7542 * Copyright(c) 2006-2007, Ext JS, LLC.
7544 * Originally Released Under LGPL - original licence link has changed is not relivant.
7547 * <script type="text/javascript">
7550 * @class Roo.form.VTypes
7551 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7554 Roo.form.VTypes = function(){
7555 // closure these in so they are only created once.
7556 var alpha = /^[a-zA-Z_]+$/;
7557 var alphanum = /^[a-zA-Z0-9_]+$/;
7558 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7559 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7561 // All these messages and functions are configurable
7564 * The function used to validate email addresses
7565 * @param {String} value The email address
7567 'email' : function(v){
7568 return email.test(v);
7571 * The error text to display when the email validation function returns false
7574 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7576 * The keystroke filter mask to be applied on email input
7579 'emailMask' : /[a-z0-9_\.\-@]/i,
7582 * The function used to validate URLs
7583 * @param {String} value The URL
7585 'url' : function(v){
7589 * The error text to display when the url validation function returns false
7592 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7595 * The function used to validate alpha values
7596 * @param {String} value The value
7598 'alpha' : function(v){
7599 return alpha.test(v);
7602 * The error text to display when the alpha validation function returns false
7605 'alphaText' : 'This field should only contain letters and _',
7607 * The keystroke filter mask to be applied on alpha input
7610 'alphaMask' : /[a-z_]/i,
7613 * The function used to validate alphanumeric values
7614 * @param {String} value The value
7616 'alphanum' : function(v){
7617 return alphanum.test(v);
7620 * The error text to display when the alphanumeric validation function returns false
7623 'alphanumText' : 'This field should only contain letters, numbers and _',
7625 * The keystroke filter mask to be applied on alphanumeric input
7628 'alphanumMask' : /[a-z0-9_]/i
7638 * @class Roo.bootstrap.Input
7639 * @extends Roo.bootstrap.Component
7640 * Bootstrap Input class
7641 * @cfg {Boolean} disabled is it disabled
7642 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7643 * @cfg {String} name name of the input
7644 * @cfg {string} fieldLabel - the label associated
7645 * @cfg {string} placeholder - placeholder to put in text.
7646 * @cfg {string} before - input group add on before
7647 * @cfg {string} after - input group add on after
7648 * @cfg {string} size - (lg|sm) or leave empty..
7649 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7650 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7651 * @cfg {Number} md colspan out of 12 for computer-sized screens
7652 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7653 * @cfg {string} value default value of the input
7654 * @cfg {Number} labelWidth set the width of label (0-12)
7655 * @cfg {String} labelAlign (top|left)
7656 * @cfg {Boolean} readOnly Specifies that the field should be read-only
7657 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7659 * @cfg {String} align (left|center|right) Default left
7660 * @cfg {Boolean} forceFeedback (true|false) Default false
7666 * Create a new Input
7667 * @param {Object} config The config object
7670 Roo.bootstrap.Input = function(config){
7671 Roo.bootstrap.Input.superclass.constructor.call(this, config);
7676 * Fires when this field receives input focus.
7677 * @param {Roo.form.Field} this
7682 * Fires when this field loses input focus.
7683 * @param {Roo.form.Field} this
7688 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
7689 * {@link Roo.EventObject#getKey} to determine which key was pressed.
7690 * @param {Roo.form.Field} this
7691 * @param {Roo.EventObject} e The event object
7696 * Fires just before the field blurs if the field value has changed.
7697 * @param {Roo.form.Field} this
7698 * @param {Mixed} newValue The new value
7699 * @param {Mixed} oldValue The original value
7704 * Fires after the field has been marked as invalid.
7705 * @param {Roo.form.Field} this
7706 * @param {String} msg The validation message
7711 * Fires after the field has been validated with no errors.
7712 * @param {Roo.form.Field} this
7717 * Fires after the key up
7718 * @param {Roo.form.Field} this
7719 * @param {Roo.EventObject} e The event Object
7725 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
7727 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7728 automatic validation (defaults to "keyup").
7730 validationEvent : "keyup",
7732 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7734 validateOnBlur : true,
7736 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7738 validationDelay : 250,
7740 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7742 focusClass : "x-form-focus", // not needed???
7746 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7748 invalidClass : "has-warning",
7751 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7753 validClass : "has-success",
7756 * @cfg {Boolean} hasFeedback (true|false) default true
7761 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7763 invalidFeedbackClass : "glyphicon-warning-sign",
7766 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7768 validFeedbackClass : "glyphicon-ok",
7771 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7773 selectOnFocus : false,
7776 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7780 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7785 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7787 disableKeyFilter : false,
7790 * @cfg {Boolean} disabled True to disable the field (defaults to false).
7794 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7798 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7800 blankText : "This field is required",
7803 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7807 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7809 maxLength : Number.MAX_VALUE,
7811 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7813 minLengthText : "The minimum length for this field is {0}",
7815 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7817 maxLengthText : "The maximum length for this field is {0}",
7821 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7822 * If available, this function will be called only after the basic validators all return true, and will be passed the
7823 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7827 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7828 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7829 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
7833 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7837 autocomplete: false,
7856 formatedValue : false,
7857 forceFeedback : false,
7859 parentLabelAlign : function()
7862 while (parent.parent()) {
7863 parent = parent.parent();
7864 if (typeof(parent.labelAlign) !='undefined') {
7865 return parent.labelAlign;
7872 getAutoCreate : function(){
7874 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7880 if(this.inputType != 'hidden'){
7881 cfg.cls = 'form-group' //input-group
7887 type : this.inputType,
7889 cls : 'form-control',
7890 placeholder : this.placeholder || '',
7891 autocomplete : this.autocomplete || 'new-password'
7896 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7899 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7900 input.maxLength = this.maxLength;
7903 if (this.disabled) {
7904 input.disabled=true;
7907 if (this.readOnly) {
7908 input.readonly=true;
7912 input.name = this.name;
7915 input.cls += ' input-' + this.size;
7918 ['xs','sm','md','lg'].map(function(size){
7919 if (settings[size]) {
7920 cfg.cls += ' col-' + size + '-' + settings[size];
7924 var inputblock = input;
7928 cls: 'glyphicon form-control-feedback'
7931 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7934 cls : 'has-feedback',
7942 if (this.before || this.after) {
7945 cls : 'input-group',
7949 if (this.before && typeof(this.before) == 'string') {
7951 inputblock.cn.push({
7953 cls : 'roo-input-before input-group-addon',
7957 if (this.before && typeof(this.before) == 'object') {
7958 this.before = Roo.factory(this.before);
7959 Roo.log(this.before);
7960 inputblock.cn.push({
7962 cls : 'roo-input-before input-group-' +
7963 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7967 inputblock.cn.push(input);
7969 if (this.after && typeof(this.after) == 'string') {
7970 inputblock.cn.push({
7972 cls : 'roo-input-after input-group-addon',
7976 if (this.after && typeof(this.after) == 'object') {
7977 this.after = Roo.factory(this.after);
7978 Roo.log(this.after);
7979 inputblock.cn.push({
7981 cls : 'roo-input-after input-group-' +
7982 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7986 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7987 inputblock.cls += ' has-feedback';
7988 inputblock.cn.push(feedback);
7992 if (align ==='left' && this.fieldLabel.length) {
7993 Roo.log("left and has label");
7999 cls : 'control-label col-sm-' + this.labelWidth,
8000 html : this.fieldLabel
8004 cls : "col-sm-" + (12 - this.labelWidth),
8011 } else if ( this.fieldLabel.length) {
8017 //cls : 'input-group-addon',
8018 html : this.fieldLabel
8028 Roo.log(" no label && no align");
8037 Roo.log('input-parentType: ' + this.parentType);
8039 if (this.parentType === 'Navbar' && this.parent().bar) {
8040 cfg.cls += ' navbar-form';
8048 * return the real input element.
8050 inputEl: function ()
8052 return this.el.select('input.form-control',true).first();
8055 tooltipEl : function()
8057 return this.inputEl();
8060 setDisabled : function(v)
8062 var i = this.inputEl().dom;
8064 i.removeAttribute('disabled');
8068 i.setAttribute('disabled','true');
8070 initEvents : function()
8073 this.inputEl().on("keydown" , this.fireKey, this);
8074 this.inputEl().on("focus", this.onFocus, this);
8075 this.inputEl().on("blur", this.onBlur, this);
8077 this.inputEl().relayEvent('keyup', this);
8079 // reference to original value for reset
8080 this.originalValue = this.getValue();
8081 //Roo.form.TextField.superclass.initEvents.call(this);
8082 if(this.validationEvent == 'keyup'){
8083 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8084 this.inputEl().on('keyup', this.filterValidation, this);
8086 else if(this.validationEvent !== false){
8087 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8090 if(this.selectOnFocus){
8091 this.on("focus", this.preFocus, this);
8094 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8095 this.inputEl().on("keypress", this.filterKeys, this);
8098 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8099 this.el.on("click", this.autoSize, this);
8102 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8103 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8106 if (typeof(this.before) == 'object') {
8107 this.before.render(this.el.select('.roo-input-before',true).first());
8109 if (typeof(this.after) == 'object') {
8110 this.after.render(this.el.select('.roo-input-after',true).first());
8115 filterValidation : function(e){
8116 if(!e.isNavKeyPress()){
8117 this.validationTask.delay(this.validationDelay);
8121 * Validates the field value
8122 * @return {Boolean} True if the value is valid, else false
8124 validate : function(){
8125 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8126 if(this.disabled || this.validateValue(this.getRawValue())){
8137 * Validates a value according to the field's validation rules and marks the field as invalid
8138 * if the validation fails
8139 * @param {Mixed} value The value to validate
8140 * @return {Boolean} True if the value is valid, else false
8142 validateValue : function(value){
8143 if(value.length < 1) { // if it's blank
8144 if(this.allowBlank){
8150 if(value.length < this.minLength){
8153 if(value.length > this.maxLength){
8157 var vt = Roo.form.VTypes;
8158 if(!vt[this.vtype](value, this)){
8162 if(typeof this.validator == "function"){
8163 var msg = this.validator(value);
8169 if(this.regex && !this.regex.test(value)){
8179 fireKey : function(e){
8180 //Roo.log('field ' + e.getKey());
8181 if(e.isNavKeyPress()){
8182 this.fireEvent("specialkey", this, e);
8185 focus : function (selectText){
8187 this.inputEl().focus();
8188 if(selectText === true){
8189 this.inputEl().dom.select();
8195 onFocus : function(){
8196 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8197 // this.el.addClass(this.focusClass);
8200 this.hasFocus = true;
8201 this.startValue = this.getValue();
8202 this.fireEvent("focus", this);
8206 beforeBlur : Roo.emptyFn,
8210 onBlur : function(){
8212 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8213 //this.el.removeClass(this.focusClass);
8215 this.hasFocus = false;
8216 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8219 var v = this.getValue();
8220 if(String(v) !== String(this.startValue)){
8221 this.fireEvent('change', this, v, this.startValue);
8223 this.fireEvent("blur", this);
8227 * Resets the current field value to the originally loaded value and clears any validation messages
8230 this.setValue(this.originalValue);
8234 * Returns the name of the field
8235 * @return {Mixed} name The name field
8237 getName: function(){
8241 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8242 * @return {Mixed} value The field value
8244 getValue : function(){
8246 var v = this.inputEl().getValue();
8251 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8252 * @return {Mixed} value The field value
8254 getRawValue : function(){
8255 var v = this.inputEl().getValue();
8261 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8262 * @param {Mixed} value The value to set
8264 setRawValue : function(v){
8265 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8268 selectText : function(start, end){
8269 var v = this.getRawValue();
8271 start = start === undefined ? 0 : start;
8272 end = end === undefined ? v.length : end;
8273 var d = this.inputEl().dom;
8274 if(d.setSelectionRange){
8275 d.setSelectionRange(start, end);
8276 }else if(d.createTextRange){
8277 var range = d.createTextRange();
8278 range.moveStart("character", start);
8279 range.moveEnd("character", v.length-end);
8286 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8287 * @param {Mixed} value The value to set
8289 setValue : function(v){
8292 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8298 processValue : function(value){
8299 if(this.stripCharsRe){
8300 var newValue = value.replace(this.stripCharsRe, '');
8301 if(newValue !== value){
8302 this.setRawValue(newValue);
8309 preFocus : function(){
8311 if(this.selectOnFocus){
8312 this.inputEl().dom.select();
8315 filterKeys : function(e){
8317 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8320 var c = e.getCharCode(), cc = String.fromCharCode(c);
8321 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8324 if(!this.maskRe.test(cc)){
8329 * Clear any invalid styles/messages for this field
8331 clearInvalid : function(){
8333 if(!this.el || this.preventMark){ // not rendered
8336 this.el.removeClass(this.invalidClass);
8338 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8340 var feedback = this.el.select('.form-control-feedback', true).first();
8343 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8348 this.fireEvent('valid', this);
8352 * Mark this field as valid
8354 markValid : function(){
8355 if(!this.el || this.preventMark){ // not rendered
8359 this.el.removeClass([this.invalidClass, this.validClass]);
8361 var feedback = this.el.select('.form-control-feedback', true).first();
8364 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8367 if(this.disabled || this.allowBlank){
8371 this.el.addClass(this.validClass);
8373 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8375 var feedback = this.el.select('.form-control-feedback', true).first();
8378 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8379 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8384 this.fireEvent('valid', this);
8388 * Mark this field as invalid
8389 * @param {String} msg The validation message
8391 markInvalid : function(msg)
8393 if(!this.el || this.preventMark){ // not rendered
8397 this.el.removeClass([this.invalidClass, this.validClass]);
8399 var feedback = this.el.select('.form-control-feedback', true).first();
8402 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8405 if(this.disabled || this.allowBlank){
8409 this.el.addClass(this.invalidClass);
8411 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8413 var feedback = this.el.select('.form-control-feedback', true).first();
8416 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8418 if(this.getValue().length || this.forceFeedback){
8419 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8426 this.fireEvent('invalid', this, msg);
8429 SafariOnKeyDown : function(event)
8431 // this is a workaround for a password hang bug on chrome/ webkit.
8433 var isSelectAll = false;
8435 if(this.inputEl().dom.selectionEnd > 0){
8436 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8438 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8439 event.preventDefault();
8444 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8446 event.preventDefault();
8447 // this is very hacky as keydown always get's upper case.
8449 var cc = String.fromCharCode(event.getCharCode());
8450 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8454 adjustWidth : function(tag, w){
8455 tag = tag.toLowerCase();
8456 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8457 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8461 if(tag == 'textarea'){
8464 }else if(Roo.isOpera){
8468 if(tag == 'textarea'){
8487 * @class Roo.bootstrap.TextArea
8488 * @extends Roo.bootstrap.Input
8489 * Bootstrap TextArea class
8490 * @cfg {Number} cols Specifies the visible width of a text area
8491 * @cfg {Number} rows Specifies the visible number of lines in a text area
8492 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8493 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8494 * @cfg {string} html text
8497 * Create a new TextArea
8498 * @param {Object} config The config object
8501 Roo.bootstrap.TextArea = function(config){
8502 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8506 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8516 getAutoCreate : function(){
8518 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8529 value : this.value || '',
8530 html: this.html || '',
8531 cls : 'form-control',
8532 placeholder : this.placeholder || ''
8536 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8537 input.maxLength = this.maxLength;
8541 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8545 input.cols = this.cols;
8548 if (this.readOnly) {
8549 input.readonly = true;
8553 input.name = this.name;
8557 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8561 ['xs','sm','md','lg'].map(function(size){
8562 if (settings[size]) {
8563 cfg.cls += ' col-' + size + '-' + settings[size];
8567 var inputblock = input;
8569 if(this.hasFeedback && !this.allowBlank){
8573 cls: 'glyphicon form-control-feedback'
8577 cls : 'has-feedback',
8586 if (this.before || this.after) {
8589 cls : 'input-group',
8593 inputblock.cn.push({
8595 cls : 'input-group-addon',
8600 inputblock.cn.push(input);
8602 if(this.hasFeedback && !this.allowBlank){
8603 inputblock.cls += ' has-feedback';
8604 inputblock.cn.push(feedback);
8608 inputblock.cn.push({
8610 cls : 'input-group-addon',
8617 if (align ==='left' && this.fieldLabel.length) {
8618 Roo.log("left and has label");
8624 cls : 'control-label col-sm-' + this.labelWidth,
8625 html : this.fieldLabel
8629 cls : "col-sm-" + (12 - this.labelWidth),
8636 } else if ( this.fieldLabel.length) {
8642 //cls : 'input-group-addon',
8643 html : this.fieldLabel
8653 Roo.log(" no label && no align");
8663 if (this.disabled) {
8664 input.disabled=true;
8671 * return the real textarea element.
8673 inputEl: function ()
8675 return this.el.select('textarea.form-control',true).first();
8683 * trigger field - base class for combo..
8688 * @class Roo.bootstrap.TriggerField
8689 * @extends Roo.bootstrap.Input
8690 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8691 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8692 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8693 * for which you can provide a custom implementation. For example:
8695 var trigger = new Roo.bootstrap.TriggerField();
8696 trigger.onTriggerClick = myTriggerFn;
8697 trigger.applyTo('my-field');
8700 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8701 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8702 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
8703 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8704 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8707 * Create a new TriggerField.
8708 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8709 * to the base TextField)
8711 Roo.bootstrap.TriggerField = function(config){
8712 this.mimicing = false;
8713 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8716 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
8718 * @cfg {String} triggerClass A CSS class to apply to the trigger
8721 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8726 * @cfg {Boolean} removable (true|false) special filter default false
8730 /** @cfg {Boolean} grow @hide */
8731 /** @cfg {Number} growMin @hide */
8732 /** @cfg {Number} growMax @hide */
8738 autoSize: Roo.emptyFn,
8745 actionMode : 'wrap',
8750 getAutoCreate : function(){
8752 var align = this.labelAlign || this.parentLabelAlign();
8757 cls: 'form-group' //input-group
8764 type : this.inputType,
8765 cls : 'form-control',
8766 autocomplete: 'new-password',
8767 placeholder : this.placeholder || ''
8771 input.name = this.name;
8774 input.cls += ' input-' + this.size;
8777 if (this.disabled) {
8778 input.disabled=true;
8781 var inputblock = input;
8783 if(this.hasFeedback && !this.allowBlank){
8787 cls: 'glyphicon form-control-feedback'
8790 if(this.removable && !this.editable && !this.tickable){
8792 cls : 'has-feedback',
8798 cls : 'roo-combo-removable-btn close'
8805 cls : 'has-feedback',
8814 if(this.removable && !this.editable && !this.tickable){
8816 cls : 'roo-removable',
8822 cls : 'roo-combo-removable-btn close'
8829 if (this.before || this.after) {
8832 cls : 'input-group',
8836 inputblock.cn.push({
8838 cls : 'input-group-addon',
8843 inputblock.cn.push(input);
8845 if(this.hasFeedback && !this.allowBlank){
8846 inputblock.cls += ' has-feedback';
8847 inputblock.cn.push(feedback);
8851 inputblock.cn.push({
8853 cls : 'input-group-addon',
8866 cls: 'form-hidden-field'
8874 Roo.log('multiple');
8882 cls: 'form-hidden-field'
8886 cls: 'select2-choices',
8890 cls: 'select2-search-field',
8903 cls: 'select2-container input-group',
8908 // cls: 'typeahead typeahead-long dropdown-menu',
8909 // style: 'display:none'
8914 if(!this.multiple && this.showToggleBtn){
8920 if (this.caret != false) {
8923 cls: 'fa fa-' + this.caret
8930 cls : 'input-group-addon btn dropdown-toggle',
8935 cls: 'combobox-clear',
8949 combobox.cls += ' select2-container-multi';
8952 if (align ==='left' && this.fieldLabel.length) {
8954 Roo.log("left and has label");
8960 cls : 'control-label col-sm-' + this.labelWidth,
8961 html : this.fieldLabel
8965 cls : "col-sm-" + (12 - this.labelWidth),
8972 } else if ( this.fieldLabel.length) {
8978 //cls : 'input-group-addon',
8979 html : this.fieldLabel
8989 Roo.log(" no label && no align");
8996 ['xs','sm','md','lg'].map(function(size){
8997 if (settings[size]) {
8998 cfg.cls += ' col-' + size + '-' + settings[size];
9009 onResize : function(w, h){
9010 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9011 // if(typeof w == 'number'){
9012 // var x = w - this.trigger.getWidth();
9013 // this.inputEl().setWidth(this.adjustWidth('input', x));
9014 // this.trigger.setStyle('left', x+'px');
9019 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9022 getResizeEl : function(){
9023 return this.inputEl();
9027 getPositionEl : function(){
9028 return this.inputEl();
9032 alignErrorIcon : function(){
9033 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9037 initEvents : function(){
9041 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9042 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9043 if(!this.multiple && this.showToggleBtn){
9044 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9045 if(this.hideTrigger){
9046 this.trigger.setDisplayed(false);
9048 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9052 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9055 if(this.removable && !this.editable && !this.tickable){
9056 var close = this.closeTriggerEl();
9059 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9060 close.on('click', this.removeBtnClick, this, close);
9064 //this.trigger.addClassOnOver('x-form-trigger-over');
9065 //this.trigger.addClassOnClick('x-form-trigger-click');
9068 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9072 closeTriggerEl : function()
9074 var close = this.el.select('.roo-combo-removable-btn', true).first();
9075 return close ? close : false;
9078 removeBtnClick : function(e, h, el)
9082 if(this.fireEvent("remove", this) !== false){
9087 createList : function()
9089 this.list = Roo.get(document.body).createChild({
9091 cls: 'typeahead typeahead-long dropdown-menu',
9092 style: 'display:none'
9095 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9100 initTrigger : function(){
9105 onDestroy : function(){
9107 this.trigger.removeAllListeners();
9108 // this.trigger.remove();
9111 // this.wrap.remove();
9113 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9117 onFocus : function(){
9118 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9121 this.wrap.addClass('x-trigger-wrap-focus');
9122 this.mimicing = true;
9123 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9124 if(this.monitorTab){
9125 this.el.on("keydown", this.checkTab, this);
9132 checkTab : function(e){
9133 if(e.getKey() == e.TAB){
9139 onBlur : function(){
9144 mimicBlur : function(e, t){
9146 if(!this.wrap.contains(t) && this.validateBlur()){
9153 triggerBlur : function(){
9154 this.mimicing = false;
9155 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9156 if(this.monitorTab){
9157 this.el.un("keydown", this.checkTab, this);
9159 //this.wrap.removeClass('x-trigger-wrap-focus');
9160 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9164 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9165 validateBlur : function(e, t){
9170 onDisable : function(){
9171 this.inputEl().dom.disabled = true;
9172 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9174 // this.wrap.addClass('x-item-disabled');
9179 onEnable : function(){
9180 this.inputEl().dom.disabled = false;
9181 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9183 // this.el.removeClass('x-item-disabled');
9188 onShow : function(){
9189 var ae = this.getActionEl();
9192 ae.dom.style.display = '';
9193 ae.dom.style.visibility = 'visible';
9199 onHide : function(){
9200 var ae = this.getActionEl();
9201 ae.dom.style.display = 'none';
9205 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9206 * by an implementing function.
9208 * @param {EventObject} e
9210 onTriggerClick : Roo.emptyFn
9214 * Ext JS Library 1.1.1
9215 * Copyright(c) 2006-2007, Ext JS, LLC.
9217 * Originally Released Under LGPL - original licence link has changed is not relivant.
9220 * <script type="text/javascript">
9225 * @class Roo.data.SortTypes
9227 * Defines the default sorting (casting?) comparison functions used when sorting data.
9229 Roo.data.SortTypes = {
9231 * Default sort that does nothing
9232 * @param {Mixed} s The value being converted
9233 * @return {Mixed} The comparison value
9240 * The regular expression used to strip tags
9244 stripTagsRE : /<\/?[^>]+>/gi,
9247 * Strips all HTML tags to sort on text only
9248 * @param {Mixed} s The value being converted
9249 * @return {String} The comparison value
9251 asText : function(s){
9252 return String(s).replace(this.stripTagsRE, "");
9256 * Strips all HTML tags to sort on text only - Case insensitive
9257 * @param {Mixed} s The value being converted
9258 * @return {String} The comparison value
9260 asUCText : function(s){
9261 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9265 * Case insensitive string
9266 * @param {Mixed} s The value being converted
9267 * @return {String} The comparison value
9269 asUCString : function(s) {
9270 return String(s).toUpperCase();
9275 * @param {Mixed} s The value being converted
9276 * @return {Number} The comparison value
9278 asDate : function(s) {
9282 if(s instanceof Date){
9285 return Date.parse(String(s));
9290 * @param {Mixed} s The value being converted
9291 * @return {Float} The comparison value
9293 asFloat : function(s) {
9294 var val = parseFloat(String(s).replace(/,/g, ""));
9295 if(isNaN(val)) val = 0;
9301 * @param {Mixed} s The value being converted
9302 * @return {Number} The comparison value
9304 asInt : function(s) {
9305 var val = parseInt(String(s).replace(/,/g, ""));
9306 if(isNaN(val)) val = 0;
9311 * Ext JS Library 1.1.1
9312 * Copyright(c) 2006-2007, Ext JS, LLC.
9314 * Originally Released Under LGPL - original licence link has changed is not relivant.
9317 * <script type="text/javascript">
9321 * @class Roo.data.Record
9322 * Instances of this class encapsulate both record <em>definition</em> information, and record
9323 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9324 * to access Records cached in an {@link Roo.data.Store} object.<br>
9326 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9327 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9330 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9332 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9333 * {@link #create}. The parameters are the same.
9334 * @param {Array} data An associative Array of data values keyed by the field name.
9335 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9336 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9337 * not specified an integer id is generated.
9339 Roo.data.Record = function(data, id){
9340 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9345 * Generate a constructor for a specific record layout.
9346 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9347 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9348 * Each field definition object may contain the following properties: <ul>
9349 * <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,
9350 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9351 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9352 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9353 * is being used, then this is a string containing the javascript expression to reference the data relative to
9354 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9355 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9356 * this may be omitted.</p></li>
9357 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9358 * <ul><li>auto (Default, implies no conversion)</li>
9363 * <li>date</li></ul></p></li>
9364 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9365 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9366 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9367 * by the Reader into an object that will be stored in the Record. It is passed the
9368 * following parameters:<ul>
9369 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9371 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9373 * <br>usage:<br><pre><code>
9374 var TopicRecord = Roo.data.Record.create(
9375 {name: 'title', mapping: 'topic_title'},
9376 {name: 'author', mapping: 'username'},
9377 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9378 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9379 {name: 'lastPoster', mapping: 'user2'},
9380 {name: 'excerpt', mapping: 'post_text'}
9383 var myNewRecord = new TopicRecord({
9384 title: 'Do my job please',
9387 lastPost: new Date(),
9388 lastPoster: 'Animal',
9389 excerpt: 'No way dude!'
9391 myStore.add(myNewRecord);
9396 Roo.data.Record.create = function(o){
9398 f.superclass.constructor.apply(this, arguments);
9400 Roo.extend(f, Roo.data.Record);
9401 var p = f.prototype;
9402 p.fields = new Roo.util.MixedCollection(false, function(field){
9405 for(var i = 0, len = o.length; i < len; i++){
9406 p.fields.add(new Roo.data.Field(o[i]));
9408 f.getField = function(name){
9409 return p.fields.get(name);
9414 Roo.data.Record.AUTO_ID = 1000;
9415 Roo.data.Record.EDIT = 'edit';
9416 Roo.data.Record.REJECT = 'reject';
9417 Roo.data.Record.COMMIT = 'commit';
9419 Roo.data.Record.prototype = {
9421 * Readonly flag - true if this record has been modified.
9430 join : function(store){
9435 * Set the named field to the specified value.
9436 * @param {String} name The name of the field to set.
9437 * @param {Object} value The value to set the field to.
9439 set : function(name, value){
9440 if(this.data[name] == value){
9447 if(typeof this.modified[name] == 'undefined'){
9448 this.modified[name] = this.data[name];
9450 this.data[name] = value;
9451 if(!this.editing && this.store){
9452 this.store.afterEdit(this);
9457 * Get the value of the named field.
9458 * @param {String} name The name of the field to get the value of.
9459 * @return {Object} The value of the field.
9461 get : function(name){
9462 return this.data[name];
9466 beginEdit : function(){
9467 this.editing = true;
9472 cancelEdit : function(){
9473 this.editing = false;
9474 delete this.modified;
9478 endEdit : function(){
9479 this.editing = false;
9480 if(this.dirty && this.store){
9481 this.store.afterEdit(this);
9486 * Usually called by the {@link Roo.data.Store} which owns the Record.
9487 * Rejects all changes made to the Record since either creation, or the last commit operation.
9488 * Modified fields are reverted to their original values.
9490 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9491 * of reject operations.
9493 reject : function(){
9494 var m = this.modified;
9496 if(typeof m[n] != "function"){
9497 this.data[n] = m[n];
9501 delete this.modified;
9502 this.editing = false;
9504 this.store.afterReject(this);
9509 * Usually called by the {@link Roo.data.Store} which owns the Record.
9510 * Commits all changes made to the Record since either creation, or the last commit operation.
9512 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9513 * of commit operations.
9515 commit : function(){
9517 delete this.modified;
9518 this.editing = false;
9520 this.store.afterCommit(this);
9525 hasError : function(){
9526 return this.error != null;
9530 clearError : function(){
9535 * Creates a copy of this record.
9536 * @param {String} id (optional) A new record id if you don't want to use this record's id
9539 copy : function(newId) {
9540 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9544 * Ext JS Library 1.1.1
9545 * Copyright(c) 2006-2007, Ext JS, LLC.
9547 * Originally Released Under LGPL - original licence link has changed is not relivant.
9550 * <script type="text/javascript">
9556 * @class Roo.data.Store
9557 * @extends Roo.util.Observable
9558 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9559 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9561 * 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
9562 * has no knowledge of the format of the data returned by the Proxy.<br>
9564 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9565 * instances from the data object. These records are cached and made available through accessor functions.
9567 * Creates a new Store.
9568 * @param {Object} config A config object containing the objects needed for the Store to access data,
9569 * and read the data into Records.
9571 Roo.data.Store = function(config){
9572 this.data = new Roo.util.MixedCollection(false);
9573 this.data.getKey = function(o){
9576 this.baseParams = {};
9583 "multisort" : "_multisort"
9586 if(config && config.data){
9587 this.inlineData = config.data;
9591 Roo.apply(this, config);
9593 if(this.reader){ // reader passed
9594 this.reader = Roo.factory(this.reader, Roo.data);
9595 this.reader.xmodule = this.xmodule || false;
9596 if(!this.recordType){
9597 this.recordType = this.reader.recordType;
9599 if(this.reader.onMetaChange){
9600 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9604 if(this.recordType){
9605 this.fields = this.recordType.prototype.fields;
9611 * @event datachanged
9612 * Fires when the data cache has changed, and a widget which is using this Store
9613 * as a Record cache should refresh its view.
9614 * @param {Store} this
9619 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9620 * @param {Store} this
9621 * @param {Object} meta The JSON metadata
9626 * Fires when Records have been added to the Store
9627 * @param {Store} this
9628 * @param {Roo.data.Record[]} records The array of Records added
9629 * @param {Number} index The index at which the record(s) were added
9634 * Fires when a Record has been removed from the Store
9635 * @param {Store} this
9636 * @param {Roo.data.Record} record The Record that was removed
9637 * @param {Number} index The index at which the record was removed
9642 * Fires when a Record has been updated
9643 * @param {Store} this
9644 * @param {Roo.data.Record} record The Record that was updated
9645 * @param {String} operation The update operation being performed. Value may be one of:
9647 Roo.data.Record.EDIT
9648 Roo.data.Record.REJECT
9649 Roo.data.Record.COMMIT
9655 * Fires when the data cache has been cleared.
9656 * @param {Store} this
9661 * Fires before a request is made for a new data object. If the beforeload handler returns false
9662 * the load action will be canceled.
9663 * @param {Store} this
9664 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9668 * @event beforeloadadd
9669 * Fires after a new set of Records has been loaded.
9670 * @param {Store} this
9671 * @param {Roo.data.Record[]} records The Records that were loaded
9672 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9674 beforeloadadd : true,
9677 * Fires after a new set of Records has been loaded, before they are added to the store.
9678 * @param {Store} this
9679 * @param {Roo.data.Record[]} records The Records that were loaded
9680 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9681 * @params {Object} return from reader
9685 * @event loadexception
9686 * Fires if an exception occurs in the Proxy during loading.
9687 * Called with the signature of the Proxy's "loadexception" event.
9688 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9691 * @param {Object} return from JsonData.reader() - success, totalRecords, records
9692 * @param {Object} load options
9693 * @param {Object} jsonData from your request (normally this contains the Exception)
9695 loadexception : true
9699 this.proxy = Roo.factory(this.proxy, Roo.data);
9700 this.proxy.xmodule = this.xmodule || false;
9701 this.relayEvents(this.proxy, ["loadexception"]);
9703 this.sortToggle = {};
9704 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9706 Roo.data.Store.superclass.constructor.call(this);
9708 if(this.inlineData){
9709 this.loadData(this.inlineData);
9710 delete this.inlineData;
9714 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9716 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
9717 * without a remote query - used by combo/forms at present.
9721 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9724 * @cfg {Array} data Inline data to be loaded when the store is initialized.
9727 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9728 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9731 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9732 * on any HTTP request
9735 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9738 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9742 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9743 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9748 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9749 * loaded or when a record is removed. (defaults to false).
9751 pruneModifiedRecords : false,
9757 * Add Records to the Store and fires the add event.
9758 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9760 add : function(records){
9761 records = [].concat(records);
9762 for(var i = 0, len = records.length; i < len; i++){
9763 records[i].join(this);
9765 var index = this.data.length;
9766 this.data.addAll(records);
9767 this.fireEvent("add", this, records, index);
9771 * Remove a Record from the Store and fires the remove event.
9772 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9774 remove : function(record){
9775 var index = this.data.indexOf(record);
9776 this.data.removeAt(index);
9777 if(this.pruneModifiedRecords){
9778 this.modified.remove(record);
9780 this.fireEvent("remove", this, record, index);
9784 * Remove all Records from the Store and fires the clear event.
9786 removeAll : function(){
9788 if(this.pruneModifiedRecords){
9791 this.fireEvent("clear", this);
9795 * Inserts Records to the Store at the given index and fires the add event.
9796 * @param {Number} index The start index at which to insert the passed Records.
9797 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9799 insert : function(index, records){
9800 records = [].concat(records);
9801 for(var i = 0, len = records.length; i < len; i++){
9802 this.data.insert(index, records[i]);
9803 records[i].join(this);
9805 this.fireEvent("add", this, records, index);
9809 * Get the index within the cache of the passed Record.
9810 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9811 * @return {Number} The index of the passed Record. Returns -1 if not found.
9813 indexOf : function(record){
9814 return this.data.indexOf(record);
9818 * Get the index within the cache of the Record with the passed id.
9819 * @param {String} id The id of the Record to find.
9820 * @return {Number} The index of the Record. Returns -1 if not found.
9822 indexOfId : function(id){
9823 return this.data.indexOfKey(id);
9827 * Get the Record with the specified id.
9828 * @param {String} id The id of the Record to find.
9829 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9831 getById : function(id){
9832 return this.data.key(id);
9836 * Get the Record at the specified index.
9837 * @param {Number} index The index of the Record to find.
9838 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9840 getAt : function(index){
9841 return this.data.itemAt(index);
9845 * Returns a range of Records between specified indices.
9846 * @param {Number} startIndex (optional) The starting index (defaults to 0)
9847 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9848 * @return {Roo.data.Record[]} An array of Records
9850 getRange : function(start, end){
9851 return this.data.getRange(start, end);
9855 storeOptions : function(o){
9856 o = Roo.apply({}, o);
9859 this.lastOptions = o;
9863 * Loads the Record cache from the configured Proxy using the configured Reader.
9865 * If using remote paging, then the first load call must specify the <em>start</em>
9866 * and <em>limit</em> properties in the options.params property to establish the initial
9867 * position within the dataset, and the number of Records to cache on each read from the Proxy.
9869 * <strong>It is important to note that for remote data sources, loading is asynchronous,
9870 * and this call will return before the new data has been loaded. Perform any post-processing
9871 * in a callback function, or in a "load" event handler.</strong>
9873 * @param {Object} options An object containing properties which control loading options:<ul>
9874 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9875 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9876 * passed the following arguments:<ul>
9877 * <li>r : Roo.data.Record[]</li>
9878 * <li>options: Options object from the load call</li>
9879 * <li>success: Boolean success indicator</li></ul></li>
9880 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9881 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9884 load : function(options){
9885 options = options || {};
9886 if(this.fireEvent("beforeload", this, options) !== false){
9887 this.storeOptions(options);
9888 var p = Roo.apply(options.params || {}, this.baseParams);
9889 // if meta was not loaded from remote source.. try requesting it.
9890 if (!this.reader.metaFromRemote) {
9893 if(this.sortInfo && this.remoteSort){
9894 var pn = this.paramNames;
9895 p[pn["sort"]] = this.sortInfo.field;
9896 p[pn["dir"]] = this.sortInfo.direction;
9898 if (this.multiSort) {
9899 var pn = this.paramNames;
9900 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9903 this.proxy.load(p, this.reader, this.loadRecords, this, options);
9908 * Reloads the Record cache from the configured Proxy using the configured Reader and
9909 * the options from the last load operation performed.
9910 * @param {Object} options (optional) An object containing properties which may override the options
9911 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9912 * the most recently used options are reused).
9914 reload : function(options){
9915 this.load(Roo.applyIf(options||{}, this.lastOptions));
9919 // Called as a callback by the Reader during a load operation.
9920 loadRecords : function(o, options, success){
9921 if(!o || success === false){
9922 if(success !== false){
9923 this.fireEvent("load", this, [], options, o);
9925 if(options.callback){
9926 options.callback.call(options.scope || this, [], options, false);
9930 // if data returned failure - throw an exception.
9931 if (o.success === false) {
9932 // show a message if no listener is registered.
9933 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9934 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9936 // loadmask wil be hooked into this..
9937 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9940 var r = o.records, t = o.totalRecords || r.length;
9942 this.fireEvent("beforeloadadd", this, r, options, o);
9944 if(!options || options.add !== true){
9945 if(this.pruneModifiedRecords){
9948 for(var i = 0, len = r.length; i < len; i++){
9952 this.data = this.snapshot;
9953 delete this.snapshot;
9956 this.data.addAll(r);
9957 this.totalLength = t;
9959 this.fireEvent("datachanged", this);
9961 this.totalLength = Math.max(t, this.data.length+r.length);
9964 this.fireEvent("load", this, r, options, o);
9965 if(options.callback){
9966 options.callback.call(options.scope || this, r, options, true);
9972 * Loads data from a passed data block. A Reader which understands the format of the data
9973 * must have been configured in the constructor.
9974 * @param {Object} data The data block from which to read the Records. The format of the data expected
9975 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9976 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9978 loadData : function(o, append){
9979 var r = this.reader.readRecords(o);
9980 this.loadRecords(r, {add: append}, true);
9984 * Gets the number of cached records.
9986 * <em>If using paging, this may not be the total size of the dataset. If the data object
9987 * used by the Reader contains the dataset size, then the getTotalCount() function returns
9988 * the data set size</em>
9990 getCount : function(){
9991 return this.data.length || 0;
9995 * Gets the total number of records in the dataset as returned by the server.
9997 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9998 * the dataset size</em>
10000 getTotalCount : function(){
10001 return this.totalLength || 0;
10005 * Returns the sort state of the Store as an object with two properties:
10007 field {String} The name of the field by which the Records are sorted
10008 direction {String} The sort order, "ASC" or "DESC"
10011 getSortState : function(){
10012 return this.sortInfo;
10016 applySort : function(){
10017 if(this.sortInfo && !this.remoteSort){
10018 var s = this.sortInfo, f = s.field;
10019 var st = this.fields.get(f).sortType;
10020 var fn = function(r1, r2){
10021 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10022 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10024 this.data.sort(s.direction, fn);
10025 if(this.snapshot && this.snapshot != this.data){
10026 this.snapshot.sort(s.direction, fn);
10032 * Sets the default sort column and order to be used by the next load operation.
10033 * @param {String} fieldName The name of the field to sort by.
10034 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10036 setDefaultSort : function(field, dir){
10037 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10041 * Sort the Records.
10042 * If remote sorting is used, the sort is performed on the server, and the cache is
10043 * reloaded. If local sorting is used, the cache is sorted internally.
10044 * @param {String} fieldName The name of the field to sort by.
10045 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10047 sort : function(fieldName, dir){
10048 var f = this.fields.get(fieldName);
10050 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10052 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10053 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10058 this.sortToggle[f.name] = dir;
10059 this.sortInfo = {field: f.name, direction: dir};
10060 if(!this.remoteSort){
10062 this.fireEvent("datachanged", this);
10064 this.load(this.lastOptions);
10069 * Calls the specified function for each of the Records in the cache.
10070 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10071 * Returning <em>false</em> aborts and exits the iteration.
10072 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10074 each : function(fn, scope){
10075 this.data.each(fn, scope);
10079 * Gets all records modified since the last commit. Modified records are persisted across load operations
10080 * (e.g., during paging).
10081 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10083 getModifiedRecords : function(){
10084 return this.modified;
10088 createFilterFn : function(property, value, anyMatch){
10089 if(!value.exec){ // not a regex
10090 value = String(value);
10091 if(value.length == 0){
10094 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10096 return function(r){
10097 return value.test(r.data[property]);
10102 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10103 * @param {String} property A field on your records
10104 * @param {Number} start The record index to start at (defaults to 0)
10105 * @param {Number} end The last record index to include (defaults to length - 1)
10106 * @return {Number} The sum
10108 sum : function(property, start, end){
10109 var rs = this.data.items, v = 0;
10110 start = start || 0;
10111 end = (end || end === 0) ? end : rs.length-1;
10113 for(var i = start; i <= end; i++){
10114 v += (rs[i].data[property] || 0);
10120 * Filter the records by a specified property.
10121 * @param {String} field A field on your records
10122 * @param {String/RegExp} value Either a string that the field
10123 * should start with or a RegExp to test against the field
10124 * @param {Boolean} anyMatch True to match any part not just the beginning
10126 filter : function(property, value, anyMatch){
10127 var fn = this.createFilterFn(property, value, anyMatch);
10128 return fn ? this.filterBy(fn) : this.clearFilter();
10132 * Filter by a function. The specified function will be called with each
10133 * record in this data source. If the function returns true the record is included,
10134 * otherwise it is filtered.
10135 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10136 * @param {Object} scope (optional) The scope of the function (defaults to this)
10138 filterBy : function(fn, scope){
10139 this.snapshot = this.snapshot || this.data;
10140 this.data = this.queryBy(fn, scope||this);
10141 this.fireEvent("datachanged", this);
10145 * Query the records by a specified property.
10146 * @param {String} field A field on your records
10147 * @param {String/RegExp} value Either a string that the field
10148 * should start with or a RegExp to test against the field
10149 * @param {Boolean} anyMatch True to match any part not just the beginning
10150 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10152 query : function(property, value, anyMatch){
10153 var fn = this.createFilterFn(property, value, anyMatch);
10154 return fn ? this.queryBy(fn) : this.data.clone();
10158 * Query by a function. The specified function will be called with each
10159 * record in this data source. If the function returns true the record is included
10161 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10162 * @param {Object} scope (optional) The scope of the function (defaults to this)
10163 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10165 queryBy : function(fn, scope){
10166 var data = this.snapshot || this.data;
10167 return data.filterBy(fn, scope||this);
10171 * Collects unique values for a particular dataIndex from this store.
10172 * @param {String} dataIndex The property to collect
10173 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10174 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10175 * @return {Array} An array of the unique values
10177 collect : function(dataIndex, allowNull, bypassFilter){
10178 var d = (bypassFilter === true && this.snapshot) ?
10179 this.snapshot.items : this.data.items;
10180 var v, sv, r = [], l = {};
10181 for(var i = 0, len = d.length; i < len; i++){
10182 v = d[i].data[dataIndex];
10184 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10193 * Revert to a view of the Record cache with no filtering applied.
10194 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10196 clearFilter : function(suppressEvent){
10197 if(this.snapshot && this.snapshot != this.data){
10198 this.data = this.snapshot;
10199 delete this.snapshot;
10200 if(suppressEvent !== true){
10201 this.fireEvent("datachanged", this);
10207 afterEdit : function(record){
10208 if(this.modified.indexOf(record) == -1){
10209 this.modified.push(record);
10211 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10215 afterReject : function(record){
10216 this.modified.remove(record);
10217 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10221 afterCommit : function(record){
10222 this.modified.remove(record);
10223 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10227 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10228 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10230 commitChanges : function(){
10231 var m = this.modified.slice(0);
10232 this.modified = [];
10233 for(var i = 0, len = m.length; i < len; i++){
10239 * Cancel outstanding changes on all changed records.
10241 rejectChanges : function(){
10242 var m = this.modified.slice(0);
10243 this.modified = [];
10244 for(var i = 0, len = m.length; i < len; i++){
10249 onMetaChange : function(meta, rtype, o){
10250 this.recordType = rtype;
10251 this.fields = rtype.prototype.fields;
10252 delete this.snapshot;
10253 this.sortInfo = meta.sortInfo || this.sortInfo;
10254 this.modified = [];
10255 this.fireEvent('metachange', this, this.reader.meta);
10258 moveIndex : function(data, type)
10260 var index = this.indexOf(data);
10262 var newIndex = index + type;
10266 this.insert(newIndex, data);
10271 * Ext JS Library 1.1.1
10272 * Copyright(c) 2006-2007, Ext JS, LLC.
10274 * Originally Released Under LGPL - original licence link has changed is not relivant.
10277 * <script type="text/javascript">
10281 * @class Roo.data.SimpleStore
10282 * @extends Roo.data.Store
10283 * Small helper class to make creating Stores from Array data easier.
10284 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10285 * @cfg {Array} fields An array of field definition objects, or field name strings.
10286 * @cfg {Array} data The multi-dimensional array of data
10288 * @param {Object} config
10290 Roo.data.SimpleStore = function(config){
10291 Roo.data.SimpleStore.superclass.constructor.call(this, {
10293 reader: new Roo.data.ArrayReader({
10296 Roo.data.Record.create(config.fields)
10298 proxy : new Roo.data.MemoryProxy(config.data)
10302 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10304 * Ext JS Library 1.1.1
10305 * Copyright(c) 2006-2007, Ext JS, LLC.
10307 * Originally Released Under LGPL - original licence link has changed is not relivant.
10310 * <script type="text/javascript">
10315 * @extends Roo.data.Store
10316 * @class Roo.data.JsonStore
10317 * Small helper class to make creating Stores for JSON data easier. <br/>
10319 var store = new Roo.data.JsonStore({
10320 url: 'get-images.php',
10322 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10325 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10326 * JsonReader and HttpProxy (unless inline data is provided).</b>
10327 * @cfg {Array} fields An array of field definition objects, or field name strings.
10329 * @param {Object} config
10331 Roo.data.JsonStore = function(c){
10332 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10333 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10334 reader: new Roo.data.JsonReader(c, c.fields)
10337 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10339 * Ext JS Library 1.1.1
10340 * Copyright(c) 2006-2007, Ext JS, LLC.
10342 * Originally Released Under LGPL - original licence link has changed is not relivant.
10345 * <script type="text/javascript">
10349 Roo.data.Field = function(config){
10350 if(typeof config == "string"){
10351 config = {name: config};
10353 Roo.apply(this, config);
10356 this.type = "auto";
10359 var st = Roo.data.SortTypes;
10360 // named sortTypes are supported, here we look them up
10361 if(typeof this.sortType == "string"){
10362 this.sortType = st[this.sortType];
10365 // set default sortType for strings and dates
10366 if(!this.sortType){
10369 this.sortType = st.asUCString;
10372 this.sortType = st.asDate;
10375 this.sortType = st.none;
10380 var stripRe = /[\$,%]/g;
10382 // prebuilt conversion function for this field, instead of
10383 // switching every time we're reading a value
10385 var cv, dateFormat = this.dateFormat;
10390 cv = function(v){ return v; };
10393 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10397 return v !== undefined && v !== null && v !== '' ?
10398 parseInt(String(v).replace(stripRe, ""), 10) : '';
10403 return v !== undefined && v !== null && v !== '' ?
10404 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10409 cv = function(v){ return v === true || v === "true" || v == 1; };
10416 if(v instanceof Date){
10420 if(dateFormat == "timestamp"){
10421 return new Date(v*1000);
10423 return Date.parseDate(v, dateFormat);
10425 var parsed = Date.parse(v);
10426 return parsed ? new Date(parsed) : null;
10435 Roo.data.Field.prototype = {
10443 * Ext JS Library 1.1.1
10444 * Copyright(c) 2006-2007, Ext JS, LLC.
10446 * Originally Released Under LGPL - original licence link has changed is not relivant.
10449 * <script type="text/javascript">
10452 // Base class for reading structured data from a data source. This class is intended to be
10453 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10456 * @class Roo.data.DataReader
10457 * Base class for reading structured data from a data source. This class is intended to be
10458 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10461 Roo.data.DataReader = function(meta, recordType){
10465 this.recordType = recordType instanceof Array ?
10466 Roo.data.Record.create(recordType) : recordType;
10469 Roo.data.DataReader.prototype = {
10471 * Create an empty record
10472 * @param {Object} data (optional) - overlay some values
10473 * @return {Roo.data.Record} record created.
10475 newRow : function(d) {
10477 this.recordType.prototype.fields.each(function(c) {
10479 case 'int' : da[c.name] = 0; break;
10480 case 'date' : da[c.name] = new Date(); break;
10481 case 'float' : da[c.name] = 0.0; break;
10482 case 'boolean' : da[c.name] = false; break;
10483 default : da[c.name] = ""; break;
10487 return new this.recordType(Roo.apply(da, d));
10492 * Ext JS Library 1.1.1
10493 * Copyright(c) 2006-2007, Ext JS, LLC.
10495 * Originally Released Under LGPL - original licence link has changed is not relivant.
10498 * <script type="text/javascript">
10502 * @class Roo.data.DataProxy
10503 * @extends Roo.data.Observable
10504 * This class is an abstract base class for implementations which provide retrieval of
10505 * unformatted data objects.<br>
10507 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10508 * (of the appropriate type which knows how to parse the data object) to provide a block of
10509 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10511 * Custom implementations must implement the load method as described in
10512 * {@link Roo.data.HttpProxy#load}.
10514 Roo.data.DataProxy = function(){
10517 * @event beforeload
10518 * Fires before a network request is made to retrieve a data object.
10519 * @param {Object} This DataProxy object.
10520 * @param {Object} params The params parameter to the load function.
10525 * Fires before the load method's callback is called.
10526 * @param {Object} This DataProxy object.
10527 * @param {Object} o The data object.
10528 * @param {Object} arg The callback argument object passed to the load function.
10532 * @event loadexception
10533 * Fires if an Exception occurs during data retrieval.
10534 * @param {Object} This DataProxy object.
10535 * @param {Object} o The data object.
10536 * @param {Object} arg The callback argument object passed to the load function.
10537 * @param {Object} e The Exception.
10539 loadexception : true
10541 Roo.data.DataProxy.superclass.constructor.call(this);
10544 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10547 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10551 * Ext JS Library 1.1.1
10552 * Copyright(c) 2006-2007, Ext JS, LLC.
10554 * Originally Released Under LGPL - original licence link has changed is not relivant.
10557 * <script type="text/javascript">
10560 * @class Roo.data.MemoryProxy
10561 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10562 * to the Reader when its load method is called.
10564 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10566 Roo.data.MemoryProxy = function(data){
10570 Roo.data.MemoryProxy.superclass.constructor.call(this);
10574 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10576 * Load data from the requested source (in this case an in-memory
10577 * data object passed to the constructor), read the data object into
10578 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10579 * process that block using the passed callback.
10580 * @param {Object} params This parameter is not used by the MemoryProxy class.
10581 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10582 * object into a block of Roo.data.Records.
10583 * @param {Function} callback The function into which to pass the block of Roo.data.records.
10584 * The function must be passed <ul>
10585 * <li>The Record block object</li>
10586 * <li>The "arg" argument from the load function</li>
10587 * <li>A boolean success indicator</li>
10589 * @param {Object} scope The scope in which to call the callback
10590 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10592 load : function(params, reader, callback, scope, arg){
10593 params = params || {};
10596 result = reader.readRecords(this.data);
10598 this.fireEvent("loadexception", this, arg, null, e);
10599 callback.call(scope, null, arg, false);
10602 callback.call(scope, result, arg, true);
10606 update : function(params, records){
10611 * Ext JS Library 1.1.1
10612 * Copyright(c) 2006-2007, Ext JS, LLC.
10614 * Originally Released Under LGPL - original licence link has changed is not relivant.
10617 * <script type="text/javascript">
10620 * @class Roo.data.HttpProxy
10621 * @extends Roo.data.DataProxy
10622 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10623 * configured to reference a certain URL.<br><br>
10625 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10626 * from which the running page was served.<br><br>
10628 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10630 * Be aware that to enable the browser to parse an XML document, the server must set
10631 * the Content-Type header in the HTTP response to "text/xml".
10633 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10634 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
10635 * will be used to make the request.
10637 Roo.data.HttpProxy = function(conn){
10638 Roo.data.HttpProxy.superclass.constructor.call(this);
10639 // is conn a conn config or a real conn?
10641 this.useAjax = !conn || !conn.events;
10645 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10646 // thse are take from connection...
10649 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10652 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10653 * extra parameters to each request made by this object. (defaults to undefined)
10656 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10657 * to each request made by this object. (defaults to undefined)
10660 * @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)
10663 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10666 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10672 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10676 * Return the {@link Roo.data.Connection} object being used by this Proxy.
10677 * @return {Connection} The Connection object. This object may be used to subscribe to events on
10678 * a finer-grained basis than the DataProxy events.
10680 getConnection : function(){
10681 return this.useAjax ? Roo.Ajax : this.conn;
10685 * Load data from the configured {@link Roo.data.Connection}, read the data object into
10686 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10687 * process that block using the passed callback.
10688 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10689 * for the request to the remote server.
10690 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10691 * object into a block of Roo.data.Records.
10692 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10693 * The function must be passed <ul>
10694 * <li>The Record block object</li>
10695 * <li>The "arg" argument from the load function</li>
10696 * <li>A boolean success indicator</li>
10698 * @param {Object} scope The scope in which to call the callback
10699 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10701 load : function(params, reader, callback, scope, arg){
10702 if(this.fireEvent("beforeload", this, params) !== false){
10704 params : params || {},
10706 callback : callback,
10711 callback : this.loadResponse,
10715 Roo.applyIf(o, this.conn);
10716 if(this.activeRequest){
10717 Roo.Ajax.abort(this.activeRequest);
10719 this.activeRequest = Roo.Ajax.request(o);
10721 this.conn.request(o);
10724 callback.call(scope||this, null, arg, false);
10729 loadResponse : function(o, success, response){
10730 delete this.activeRequest;
10732 this.fireEvent("loadexception", this, o, response);
10733 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10738 result = o.reader.read(response);
10740 this.fireEvent("loadexception", this, o, response, e);
10741 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10745 this.fireEvent("load", this, o, o.request.arg);
10746 o.request.callback.call(o.request.scope, result, o.request.arg, true);
10750 update : function(dataSet){
10755 updateResponse : function(dataSet){
10760 * Ext JS Library 1.1.1
10761 * Copyright(c) 2006-2007, Ext JS, LLC.
10763 * Originally Released Under LGPL - original licence link has changed is not relivant.
10766 * <script type="text/javascript">
10770 * @class Roo.data.ScriptTagProxy
10771 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10772 * other than the originating domain of the running page.<br><br>
10774 * <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
10775 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10777 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10778 * source code that is used as the source inside a <script> tag.<br><br>
10780 * In order for the browser to process the returned data, the server must wrap the data object
10781 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10782 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10783 * depending on whether the callback name was passed:
10786 boolean scriptTag = false;
10787 String cb = request.getParameter("callback");
10790 response.setContentType("text/javascript");
10792 response.setContentType("application/x-json");
10794 Writer out = response.getWriter();
10796 out.write(cb + "(");
10798 out.print(dataBlock.toJsonString());
10805 * @param {Object} config A configuration object.
10807 Roo.data.ScriptTagProxy = function(config){
10808 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10809 Roo.apply(this, config);
10810 this.head = document.getElementsByTagName("head")[0];
10813 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10815 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10817 * @cfg {String} url The URL from which to request the data object.
10820 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10824 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10825 * the server the name of the callback function set up by the load call to process the returned data object.
10826 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10827 * javascript output which calls this named function passing the data object as its only parameter.
10829 callbackParam : "callback",
10831 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10832 * name to the request.
10837 * Load data from the configured URL, read the data object into
10838 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10839 * process that block using the passed callback.
10840 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10841 * for the request to the remote server.
10842 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10843 * object into a block of Roo.data.Records.
10844 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10845 * The function must be passed <ul>
10846 * <li>The Record block object</li>
10847 * <li>The "arg" argument from the load function</li>
10848 * <li>A boolean success indicator</li>
10850 * @param {Object} scope The scope in which to call the callback
10851 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10853 load : function(params, reader, callback, scope, arg){
10854 if(this.fireEvent("beforeload", this, params) !== false){
10856 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10858 var url = this.url;
10859 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10861 url += "&_dc=" + (new Date().getTime());
10863 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10866 cb : "stcCallback"+transId,
10867 scriptId : "stcScript"+transId,
10871 callback : callback,
10877 window[trans.cb] = function(o){
10878 conn.handleResponse(o, trans);
10881 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10883 if(this.autoAbort !== false){
10887 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10889 var script = document.createElement("script");
10890 script.setAttribute("src", url);
10891 script.setAttribute("type", "text/javascript");
10892 script.setAttribute("id", trans.scriptId);
10893 this.head.appendChild(script);
10895 this.trans = trans;
10897 callback.call(scope||this, null, arg, false);
10902 isLoading : function(){
10903 return this.trans ? true : false;
10907 * Abort the current server request.
10909 abort : function(){
10910 if(this.isLoading()){
10911 this.destroyTrans(this.trans);
10916 destroyTrans : function(trans, isLoaded){
10917 this.head.removeChild(document.getElementById(trans.scriptId));
10918 clearTimeout(trans.timeoutId);
10920 window[trans.cb] = undefined;
10922 delete window[trans.cb];
10925 // if hasn't been loaded, wait for load to remove it to prevent script error
10926 window[trans.cb] = function(){
10927 window[trans.cb] = undefined;
10929 delete window[trans.cb];
10936 handleResponse : function(o, trans){
10937 this.trans = false;
10938 this.destroyTrans(trans, true);
10941 result = trans.reader.readRecords(o);
10943 this.fireEvent("loadexception", this, o, trans.arg, e);
10944 trans.callback.call(trans.scope||window, null, trans.arg, false);
10947 this.fireEvent("load", this, o, trans.arg);
10948 trans.callback.call(trans.scope||window, result, trans.arg, true);
10952 handleFailure : function(trans){
10953 this.trans = false;
10954 this.destroyTrans(trans, false);
10955 this.fireEvent("loadexception", this, null, trans.arg);
10956 trans.callback.call(trans.scope||window, null, trans.arg, false);
10960 * Ext JS Library 1.1.1
10961 * Copyright(c) 2006-2007, Ext JS, LLC.
10963 * Originally Released Under LGPL - original licence link has changed is not relivant.
10966 * <script type="text/javascript">
10970 * @class Roo.data.JsonReader
10971 * @extends Roo.data.DataReader
10972 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10973 * based on mappings in a provided Roo.data.Record constructor.
10975 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10976 * in the reply previously.
10981 var RecordDef = Roo.data.Record.create([
10982 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
10983 {name: 'occupation'} // This field will use "occupation" as the mapping.
10985 var myReader = new Roo.data.JsonReader({
10986 totalProperty: "results", // The property which contains the total dataset size (optional)
10987 root: "rows", // The property which contains an Array of row objects
10988 id: "id" // The property within each row object that provides an ID for the record (optional)
10992 * This would consume a JSON file like this:
10994 { 'results': 2, 'rows': [
10995 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10996 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10999 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11000 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11001 * paged from the remote server.
11002 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11003 * @cfg {String} root name of the property which contains the Array of row objects.
11004 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11005 * @cfg {Array} fields Array of field definition objects
11007 * Create a new JsonReader
11008 * @param {Object} meta Metadata configuration options
11009 * @param {Object} recordType Either an Array of field definition objects,
11010 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11012 Roo.data.JsonReader = function(meta, recordType){
11015 // set some defaults:
11016 Roo.applyIf(meta, {
11017 totalProperty: 'total',
11018 successProperty : 'success',
11023 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11025 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11028 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11029 * Used by Store query builder to append _requestMeta to params.
11032 metaFromRemote : false,
11034 * This method is only used by a DataProxy which has retrieved data from a remote server.
11035 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11036 * @return {Object} data A data block which is used by an Roo.data.Store object as
11037 * a cache of Roo.data.Records.
11039 read : function(response){
11040 var json = response.responseText;
11042 var o = /* eval:var:o */ eval("("+json+")");
11044 throw {message: "JsonReader.read: Json object not found"};
11050 this.metaFromRemote = true;
11051 this.meta = o.metaData;
11052 this.recordType = Roo.data.Record.create(o.metaData.fields);
11053 this.onMetaChange(this.meta, this.recordType, o);
11055 return this.readRecords(o);
11058 // private function a store will implement
11059 onMetaChange : function(meta, recordType, o){
11066 simpleAccess: function(obj, subsc) {
11073 getJsonAccessor: function(){
11075 return function(expr) {
11077 return(re.test(expr))
11078 ? new Function("obj", "return obj." + expr)
11083 return Roo.emptyFn;
11088 * Create a data block containing Roo.data.Records from an XML document.
11089 * @param {Object} o An object which contains an Array of row objects in the property specified
11090 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11091 * which contains the total size of the dataset.
11092 * @return {Object} data A data block which is used by an Roo.data.Store object as
11093 * a cache of Roo.data.Records.
11095 readRecords : function(o){
11097 * After any data loads, the raw JSON data is available for further custom processing.
11101 var s = this.meta, Record = this.recordType,
11102 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11104 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11106 if(s.totalProperty) {
11107 this.getTotal = this.getJsonAccessor(s.totalProperty);
11109 if(s.successProperty) {
11110 this.getSuccess = this.getJsonAccessor(s.successProperty);
11112 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11114 var g = this.getJsonAccessor(s.id);
11115 this.getId = function(rec) {
11117 return (r === undefined || r === "") ? null : r;
11120 this.getId = function(){return null;};
11123 for(var jj = 0; jj < fl; jj++){
11125 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11126 this.ef[jj] = this.getJsonAccessor(map);
11130 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11131 if(s.totalProperty){
11132 var vt = parseInt(this.getTotal(o), 10);
11137 if(s.successProperty){
11138 var vs = this.getSuccess(o);
11139 if(vs === false || vs === 'false'){
11144 for(var i = 0; i < c; i++){
11147 var id = this.getId(n);
11148 for(var j = 0; j < fl; j++){
11150 var v = this.ef[j](n);
11152 Roo.log('missing convert for ' + f.name);
11156 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11158 var record = new Record(values, id);
11160 records[i] = record;
11166 totalRecords : totalRecords
11171 * Ext JS Library 1.1.1
11172 * Copyright(c) 2006-2007, Ext JS, LLC.
11174 * Originally Released Under LGPL - original licence link has changed is not relivant.
11177 * <script type="text/javascript">
11181 * @class Roo.data.ArrayReader
11182 * @extends Roo.data.DataReader
11183 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11184 * Each element of that Array represents a row of data fields. The
11185 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11186 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11190 var RecordDef = Roo.data.Record.create([
11191 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11192 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11194 var myReader = new Roo.data.ArrayReader({
11195 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11199 * This would consume an Array like this:
11201 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11203 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11205 * Create a new JsonReader
11206 * @param {Object} meta Metadata configuration options.
11207 * @param {Object} recordType Either an Array of field definition objects
11208 * as specified to {@link Roo.data.Record#create},
11209 * or an {@link Roo.data.Record} object
11210 * created using {@link Roo.data.Record#create}.
11212 Roo.data.ArrayReader = function(meta, recordType){
11213 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11216 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11218 * Create a data block containing Roo.data.Records from an XML document.
11219 * @param {Object} o An Array of row objects which represents the dataset.
11220 * @return {Object} data A data block which is used by an Roo.data.Store object as
11221 * a cache of Roo.data.Records.
11223 readRecords : function(o){
11224 var sid = this.meta ? this.meta.id : null;
11225 var recordType = this.recordType, fields = recordType.prototype.fields;
11228 for(var i = 0; i < root.length; i++){
11231 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11232 for(var j = 0, jlen = fields.length; j < jlen; j++){
11233 var f = fields.items[j];
11234 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11235 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11237 values[f.name] = v;
11239 var record = new recordType(values, id);
11241 records[records.length] = record;
11245 totalRecords : records.length
11254 * @class Roo.bootstrap.ComboBox
11255 * @extends Roo.bootstrap.TriggerField
11256 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11257 * @cfg {Boolean} append (true|false) default false
11258 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11259 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11260 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11261 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11262 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11263 * @cfg {Boolean} animate default true
11264 * @cfg {Boolean} emptyResultText only for touch device
11265 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11267 * Create a new ComboBox.
11268 * @param {Object} config Configuration options
11270 Roo.bootstrap.ComboBox = function(config){
11271 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11275 * Fires when the dropdown list is expanded
11276 * @param {Roo.bootstrap.ComboBox} combo This combo box
11281 * Fires when the dropdown list is collapsed
11282 * @param {Roo.bootstrap.ComboBox} combo This combo box
11286 * @event beforeselect
11287 * Fires before a list item is selected. Return false to cancel the selection.
11288 * @param {Roo.bootstrap.ComboBox} combo This combo box
11289 * @param {Roo.data.Record} record The data record returned from the underlying store
11290 * @param {Number} index The index of the selected item in the dropdown list
11292 'beforeselect' : true,
11295 * Fires when a list item is selected
11296 * @param {Roo.bootstrap.ComboBox} combo This combo box
11297 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11298 * @param {Number} index The index of the selected item in the dropdown list
11302 * @event beforequery
11303 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11304 * The event object passed has these properties:
11305 * @param {Roo.bootstrap.ComboBox} combo This combo box
11306 * @param {String} query The query
11307 * @param {Boolean} forceAll true to force "all" query
11308 * @param {Boolean} cancel true to cancel the query
11309 * @param {Object} e The query event object
11311 'beforequery': true,
11314 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11315 * @param {Roo.bootstrap.ComboBox} combo This combo box
11320 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11321 * @param {Roo.bootstrap.ComboBox} combo This combo box
11322 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11327 * Fires when the remove value from the combobox array
11328 * @param {Roo.bootstrap.ComboBox} combo This combo box
11332 * @event specialfilter
11333 * Fires when specialfilter
11334 * @param {Roo.bootstrap.ComboBox} combo This combo box
11336 'specialfilter' : true
11341 this.tickItems = [];
11343 this.selectedIndex = -1;
11344 if(this.mode == 'local'){
11345 if(config.queryDelay === undefined){
11346 this.queryDelay = 10;
11348 if(config.minChars === undefined){
11354 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11357 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11358 * rendering into an Roo.Editor, defaults to false)
11361 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11362 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11365 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11368 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11369 * the dropdown list (defaults to undefined, with no header element)
11373 * @cfg {String/Roo.Template} tpl The template to use to render the output
11377 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11379 listWidth: undefined,
11381 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11382 * mode = 'remote' or 'text' if mode = 'local')
11384 displayField: undefined,
11387 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11388 * mode = 'remote' or 'value' if mode = 'local').
11389 * Note: use of a valueField requires the user make a selection
11390 * in order for a value to be mapped.
11392 valueField: undefined,
11396 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11397 * field's data value (defaults to the underlying DOM element's name)
11399 hiddenName: undefined,
11401 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11405 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11407 selectedClass: 'active',
11410 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11414 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11415 * anchor positions (defaults to 'tl-bl')
11417 listAlign: 'tl-bl?',
11419 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11423 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11424 * query specified by the allQuery config option (defaults to 'query')
11426 triggerAction: 'query',
11428 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11429 * (defaults to 4, does not apply if editable = false)
11433 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11434 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11438 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11439 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11443 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11444 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11448 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11449 * when editable = true (defaults to false)
11451 selectOnFocus:false,
11453 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11455 queryParam: 'query',
11457 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
11458 * when mode = 'remote' (defaults to 'Loading...')
11460 loadingText: 'Loading...',
11462 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11466 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11470 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11471 * traditional select (defaults to true)
11475 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11479 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11483 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11484 * listWidth has a higher value)
11488 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11489 * allow the user to set arbitrary text into the field (defaults to false)
11491 forceSelection:false,
11493 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11494 * if typeAhead = true (defaults to 250)
11496 typeAheadDelay : 250,
11498 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11499 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11501 valueNotFoundText : undefined,
11503 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11505 blockFocus : false,
11508 * @cfg {Boolean} disableClear Disable showing of clear button.
11510 disableClear : false,
11512 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
11514 alwaysQuery : false,
11517 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
11522 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11524 invalidClass : "has-warning",
11527 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11529 validClass : "has-success",
11532 * @cfg {Boolean} specialFilter (true|false) special filter default false
11534 specialFilter : false,
11537 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11539 mobileTouchView : true,
11551 btnPosition : 'right',
11552 triggerList : true,
11553 showToggleBtn : true,
11555 emptyResultText: 'Empty',
11556 triggerText : 'Select',
11558 // element that contains real text value.. (when hidden is used..)
11560 getAutoCreate : function()
11568 if(Roo.isTouch && this.mobileTouchView){
11569 cfg = this.getAutoCreateTouchView();
11576 if(!this.tickable){
11577 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11582 * ComboBox with tickable selections
11585 var align = this.labelAlign || this.parentLabelAlign();
11588 cls : 'form-group roo-combobox-tickable' //input-group
11593 cls : 'tickable-buttons',
11598 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11599 html : this.triggerText
11605 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11612 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11619 buttons.cn.unshift({
11621 cls: 'select2-search-field-input'
11627 Roo.each(buttons.cn, function(c){
11629 c.cls += ' btn-' + _this.size;
11632 if (_this.disabled) {
11643 cls: 'form-hidden-field'
11647 cls: 'select2-choices',
11651 cls: 'select2-search-field',
11663 cls: 'select2-container input-group select2-container-multi',
11668 // cls: 'typeahead typeahead-long dropdown-menu',
11669 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
11674 if(this.hasFeedback && !this.allowBlank){
11678 cls: 'glyphicon form-control-feedback'
11681 combobox.cn.push(feedback);
11684 if (align ==='left' && this.fieldLabel.length) {
11686 Roo.log("left and has label");
11692 cls : 'control-label col-sm-' + this.labelWidth,
11693 html : this.fieldLabel
11697 cls : "col-sm-" + (12 - this.labelWidth),
11704 } else if ( this.fieldLabel.length) {
11710 //cls : 'input-group-addon',
11711 html : this.fieldLabel
11721 Roo.log(" no label && no align");
11728 ['xs','sm','md','lg'].map(function(size){
11729 if (settings[size]) {
11730 cfg.cls += ' col-' + size + '-' + settings[size];
11738 _initEventsCalled : false,
11741 initEvents: function()
11744 if (this._initEventsCalled) { // as we call render... prevent looping...
11747 this._initEventsCalled = true;
11750 throw "can not find store for combo";
11753 this.store = Roo.factory(this.store, Roo.data);
11755 // if we are building from html. then this element is so complex, that we can not really
11756 // use the rendered HTML.
11757 // so we have to trash and replace the previous code.
11758 if (Roo.XComponent.build_from_html) {
11760 // remove this element....
11761 var e = this.el.dom, k=0;
11762 while (e ) { e = e.previousSibling; ++k;}
11767 this.rendered = false;
11769 this.render(this.parent().getChildContainer(true), k);
11780 if(Roo.isTouch && this.mobileTouchView){
11781 this.initTouchView();
11786 this.initTickableEvents();
11790 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11792 if(this.hiddenName){
11794 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11796 this.hiddenField.dom.value =
11797 this.hiddenValue !== undefined ? this.hiddenValue :
11798 this.value !== undefined ? this.value : '';
11800 // prevent input submission
11801 this.el.dom.removeAttribute('name');
11802 this.hiddenField.dom.setAttribute('name', this.hiddenName);
11807 // this.el.dom.setAttribute('autocomplete', 'off');
11810 var cls = 'x-combo-list';
11812 //this.list = new Roo.Layer({
11813 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11819 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11820 _this.list.setWidth(lw);
11823 this.list.on('mouseover', this.onViewOver, this);
11824 this.list.on('mousemove', this.onViewMove, this);
11826 this.list.on('scroll', this.onViewScroll, this);
11829 this.list.swallowEvent('mousewheel');
11830 this.assetHeight = 0;
11833 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11834 this.assetHeight += this.header.getHeight();
11837 this.innerList = this.list.createChild({cls:cls+'-inner'});
11838 this.innerList.on('mouseover', this.onViewOver, this);
11839 this.innerList.on('mousemove', this.onViewMove, this);
11840 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11842 if(this.allowBlank && !this.pageSize && !this.disableClear){
11843 this.footer = this.list.createChild({cls:cls+'-ft'});
11844 this.pageTb = new Roo.Toolbar(this.footer);
11848 this.footer = this.list.createChild({cls:cls+'-ft'});
11849 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11850 {pageSize: this.pageSize});
11854 if (this.pageTb && this.allowBlank && !this.disableClear) {
11856 this.pageTb.add(new Roo.Toolbar.Fill(), {
11857 cls: 'x-btn-icon x-btn-clear',
11859 handler: function()
11862 _this.clearValue();
11863 _this.onSelect(false, -1);
11868 this.assetHeight += this.footer.getHeight();
11873 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11876 this.view = new Roo.View(this.list, this.tpl, {
11877 singleSelect:true, store: this.store, selectedClass: this.selectedClass
11879 //this.view.wrapEl.setDisplayed(false);
11880 this.view.on('click', this.onViewClick, this);
11884 this.store.on('beforeload', this.onBeforeLoad, this);
11885 this.store.on('load', this.onLoad, this);
11886 this.store.on('loadexception', this.onLoadException, this);
11888 if(this.resizable){
11889 this.resizer = new Roo.Resizable(this.list, {
11890 pinned:true, handles:'se'
11892 this.resizer.on('resize', function(r, w, h){
11893 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11894 this.listWidth = w;
11895 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11896 this.restrictHeight();
11898 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11901 if(!this.editable){
11902 this.editable = true;
11903 this.setEditable(false);
11908 if (typeof(this.events.add.listeners) != 'undefined') {
11910 this.addicon = this.wrap.createChild(
11911 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
11913 this.addicon.on('click', function(e) {
11914 this.fireEvent('add', this);
11917 if (typeof(this.events.edit.listeners) != 'undefined') {
11919 this.editicon = this.wrap.createChild(
11920 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
11921 if (this.addicon) {
11922 this.editicon.setStyle('margin-left', '40px');
11924 this.editicon.on('click', function(e) {
11926 // we fire even if inothing is selected..
11927 this.fireEvent('edit', this, this.lastData );
11933 this.keyNav = new Roo.KeyNav(this.inputEl(), {
11934 "up" : function(e){
11935 this.inKeyMode = true;
11939 "down" : function(e){
11940 if(!this.isExpanded()){
11941 this.onTriggerClick();
11943 this.inKeyMode = true;
11948 "enter" : function(e){
11949 // this.onViewClick();
11953 if(this.fireEvent("specialkey", this, e)){
11954 this.onViewClick(false);
11960 "esc" : function(e){
11964 "tab" : function(e){
11967 if(this.fireEvent("specialkey", this, e)){
11968 this.onViewClick(false);
11976 doRelay : function(foo, bar, hname){
11977 if(hname == 'down' || this.scope.isExpanded()){
11978 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11987 this.queryDelay = Math.max(this.queryDelay || 10,
11988 this.mode == 'local' ? 10 : 250);
11991 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11993 if(this.typeAhead){
11994 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11996 if(this.editable !== false){
11997 this.inputEl().on("keyup", this.onKeyUp, this);
11999 if(this.forceSelection){
12000 this.inputEl().on('blur', this.doForce, this);
12004 this.choices = this.el.select('ul.select2-choices', true).first();
12005 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12009 initTickableEvents: function()
12013 if(this.hiddenName){
12015 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12017 this.hiddenField.dom.value =
12018 this.hiddenValue !== undefined ? this.hiddenValue :
12019 this.value !== undefined ? this.value : '';
12021 // prevent input submission
12022 this.el.dom.removeAttribute('name');
12023 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12028 // this.list = this.el.select('ul.dropdown-menu',true).first();
12030 this.choices = this.el.select('ul.select2-choices', true).first();
12031 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12032 if(this.triggerList){
12033 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12036 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12037 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12039 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12040 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12042 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12043 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12045 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12046 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12047 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12050 this.cancelBtn.hide();
12055 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12056 _this.list.setWidth(lw);
12059 this.list.on('mouseover', this.onViewOver, this);
12060 this.list.on('mousemove', this.onViewMove, this);
12062 this.list.on('scroll', this.onViewScroll, this);
12065 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>';
12068 this.view = new Roo.View(this.list, this.tpl, {
12069 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12072 //this.view.wrapEl.setDisplayed(false);
12073 this.view.on('click', this.onViewClick, this);
12077 this.store.on('beforeload', this.onBeforeLoad, this);
12078 this.store.on('load', this.onLoad, this);
12079 this.store.on('loadexception', this.onLoadException, this);
12082 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12083 "up" : function(e){
12084 this.inKeyMode = true;
12088 "down" : function(e){
12089 this.inKeyMode = true;
12093 "enter" : function(e){
12094 if(this.fireEvent("specialkey", this, e)){
12095 this.onViewClick(false);
12101 "esc" : function(e){
12102 this.onTickableFooterButtonClick(e, false, false);
12105 "tab" : function(e){
12106 this.fireEvent("specialkey", this, e);
12108 this.onTickableFooterButtonClick(e, false, false);
12115 doRelay : function(e, fn, key){
12116 if(this.scope.isExpanded()){
12117 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12126 this.queryDelay = Math.max(this.queryDelay || 10,
12127 this.mode == 'local' ? 10 : 250);
12130 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12132 if(this.typeAhead){
12133 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12136 if(this.editable !== false){
12137 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12142 onDestroy : function(){
12144 this.view.setStore(null);
12145 this.view.el.removeAllListeners();
12146 this.view.el.remove();
12147 this.view.purgeListeners();
12150 this.list.dom.innerHTML = '';
12154 this.store.un('beforeload', this.onBeforeLoad, this);
12155 this.store.un('load', this.onLoad, this);
12156 this.store.un('loadexception', this.onLoadException, this);
12158 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12162 fireKey : function(e){
12163 if(e.isNavKeyPress() && !this.list.isVisible()){
12164 this.fireEvent("specialkey", this, e);
12169 onResize: function(w, h){
12170 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12172 // if(typeof w != 'number'){
12173 // // we do not handle it!?!?
12176 // var tw = this.trigger.getWidth();
12177 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12178 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12180 // this.inputEl().setWidth( this.adjustWidth('input', x));
12182 // //this.trigger.setStyle('left', x+'px');
12184 // if(this.list && this.listWidth === undefined){
12185 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12186 // this.list.setWidth(lw);
12187 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12195 * Allow or prevent the user from directly editing the field text. If false is passed,
12196 * the user will only be able to select from the items defined in the dropdown list. This method
12197 * is the runtime equivalent of setting the 'editable' config option at config time.
12198 * @param {Boolean} value True to allow the user to directly edit the field text
12200 setEditable : function(value){
12201 if(value == this.editable){
12204 this.editable = value;
12206 this.inputEl().dom.setAttribute('readOnly', true);
12207 this.inputEl().on('mousedown', this.onTriggerClick, this);
12208 this.inputEl().addClass('x-combo-noedit');
12210 this.inputEl().dom.setAttribute('readOnly', false);
12211 this.inputEl().un('mousedown', this.onTriggerClick, this);
12212 this.inputEl().removeClass('x-combo-noedit');
12218 onBeforeLoad : function(combo,opts){
12219 if(!this.hasFocus){
12223 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12225 this.restrictHeight();
12226 this.selectedIndex = -1;
12230 onLoad : function(){
12232 this.hasQuery = false;
12234 if(!this.hasFocus){
12238 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12239 this.loading.hide();
12242 if(this.store.getCount() > 0){
12244 this.restrictHeight();
12245 if(this.lastQuery == this.allQuery){
12246 if(this.editable && !this.tickable){
12247 this.inputEl().dom.select();
12251 !this.selectByValue(this.value, true) &&
12254 !this.store.lastOptions ||
12255 typeof(this.store.lastOptions.add) == 'undefined' ||
12256 this.store.lastOptions.add != true
12259 this.select(0, true);
12262 if(this.autoFocus){
12265 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12266 this.taTask.delay(this.typeAheadDelay);
12270 this.onEmptyResults();
12276 onLoadException : function()
12278 this.hasQuery = false;
12280 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12281 this.loading.hide();
12284 if(this.tickable && this.editable){
12290 Roo.log(this.store.reader.jsonData);
12291 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12293 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12299 onTypeAhead : function(){
12300 if(this.store.getCount() > 0){
12301 var r = this.store.getAt(0);
12302 var newValue = r.data[this.displayField];
12303 var len = newValue.length;
12304 var selStart = this.getRawValue().length;
12306 if(selStart != len){
12307 this.setRawValue(newValue);
12308 this.selectText(selStart, newValue.length);
12314 onSelect : function(record, index){
12316 if(this.fireEvent('beforeselect', this, record, index) !== false){
12318 this.setFromData(index > -1 ? record.data : false);
12321 this.fireEvent('select', this, record, index);
12326 * Returns the currently selected field value or empty string if no value is set.
12327 * @return {String} value The selected value
12329 getValue : function(){
12332 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12335 if(this.valueField){
12336 return typeof this.value != 'undefined' ? this.value : '';
12338 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12343 * Clears any text/value currently set in the field
12345 clearValue : function(){
12346 if(this.hiddenField){
12347 this.hiddenField.dom.value = '';
12350 this.setRawValue('');
12351 this.lastSelectionText = '';
12352 this.lastData = false;
12354 var close = this.closeTriggerEl();
12363 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12364 * will be displayed in the field. If the value does not match the data value of an existing item,
12365 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12366 * Otherwise the field will be blank (although the value will still be set).
12367 * @param {String} value The value to match
12369 setValue : function(v){
12376 if(this.valueField){
12377 var r = this.findRecord(this.valueField, v);
12379 text = r.data[this.displayField];
12380 }else if(this.valueNotFoundText !== undefined){
12381 text = this.valueNotFoundText;
12384 this.lastSelectionText = text;
12385 if(this.hiddenField){
12386 this.hiddenField.dom.value = v;
12388 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12391 var close = this.closeTriggerEl();
12394 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12398 * @property {Object} the last set data for the element
12403 * Sets the value of the field based on a object which is related to the record format for the store.
12404 * @param {Object} value the value to set as. or false on reset?
12406 setFromData : function(o){
12413 var dv = ''; // display value
12414 var vv = ''; // value value..
12416 if (this.displayField) {
12417 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12419 // this is an error condition!!!
12420 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12423 if(this.valueField){
12424 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12427 var close = this.closeTriggerEl();
12430 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12433 if(this.hiddenField){
12434 this.hiddenField.dom.value = vv;
12436 this.lastSelectionText = dv;
12437 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12441 // no hidden field.. - we store the value in 'value', but still display
12442 // display field!!!!
12443 this.lastSelectionText = dv;
12444 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12451 reset : function(){
12452 // overridden so that last data is reset..
12459 this.setValue(this.originalValue);
12460 this.clearInvalid();
12461 this.lastData = false;
12463 this.view.clearSelections();
12467 findRecord : function(prop, value){
12469 if(this.store.getCount() > 0){
12470 this.store.each(function(r){
12471 if(r.data[prop] == value){
12481 getName: function()
12483 // returns hidden if it's set..
12484 if (!this.rendered) {return ''};
12485 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
12489 onViewMove : function(e, t){
12490 this.inKeyMode = false;
12494 onViewOver : function(e, t){
12495 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12498 var item = this.view.findItemFromChild(t);
12501 var index = this.view.indexOf(item);
12502 this.select(index, false);
12507 onViewClick : function(view, doFocus, el, e)
12509 var index = this.view.getSelectedIndexes()[0];
12511 var r = this.store.getAt(index);
12515 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12522 Roo.each(this.tickItems, function(v,k){
12524 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12525 _this.tickItems.splice(k, 1);
12527 if(typeof(e) == 'undefined' && view == false){
12528 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12540 this.tickItems.push(r.data);
12542 if(typeof(e) == 'undefined' && view == false){
12543 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12550 this.onSelect(r, index);
12552 if(doFocus !== false && !this.blockFocus){
12553 this.inputEl().focus();
12558 restrictHeight : function(){
12559 //this.innerList.dom.style.height = '';
12560 //var inner = this.innerList.dom;
12561 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12562 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12563 //this.list.beginUpdate();
12564 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12565 this.list.alignTo(this.inputEl(), this.listAlign);
12566 this.list.alignTo(this.inputEl(), this.listAlign);
12567 //this.list.endUpdate();
12571 onEmptyResults : function(){
12573 if(this.tickable && this.editable){
12574 this.restrictHeight();
12582 * Returns true if the dropdown list is expanded, else false.
12584 isExpanded : function(){
12585 return this.list.isVisible();
12589 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12590 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12591 * @param {String} value The data value of the item to select
12592 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12593 * selected item if it is not currently in view (defaults to true)
12594 * @return {Boolean} True if the value matched an item in the list, else false
12596 selectByValue : function(v, scrollIntoView){
12597 if(v !== undefined && v !== null){
12598 var r = this.findRecord(this.valueField || this.displayField, v);
12600 this.select(this.store.indexOf(r), scrollIntoView);
12608 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12609 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12610 * @param {Number} index The zero-based index of the list item to select
12611 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12612 * selected item if it is not currently in view (defaults to true)
12614 select : function(index, scrollIntoView){
12615 this.selectedIndex = index;
12616 this.view.select(index);
12617 if(scrollIntoView !== false){
12618 var el = this.view.getNode(index);
12620 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12623 this.list.scrollChildIntoView(el, false);
12629 selectNext : function(){
12630 var ct = this.store.getCount();
12632 if(this.selectedIndex == -1){
12634 }else if(this.selectedIndex < ct-1){
12635 this.select(this.selectedIndex+1);
12641 selectPrev : function(){
12642 var ct = this.store.getCount();
12644 if(this.selectedIndex == -1){
12646 }else if(this.selectedIndex != 0){
12647 this.select(this.selectedIndex-1);
12653 onKeyUp : function(e){
12654 if(this.editable !== false && !e.isSpecialKey()){
12655 this.lastKey = e.getKey();
12656 this.dqTask.delay(this.queryDelay);
12661 validateBlur : function(){
12662 return !this.list || !this.list.isVisible();
12666 initQuery : function(){
12668 var v = this.getRawValue();
12670 if(this.tickable && this.editable){
12671 v = this.tickableInputEl().getValue();
12678 doForce : function(){
12679 if(this.inputEl().dom.value.length > 0){
12680 this.inputEl().dom.value =
12681 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12687 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
12688 * query allowing the query action to be canceled if needed.
12689 * @param {String} query The SQL query to execute
12690 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12691 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
12692 * saved in the current store (defaults to false)
12694 doQuery : function(q, forceAll){
12696 if(q === undefined || q === null){
12701 forceAll: forceAll,
12705 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12710 forceAll = qe.forceAll;
12711 if(forceAll === true || (q.length >= this.minChars)){
12713 this.hasQuery = true;
12715 if(this.lastQuery != q || this.alwaysQuery){
12716 this.lastQuery = q;
12717 if(this.mode == 'local'){
12718 this.selectedIndex = -1;
12720 this.store.clearFilter();
12723 if(this.specialFilter){
12724 this.fireEvent('specialfilter', this);
12729 this.store.filter(this.displayField, q);
12732 this.store.fireEvent("datachanged", this.store);
12739 this.store.baseParams[this.queryParam] = q;
12741 var options = {params : this.getParams(q)};
12744 options.add = true;
12745 options.params.start = this.page * this.pageSize;
12748 this.store.load(options);
12751 * this code will make the page width larger, at the beginning, the list not align correctly,
12752 * we should expand the list on onLoad
12753 * so command out it
12758 this.selectedIndex = -1;
12763 this.loadNext = false;
12767 getParams : function(q){
12769 //p[this.queryParam] = q;
12773 p.limit = this.pageSize;
12779 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12781 collapse : function(){
12782 if(!this.isExpanded()){
12789 this.hasFocus = false;
12791 this.cancelBtn.hide();
12792 this.trigger.show();
12795 this.tickableInputEl().dom.value = '';
12796 this.tickableInputEl().blur();
12801 Roo.get(document).un('mousedown', this.collapseIf, this);
12802 Roo.get(document).un('mousewheel', this.collapseIf, this);
12803 if (!this.editable) {
12804 Roo.get(document).un('keydown', this.listKeyPress, this);
12806 this.fireEvent('collapse', this);
12810 collapseIf : function(e){
12811 var in_combo = e.within(this.el);
12812 var in_list = e.within(this.list);
12813 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12815 if (in_combo || in_list || is_list) {
12816 //e.stopPropagation();
12821 this.onTickableFooterButtonClick(e, false, false);
12829 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12831 expand : function(){
12833 if(this.isExpanded() || !this.hasFocus){
12837 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12838 this.list.setWidth(lw);
12845 this.restrictHeight();
12849 this.tickItems = Roo.apply([], this.item);
12852 this.cancelBtn.show();
12853 this.trigger.hide();
12856 this.tickableInputEl().focus();
12861 Roo.get(document).on('mousedown', this.collapseIf, this);
12862 Roo.get(document).on('mousewheel', this.collapseIf, this);
12863 if (!this.editable) {
12864 Roo.get(document).on('keydown', this.listKeyPress, this);
12867 this.fireEvent('expand', this);
12871 // Implements the default empty TriggerField.onTriggerClick function
12872 onTriggerClick : function(e)
12874 Roo.log('trigger click');
12876 if(this.disabled || !this.triggerList){
12881 this.loadNext = false;
12883 if(this.isExpanded()){
12885 if (!this.blockFocus) {
12886 this.inputEl().focus();
12890 this.hasFocus = true;
12891 if(this.triggerAction == 'all') {
12892 this.doQuery(this.allQuery, true);
12894 this.doQuery(this.getRawValue());
12896 if (!this.blockFocus) {
12897 this.inputEl().focus();
12902 onTickableTriggerClick : function(e)
12909 this.loadNext = false;
12910 this.hasFocus = true;
12912 if(this.triggerAction == 'all') {
12913 this.doQuery(this.allQuery, true);
12915 this.doQuery(this.getRawValue());
12919 onSearchFieldClick : function(e)
12921 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12922 this.onTickableFooterButtonClick(e, false, false);
12926 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12931 this.loadNext = false;
12932 this.hasFocus = true;
12934 if(this.triggerAction == 'all') {
12935 this.doQuery(this.allQuery, true);
12937 this.doQuery(this.getRawValue());
12941 listKeyPress : function(e)
12943 //Roo.log('listkeypress');
12944 // scroll to first matching element based on key pres..
12945 if (e.isSpecialKey()) {
12948 var k = String.fromCharCode(e.getKey()).toUpperCase();
12951 var csel = this.view.getSelectedNodes();
12952 var cselitem = false;
12954 var ix = this.view.indexOf(csel[0]);
12955 cselitem = this.store.getAt(ix);
12956 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12962 this.store.each(function(v) {
12964 // start at existing selection.
12965 if (cselitem.id == v.id) {
12971 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12972 match = this.store.indexOf(v);
12978 if (match === false) {
12979 return true; // no more action?
12982 this.view.select(match);
12983 var sn = Roo.get(this.view.getSelectedNodes()[0])
12984 sn.scrollIntoView(sn.dom.parentNode, false);
12987 onViewScroll : function(e, t){
12989 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){
12993 this.hasQuery = true;
12995 this.loading = this.list.select('.loading', true).first();
12997 if(this.loading === null){
12998 this.list.createChild({
13000 cls: 'loading select2-more-results select2-active',
13001 html: 'Loading more results...'
13004 this.loading = this.list.select('.loading', true).first();
13006 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13008 this.loading.hide();
13011 this.loading.show();
13016 this.loadNext = true;
13018 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13023 addItem : function(o)
13025 var dv = ''; // display value
13027 if (this.displayField) {
13028 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13030 // this is an error condition!!!
13031 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13038 var choice = this.choices.createChild({
13040 cls: 'select2-search-choice',
13049 cls: 'select2-search-choice-close',
13054 }, this.searchField);
13056 var close = choice.select('a.select2-search-choice-close', true).first()
13058 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13066 this.inputEl().dom.value = '';
13071 onRemoveItem : function(e, _self, o)
13073 e.preventDefault();
13075 this.lastItem = Roo.apply([], this.item);
13077 var index = this.item.indexOf(o.data) * 1;
13080 Roo.log('not this item?!');
13084 this.item.splice(index, 1);
13089 this.fireEvent('remove', this, e);
13095 syncValue : function()
13097 if(!this.item.length){
13104 Roo.each(this.item, function(i){
13105 if(_this.valueField){
13106 value.push(i[_this.valueField]);
13113 this.value = value.join(',');
13115 if(this.hiddenField){
13116 this.hiddenField.dom.value = this.value;
13119 this.store.fireEvent("datachanged", this.store);
13122 clearItem : function()
13124 if(!this.multiple){
13130 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13138 if(this.tickable && !Roo.isTouch){
13139 this.view.refresh();
13143 inputEl: function ()
13145 if(Roo.isTouch && this.mobileTouchView){
13146 return this.el.select('input.form-control',true).first();
13150 return this.searchField;
13153 return this.el.select('input.form-control',true).first();
13157 onTickableFooterButtonClick : function(e, btn, el)
13159 e.preventDefault();
13161 this.lastItem = Roo.apply([], this.item);
13163 if(btn && btn.name == 'cancel'){
13164 this.tickItems = Roo.apply([], this.item);
13173 Roo.each(this.tickItems, function(o){
13181 validate : function()
13183 var v = this.getRawValue();
13186 v = this.getValue();
13189 if(this.disabled || this.allowBlank || v.length){
13194 this.markInvalid();
13198 tickableInputEl : function()
13200 if(!this.tickable || !this.editable){
13201 return this.inputEl();
13204 return this.inputEl().select('.select2-search-field-input', true).first();
13208 getAutoCreateTouchView : function()
13213 cls: 'form-group' //input-group
13219 type : this.inputType,
13220 cls : 'form-control x-combo-noedit',
13221 autocomplete: 'new-password',
13222 placeholder : this.placeholder || '',
13227 input.name = this.name;
13231 input.cls += ' input-' + this.size;
13234 if (this.disabled) {
13235 input.disabled = true;
13246 inputblock.cls += ' input-group';
13248 inputblock.cn.unshift({
13250 cls : 'input-group-addon',
13255 if(this.removable && !this.multiple){
13256 inputblock.cls += ' roo-removable';
13258 inputblock.cn.push({
13261 cls : 'roo-combo-removable-btn close'
13265 if(this.hasFeedback && !this.allowBlank){
13267 inputblock.cls += ' has-feedback';
13269 inputblock.cn.push({
13271 cls: 'glyphicon form-control-feedback'
13278 inputblock.cls += (this.before) ? '' : ' input-group';
13280 inputblock.cn.push({
13282 cls : 'input-group-addon',
13293 cls: 'form-hidden-field'
13307 cls: 'form-hidden-field'
13311 cls: 'select2-choices',
13315 cls: 'select2-search-field',
13328 cls: 'select2-container input-group',
13335 combobox.cls += ' select2-container-multi';
13338 var align = this.labelAlign || this.parentLabelAlign();
13342 if(this.fieldLabel.length){
13344 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13345 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13350 cls : 'control-label ' + lw,
13351 html : this.fieldLabel
13363 var settings = this;
13365 ['xs','sm','md','lg'].map(function(size){
13366 if (settings[size]) {
13367 cfg.cls += ' col-' + size + '-' + settings[size];
13374 initTouchView : function()
13376 this.renderTouchView();
13378 this.touchViewEl.on('scroll', function(){
13379 this.el.dom.scrollTop = 0;
13382 this.inputEl().on("click", this.showTouchView, this);
13383 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13384 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13386 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13388 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13389 this.store.on('load', this.onTouchViewLoad, this);
13390 this.store.on('loadexception', this.onTouchViewLoadException, this);
13392 if(this.hiddenName){
13394 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13396 this.hiddenField.dom.value =
13397 this.hiddenValue !== undefined ? this.hiddenValue :
13398 this.value !== undefined ? this.value : '';
13400 this.el.dom.removeAttribute('name');
13401 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13405 this.choices = this.el.select('ul.select2-choices', true).first();
13406 this.searchField = this.el.select('ul li.select2-search-field', true).first();
13409 if(this.removable && !this.multiple){
13410 var close = this.closeTriggerEl();
13412 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13413 close.on('click', this.removeBtnClick, this, close);
13422 renderTouchView : function()
13424 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13425 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13427 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13428 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13430 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13431 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13432 this.touchViewBodyEl.setStyle('overflow', 'auto');
13434 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13435 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13437 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13438 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13442 showTouchView : function()
13444 this.touchViewHeaderEl.hide();
13446 if(this.fieldLabel.length){
13447 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13448 this.touchViewHeaderEl.show();
13451 this.touchViewEl.show();
13453 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13454 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13456 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13458 if(this.fieldLabel.length){
13459 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13462 this.touchViewBodyEl.setHeight(bodyHeight);
13466 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13468 this.touchViewEl.addClass('in');
13471 this.doTouchViewQuery();
13475 hideTouchView : function()
13477 this.touchViewEl.removeClass('in');
13481 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13483 this.touchViewEl.setStyle('display', 'none');
13488 setTouchViewValue : function()
13495 Roo.each(this.tickItems, function(o){
13500 this.hideTouchView();
13503 doTouchViewQuery : function()
13512 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13516 if(!this.alwaysQuery || this.mode == 'local'){
13517 this.onTouchViewLoad();
13524 onTouchViewBeforeLoad : function(combo,opts)
13530 onTouchViewLoad : function()
13532 if(this.store.getCount() < 1){
13533 this.onTouchViewEmptyResults();
13537 this.clearTouchView();
13539 var rawValue = this.getRawValue();
13541 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13543 this.tickItems = [];
13545 this.store.data.each(function(d, rowIndex){
13546 var row = this.touchViewListGroup.createChild(template);
13548 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13549 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13552 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13553 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13556 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13557 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13558 this.tickItems.push(d.data);
13561 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13565 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13567 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13569 if(this.fieldLabel.length){
13570 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13573 var listHeight = this.touchViewListGroup.getHeight();
13577 if(firstChecked && listHeight > bodyHeight){
13578 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13583 onTouchViewLoadException : function()
13585 this.hideTouchView();
13588 onTouchViewEmptyResults : function()
13590 this.clearTouchView();
13592 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13594 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13598 clearTouchView : function()
13600 this.touchViewListGroup.dom.innerHTML = '';
13603 onTouchViewClick : function(e, el, o)
13605 e.preventDefault();
13608 var rowIndex = o.rowIndex;
13610 var r = this.store.getAt(rowIndex);
13612 if(!this.multiple){
13613 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13614 c.dom.removeAttribute('checked');
13617 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13619 this.setFromData(r.data);
13621 var close = this.closeTriggerEl();
13627 this.hideTouchView();
13629 this.fireEvent('select', this, r, rowIndex);
13634 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13635 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13636 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13640 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13641 this.addItem(r.data);
13642 this.tickItems.push(r.data);
13648 * @cfg {Boolean} grow
13652 * @cfg {Number} growMin
13656 * @cfg {Number} growMax
13665 Roo.apply(Roo.bootstrap.ComboBox, {
13669 cls: 'modal-header',
13691 cls: 'list-group-item',
13695 cls: 'roo-combobox-list-group-item-value'
13699 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13713 listItemCheckbox : {
13715 cls: 'list-group-item',
13719 cls: 'roo-combobox-list-group-item-value'
13723 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13739 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13744 cls: 'modal-footer',
13752 cls: 'col-xs-6 text-left',
13755 cls: 'btn btn-danger roo-touch-view-cancel',
13761 cls: 'col-xs-6 text-right',
13764 cls: 'btn btn-success roo-touch-view-ok',
13775 Roo.apply(Roo.bootstrap.ComboBox, {
13777 touchViewTemplate : {
13779 cls: 'modal fade roo-combobox-touch-view',
13783 cls: 'modal-dialog',
13787 cls: 'modal-content',
13789 Roo.bootstrap.ComboBox.header,
13790 Roo.bootstrap.ComboBox.body,
13791 Roo.bootstrap.ComboBox.footer
13800 * Ext JS Library 1.1.1
13801 * Copyright(c) 2006-2007, Ext JS, LLC.
13803 * Originally Released Under LGPL - original licence link has changed is not relivant.
13806 * <script type="text/javascript">
13811 * @extends Roo.util.Observable
13812 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
13813 * This class also supports single and multi selection modes. <br>
13814 * Create a data model bound view:
13816 var store = new Roo.data.Store(...);
13818 var view = new Roo.View({
13820 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
13822 singleSelect: true,
13823 selectedClass: "ydataview-selected",
13827 // listen for node click?
13828 view.on("click", function(vw, index, node, e){
13829 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13833 dataModel.load("foobar.xml");
13835 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13837 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13838 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13840 * Note: old style constructor is still suported (container, template, config)
13843 * Create a new View
13844 * @param {Object} config The config object
13847 Roo.View = function(config, depreciated_tpl, depreciated_config){
13849 this.parent = false;
13851 if (typeof(depreciated_tpl) == 'undefined') {
13852 // new way.. - universal constructor.
13853 Roo.apply(this, config);
13854 this.el = Roo.get(this.el);
13857 this.el = Roo.get(config);
13858 this.tpl = depreciated_tpl;
13859 Roo.apply(this, depreciated_config);
13861 this.wrapEl = this.el.wrap().wrap();
13862 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13865 if(typeof(this.tpl) == "string"){
13866 this.tpl = new Roo.Template(this.tpl);
13868 // support xtype ctors..
13869 this.tpl = new Roo.factory(this.tpl, Roo);
13873 this.tpl.compile();
13878 * @event beforeclick
13879 * Fires before a click is processed. Returns false to cancel the default action.
13880 * @param {Roo.View} this
13881 * @param {Number} index The index of the target node
13882 * @param {HTMLElement} node The target node
13883 * @param {Roo.EventObject} e The raw event object
13885 "beforeclick" : true,
13888 * Fires when a template node is clicked.
13889 * @param {Roo.View} this
13890 * @param {Number} index The index of the target node
13891 * @param {HTMLElement} node The target node
13892 * @param {Roo.EventObject} e The raw event object
13897 * Fires when a template node is double clicked.
13898 * @param {Roo.View} this
13899 * @param {Number} index The index of the target node
13900 * @param {HTMLElement} node The target node
13901 * @param {Roo.EventObject} e The raw event object
13905 * @event contextmenu
13906 * Fires when a template node is right clicked.
13907 * @param {Roo.View} this
13908 * @param {Number} index The index of the target node
13909 * @param {HTMLElement} node The target node
13910 * @param {Roo.EventObject} e The raw event object
13912 "contextmenu" : true,
13914 * @event selectionchange
13915 * Fires when the selected nodes change.
13916 * @param {Roo.View} this
13917 * @param {Array} selections Array of the selected nodes
13919 "selectionchange" : true,
13922 * @event beforeselect
13923 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13924 * @param {Roo.View} this
13925 * @param {HTMLElement} node The node to be selected
13926 * @param {Array} selections Array of currently selected nodes
13928 "beforeselect" : true,
13930 * @event preparedata
13931 * Fires on every row to render, to allow you to change the data.
13932 * @param {Roo.View} this
13933 * @param {Object} data to be rendered (change this)
13935 "preparedata" : true
13943 "click": this.onClick,
13944 "dblclick": this.onDblClick,
13945 "contextmenu": this.onContextMenu,
13949 this.selections = [];
13951 this.cmp = new Roo.CompositeElementLite([]);
13953 this.store = Roo.factory(this.store, Roo.data);
13954 this.setStore(this.store, true);
13957 if ( this.footer && this.footer.xtype) {
13959 var fctr = this.wrapEl.appendChild(document.createElement("div"));
13961 this.footer.dataSource = this.store;
13962 this.footer.container = fctr;
13963 this.footer = Roo.factory(this.footer, Roo);
13964 fctr.insertFirst(this.el);
13966 // this is a bit insane - as the paging toolbar seems to detach the el..
13967 // dom.parentNode.parentNode.parentNode
13968 // they get detached?
13972 Roo.View.superclass.constructor.call(this);
13977 Roo.extend(Roo.View, Roo.util.Observable, {
13980 * @cfg {Roo.data.Store} store Data store to load data from.
13985 * @cfg {String|Roo.Element} el The container element.
13990 * @cfg {String|Roo.Template} tpl The template used by this View
13994 * @cfg {String} dataName the named area of the template to use as the data area
13995 * Works with domtemplates roo-name="name"
13999 * @cfg {String} selectedClass The css class to add to selected nodes
14001 selectedClass : "x-view-selected",
14003 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14008 * @cfg {String} text to display on mask (default Loading)
14012 * @cfg {Boolean} multiSelect Allow multiple selection
14014 multiSelect : false,
14016 * @cfg {Boolean} singleSelect Allow single selection
14018 singleSelect: false,
14021 * @cfg {Boolean} toggleSelect - selecting
14023 toggleSelect : false,
14026 * @cfg {Boolean} tickable - selecting
14031 * Returns the element this view is bound to.
14032 * @return {Roo.Element}
14034 getEl : function(){
14035 return this.wrapEl;
14041 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14043 refresh : function(){
14044 //Roo.log('refresh');
14047 // if we are using something like 'domtemplate', then
14048 // the what gets used is:
14049 // t.applySubtemplate(NAME, data, wrapping data..)
14050 // the outer template then get' applied with
14051 // the store 'extra data'
14052 // and the body get's added to the
14053 // roo-name="data" node?
14054 // <span class='roo-tpl-{name}'></span> ?????
14058 this.clearSelections();
14059 this.el.update("");
14061 var records = this.store.getRange();
14062 if(records.length < 1) {
14064 // is this valid?? = should it render a template??
14066 this.el.update(this.emptyText);
14070 if (this.dataName) {
14071 this.el.update(t.apply(this.store.meta)); //????
14072 el = this.el.child('.roo-tpl-' + this.dataName);
14075 for(var i = 0, len = records.length; i < len; i++){
14076 var data = this.prepareData(records[i].data, i, records[i]);
14077 this.fireEvent("preparedata", this, data, i, records[i]);
14079 var d = Roo.apply({}, data);
14082 Roo.apply(d, {'roo-id' : Roo.id()});
14086 Roo.each(this.parent.item, function(item){
14087 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14090 Roo.apply(d, {'roo-data-checked' : 'checked'});
14094 html[html.length] = Roo.util.Format.trim(
14096 t.applySubtemplate(this.dataName, d, this.store.meta) :
14103 el.update(html.join(""));
14104 this.nodes = el.dom.childNodes;
14105 this.updateIndexes(0);
14110 * Function to override to reformat the data that is sent to
14111 * the template for each node.
14112 * DEPRICATED - use the preparedata event handler.
14113 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14114 * a JSON object for an UpdateManager bound view).
14116 prepareData : function(data, index, record)
14118 this.fireEvent("preparedata", this, data, index, record);
14122 onUpdate : function(ds, record){
14123 // Roo.log('on update');
14124 this.clearSelections();
14125 var index = this.store.indexOf(record);
14126 var n = this.nodes[index];
14127 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14128 n.parentNode.removeChild(n);
14129 this.updateIndexes(index, index);
14135 onAdd : function(ds, records, index)
14137 //Roo.log(['on Add', ds, records, index] );
14138 this.clearSelections();
14139 if(this.nodes.length == 0){
14143 var n = this.nodes[index];
14144 for(var i = 0, len = records.length; i < len; i++){
14145 var d = this.prepareData(records[i].data, i, records[i]);
14147 this.tpl.insertBefore(n, d);
14150 this.tpl.append(this.el, d);
14153 this.updateIndexes(index);
14156 onRemove : function(ds, record, index){
14157 // Roo.log('onRemove');
14158 this.clearSelections();
14159 var el = this.dataName ?
14160 this.el.child('.roo-tpl-' + this.dataName) :
14163 el.dom.removeChild(this.nodes[index]);
14164 this.updateIndexes(index);
14168 * Refresh an individual node.
14169 * @param {Number} index
14171 refreshNode : function(index){
14172 this.onUpdate(this.store, this.store.getAt(index));
14175 updateIndexes : function(startIndex, endIndex){
14176 var ns = this.nodes;
14177 startIndex = startIndex || 0;
14178 endIndex = endIndex || ns.length - 1;
14179 for(var i = startIndex; i <= endIndex; i++){
14180 ns[i].nodeIndex = i;
14185 * Changes the data store this view uses and refresh the view.
14186 * @param {Store} store
14188 setStore : function(store, initial){
14189 if(!initial && this.store){
14190 this.store.un("datachanged", this.refresh);
14191 this.store.un("add", this.onAdd);
14192 this.store.un("remove", this.onRemove);
14193 this.store.un("update", this.onUpdate);
14194 this.store.un("clear", this.refresh);
14195 this.store.un("beforeload", this.onBeforeLoad);
14196 this.store.un("load", this.onLoad);
14197 this.store.un("loadexception", this.onLoad);
14201 store.on("datachanged", this.refresh, this);
14202 store.on("add", this.onAdd, this);
14203 store.on("remove", this.onRemove, this);
14204 store.on("update", this.onUpdate, this);
14205 store.on("clear", this.refresh, this);
14206 store.on("beforeload", this.onBeforeLoad, this);
14207 store.on("load", this.onLoad, this);
14208 store.on("loadexception", this.onLoad, this);
14216 * onbeforeLoad - masks the loading area.
14219 onBeforeLoad : function(store,opts)
14221 //Roo.log('onBeforeLoad');
14223 this.el.update("");
14225 this.el.mask(this.mask ? this.mask : "Loading" );
14227 onLoad : function ()
14234 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14235 * @param {HTMLElement} node
14236 * @return {HTMLElement} The template node
14238 findItemFromChild : function(node){
14239 var el = this.dataName ?
14240 this.el.child('.roo-tpl-' + this.dataName,true) :
14243 if(!node || node.parentNode == el){
14246 var p = node.parentNode;
14247 while(p && p != el){
14248 if(p.parentNode == el){
14257 onClick : function(e){
14258 var item = this.findItemFromChild(e.getTarget());
14260 var index = this.indexOf(item);
14261 if(this.onItemClick(item, index, e) !== false){
14262 this.fireEvent("click", this, index, item, e);
14265 this.clearSelections();
14270 onContextMenu : function(e){
14271 var item = this.findItemFromChild(e.getTarget());
14273 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14278 onDblClick : function(e){
14279 var item = this.findItemFromChild(e.getTarget());
14281 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14285 onItemClick : function(item, index, e)
14287 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14290 if (this.toggleSelect) {
14291 var m = this.isSelected(item) ? 'unselect' : 'select';
14294 _t[m](item, true, false);
14297 if(this.multiSelect || this.singleSelect){
14298 if(this.multiSelect && e.shiftKey && this.lastSelection){
14299 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14301 this.select(item, this.multiSelect && e.ctrlKey);
14302 this.lastSelection = item;
14305 if(!this.tickable){
14306 e.preventDefault();
14314 * Get the number of selected nodes.
14317 getSelectionCount : function(){
14318 return this.selections.length;
14322 * Get the currently selected nodes.
14323 * @return {Array} An array of HTMLElements
14325 getSelectedNodes : function(){
14326 return this.selections;
14330 * Get the indexes of the selected nodes.
14333 getSelectedIndexes : function(){
14334 var indexes = [], s = this.selections;
14335 for(var i = 0, len = s.length; i < len; i++){
14336 indexes.push(s[i].nodeIndex);
14342 * Clear all selections
14343 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14345 clearSelections : function(suppressEvent){
14346 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14347 this.cmp.elements = this.selections;
14348 this.cmp.removeClass(this.selectedClass);
14349 this.selections = [];
14350 if(!suppressEvent){
14351 this.fireEvent("selectionchange", this, this.selections);
14357 * Returns true if the passed node is selected
14358 * @param {HTMLElement/Number} node The node or node index
14359 * @return {Boolean}
14361 isSelected : function(node){
14362 var s = this.selections;
14366 node = this.getNode(node);
14367 return s.indexOf(node) !== -1;
14372 * @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
14373 * @param {Boolean} keepExisting (optional) true to keep existing selections
14374 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14376 select : function(nodeInfo, keepExisting, suppressEvent){
14377 if(nodeInfo instanceof Array){
14379 this.clearSelections(true);
14381 for(var i = 0, len = nodeInfo.length; i < len; i++){
14382 this.select(nodeInfo[i], true, true);
14386 var node = this.getNode(nodeInfo);
14387 if(!node || this.isSelected(node)){
14388 return; // already selected.
14391 this.clearSelections(true);
14394 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14395 Roo.fly(node).addClass(this.selectedClass);
14396 this.selections.push(node);
14397 if(!suppressEvent){
14398 this.fireEvent("selectionchange", this, this.selections);
14406 * @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
14407 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14408 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14410 unselect : function(nodeInfo, keepExisting, suppressEvent)
14412 if(nodeInfo instanceof Array){
14413 Roo.each(this.selections, function(s) {
14414 this.unselect(s, nodeInfo);
14418 var node = this.getNode(nodeInfo);
14419 if(!node || !this.isSelected(node)){
14420 //Roo.log("not selected");
14421 return; // not selected.
14425 Roo.each(this.selections, function(s) {
14427 Roo.fly(node).removeClass(this.selectedClass);
14434 this.selections= ns;
14435 this.fireEvent("selectionchange", this, this.selections);
14439 * Gets a template node.
14440 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14441 * @return {HTMLElement} The node or null if it wasn't found
14443 getNode : function(nodeInfo){
14444 if(typeof nodeInfo == "string"){
14445 return document.getElementById(nodeInfo);
14446 }else if(typeof nodeInfo == "number"){
14447 return this.nodes[nodeInfo];
14453 * Gets a range template nodes.
14454 * @param {Number} startIndex
14455 * @param {Number} endIndex
14456 * @return {Array} An array of nodes
14458 getNodes : function(start, end){
14459 var ns = this.nodes;
14460 start = start || 0;
14461 end = typeof end == "undefined" ? ns.length - 1 : end;
14464 for(var i = start; i <= end; i++){
14468 for(var i = start; i >= end; i--){
14476 * Finds the index of the passed node
14477 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14478 * @return {Number} The index of the node or -1
14480 indexOf : function(node){
14481 node = this.getNode(node);
14482 if(typeof node.nodeIndex == "number"){
14483 return node.nodeIndex;
14485 var ns = this.nodes;
14486 for(var i = 0, len = ns.length; i < len; i++){
14497 * based on jquery fullcalendar
14501 Roo.bootstrap = Roo.bootstrap || {};
14503 * @class Roo.bootstrap.Calendar
14504 * @extends Roo.bootstrap.Component
14505 * Bootstrap Calendar class
14506 * @cfg {Boolean} loadMask (true|false) default false
14507 * @cfg {Object} header generate the user specific header of the calendar, default false
14510 * Create a new Container
14511 * @param {Object} config The config object
14516 Roo.bootstrap.Calendar = function(config){
14517 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14521 * Fires when a date is selected
14522 * @param {DatePicker} this
14523 * @param {Date} date The selected date
14527 * @event monthchange
14528 * Fires when the displayed month changes
14529 * @param {DatePicker} this
14530 * @param {Date} date The selected month
14532 'monthchange': true,
14534 * @event evententer
14535 * Fires when mouse over an event
14536 * @param {Calendar} this
14537 * @param {event} Event
14539 'evententer': true,
14541 * @event eventleave
14542 * Fires when the mouse leaves an
14543 * @param {Calendar} this
14546 'eventleave': true,
14548 * @event eventclick
14549 * Fires when the mouse click an
14550 * @param {Calendar} this
14559 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
14562 * @cfg {Number} startDay
14563 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14571 getAutoCreate : function(){
14574 var fc_button = function(name, corner, style, content ) {
14575 return Roo.apply({},{
14577 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
14579 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14582 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14593 style : 'width:100%',
14600 cls : 'fc-header-left',
14602 fc_button('prev', 'left', 'arrow', '‹' ),
14603 fc_button('next', 'right', 'arrow', '›' ),
14604 { tag: 'span', cls: 'fc-header-space' },
14605 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
14613 cls : 'fc-header-center',
14617 cls: 'fc-header-title',
14620 html : 'month / year'
14628 cls : 'fc-header-right',
14630 /* fc_button('month', 'left', '', 'month' ),
14631 fc_button('week', '', '', 'week' ),
14632 fc_button('day', 'right', '', 'day' )
14644 header = this.header;
14647 var cal_heads = function() {
14649 // fixme - handle this.
14651 for (var i =0; i < Date.dayNames.length; i++) {
14652 var d = Date.dayNames[i];
14655 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14656 html : d.substring(0,3)
14660 ret[0].cls += ' fc-first';
14661 ret[6].cls += ' fc-last';
14664 var cal_cell = function(n) {
14667 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14672 cls: 'fc-day-number',
14676 cls: 'fc-day-content',
14680 style: 'position: relative;' // height: 17px;
14692 var cal_rows = function() {
14695 for (var r = 0; r < 6; r++) {
14702 for (var i =0; i < Date.dayNames.length; i++) {
14703 var d = Date.dayNames[i];
14704 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14707 row.cn[0].cls+=' fc-first';
14708 row.cn[0].cn[0].style = 'min-height:90px';
14709 row.cn[6].cls+=' fc-last';
14713 ret[0].cls += ' fc-first';
14714 ret[4].cls += ' fc-prev-last';
14715 ret[5].cls += ' fc-last';
14722 cls: 'fc-border-separate',
14723 style : 'width:100%',
14731 cls : 'fc-first fc-last',
14749 cls : 'fc-content',
14750 style : "position: relative;",
14753 cls : 'fc-view fc-view-month fc-grid',
14754 style : 'position: relative',
14755 unselectable : 'on',
14758 cls : 'fc-event-container',
14759 style : 'position:absolute;z-index:8;top:0;left:0;'
14777 initEvents : function()
14780 throw "can not find store for calendar";
14786 style: "text-align:center",
14790 style: "background-color:white;width:50%;margin:250 auto",
14794 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
14805 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14807 var size = this.el.select('.fc-content', true).first().getSize();
14808 this.maskEl.setSize(size.width, size.height);
14809 this.maskEl.enableDisplayMode("block");
14810 if(!this.loadMask){
14811 this.maskEl.hide();
14814 this.store = Roo.factory(this.store, Roo.data);
14815 this.store.on('load', this.onLoad, this);
14816 this.store.on('beforeload', this.onBeforeLoad, this);
14820 this.cells = this.el.select('.fc-day',true);
14821 //Roo.log(this.cells);
14822 this.textNodes = this.el.query('.fc-day-number');
14823 this.cells.addClassOnOver('fc-state-hover');
14825 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14826 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14827 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14828 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14830 this.on('monthchange', this.onMonthChange, this);
14832 this.update(new Date().clearTime());
14835 resize : function() {
14836 var sz = this.el.getSize();
14838 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14839 this.el.select('.fc-day-content div',true).setHeight(34);
14844 showPrevMonth : function(e){
14845 this.update(this.activeDate.add("mo", -1));
14847 showToday : function(e){
14848 this.update(new Date().clearTime());
14851 showNextMonth : function(e){
14852 this.update(this.activeDate.add("mo", 1));
14856 showPrevYear : function(){
14857 this.update(this.activeDate.add("y", -1));
14861 showNextYear : function(){
14862 this.update(this.activeDate.add("y", 1));
14867 update : function(date)
14869 var vd = this.activeDate;
14870 this.activeDate = date;
14871 // if(vd && this.el){
14872 // var t = date.getTime();
14873 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14874 // Roo.log('using add remove');
14876 // this.fireEvent('monthchange', this, date);
14878 // this.cells.removeClass("fc-state-highlight");
14879 // this.cells.each(function(c){
14880 // if(c.dateValue == t){
14881 // c.addClass("fc-state-highlight");
14882 // setTimeout(function(){
14883 // try{c.dom.firstChild.focus();}catch(e){}
14893 var days = date.getDaysInMonth();
14895 var firstOfMonth = date.getFirstDateOfMonth();
14896 var startingPos = firstOfMonth.getDay()-this.startDay;
14898 if(startingPos < this.startDay){
14902 var pm = date.add(Date.MONTH, -1);
14903 var prevStart = pm.getDaysInMonth()-startingPos;
14905 this.cells = this.el.select('.fc-day',true);
14906 this.textNodes = this.el.query('.fc-day-number');
14907 this.cells.addClassOnOver('fc-state-hover');
14909 var cells = this.cells.elements;
14910 var textEls = this.textNodes;
14912 Roo.each(cells, function(cell){
14913 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14916 days += startingPos;
14918 // convert everything to numbers so it's fast
14919 var day = 86400000;
14920 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14923 //Roo.log(prevStart);
14925 var today = new Date().clearTime().getTime();
14926 var sel = date.clearTime().getTime();
14927 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14928 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14929 var ddMatch = this.disabledDatesRE;
14930 var ddText = this.disabledDatesText;
14931 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14932 var ddaysText = this.disabledDaysText;
14933 var format = this.format;
14935 var setCellClass = function(cal, cell){
14939 //Roo.log('set Cell Class');
14941 var t = d.getTime();
14945 cell.dateValue = t;
14947 cell.className += " fc-today";
14948 cell.className += " fc-state-highlight";
14949 cell.title = cal.todayText;
14952 // disable highlight in other month..
14953 //cell.className += " fc-state-highlight";
14958 cell.className = " fc-state-disabled";
14959 cell.title = cal.minText;
14963 cell.className = " fc-state-disabled";
14964 cell.title = cal.maxText;
14968 if(ddays.indexOf(d.getDay()) != -1){
14969 cell.title = ddaysText;
14970 cell.className = " fc-state-disabled";
14973 if(ddMatch && format){
14974 var fvalue = d.dateFormat(format);
14975 if(ddMatch.test(fvalue)){
14976 cell.title = ddText.replace("%0", fvalue);
14977 cell.className = " fc-state-disabled";
14981 if (!cell.initialClassName) {
14982 cell.initialClassName = cell.dom.className;
14985 cell.dom.className = cell.initialClassName + ' ' + cell.className;
14990 for(; i < startingPos; i++) {
14991 textEls[i].innerHTML = (++prevStart);
14992 d.setDate(d.getDate()+1);
14994 cells[i].className = "fc-past fc-other-month";
14995 setCellClass(this, cells[i]);
15000 for(; i < days; i++){
15001 intDay = i - startingPos + 1;
15002 textEls[i].innerHTML = (intDay);
15003 d.setDate(d.getDate()+1);
15005 cells[i].className = ''; // "x-date-active";
15006 setCellClass(this, cells[i]);
15010 for(; i < 42; i++) {
15011 textEls[i].innerHTML = (++extraDays);
15012 d.setDate(d.getDate()+1);
15014 cells[i].className = "fc-future fc-other-month";
15015 setCellClass(this, cells[i]);
15018 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15020 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15022 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15023 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15025 if(totalRows != 6){
15026 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15027 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15030 this.fireEvent('monthchange', this, date);
15034 if(!this.internalRender){
15035 var main = this.el.dom.firstChild;
15036 var w = main.offsetWidth;
15037 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15038 Roo.fly(main).setWidth(w);
15039 this.internalRender = true;
15040 // opera does not respect the auto grow header center column
15041 // then, after it gets a width opera refuses to recalculate
15042 // without a second pass
15043 if(Roo.isOpera && !this.secondPass){
15044 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15045 this.secondPass = true;
15046 this.update.defer(10, this, [date]);
15053 findCell : function(dt) {
15054 dt = dt.clearTime().getTime();
15056 this.cells.each(function(c){
15057 //Roo.log("check " +c.dateValue + '?=' + dt);
15058 if(c.dateValue == dt){
15068 findCells : function(ev) {
15069 var s = ev.start.clone().clearTime().getTime();
15071 var e= ev.end.clone().clearTime().getTime();
15074 this.cells.each(function(c){
15075 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15077 if(c.dateValue > e){
15080 if(c.dateValue < s){
15089 // findBestRow: function(cells)
15093 // for (var i =0 ; i < cells.length;i++) {
15094 // ret = Math.max(cells[i].rows || 0,ret);
15101 addItem : function(ev)
15103 // look for vertical location slot in
15104 var cells = this.findCells(ev);
15106 // ev.row = this.findBestRow(cells);
15108 // work out the location.
15112 for(var i =0; i < cells.length; i++) {
15114 cells[i].row = cells[0].row;
15117 cells[i].row = cells[i].row + 1;
15127 if (crow.start.getY() == cells[i].getY()) {
15129 crow.end = cells[i];
15146 cells[0].events.push(ev);
15148 this.calevents.push(ev);
15151 clearEvents: function() {
15153 if(!this.calevents){
15157 Roo.each(this.cells.elements, function(c){
15163 Roo.each(this.calevents, function(e) {
15164 Roo.each(e.els, function(el) {
15165 el.un('mouseenter' ,this.onEventEnter, this);
15166 el.un('mouseleave' ,this.onEventLeave, this);
15171 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15177 renderEvents: function()
15181 this.cells.each(function(c) {
15190 if(c.row != c.events.length){
15191 r = 4 - (4 - (c.row - c.events.length));
15194 c.events = ev.slice(0, r);
15195 c.more = ev.slice(r);
15197 if(c.more.length && c.more.length == 1){
15198 c.events.push(c.more.pop());
15201 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15205 this.cells.each(function(c) {
15207 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15210 for (var e = 0; e < c.events.length; e++){
15211 var ev = c.events[e];
15212 var rows = ev.rows;
15214 for(var i = 0; i < rows.length; i++) {
15216 // how many rows should it span..
15219 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15220 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15222 unselectable : "on",
15225 cls: 'fc-event-inner',
15229 // cls: 'fc-event-time',
15230 // html : cells.length > 1 ? '' : ev.time
15234 cls: 'fc-event-title',
15235 html : String.format('{0}', ev.title)
15242 cls: 'ui-resizable-handle ui-resizable-e',
15243 html : '  '
15250 cfg.cls += ' fc-event-start';
15252 if ((i+1) == rows.length) {
15253 cfg.cls += ' fc-event-end';
15256 var ctr = _this.el.select('.fc-event-container',true).first();
15257 var cg = ctr.createChild(cfg);
15259 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15260 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15262 var r = (c.more.length) ? 1 : 0;
15263 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15264 cg.setWidth(ebox.right - sbox.x -2);
15266 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15267 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15268 cg.on('click', _this.onEventClick, _this, ev);
15279 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15280 style : 'position: absolute',
15281 unselectable : "on",
15284 cls: 'fc-event-inner',
15288 cls: 'fc-event-title',
15296 cls: 'ui-resizable-handle ui-resizable-e',
15297 html : '  '
15303 var ctr = _this.el.select('.fc-event-container',true).first();
15304 var cg = ctr.createChild(cfg);
15306 var sbox = c.select('.fc-day-content',true).first().getBox();
15307 var ebox = c.select('.fc-day-content',true).first().getBox();
15309 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15310 cg.setWidth(ebox.right - sbox.x -2);
15312 cg.on('click', _this.onMoreEventClick, _this, c.more);
15322 onEventEnter: function (e, el,event,d) {
15323 this.fireEvent('evententer', this, el, event);
15326 onEventLeave: function (e, el,event,d) {
15327 this.fireEvent('eventleave', this, el, event);
15330 onEventClick: function (e, el,event,d) {
15331 this.fireEvent('eventclick', this, el, event);
15334 onMonthChange: function () {
15338 onMoreEventClick: function(e, el, more)
15342 this.calpopover.placement = 'right';
15343 this.calpopover.setTitle('More');
15345 this.calpopover.setContent('');
15347 var ctr = this.calpopover.el.select('.popover-content', true).first();
15349 Roo.each(more, function(m){
15351 cls : 'fc-event-hori fc-event-draggable',
15354 var cg = ctr.createChild(cfg);
15356 cg.on('click', _this.onEventClick, _this, m);
15359 this.calpopover.show(el);
15364 onLoad: function ()
15366 this.calevents = [];
15369 if(this.store.getCount() > 0){
15370 this.store.data.each(function(d){
15373 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15374 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15375 time : d.data.start_time,
15376 title : d.data.title,
15377 description : d.data.description,
15378 venue : d.data.venue
15383 this.renderEvents();
15385 if(this.calevents.length && this.loadMask){
15386 this.maskEl.hide();
15390 onBeforeLoad: function()
15392 this.clearEvents();
15394 this.maskEl.show();
15408 * @class Roo.bootstrap.Popover
15409 * @extends Roo.bootstrap.Component
15410 * Bootstrap Popover class
15411 * @cfg {String} html contents of the popover (or false to use children..)
15412 * @cfg {String} title of popover (or false to hide)
15413 * @cfg {String} placement how it is placed
15414 * @cfg {String} trigger click || hover (or false to trigger manually)
15415 * @cfg {String} over what (parent or false to trigger manually.)
15416 * @cfg {Number} delay - delay before showing
15419 * Create a new Popover
15420 * @param {Object} config The config object
15423 Roo.bootstrap.Popover = function(config){
15424 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15427 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
15429 title: 'Fill in a title',
15432 placement : 'right',
15433 trigger : 'hover', // hover
15439 can_build_overlaid : false,
15441 getChildContainer : function()
15443 return this.el.select('.popover-content',true).first();
15446 getAutoCreate : function(){
15447 Roo.log('make popover?');
15449 cls : 'popover roo-dynamic',
15450 style: 'display:block',
15456 cls : 'popover-inner',
15460 cls: 'popover-title',
15464 cls : 'popover-content',
15475 setTitle: function(str)
15478 this.el.select('.popover-title',true).first().dom.innerHTML = str;
15480 setContent: function(str)
15483 this.el.select('.popover-content',true).first().dom.innerHTML = str;
15485 // as it get's added to the bottom of the page.
15486 onRender : function(ct, position)
15488 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15490 var cfg = Roo.apply({}, this.getAutoCreate());
15494 cfg.cls += ' ' + this.cls;
15497 cfg.style = this.style;
15499 Roo.log("adding to ")
15500 this.el = Roo.get(document.body).createChild(cfg, position);
15506 initEvents : function()
15508 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15509 this.el.enableDisplayMode('block');
15511 if (this.over === false) {
15514 if (this.triggers === false) {
15517 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15518 var triggers = this.trigger ? this.trigger.split(' ') : [];
15519 Roo.each(triggers, function(trigger) {
15521 if (trigger == 'click') {
15522 on_el.on('click', this.toggle, this);
15523 } else if (trigger != 'manual') {
15524 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
15525 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15527 on_el.on(eventIn ,this.enter, this);
15528 on_el.on(eventOut, this.leave, this);
15539 toggle : function () {
15540 this.hoverState == 'in' ? this.leave() : this.enter();
15543 enter : function () {
15546 clearTimeout(this.timeout);
15548 this.hoverState = 'in';
15550 if (!this.delay || !this.delay.show) {
15555 this.timeout = setTimeout(function () {
15556 if (_t.hoverState == 'in') {
15559 }, this.delay.show)
15561 leave : function() {
15562 clearTimeout(this.timeout);
15564 this.hoverState = 'out';
15566 if (!this.delay || !this.delay.hide) {
15571 this.timeout = setTimeout(function () {
15572 if (_t.hoverState == 'out') {
15575 }, this.delay.hide)
15578 show : function (on_el)
15581 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15584 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15585 if (this.html !== false) {
15586 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15588 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15589 if (!this.title.length) {
15590 this.el.select('.popover-title',true).hide();
15593 var placement = typeof this.placement == 'function' ?
15594 this.placement.call(this, this.el, on_el) :
15597 var autoToken = /\s?auto?\s?/i;
15598 var autoPlace = autoToken.test(placement);
15600 placement = placement.replace(autoToken, '') || 'top';
15604 //this.el.setXY([0,0]);
15606 this.el.dom.style.display='block';
15607 this.el.addClass(placement);
15609 //this.el.appendTo(on_el);
15611 var p = this.getPosition();
15612 var box = this.el.getBox();
15617 var align = Roo.bootstrap.Popover.alignment[placement];
15618 this.el.alignTo(on_el, align[0],align[1]);
15619 //var arrow = this.el.select('.arrow',true).first();
15620 //arrow.set(align[2],
15622 this.el.addClass('in');
15625 if (this.el.hasClass('fade')) {
15632 this.el.setXY([0,0]);
15633 this.el.removeClass('in');
15635 this.hoverState = null;
15641 Roo.bootstrap.Popover.alignment = {
15642 'left' : ['r-l', [-10,0], 'right'],
15643 'right' : ['l-r', [10,0], 'left'],
15644 'bottom' : ['t-b', [0,10], 'top'],
15645 'top' : [ 'b-t', [0,-10], 'bottom']
15656 * @class Roo.bootstrap.Progress
15657 * @extends Roo.bootstrap.Component
15658 * Bootstrap Progress class
15659 * @cfg {Boolean} striped striped of the progress bar
15660 * @cfg {Boolean} active animated of the progress bar
15664 * Create a new Progress
15665 * @param {Object} config The config object
15668 Roo.bootstrap.Progress = function(config){
15669 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15672 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
15677 getAutoCreate : function(){
15685 cfg.cls += ' progress-striped';
15689 cfg.cls += ' active';
15708 * @class Roo.bootstrap.ProgressBar
15709 * @extends Roo.bootstrap.Component
15710 * Bootstrap ProgressBar class
15711 * @cfg {Number} aria_valuenow aria-value now
15712 * @cfg {Number} aria_valuemin aria-value min
15713 * @cfg {Number} aria_valuemax aria-value max
15714 * @cfg {String} label label for the progress bar
15715 * @cfg {String} panel (success | info | warning | danger )
15716 * @cfg {String} role role of the progress bar
15717 * @cfg {String} sr_only text
15721 * Create a new ProgressBar
15722 * @param {Object} config The config object
15725 Roo.bootstrap.ProgressBar = function(config){
15726 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15729 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
15733 aria_valuemax : 100,
15739 getAutoCreate : function()
15744 cls: 'progress-bar',
15745 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15757 cfg.role = this.role;
15760 if(this.aria_valuenow){
15761 cfg['aria-valuenow'] = this.aria_valuenow;
15764 if(this.aria_valuemin){
15765 cfg['aria-valuemin'] = this.aria_valuemin;
15768 if(this.aria_valuemax){
15769 cfg['aria-valuemax'] = this.aria_valuemax;
15772 if(this.label && !this.sr_only){
15773 cfg.html = this.label;
15777 cfg.cls += ' progress-bar-' + this.panel;
15783 update : function(aria_valuenow)
15785 this.aria_valuenow = aria_valuenow;
15787 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15802 * @class Roo.bootstrap.TabGroup
15803 * @extends Roo.bootstrap.Column
15804 * Bootstrap Column class
15805 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15806 * @cfg {Boolean} carousel true to make the group behave like a carousel
15807 * @cfg {Number} bullets show the panel pointer.. default 0
15808 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15809 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15810 * @cfg {Number} timer auto slide timer .. default 0 millisecond
15813 * Create a new TabGroup
15814 * @param {Object} config The config object
15817 Roo.bootstrap.TabGroup = function(config){
15818 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15820 this.navId = Roo.id();
15823 Roo.bootstrap.TabGroup.register(this);
15827 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
15830 transition : false,
15835 slideOnTouch : false,
15837 getAutoCreate : function()
15839 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15841 cfg.cls += ' tab-content';
15843 Roo.log('get auto create...............');
15845 if (this.carousel) {
15846 cfg.cls += ' carousel slide';
15849 cls : 'carousel-inner'
15852 if(this.bullets > 0 && !Roo.isTouch){
15855 cls : 'carousel-bullets',
15859 if(this.bullets_cls){
15860 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15863 for (var i = 0; i < this.bullets; i++){
15865 cls : 'bullet bullet-' + i
15873 cfg.cn[0].cn = bullets;
15880 initEvents: function()
15882 Roo.log('-------- init events on tab group ---------');
15884 if(this.bullets > 0 && !Roo.isTouch){
15890 if(Roo.isTouch && this.slideOnTouch){
15891 this.el.on("touchstart", this.onTouchStart, this);
15894 if(this.autoslide){
15897 this.slideFn = window.setInterval(function() {
15898 _this.showPanelNext();
15904 onTouchStart : function(e, el, o)
15906 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15910 this.showPanelNext();
15913 getChildContainer : function()
15915 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15919 * register a Navigation item
15920 * @param {Roo.bootstrap.NavItem} the navitem to add
15922 register : function(item)
15924 this.tabs.push( item);
15925 item.navId = this.navId; // not really needed..
15929 getActivePanel : function()
15932 Roo.each(this.tabs, function(t) {
15942 getPanelByName : function(n)
15945 Roo.each(this.tabs, function(t) {
15946 if (t.tabId == n) {
15954 indexOfPanel : function(p)
15957 Roo.each(this.tabs, function(t,i) {
15958 if (t.tabId == p.tabId) {
15967 * show a specific panel
15968 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15969 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15971 showPanel : function (pan)
15973 if(this.transition){
15974 Roo.log("waiting for the transitionend");
15978 if (typeof(pan) == 'number') {
15979 pan = this.tabs[pan];
15981 if (typeof(pan) == 'string') {
15982 pan = this.getPanelByName(pan);
15984 if (pan.tabId == this.getActivePanel().tabId) {
15987 var cur = this.getActivePanel();
15989 if (false === cur.fireEvent('beforedeactivate')) {
15993 if(this.bullets > 0 && !Roo.isTouch){
15994 this.setActiveBullet(this.indexOfPanel(pan));
15997 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15999 this.transition = true;
16000 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16001 var lr = dir == 'next' ? 'left' : 'right';
16002 pan.el.addClass(dir); // or prev
16003 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16004 cur.el.addClass(lr); // or right
16005 pan.el.addClass(lr);
16008 cur.el.on('transitionend', function() {
16009 Roo.log("trans end?");
16011 pan.el.removeClass([lr,dir]);
16012 pan.setActive(true);
16014 cur.el.removeClass([lr]);
16015 cur.setActive(false);
16017 _this.transition = false;
16019 }, this, { single: true } );
16024 cur.setActive(false);
16025 pan.setActive(true);
16030 showPanelNext : function()
16032 var i = this.indexOfPanel(this.getActivePanel());
16034 if (i >= this.tabs.length - 1 && !this.autoslide) {
16038 if (i >= this.tabs.length - 1 && this.autoslide) {
16042 this.showPanel(this.tabs[i+1]);
16045 showPanelPrev : function()
16047 var i = this.indexOfPanel(this.getActivePanel());
16049 if (i < 1 && !this.autoslide) {
16053 if (i < 1 && this.autoslide) {
16054 i = this.tabs.length;
16057 this.showPanel(this.tabs[i-1]);
16060 initBullet : function()
16068 for (var i = 0; i < this.bullets; i++){
16069 var bullet = this.el.select('.bullet-' + i, true).first();
16075 bullet.on('click', (function(e, el, o, ii, t){
16077 e.preventDefault();
16079 _this.showPanel(ii);
16081 if(_this.autoslide && _this.slideFn){
16082 clearInterval(_this.slideFn);
16083 _this.slideFn = window.setInterval(function() {
16084 _this.showPanelNext();
16088 }).createDelegate(this, [i, bullet], true));
16092 setActiveBullet : function(i)
16098 Roo.each(this.el.select('.bullet', true).elements, function(el){
16099 el.removeClass('selected');
16102 var bullet = this.el.select('.bullet-' + i, true).first();
16108 bullet.addClass('selected');
16119 Roo.apply(Roo.bootstrap.TabGroup, {
16123 * register a Navigation Group
16124 * @param {Roo.bootstrap.NavGroup} the navgroup to add
16126 register : function(navgrp)
16128 this.groups[navgrp.navId] = navgrp;
16132 * fetch a Navigation Group based on the navigation ID
16133 * if one does not exist , it will get created.
16134 * @param {string} the navgroup to add
16135 * @returns {Roo.bootstrap.NavGroup} the navgroup
16137 get: function(navId) {
16138 if (typeof(this.groups[navId]) == 'undefined') {
16139 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16141 return this.groups[navId] ;
16156 * @class Roo.bootstrap.TabPanel
16157 * @extends Roo.bootstrap.Component
16158 * Bootstrap TabPanel class
16159 * @cfg {Boolean} active panel active
16160 * @cfg {String} html panel content
16161 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16162 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16166 * Create a new TabPanel
16167 * @param {Object} config The config object
16170 Roo.bootstrap.TabPanel = function(config){
16171 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16175 * Fires when the active status changes
16176 * @param {Roo.bootstrap.TabPanel} this
16177 * @param {Boolean} state the new state
16182 * @event beforedeactivate
16183 * Fires before a tab is de-activated - can be used to do validation on a form.
16184 * @param {Roo.bootstrap.TabPanel} this
16185 * @return {Boolean} false if there is an error
16188 'beforedeactivate': true
16191 this.tabId = this.tabId || Roo.id();
16195 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16202 getAutoCreate : function(){
16205 // item is needed for carousel - not sure if it has any effect otherwise
16206 cls: 'tab-pane item',
16207 html: this.html || ''
16211 cfg.cls += ' active';
16215 cfg.tabId = this.tabId;
16222 initEvents: function()
16224 Roo.log('-------- init events on tab panel ---------');
16226 var p = this.parent();
16227 this.navId = this.navId || p.navId;
16229 if (typeof(this.navId) != 'undefined') {
16230 // not really needed.. but just in case.. parent should be a NavGroup.
16231 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16232 Roo.log(['register', tg, this]);
16235 var i = tg.tabs.length - 1;
16237 if(this.active && tg.bullets > 0 && i < tg.bullets){
16238 tg.setActiveBullet(i);
16245 onRender : function(ct, position)
16247 // Roo.log("Call onRender: " + this.xtype);
16249 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16257 setActive: function(state)
16259 Roo.log("panel - set active " + this.tabId + "=" + state);
16261 this.active = state;
16263 this.el.removeClass('active');
16265 } else if (!this.el.hasClass('active')) {
16266 this.el.addClass('active');
16269 this.fireEvent('changed', this, state);
16286 * @class Roo.bootstrap.DateField
16287 * @extends Roo.bootstrap.Input
16288 * Bootstrap DateField class
16289 * @cfg {Number} weekStart default 0
16290 * @cfg {String} viewMode default empty, (months|years)
16291 * @cfg {String} minViewMode default empty, (months|years)
16292 * @cfg {Number} startDate default -Infinity
16293 * @cfg {Number} endDate default Infinity
16294 * @cfg {Boolean} todayHighlight default false
16295 * @cfg {Boolean} todayBtn default false
16296 * @cfg {Boolean} calendarWeeks default false
16297 * @cfg {Object} daysOfWeekDisabled default empty
16298 * @cfg {Boolean} singleMode default false (true | false)
16300 * @cfg {Boolean} keyboardNavigation default true
16301 * @cfg {String} language default en
16304 * Create a new DateField
16305 * @param {Object} config The config object
16308 Roo.bootstrap.DateField = function(config){
16309 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16313 * Fires when this field show.
16314 * @param {Roo.bootstrap.DateField} this
16315 * @param {Mixed} date The date value
16320 * Fires when this field hide.
16321 * @param {Roo.bootstrap.DateField} this
16322 * @param {Mixed} date The date value
16327 * Fires when select a date.
16328 * @param {Roo.bootstrap.DateField} this
16329 * @param {Mixed} date The date value
16333 * @event beforeselect
16334 * Fires when before select a date.
16335 * @param {Roo.bootstrap.DateField} this
16336 * @param {Mixed} date The date value
16338 beforeselect : true
16342 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16345 * @cfg {String} format
16346 * The default date format string which can be overriden for localization support. The format must be
16347 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16351 * @cfg {String} altFormats
16352 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16353 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16355 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16363 todayHighlight : false,
16369 keyboardNavigation: true,
16371 calendarWeeks: false,
16373 startDate: -Infinity,
16377 daysOfWeekDisabled: [],
16381 singleMode : false,
16383 UTCDate: function()
16385 return new Date(Date.UTC.apply(Date, arguments));
16388 UTCToday: function()
16390 var today = new Date();
16391 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16394 getDate: function() {
16395 var d = this.getUTCDate();
16396 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16399 getUTCDate: function() {
16403 setDate: function(d) {
16404 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16407 setUTCDate: function(d) {
16409 this.setValue(this.formatDate(this.date));
16412 onRender: function(ct, position)
16415 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16417 this.language = this.language || 'en';
16418 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16419 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16421 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16422 this.format = this.format || 'm/d/y';
16423 this.isInline = false;
16424 this.isInput = true;
16425 this.component = this.el.select('.add-on', true).first() || false;
16426 this.component = (this.component && this.component.length === 0) ? false : this.component;
16427 this.hasInput = this.component && this.inputEL().length;
16429 if (typeof(this.minViewMode === 'string')) {
16430 switch (this.minViewMode) {
16432 this.minViewMode = 1;
16435 this.minViewMode = 2;
16438 this.minViewMode = 0;
16443 if (typeof(this.viewMode === 'string')) {
16444 switch (this.viewMode) {
16457 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16459 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16461 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16463 this.picker().on('mousedown', this.onMousedown, this);
16464 this.picker().on('click', this.onClick, this);
16466 this.picker().addClass('datepicker-dropdown');
16468 this.startViewMode = this.viewMode;
16470 if(this.singleMode){
16471 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16472 v.setVisibilityMode(Roo.Element.DISPLAY)
16476 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16477 v.setStyle('width', '189px');
16481 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16482 if(!this.calendarWeeks){
16487 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16488 v.attr('colspan', function(i, val){
16489 return parseInt(val) + 1;
16494 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16496 this.setStartDate(this.startDate);
16497 this.setEndDate(this.endDate);
16499 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16506 if(this.isInline) {
16511 picker : function()
16513 return this.pickerEl;
16514 // return this.el.select('.datepicker', true).first();
16517 fillDow: function()
16519 var dowCnt = this.weekStart;
16528 if(this.calendarWeeks){
16536 while (dowCnt < this.weekStart + 7) {
16540 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16544 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16547 fillMonths: function()
16550 var months = this.picker().select('>.datepicker-months td', true).first();
16552 months.dom.innerHTML = '';
16558 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16561 months.createChild(month);
16568 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;
16570 if (this.date < this.startDate) {
16571 this.viewDate = new Date(this.startDate);
16572 } else if (this.date > this.endDate) {
16573 this.viewDate = new Date(this.endDate);
16575 this.viewDate = new Date(this.date);
16583 var d = new Date(this.viewDate),
16584 year = d.getUTCFullYear(),
16585 month = d.getUTCMonth(),
16586 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16587 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16588 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16589 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16590 currentDate = this.date && this.date.valueOf(),
16591 today = this.UTCToday();
16593 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16595 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16597 // this.picker.select('>tfoot th.today').
16598 // .text(dates[this.language].today)
16599 // .toggle(this.todayBtn !== false);
16601 this.updateNavArrows();
16604 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16606 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16608 prevMonth.setUTCDate(day);
16610 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16612 var nextMonth = new Date(prevMonth);
16614 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16616 nextMonth = nextMonth.valueOf();
16618 var fillMonths = false;
16620 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16622 while(prevMonth.valueOf() < nextMonth) {
16625 if (prevMonth.getUTCDay() === this.weekStart) {
16627 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16635 if(this.calendarWeeks){
16636 // ISO 8601: First week contains first thursday.
16637 // ISO also states week starts on Monday, but we can be more abstract here.
16639 // Start of current week: based on weekstart/current date
16640 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16641 // Thursday of this week
16642 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16643 // First Thursday of year, year from thursday
16644 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16645 // Calendar week: ms between thursdays, div ms per day, div 7 days
16646 calWeek = (th - yth) / 864e5 / 7 + 1;
16648 fillMonths.cn.push({
16656 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16658 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16661 if (this.todayHighlight &&
16662 prevMonth.getUTCFullYear() == today.getFullYear() &&
16663 prevMonth.getUTCMonth() == today.getMonth() &&
16664 prevMonth.getUTCDate() == today.getDate()) {
16665 clsName += ' today';
16668 if (currentDate && prevMonth.valueOf() === currentDate) {
16669 clsName += ' active';
16672 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16673 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16674 clsName += ' disabled';
16677 fillMonths.cn.push({
16679 cls: 'day ' + clsName,
16680 html: prevMonth.getDate()
16683 prevMonth.setDate(prevMonth.getDate()+1);
16686 var currentYear = this.date && this.date.getUTCFullYear();
16687 var currentMonth = this.date && this.date.getUTCMonth();
16689 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16691 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16692 v.removeClass('active');
16694 if(currentYear === year && k === currentMonth){
16695 v.addClass('active');
16698 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16699 v.addClass('disabled');
16705 year = parseInt(year/10, 10) * 10;
16707 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16709 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16712 for (var i = -1; i < 11; i++) {
16713 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16715 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16723 showMode: function(dir)
16726 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16729 Roo.each(this.picker().select('>div',true).elements, function(v){
16730 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16733 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16738 if(this.isInline) return;
16740 this.picker().removeClass(['bottom', 'top']);
16742 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16744 * place to the top of element!
16748 this.picker().addClass('top');
16749 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16754 this.picker().addClass('bottom');
16756 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16759 parseDate : function(value)
16761 if(!value || value instanceof Date){
16764 var v = Date.parseDate(value, this.format);
16765 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16766 v = Date.parseDate(value, 'Y-m-d');
16768 if(!v && this.altFormats){
16769 if(!this.altFormatsArray){
16770 this.altFormatsArray = this.altFormats.split("|");
16772 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16773 v = Date.parseDate(value, this.altFormatsArray[i]);
16779 formatDate : function(date, fmt)
16781 return (!date || !(date instanceof Date)) ?
16782 date : date.dateFormat(fmt || this.format);
16785 onFocus : function()
16787 Roo.bootstrap.DateField.superclass.onFocus.call(this);
16791 onBlur : function()
16793 Roo.bootstrap.DateField.superclass.onBlur.call(this);
16795 var d = this.inputEl().getValue();
16804 this.picker().show();
16808 this.fireEvent('show', this, this.date);
16813 if(this.isInline) return;
16814 this.picker().hide();
16815 this.viewMode = this.startViewMode;
16818 this.fireEvent('hide', this, this.date);
16822 onMousedown: function(e)
16824 e.stopPropagation();
16825 e.preventDefault();
16830 Roo.bootstrap.DateField.superclass.keyup.call(this);
16834 setValue: function(v)
16836 if(this.fireEvent('beforeselect', this, v) !== false){
16837 var d = new Date(this.parseDate(v) ).clearTime();
16839 if(isNaN(d.getTime())){
16840 this.date = this.viewDate = '';
16841 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16845 v = this.formatDate(d);
16847 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16849 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16853 this.fireEvent('select', this, this.date);
16857 getValue: function()
16859 return this.formatDate(this.date);
16862 fireKey: function(e)
16864 if (!this.picker().isVisible()){
16865 if (e.keyCode == 27) // allow escape to hide and re-show picker
16870 var dateChanged = false,
16872 newDate, newViewDate;
16877 e.preventDefault();
16881 if (!this.keyboardNavigation) break;
16882 dir = e.keyCode == 37 ? -1 : 1;
16885 newDate = this.moveYear(this.date, dir);
16886 newViewDate = this.moveYear(this.viewDate, dir);
16887 } else if (e.shiftKey){
16888 newDate = this.moveMonth(this.date, dir);
16889 newViewDate = this.moveMonth(this.viewDate, dir);
16891 newDate = new Date(this.date);
16892 newDate.setUTCDate(this.date.getUTCDate() + dir);
16893 newViewDate = new Date(this.viewDate);
16894 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16896 if (this.dateWithinRange(newDate)){
16897 this.date = newDate;
16898 this.viewDate = newViewDate;
16899 this.setValue(this.formatDate(this.date));
16901 e.preventDefault();
16902 dateChanged = true;
16907 if (!this.keyboardNavigation) break;
16908 dir = e.keyCode == 38 ? -1 : 1;
16910 newDate = this.moveYear(this.date, dir);
16911 newViewDate = this.moveYear(this.viewDate, dir);
16912 } else if (e.shiftKey){
16913 newDate = this.moveMonth(this.date, dir);
16914 newViewDate = this.moveMonth(this.viewDate, dir);
16916 newDate = new Date(this.date);
16917 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16918 newViewDate = new Date(this.viewDate);
16919 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16921 if (this.dateWithinRange(newDate)){
16922 this.date = newDate;
16923 this.viewDate = newViewDate;
16924 this.setValue(this.formatDate(this.date));
16926 e.preventDefault();
16927 dateChanged = true;
16931 this.setValue(this.formatDate(this.date));
16933 e.preventDefault();
16936 this.setValue(this.formatDate(this.date));
16950 onClick: function(e)
16952 e.stopPropagation();
16953 e.preventDefault();
16955 var target = e.getTarget();
16957 if(target.nodeName.toLowerCase() === 'i'){
16958 target = Roo.get(target).dom.parentNode;
16961 var nodeName = target.nodeName;
16962 var className = target.className;
16963 var html = target.innerHTML;
16964 //Roo.log(nodeName);
16966 switch(nodeName.toLowerCase()) {
16968 switch(className) {
16974 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16975 switch(this.viewMode){
16977 this.viewDate = this.moveMonth(this.viewDate, dir);
16981 this.viewDate = this.moveYear(this.viewDate, dir);
16987 var date = new Date();
16988 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16990 this.setValue(this.formatDate(this.date));
16997 if (className.indexOf('disabled') < 0) {
16998 this.viewDate.setUTCDate(1);
16999 if (className.indexOf('month') > -1) {
17000 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17002 var year = parseInt(html, 10) || 0;
17003 this.viewDate.setUTCFullYear(year);
17007 if(this.singleMode){
17008 this.setValue(this.formatDate(this.viewDate));
17019 //Roo.log(className);
17020 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17021 var day = parseInt(html, 10) || 1;
17022 var year = this.viewDate.getUTCFullYear(),
17023 month = this.viewDate.getUTCMonth();
17025 if (className.indexOf('old') > -1) {
17032 } else if (className.indexOf('new') > -1) {
17040 //Roo.log([year,month,day]);
17041 this.date = this.UTCDate(year, month, day,0,0,0,0);
17042 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17044 //Roo.log(this.formatDate(this.date));
17045 this.setValue(this.formatDate(this.date));
17052 setStartDate: function(startDate)
17054 this.startDate = startDate || -Infinity;
17055 if (this.startDate !== -Infinity) {
17056 this.startDate = this.parseDate(this.startDate);
17059 this.updateNavArrows();
17062 setEndDate: function(endDate)
17064 this.endDate = endDate || Infinity;
17065 if (this.endDate !== Infinity) {
17066 this.endDate = this.parseDate(this.endDate);
17069 this.updateNavArrows();
17072 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17074 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17075 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17076 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17078 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17079 return parseInt(d, 10);
17082 this.updateNavArrows();
17085 updateNavArrows: function()
17087 if(this.singleMode){
17091 var d = new Date(this.viewDate),
17092 year = d.getUTCFullYear(),
17093 month = d.getUTCMonth();
17095 Roo.each(this.picker().select('.prev', true).elements, function(v){
17097 switch (this.viewMode) {
17100 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17106 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17113 Roo.each(this.picker().select('.next', true).elements, function(v){
17115 switch (this.viewMode) {
17118 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17124 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17132 moveMonth: function(date, dir)
17134 if (!dir) return date;
17135 var new_date = new Date(date.valueOf()),
17136 day = new_date.getUTCDate(),
17137 month = new_date.getUTCMonth(),
17138 mag = Math.abs(dir),
17140 dir = dir > 0 ? 1 : -1;
17143 // If going back one month, make sure month is not current month
17144 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17146 return new_date.getUTCMonth() == month;
17148 // If going forward one month, make sure month is as expected
17149 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17151 return new_date.getUTCMonth() != new_month;
17153 new_month = month + dir;
17154 new_date.setUTCMonth(new_month);
17155 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17156 if (new_month < 0 || new_month > 11)
17157 new_month = (new_month + 12) % 12;
17159 // For magnitudes >1, move one month at a time...
17160 for (var i=0; i<mag; i++)
17161 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17162 new_date = this.moveMonth(new_date, dir);
17163 // ...then reset the day, keeping it in the new month
17164 new_month = new_date.getUTCMonth();
17165 new_date.setUTCDate(day);
17167 return new_month != new_date.getUTCMonth();
17170 // Common date-resetting loop -- if date is beyond end of month, make it
17173 new_date.setUTCDate(--day);
17174 new_date.setUTCMonth(new_month);
17179 moveYear: function(date, dir)
17181 return this.moveMonth(date, dir*12);
17184 dateWithinRange: function(date)
17186 return date >= this.startDate && date <= this.endDate;
17192 this.picker().remove();
17197 Roo.apply(Roo.bootstrap.DateField, {
17208 html: '<i class="fa fa-arrow-left"/>'
17218 html: '<i class="fa fa-arrow-right"/>'
17260 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17261 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17262 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17263 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17264 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17277 navFnc: 'FullYear',
17282 navFnc: 'FullYear',
17287 Roo.apply(Roo.bootstrap.DateField, {
17291 cls: 'datepicker dropdown-menu roo-dynamic',
17295 cls: 'datepicker-days',
17299 cls: 'table-condensed',
17301 Roo.bootstrap.DateField.head,
17305 Roo.bootstrap.DateField.footer
17312 cls: 'datepicker-months',
17316 cls: 'table-condensed',
17318 Roo.bootstrap.DateField.head,
17319 Roo.bootstrap.DateField.content,
17320 Roo.bootstrap.DateField.footer
17327 cls: 'datepicker-years',
17331 cls: 'table-condensed',
17333 Roo.bootstrap.DateField.head,
17334 Roo.bootstrap.DateField.content,
17335 Roo.bootstrap.DateField.footer
17354 * @class Roo.bootstrap.TimeField
17355 * @extends Roo.bootstrap.Input
17356 * Bootstrap DateField class
17360 * Create a new TimeField
17361 * @param {Object} config The config object
17364 Roo.bootstrap.TimeField = function(config){
17365 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17369 * Fires when this field show.
17370 * @param {Roo.bootstrap.DateField} thisthis
17371 * @param {Mixed} date The date value
17376 * Fires when this field hide.
17377 * @param {Roo.bootstrap.DateField} this
17378 * @param {Mixed} date The date value
17383 * Fires when select a date.
17384 * @param {Roo.bootstrap.DateField} this
17385 * @param {Mixed} date The date value
17391 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17394 * @cfg {String} format
17395 * The default time format string which can be overriden for localization support. The format must be
17396 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17400 onRender: function(ct, position)
17403 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17405 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17407 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17409 this.pop = this.picker().select('>.datepicker-time',true).first();
17410 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17412 this.picker().on('mousedown', this.onMousedown, this);
17413 this.picker().on('click', this.onClick, this);
17415 this.picker().addClass('datepicker-dropdown');
17420 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17421 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17422 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17423 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17424 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17425 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17429 fireKey: function(e){
17430 if (!this.picker().isVisible()){
17431 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17437 e.preventDefault();
17445 this.onTogglePeriod();
17448 this.onIncrementMinutes();
17451 this.onDecrementMinutes();
17460 onClick: function(e) {
17461 e.stopPropagation();
17462 e.preventDefault();
17465 picker : function()
17467 return this.el.select('.datepicker', true).first();
17470 fillTime: function()
17472 var time = this.pop.select('tbody', true).first();
17474 time.dom.innerHTML = '';
17489 cls: 'hours-up glyphicon glyphicon-chevron-up'
17509 cls: 'minutes-up glyphicon glyphicon-chevron-up'
17530 cls: 'timepicker-hour',
17545 cls: 'timepicker-minute',
17560 cls: 'btn btn-primary period',
17582 cls: 'hours-down glyphicon glyphicon-chevron-down'
17602 cls: 'minutes-down glyphicon glyphicon-chevron-down'
17620 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17627 var hours = this.time.getHours();
17628 var minutes = this.time.getMinutes();
17641 hours = hours - 12;
17645 hours = '0' + hours;
17649 minutes = '0' + minutes;
17652 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17653 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17654 this.pop.select('button', true).first().dom.innerHTML = period;
17660 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17662 var cls = ['bottom'];
17664 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17671 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17676 this.picker().addClass(cls.join('-'));
17680 Roo.each(cls, function(c){
17682 _this.picker().setTop(_this.inputEl().getHeight());
17686 _this.picker().setTop(0 - _this.picker().getHeight());
17691 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17695 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17702 onFocus : function()
17704 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17708 onBlur : function()
17710 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17716 this.picker().show();
17721 this.fireEvent('show', this, this.date);
17726 this.picker().hide();
17729 this.fireEvent('hide', this, this.date);
17732 setTime : function()
17735 this.setValue(this.time.format(this.format));
17737 this.fireEvent('select', this, this.date);
17742 onMousedown: function(e){
17743 e.stopPropagation();
17744 e.preventDefault();
17747 onIncrementHours: function()
17749 Roo.log('onIncrementHours');
17750 this.time = this.time.add(Date.HOUR, 1);
17755 onDecrementHours: function()
17757 Roo.log('onDecrementHours');
17758 this.time = this.time.add(Date.HOUR, -1);
17762 onIncrementMinutes: function()
17764 Roo.log('onIncrementMinutes');
17765 this.time = this.time.add(Date.MINUTE, 1);
17769 onDecrementMinutes: function()
17771 Roo.log('onDecrementMinutes');
17772 this.time = this.time.add(Date.MINUTE, -1);
17776 onTogglePeriod: function()
17778 Roo.log('onTogglePeriod');
17779 this.time = this.time.add(Date.HOUR, 12);
17786 Roo.apply(Roo.bootstrap.TimeField, {
17816 cls: 'btn btn-info ok',
17828 Roo.apply(Roo.bootstrap.TimeField, {
17832 cls: 'datepicker dropdown-menu',
17836 cls: 'datepicker-time',
17840 cls: 'table-condensed',
17842 Roo.bootstrap.TimeField.content,
17843 Roo.bootstrap.TimeField.footer
17862 * @class Roo.bootstrap.MonthField
17863 * @extends Roo.bootstrap.Input
17864 * Bootstrap MonthField class
17866 * @cfg {String} language default en
17869 * Create a new MonthField
17870 * @param {Object} config The config object
17873 Roo.bootstrap.MonthField = function(config){
17874 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17879 * Fires when this field show.
17880 * @param {Roo.bootstrap.MonthField} this
17881 * @param {Mixed} date The date value
17886 * Fires when this field hide.
17887 * @param {Roo.bootstrap.MonthField} this
17888 * @param {Mixed} date The date value
17893 * Fires when select a date.
17894 * @param {Roo.bootstrap.MonthField} this
17895 * @param {String} oldvalue The old value
17896 * @param {String} newvalue The new value
17902 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
17904 onRender: function(ct, position)
17907 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17909 this.language = this.language || 'en';
17910 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17911 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17913 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17914 this.isInline = false;
17915 this.isInput = true;
17916 this.component = this.el.select('.add-on', true).first() || false;
17917 this.component = (this.component && this.component.length === 0) ? false : this.component;
17918 this.hasInput = this.component && this.inputEL().length;
17920 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17922 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17924 this.picker().on('mousedown', this.onMousedown, this);
17925 this.picker().on('click', this.onClick, this);
17927 this.picker().addClass('datepicker-dropdown');
17929 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17930 v.setStyle('width', '189px');
17937 if(this.isInline) {
17943 setValue: function(v, suppressEvent)
17945 var o = this.getValue();
17947 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17951 if(suppressEvent !== true){
17952 this.fireEvent('select', this, o, v);
17957 getValue: function()
17962 onClick: function(e)
17964 e.stopPropagation();
17965 e.preventDefault();
17967 var target = e.getTarget();
17969 if(target.nodeName.toLowerCase() === 'i'){
17970 target = Roo.get(target).dom.parentNode;
17973 var nodeName = target.nodeName;
17974 var className = target.className;
17975 var html = target.innerHTML;
17977 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17981 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17983 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17989 picker : function()
17991 return this.pickerEl;
17994 fillMonths: function()
17997 var months = this.picker().select('>.datepicker-months td', true).first();
17999 months.dom.innerHTML = '';
18005 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18008 months.createChild(month);
18017 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18018 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18021 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18022 e.removeClass('active');
18024 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18025 e.addClass('active');
18032 if(this.isInline) return;
18034 this.picker().removeClass(['bottom', 'top']);
18036 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18038 * place to the top of element!
18042 this.picker().addClass('top');
18043 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18048 this.picker().addClass('bottom');
18050 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18053 onFocus : function()
18055 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18059 onBlur : function()
18061 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18063 var d = this.inputEl().getValue();
18072 this.picker().show();
18073 this.picker().select('>.datepicker-months', true).first().show();
18077 this.fireEvent('show', this, this.date);
18082 if(this.isInline) return;
18083 this.picker().hide();
18084 this.fireEvent('hide', this, this.date);
18088 onMousedown: function(e)
18090 e.stopPropagation();
18091 e.preventDefault();
18096 Roo.bootstrap.MonthField.superclass.keyup.call(this);
18100 fireKey: function(e)
18102 if (!this.picker().isVisible()){
18103 if (e.keyCode == 27) // allow escape to hide and re-show picker
18113 e.preventDefault();
18117 dir = e.keyCode == 37 ? -1 : 1;
18119 this.vIndex = this.vIndex + dir;
18121 if(this.vIndex < 0){
18125 if(this.vIndex > 11){
18129 if(isNaN(this.vIndex)){
18133 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18139 dir = e.keyCode == 38 ? -1 : 1;
18141 this.vIndex = this.vIndex + dir * 4;
18143 if(this.vIndex < 0){
18147 if(this.vIndex > 11){
18151 if(isNaN(this.vIndex)){
18155 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18160 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18161 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18165 e.preventDefault();
18168 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18169 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18185 this.picker().remove();
18190 Roo.apply(Roo.bootstrap.MonthField, {
18209 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18210 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18215 Roo.apply(Roo.bootstrap.MonthField, {
18219 cls: 'datepicker dropdown-menu roo-dynamic',
18223 cls: 'datepicker-months',
18227 cls: 'table-condensed',
18229 Roo.bootstrap.DateField.content
18249 * @class Roo.bootstrap.CheckBox
18250 * @extends Roo.bootstrap.Input
18251 * Bootstrap CheckBox class
18253 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18254 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18255 * @cfg {String} boxLabel The text that appears beside the checkbox
18256 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18257 * @cfg {Boolean} checked initnal the element
18258 * @cfg {Boolean} inline inline the element (default false)
18259 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18262 * Create a new CheckBox
18263 * @param {Object} config The config object
18266 Roo.bootstrap.CheckBox = function(config){
18267 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18272 * Fires when the element is checked or unchecked.
18273 * @param {Roo.bootstrap.CheckBox} this This input
18274 * @param {Boolean} checked The new checked value
18281 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18283 inputType: 'checkbox',
18291 getAutoCreate : function()
18293 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18299 cfg.cls = 'form-group ' + this.inputType; //input-group
18302 cfg.cls += ' ' + this.inputType + '-inline';
18308 type : this.inputType,
18309 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18310 cls : 'roo-' + this.inputType, //'form-box',
18311 placeholder : this.placeholder || ''
18315 if (this.weight) { // Validity check?
18316 cfg.cls += " " + this.inputType + "-" + this.weight;
18319 if (this.disabled) {
18320 input.disabled=true;
18324 input.checked = this.checked;
18328 input.name = this.name;
18332 input.cls += ' input-' + this.size;
18337 ['xs','sm','md','lg'].map(function(size){
18338 if (settings[size]) {
18339 cfg.cls += ' col-' + size + '-' + settings[size];
18343 var inputblock = input;
18345 if (this.before || this.after) {
18348 cls : 'input-group',
18353 inputblock.cn.push({
18355 cls : 'input-group-addon',
18360 inputblock.cn.push(input);
18363 inputblock.cn.push({
18365 cls : 'input-group-addon',
18372 if (align ==='left' && this.fieldLabel.length) {
18373 Roo.log("left and has label");
18379 cls : 'control-label col-md-' + this.labelWidth,
18380 html : this.fieldLabel
18384 cls : "col-md-" + (12 - this.labelWidth),
18391 } else if ( this.fieldLabel.length) {
18396 tag: this.boxLabel ? 'span' : 'label',
18398 cls: 'control-label box-input-label',
18399 //cls : 'input-group-addon',
18400 html : this.fieldLabel
18410 Roo.log(" no label && no align");
18411 cfg.cn = [ inputblock ] ;
18416 var boxLabelCfg = {
18418 //'for': id, // box label is handled by onclick - so no for...
18420 html: this.boxLabel
18424 boxLabelCfg.tooltip = this.tooltip;
18427 cfg.cn.push(boxLabelCfg);
18437 * return the real input element.
18439 inputEl: function ()
18441 return this.el.select('input.roo-' + this.inputType,true).first();
18444 labelEl: function()
18446 return this.el.select('label.control-label',true).first();
18448 /* depricated... */
18452 return this.labelEl();
18455 initEvents : function()
18457 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18459 this.inputEl().on('click', this.onClick, this);
18461 if (this.boxLabel) {
18462 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18465 this.startValue = this.getValue();
18468 Roo.bootstrap.CheckBox.register(this);
18472 onClick : function()
18474 this.setChecked(!this.checked);
18477 setChecked : function(state,suppressEvent)
18479 this.startValue = this.getValue();
18481 if(this.inputType == 'radio'){
18483 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18484 e.dom.checked = false;
18487 this.inputEl().dom.checked = true;
18489 this.inputEl().dom.value = this.inputValue;
18491 if(suppressEvent !== true){
18492 this.fireEvent('check', this, true);
18500 this.checked = state;
18502 this.inputEl().dom.checked = state;
18504 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18506 if(suppressEvent !== true){
18507 this.fireEvent('check', this, state);
18513 getValue : function()
18515 if(this.inputType == 'radio'){
18516 return this.getGroupValue();
18519 return this.inputEl().getValue();
18523 getGroupValue : function()
18525 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18529 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18532 setValue : function(v,suppressEvent)
18534 if(this.inputType == 'radio'){
18535 this.setGroupValue(v, suppressEvent);
18539 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18544 setGroupValue : function(v, suppressEvent)
18546 this.startValue = this.getValue();
18548 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18549 e.dom.checked = false;
18551 if(e.dom.value == v){
18552 e.dom.checked = true;
18556 if(suppressEvent !== true){
18557 this.fireEvent('check', this, true);
18565 validate : function()
18569 (this.inputType == 'radio' && this.validateRadio()) ||
18570 (this.inputType == 'checkbox' && this.validateCheckbox())
18576 this.markInvalid();
18580 validateRadio : function()
18584 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18585 if(!e.dom.checked){
18597 validateCheckbox : function()
18600 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18603 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18611 for(var i in group){
18616 r = (group[i].getValue() == group[i].inputValue) ? true : false;
18623 * Mark this field as valid
18625 markValid : function()
18627 if(this.allowBlank){
18633 this.fireEvent('valid', this);
18635 if(this.inputType == 'radio'){
18636 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18637 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18638 e.findParent('.form-group', false, true).addClass(_this.validClass);
18645 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18646 this.el.findParent('.form-group', false, true).addClass(this.validClass);
18650 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18656 for(var i in group){
18657 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18658 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18663 * Mark this field as invalid
18664 * @param {String} msg The validation message
18666 markInvalid : function(msg)
18668 if(this.allowBlank){
18674 this.fireEvent('invalid', this, msg);
18676 if(this.inputType == 'radio'){
18677 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18678 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18679 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18686 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18687 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18691 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18697 for(var i in group){
18698 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18699 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18706 Roo.apply(Roo.bootstrap.CheckBox, {
18711 * register a CheckBox Group
18712 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18714 register : function(checkbox)
18716 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18717 this.groups[checkbox.groupId] = {};
18720 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18724 this.groups[checkbox.groupId][checkbox.name] = checkbox;
18728 * fetch a CheckBox Group based on the group ID
18729 * @param {string} the group ID
18730 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18732 get: function(groupId) {
18733 if (typeof(this.groups[groupId]) == 'undefined') {
18737 return this.groups[groupId] ;
18749 *<div class="radio">
18751 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18752 Option one is this and that—be sure to include why it's great
18759 *<label class="radio-inline">fieldLabel</label>
18760 *<label class="radio-inline">
18761 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18769 * @class Roo.bootstrap.Radio
18770 * @extends Roo.bootstrap.CheckBox
18771 * Bootstrap Radio class
18774 * Create a new Radio
18775 * @param {Object} config The config object
18778 Roo.bootstrap.Radio = function(config){
18779 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18783 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
18785 inputType: 'radio',
18789 getAutoCreate : function()
18791 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18792 align = align || 'left'; // default...
18799 tag : this.inline ? 'span' : 'div',
18804 var inline = this.inline ? ' radio-inline' : '';
18808 // does not need for, as we wrap the input with it..
18810 cls : 'control-label box-label' + inline,
18813 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18817 //cls : 'control-label' + inline,
18818 html : this.fieldLabel,
18819 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18828 type : this.inputType,
18829 //value : (!this.checked) ? this.valueOff : this.inputValue,
18830 value : this.inputValue,
18832 placeholder : this.placeholder || '' // ?? needed????
18835 if (this.weight) { // Validity check?
18836 input.cls += " radio-" + this.weight;
18838 if (this.disabled) {
18839 input.disabled=true;
18843 input.checked = this.checked;
18847 input.name = this.name;
18851 input.cls += ' input-' + this.size;
18854 //?? can span's inline have a width??
18857 ['xs','sm','md','lg'].map(function(size){
18858 if (settings[size]) {
18859 cfg.cls += ' col-' + size + '-' + settings[size];
18863 var inputblock = input;
18865 if (this.before || this.after) {
18868 cls : 'input-group',
18873 inputblock.cn.push({
18875 cls : 'input-group-addon',
18879 inputblock.cn.push(input);
18881 inputblock.cn.push({
18883 cls : 'input-group-addon',
18891 if (this.fieldLabel && this.fieldLabel.length) {
18892 cfg.cn.push(fieldLabel);
18895 // normal bootstrap puts the input inside the label.
18896 // however with our styled version - it has to go after the input.
18898 //lbl.cn.push(inputblock);
18902 cls: 'radio' + inline,
18909 cfg.cn.push( lblwrap);
18914 html: this.boxLabel
18923 initEvents : function()
18925 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18927 this.inputEl().on('click', this.onClick, this);
18928 if (this.boxLabel) {
18929 Roo.log('find label')
18930 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
18935 inputEl: function ()
18937 return this.el.select('input.roo-radio',true).first();
18939 onClick : function()
18942 this.setChecked(true);
18945 setChecked : function(state,suppressEvent)
18948 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18949 v.dom.checked = false;
18952 Roo.log(this.inputEl().dom);
18953 this.checked = state;
18954 this.inputEl().dom.checked = state;
18956 if(suppressEvent !== true){
18957 this.fireEvent('check', this, state);
18960 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18964 getGroupValue : function()
18967 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18968 if(v.dom.checked == true){
18969 value = v.dom.value;
18977 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
18978 * @return {Mixed} value The field value
18980 getValue : function(){
18981 return this.getGroupValue();
18987 //<script type="text/javascript">
18990 * Based Ext JS Library 1.1.1
18991 * Copyright(c) 2006-2007, Ext JS, LLC.
18997 * @class Roo.HtmlEditorCore
18998 * @extends Roo.Component
18999 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19001 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19004 Roo.HtmlEditorCore = function(config){
19007 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19012 * @event initialize
19013 * Fires when the editor is fully initialized (including the iframe)
19014 * @param {Roo.HtmlEditorCore} this
19019 * Fires when the editor is first receives the focus. Any insertion must wait
19020 * until after this event.
19021 * @param {Roo.HtmlEditorCore} this
19025 * @event beforesync
19026 * Fires before the textarea is updated with content from the editor iframe. Return false
19027 * to cancel the sync.
19028 * @param {Roo.HtmlEditorCore} this
19029 * @param {String} html
19033 * @event beforepush
19034 * Fires before the iframe editor is updated with content from the textarea. Return false
19035 * to cancel the push.
19036 * @param {Roo.HtmlEditorCore} this
19037 * @param {String} html
19042 * Fires when the textarea is updated with content from the editor iframe.
19043 * @param {Roo.HtmlEditorCore} this
19044 * @param {String} html
19049 * Fires when the iframe editor is updated with content from the textarea.
19050 * @param {Roo.HtmlEditorCore} this
19051 * @param {String} html
19056 * @event editorevent
19057 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19058 * @param {Roo.HtmlEditorCore} this
19064 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19066 // defaults : white / black...
19067 this.applyBlacklists();
19074 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
19078 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
19084 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
19089 * @cfg {Number} height (in pixels)
19093 * @cfg {Number} width (in pixels)
19098 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19101 stylesheets: false,
19106 // private properties
19107 validationEvent : false,
19109 initialized : false,
19111 sourceEditMode : false,
19112 onFocus : Roo.emptyFn,
19114 hideMode:'offsets',
19118 // blacklist + whitelisted elements..
19125 * Protected method that will not generally be called directly. It
19126 * is called when the editor initializes the iframe with HTML contents. Override this method if you
19127 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19129 getDocMarkup : function(){
19133 // inherit styels from page...??
19134 if (this.stylesheets === false) {
19136 Roo.get(document.head).select('style').each(function(node) {
19137 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19140 Roo.get(document.head).select('link').each(function(node) {
19141 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19144 } else if (!this.stylesheets.length) {
19146 st = '<style type="text/css">' +
19147 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19153 st += '<style type="text/css">' +
19154 'IMG { cursor: pointer } ' +
19158 return '<html><head>' + st +
19159 //<style type="text/css">' +
19160 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19162 ' </head><body class="roo-htmleditor-body"></body></html>';
19166 onRender : function(ct, position)
19169 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19170 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19173 this.el.dom.style.border = '0 none';
19174 this.el.dom.setAttribute('tabIndex', -1);
19175 this.el.addClass('x-hidden hide');
19179 if(Roo.isIE){ // fix IE 1px bogus margin
19180 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19184 this.frameId = Roo.id();
19188 var iframe = this.owner.wrap.createChild({
19190 cls: 'form-control', // bootstrap..
19192 name: this.frameId,
19193 frameBorder : 'no',
19194 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19199 this.iframe = iframe.dom;
19201 this.assignDocWin();
19203 this.doc.designMode = 'on';
19206 this.doc.write(this.getDocMarkup());
19210 var task = { // must defer to wait for browser to be ready
19212 //console.log("run task?" + this.doc.readyState);
19213 this.assignDocWin();
19214 if(this.doc.body || this.doc.readyState == 'complete'){
19216 this.doc.designMode="on";
19220 Roo.TaskMgr.stop(task);
19221 this.initEditor.defer(10, this);
19228 Roo.TaskMgr.start(task);
19233 onResize : function(w, h)
19235 Roo.log('resize: ' +w + ',' + h );
19236 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19240 if(typeof w == 'number'){
19242 this.iframe.style.width = w + 'px';
19244 if(typeof h == 'number'){
19246 this.iframe.style.height = h + 'px';
19248 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19255 * Toggles the editor between standard and source edit mode.
19256 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19258 toggleSourceEdit : function(sourceEditMode){
19260 this.sourceEditMode = sourceEditMode === true;
19262 if(this.sourceEditMode){
19264 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19267 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19268 //this.iframe.className = '';
19271 //this.setSize(this.owner.wrap.getSize());
19272 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19279 * Protected method that will not generally be called directly. If you need/want
19280 * custom HTML cleanup, this is the method you should override.
19281 * @param {String} html The HTML to be cleaned
19282 * return {String} The cleaned HTML
19284 cleanHtml : function(html){
19285 html = String(html);
19286 if(html.length > 5){
19287 if(Roo.isSafari){ // strip safari nonsense
19288 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19291 if(html == ' '){
19298 * HTML Editor -> Textarea
19299 * Protected method that will not generally be called directly. Syncs the contents
19300 * of the editor iframe with the textarea.
19302 syncValue : function(){
19303 if(this.initialized){
19304 var bd = (this.doc.body || this.doc.documentElement);
19305 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19306 var html = bd.innerHTML;
19308 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19309 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19311 html = '<div style="'+m[0]+'">' + html + '</div>';
19314 html = this.cleanHtml(html);
19315 // fix up the special chars.. normaly like back quotes in word...
19316 // however we do not want to do this with chinese..
19317 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19318 var cc = b.charCodeAt();
19320 (cc >= 0x4E00 && cc < 0xA000 ) ||
19321 (cc >= 0x3400 && cc < 0x4E00 ) ||
19322 (cc >= 0xf900 && cc < 0xfb00 )
19328 if(this.owner.fireEvent('beforesync', this, html) !== false){
19329 this.el.dom.value = html;
19330 this.owner.fireEvent('sync', this, html);
19336 * Protected method that will not generally be called directly. Pushes the value of the textarea
19337 * into the iframe editor.
19339 pushValue : function(){
19340 if(this.initialized){
19341 var v = this.el.dom.value.trim();
19343 // if(v.length < 1){
19347 if(this.owner.fireEvent('beforepush', this, v) !== false){
19348 var d = (this.doc.body || this.doc.documentElement);
19350 this.cleanUpPaste();
19351 this.el.dom.value = d.innerHTML;
19352 this.owner.fireEvent('push', this, v);
19358 deferFocus : function(){
19359 this.focus.defer(10, this);
19363 focus : function(){
19364 if(this.win && !this.sourceEditMode){
19371 assignDocWin: function()
19373 var iframe = this.iframe;
19376 this.doc = iframe.contentWindow.document;
19377 this.win = iframe.contentWindow;
19379 // if (!Roo.get(this.frameId)) {
19382 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19383 // this.win = Roo.get(this.frameId).dom.contentWindow;
19385 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19389 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19390 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19395 initEditor : function(){
19396 //console.log("INIT EDITOR");
19397 this.assignDocWin();
19401 this.doc.designMode="on";
19403 this.doc.write(this.getDocMarkup());
19406 var dbody = (this.doc.body || this.doc.documentElement);
19407 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19408 // this copies styles from the containing element into thsi one..
19409 // not sure why we need all of this..
19410 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19412 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19413 //ss['background-attachment'] = 'fixed'; // w3c
19414 dbody.bgProperties = 'fixed'; // ie
19415 //Roo.DomHelper.applyStyles(dbody, ss);
19416 Roo.EventManager.on(this.doc, {
19417 //'mousedown': this.onEditorEvent,
19418 'mouseup': this.onEditorEvent,
19419 'dblclick': this.onEditorEvent,
19420 'click': this.onEditorEvent,
19421 'keyup': this.onEditorEvent,
19426 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19428 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19429 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19431 this.initialized = true;
19433 this.owner.fireEvent('initialize', this);
19438 onDestroy : function(){
19444 //for (var i =0; i < this.toolbars.length;i++) {
19445 // // fixme - ask toolbars for heights?
19446 // this.toolbars[i].onDestroy();
19449 //this.wrap.dom.innerHTML = '';
19450 //this.wrap.remove();
19455 onFirstFocus : function(){
19457 this.assignDocWin();
19460 this.activated = true;
19463 if(Roo.isGecko){ // prevent silly gecko errors
19465 var s = this.win.getSelection();
19466 if(!s.focusNode || s.focusNode.nodeType != 3){
19467 var r = s.getRangeAt(0);
19468 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19473 this.execCmd('useCSS', true);
19474 this.execCmd('styleWithCSS', false);
19477 this.owner.fireEvent('activate', this);
19481 adjustFont: function(btn){
19482 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19483 //if(Roo.isSafari){ // safari
19486 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19487 if(Roo.isSafari){ // safari
19488 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19489 v = (v < 10) ? 10 : v;
19490 v = (v > 48) ? 48 : v;
19491 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19496 v = Math.max(1, v+adjust);
19498 this.execCmd('FontSize', v );
19501 onEditorEvent : function(e)
19503 this.owner.fireEvent('editorevent', this, e);
19504 // this.updateToolbar();
19505 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19508 insertTag : function(tg)
19510 // could be a bit smarter... -> wrap the current selected tRoo..
19511 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19513 range = this.createRange(this.getSelection());
19514 var wrappingNode = this.doc.createElement(tg.toLowerCase());
19515 wrappingNode.appendChild(range.extractContents());
19516 range.insertNode(wrappingNode);
19523 this.execCmd("formatblock", tg);
19527 insertText : function(txt)
19531 var range = this.createRange();
19532 range.deleteContents();
19533 //alert(Sender.getAttribute('label'));
19535 range.insertNode(this.doc.createTextNode(txt));
19541 * Executes a Midas editor command on the editor document and performs necessary focus and
19542 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19543 * @param {String} cmd The Midas command
19544 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19546 relayCmd : function(cmd, value){
19548 this.execCmd(cmd, value);
19549 this.owner.fireEvent('editorevent', this);
19550 //this.updateToolbar();
19551 this.owner.deferFocus();
19555 * Executes a Midas editor command directly on the editor document.
19556 * For visual commands, you should use {@link #relayCmd} instead.
19557 * <b>This should only be called after the editor is initialized.</b>
19558 * @param {String} cmd The Midas command
19559 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19561 execCmd : function(cmd, value){
19562 this.doc.execCommand(cmd, false, value === undefined ? null : value);
19569 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19571 * @param {String} text | dom node..
19573 insertAtCursor : function(text)
19578 if(!this.activated){
19584 var r = this.doc.selection.createRange();
19595 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19599 // from jquery ui (MIT licenced)
19601 var win = this.win;
19603 if (win.getSelection && win.getSelection().getRangeAt) {
19604 range = win.getSelection().getRangeAt(0);
19605 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19606 range.insertNode(node);
19607 } else if (win.document.selection && win.document.selection.createRange) {
19608 // no firefox support
19609 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19610 win.document.selection.createRange().pasteHTML(txt);
19612 // no firefox support
19613 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19614 this.execCmd('InsertHTML', txt);
19623 mozKeyPress : function(e){
19625 var c = e.getCharCode(), cmd;
19628 c = String.fromCharCode(c).toLowerCase();
19642 this.cleanUpPaste.defer(100, this);
19650 e.preventDefault();
19658 fixKeys : function(){ // load time branching for fastest keydown performance
19660 return function(e){
19661 var k = e.getKey(), r;
19664 r = this.doc.selection.createRange();
19667 r.pasteHTML('    ');
19674 r = this.doc.selection.createRange();
19676 var target = r.parentElement();
19677 if(!target || target.tagName.toLowerCase() != 'li'){
19679 r.pasteHTML('<br />');
19685 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19686 this.cleanUpPaste.defer(100, this);
19692 }else if(Roo.isOpera){
19693 return function(e){
19694 var k = e.getKey();
19698 this.execCmd('InsertHTML','    ');
19701 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19702 this.cleanUpPaste.defer(100, this);
19707 }else if(Roo.isSafari){
19708 return function(e){
19709 var k = e.getKey();
19713 this.execCmd('InsertText','\t');
19717 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19718 this.cleanUpPaste.defer(100, this);
19726 getAllAncestors: function()
19728 var p = this.getSelectedNode();
19731 a.push(p); // push blank onto stack..
19732 p = this.getParentElement();
19736 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19740 a.push(this.doc.body);
19744 lastSelNode : false,
19747 getSelection : function()
19749 this.assignDocWin();
19750 return Roo.isIE ? this.doc.selection : this.win.getSelection();
19753 getSelectedNode: function()
19755 // this may only work on Gecko!!!
19757 // should we cache this!!!!
19762 var range = this.createRange(this.getSelection()).cloneRange();
19765 var parent = range.parentElement();
19767 var testRange = range.duplicate();
19768 testRange.moveToElementText(parent);
19769 if (testRange.inRange(range)) {
19772 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19775 parent = parent.parentElement;
19780 // is ancestor a text element.
19781 var ac = range.commonAncestorContainer;
19782 if (ac.nodeType == 3) {
19783 ac = ac.parentNode;
19786 var ar = ac.childNodes;
19789 var other_nodes = [];
19790 var has_other_nodes = false;
19791 for (var i=0;i<ar.length;i++) {
19792 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
19795 // fullly contained node.
19797 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19802 // probably selected..
19803 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19804 other_nodes.push(ar[i]);
19808 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
19813 has_other_nodes = true;
19815 if (!nodes.length && other_nodes.length) {
19816 nodes= other_nodes;
19818 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19824 createRange: function(sel)
19826 // this has strange effects when using with
19827 // top toolbar - not sure if it's a great idea.
19828 //this.editor.contentWindow.focus();
19829 if (typeof sel != "undefined") {
19831 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19833 return this.doc.createRange();
19836 return this.doc.createRange();
19839 getParentElement: function()
19842 this.assignDocWin();
19843 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19845 var range = this.createRange(sel);
19848 var p = range.commonAncestorContainer;
19849 while (p.nodeType == 3) { // text node
19860 * Range intersection.. the hard stuff...
19864 * [ -- selected range --- ]
19868 * if end is before start or hits it. fail.
19869 * if start is after end or hits it fail.
19871 * if either hits (but other is outside. - then it's not
19877 // @see http://www.thismuchiknow.co.uk/?p=64.
19878 rangeIntersectsNode : function(range, node)
19880 var nodeRange = node.ownerDocument.createRange();
19882 nodeRange.selectNode(node);
19884 nodeRange.selectNodeContents(node);
19887 var rangeStartRange = range.cloneRange();
19888 rangeStartRange.collapse(true);
19890 var rangeEndRange = range.cloneRange();
19891 rangeEndRange.collapse(false);
19893 var nodeStartRange = nodeRange.cloneRange();
19894 nodeStartRange.collapse(true);
19896 var nodeEndRange = nodeRange.cloneRange();
19897 nodeEndRange.collapse(false);
19899 return rangeStartRange.compareBoundaryPoints(
19900 Range.START_TO_START, nodeEndRange) == -1 &&
19901 rangeEndRange.compareBoundaryPoints(
19902 Range.START_TO_START, nodeStartRange) == 1;
19906 rangeCompareNode : function(range, node)
19908 var nodeRange = node.ownerDocument.createRange();
19910 nodeRange.selectNode(node);
19912 nodeRange.selectNodeContents(node);
19916 range.collapse(true);
19918 nodeRange.collapse(true);
19920 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19921 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
19923 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19925 var nodeIsBefore = ss == 1;
19926 var nodeIsAfter = ee == -1;
19928 if (nodeIsBefore && nodeIsAfter)
19930 if (!nodeIsBefore && nodeIsAfter)
19931 return 1; //right trailed.
19933 if (nodeIsBefore && !nodeIsAfter)
19934 return 2; // left trailed.
19939 // private? - in a new class?
19940 cleanUpPaste : function()
19942 // cleans up the whole document..
19943 Roo.log('cleanuppaste');
19945 this.cleanUpChildren(this.doc.body);
19946 var clean = this.cleanWordChars(this.doc.body.innerHTML);
19947 if (clean != this.doc.body.innerHTML) {
19948 this.doc.body.innerHTML = clean;
19953 cleanWordChars : function(input) {// change the chars to hex code
19954 var he = Roo.HtmlEditorCore;
19956 var output = input;
19957 Roo.each(he.swapCodes, function(sw) {
19958 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19960 output = output.replace(swapper, sw[1]);
19967 cleanUpChildren : function (n)
19969 if (!n.childNodes.length) {
19972 for (var i = n.childNodes.length-1; i > -1 ; i--) {
19973 this.cleanUpChild(n.childNodes[i]);
19980 cleanUpChild : function (node)
19983 //console.log(node);
19984 if (node.nodeName == "#text") {
19985 // clean up silly Windows -- stuff?
19988 if (node.nodeName == "#comment") {
19989 node.parentNode.removeChild(node);
19990 // clean up silly Windows -- stuff?
19993 var lcname = node.tagName.toLowerCase();
19994 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19995 // whitelist of tags..
19997 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19999 node.parentNode.removeChild(node);
20004 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20006 // remove <a name=....> as rendering on yahoo mailer is borked with this.
20007 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20009 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20010 // remove_keep_children = true;
20013 if (remove_keep_children) {
20014 this.cleanUpChildren(node);
20015 // inserts everything just before this node...
20016 while (node.childNodes.length) {
20017 var cn = node.childNodes[0];
20018 node.removeChild(cn);
20019 node.parentNode.insertBefore(cn, node);
20021 node.parentNode.removeChild(node);
20025 if (!node.attributes || !node.attributes.length) {
20026 this.cleanUpChildren(node);
20030 function cleanAttr(n,v)
20033 if (v.match(/^\./) || v.match(/^\//)) {
20036 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20039 if (v.match(/^#/)) {
20042 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20043 node.removeAttribute(n);
20047 var cwhite = this.cwhite;
20048 var cblack = this.cblack;
20050 function cleanStyle(n,v)
20052 if (v.match(/expression/)) { //XSS?? should we even bother..
20053 node.removeAttribute(n);
20057 var parts = v.split(/;/);
20060 Roo.each(parts, function(p) {
20061 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20065 var l = p.split(':').shift().replace(/\s+/g,'');
20066 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20068 if ( cwhite.length && cblack.indexOf(l) > -1) {
20069 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20070 //node.removeAttribute(n);
20074 // only allow 'c whitelisted system attributes'
20075 if ( cwhite.length && cwhite.indexOf(l) < 0) {
20076 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20077 //node.removeAttribute(n);
20087 if (clean.length) {
20088 node.setAttribute(n, clean.join(';'));
20090 node.removeAttribute(n);
20096 for (var i = node.attributes.length-1; i > -1 ; i--) {
20097 var a = node.attributes[i];
20100 if (a.name.toLowerCase().substr(0,2)=='on') {
20101 node.removeAttribute(a.name);
20104 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20105 node.removeAttribute(a.name);
20108 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20109 cleanAttr(a.name,a.value); // fixme..
20112 if (a.name == 'style') {
20113 cleanStyle(a.name,a.value);
20116 /// clean up MS crap..
20117 // tecnically this should be a list of valid class'es..
20120 if (a.name == 'class') {
20121 if (a.value.match(/^Mso/)) {
20122 node.className = '';
20125 if (a.value.match(/body/)) {
20126 node.className = '';
20137 this.cleanUpChildren(node);
20143 * Clean up MS wordisms...
20145 cleanWord : function(node)
20150 this.cleanWord(this.doc.body);
20153 if (node.nodeName == "#text") {
20154 // clean up silly Windows -- stuff?
20157 if (node.nodeName == "#comment") {
20158 node.parentNode.removeChild(node);
20159 // clean up silly Windows -- stuff?
20163 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20164 node.parentNode.removeChild(node);
20168 // remove - but keep children..
20169 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20170 while (node.childNodes.length) {
20171 var cn = node.childNodes[0];
20172 node.removeChild(cn);
20173 node.parentNode.insertBefore(cn, node);
20175 node.parentNode.removeChild(node);
20176 this.iterateChildren(node, this.cleanWord);
20180 if (node.className.length) {
20182 var cn = node.className.split(/\W+/);
20184 Roo.each(cn, function(cls) {
20185 if (cls.match(/Mso[a-zA-Z]+/)) {
20190 node.className = cna.length ? cna.join(' ') : '';
20192 node.removeAttribute("class");
20196 if (node.hasAttribute("lang")) {
20197 node.removeAttribute("lang");
20200 if (node.hasAttribute("style")) {
20202 var styles = node.getAttribute("style").split(";");
20204 Roo.each(styles, function(s) {
20205 if (!s.match(/:/)) {
20208 var kv = s.split(":");
20209 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20212 // what ever is left... we allow.
20215 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20216 if (!nstyle.length) {
20217 node.removeAttribute('style');
20220 this.iterateChildren(node, this.cleanWord);
20226 * iterateChildren of a Node, calling fn each time, using this as the scole..
20227 * @param {DomNode} node node to iterate children of.
20228 * @param {Function} fn method of this class to call on each item.
20230 iterateChildren : function(node, fn)
20232 if (!node.childNodes.length) {
20235 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20236 fn.call(this, node.childNodes[i])
20242 * cleanTableWidths.
20244 * Quite often pasting from word etc.. results in tables with column and widths.
20245 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20248 cleanTableWidths : function(node)
20253 this.cleanTableWidths(this.doc.body);
20258 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20261 Roo.log(node.tagName);
20262 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20263 this.iterateChildren(node, this.cleanTableWidths);
20266 if (node.hasAttribute('width')) {
20267 node.removeAttribute('width');
20271 if (node.hasAttribute("style")) {
20274 var styles = node.getAttribute("style").split(";");
20276 Roo.each(styles, function(s) {
20277 if (!s.match(/:/)) {
20280 var kv = s.split(":");
20281 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20284 // what ever is left... we allow.
20287 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20288 if (!nstyle.length) {
20289 node.removeAttribute('style');
20293 this.iterateChildren(node, this.cleanTableWidths);
20301 domToHTML : function(currentElement, depth, nopadtext) {
20303 depth = depth || 0;
20304 nopadtext = nopadtext || false;
20306 if (!currentElement) {
20307 return this.domToHTML(this.doc.body);
20310 //Roo.log(currentElement);
20312 var allText = false;
20313 var nodeName = currentElement.nodeName;
20314 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20316 if (nodeName == '#text') {
20318 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20323 if (nodeName != 'BODY') {
20326 // Prints the node tagName, such as <A>, <IMG>, etc
20329 for(i = 0; i < currentElement.attributes.length;i++) {
20331 var aname = currentElement.attributes.item(i).name;
20332 if (!currentElement.attributes.item(i).value.length) {
20335 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20338 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20347 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20350 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20355 // Traverse the tree
20357 var currentElementChild = currentElement.childNodes.item(i);
20358 var allText = true;
20359 var innerHTML = '';
20361 while (currentElementChild) {
20362 // Formatting code (indent the tree so it looks nice on the screen)
20363 var nopad = nopadtext;
20364 if (lastnode == 'SPAN') {
20368 if (currentElementChild.nodeName == '#text') {
20369 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20370 toadd = nopadtext ? toadd : toadd.trim();
20371 if (!nopad && toadd.length > 80) {
20372 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20374 innerHTML += toadd;
20377 currentElementChild = currentElement.childNodes.item(i);
20383 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20385 // Recursively traverse the tree structure of the child node
20386 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20387 lastnode = currentElementChild.nodeName;
20389 currentElementChild=currentElement.childNodes.item(i);
20395 // The remaining code is mostly for formatting the tree
20396 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20401 ret+= "</"+tagName+">";
20407 applyBlacklists : function()
20409 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20410 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20414 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20415 if (b.indexOf(tag) > -1) {
20418 this.white.push(tag);
20422 Roo.each(w, function(tag) {
20423 if (b.indexOf(tag) > -1) {
20426 if (this.white.indexOf(tag) > -1) {
20429 this.white.push(tag);
20434 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20435 if (w.indexOf(tag) > -1) {
20438 this.black.push(tag);
20442 Roo.each(b, function(tag) {
20443 if (w.indexOf(tag) > -1) {
20446 if (this.black.indexOf(tag) > -1) {
20449 this.black.push(tag);
20454 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20455 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20459 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20460 if (b.indexOf(tag) > -1) {
20463 this.cwhite.push(tag);
20467 Roo.each(w, function(tag) {
20468 if (b.indexOf(tag) > -1) {
20471 if (this.cwhite.indexOf(tag) > -1) {
20474 this.cwhite.push(tag);
20479 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20480 if (w.indexOf(tag) > -1) {
20483 this.cblack.push(tag);
20487 Roo.each(b, function(tag) {
20488 if (w.indexOf(tag) > -1) {
20491 if (this.cblack.indexOf(tag) > -1) {
20494 this.cblack.push(tag);
20499 setStylesheets : function(stylesheets)
20501 if(typeof(stylesheets) == 'string'){
20502 Roo.get(this.iframe.contentDocument.head).createChild({
20504 rel : 'stylesheet',
20513 Roo.each(stylesheets, function(s) {
20518 Roo.get(_this.iframe.contentDocument.head).createChild({
20520 rel : 'stylesheet',
20529 removeStylesheets : function()
20533 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20538 // hide stuff that is not compatible
20552 * @event specialkey
20556 * @cfg {String} fieldClass @hide
20559 * @cfg {String} focusClass @hide
20562 * @cfg {String} autoCreate @hide
20565 * @cfg {String} inputType @hide
20568 * @cfg {String} invalidClass @hide
20571 * @cfg {String} invalidText @hide
20574 * @cfg {String} msgFx @hide
20577 * @cfg {String} validateOnBlur @hide
20581 Roo.HtmlEditorCore.white = [
20582 'area', 'br', 'img', 'input', 'hr', 'wbr',
20584 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
20585 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
20586 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
20587 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
20588 'table', 'ul', 'xmp',
20590 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
20593 'dir', 'menu', 'ol', 'ul', 'dl',
20599 Roo.HtmlEditorCore.black = [
20600 // 'embed', 'object', // enable - backend responsiblity to clean thiese
20602 'base', 'basefont', 'bgsound', 'blink', 'body',
20603 'frame', 'frameset', 'head', 'html', 'ilayer',
20604 'iframe', 'layer', 'link', 'meta', 'object',
20605 'script', 'style' ,'title', 'xml' // clean later..
20607 Roo.HtmlEditorCore.clean = [
20608 'script', 'style', 'title', 'xml'
20610 Roo.HtmlEditorCore.remove = [
20615 Roo.HtmlEditorCore.ablack = [
20619 Roo.HtmlEditorCore.aclean = [
20620 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
20624 Roo.HtmlEditorCore.pwhite= [
20625 'http', 'https', 'mailto'
20628 // white listed style attributes.
20629 Roo.HtmlEditorCore.cwhite= [
20630 // 'text-align', /// default is to allow most things..
20636 // black listed style attributes.
20637 Roo.HtmlEditorCore.cblack= [
20638 // 'font-size' -- this can be set by the project
20642 Roo.HtmlEditorCore.swapCodes =[
20661 * @class Roo.bootstrap.HtmlEditor
20662 * @extends Roo.bootstrap.TextArea
20663 * Bootstrap HtmlEditor class
20666 * Create a new HtmlEditor
20667 * @param {Object} config The config object
20670 Roo.bootstrap.HtmlEditor = function(config){
20671 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20672 if (!this.toolbars) {
20673 this.toolbars = [];
20675 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20678 * @event initialize
20679 * Fires when the editor is fully initialized (including the iframe)
20680 * @param {HtmlEditor} this
20685 * Fires when the editor is first receives the focus. Any insertion must wait
20686 * until after this event.
20687 * @param {HtmlEditor} this
20691 * @event beforesync
20692 * Fires before the textarea is updated with content from the editor iframe. Return false
20693 * to cancel the sync.
20694 * @param {HtmlEditor} this
20695 * @param {String} html
20699 * @event beforepush
20700 * Fires before the iframe editor is updated with content from the textarea. Return false
20701 * to cancel the push.
20702 * @param {HtmlEditor} this
20703 * @param {String} html
20708 * Fires when the textarea is updated with content from the editor iframe.
20709 * @param {HtmlEditor} this
20710 * @param {String} html
20715 * Fires when the iframe editor is updated with content from the textarea.
20716 * @param {HtmlEditor} this
20717 * @param {String} html
20721 * @event editmodechange
20722 * Fires when the editor switches edit modes
20723 * @param {HtmlEditor} this
20724 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20726 editmodechange: true,
20728 * @event editorevent
20729 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20730 * @param {HtmlEditor} this
20734 * @event firstfocus
20735 * Fires when on first focus - needed by toolbars..
20736 * @param {HtmlEditor} this
20741 * Auto save the htmlEditor value as a file into Events
20742 * @param {HtmlEditor} this
20746 * @event savedpreview
20747 * preview the saved version of htmlEditor
20748 * @param {HtmlEditor} this
20755 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
20759 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20764 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20769 * @cfg {Number} height (in pixels)
20773 * @cfg {Number} width (in pixels)
20778 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20781 stylesheets: false,
20786 // private properties
20787 validationEvent : false,
20789 initialized : false,
20792 onFocus : Roo.emptyFn,
20794 hideMode:'offsets',
20797 tbContainer : false,
20799 toolbarContainer :function() {
20800 return this.wrap.select('.x-html-editor-tb',true).first();
20804 * Protected method that will not generally be called directly. It
20805 * is called when the editor creates its toolbar. Override this method if you need to
20806 * add custom toolbar buttons.
20807 * @param {HtmlEditor} editor
20809 createToolbar : function(){
20811 Roo.log("create toolbars");
20813 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20814 this.toolbars[0].render(this.toolbarContainer());
20818 // if (!editor.toolbars || !editor.toolbars.length) {
20819 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20822 // for (var i =0 ; i < editor.toolbars.length;i++) {
20823 // editor.toolbars[i] = Roo.factory(
20824 // typeof(editor.toolbars[i]) == 'string' ?
20825 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
20826 // Roo.bootstrap.HtmlEditor);
20827 // editor.toolbars[i].init(editor);
20833 onRender : function(ct, position)
20835 // Roo.log("Call onRender: " + this.xtype);
20837 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20839 this.wrap = this.inputEl().wrap({
20840 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20843 this.editorcore.onRender(ct, position);
20845 if (this.resizable) {
20846 this.resizeEl = new Roo.Resizable(this.wrap, {
20850 minHeight : this.height,
20851 height: this.height,
20852 handles : this.resizable,
20855 resize : function(r, w, h) {
20856 _t.onResize(w,h); // -something
20862 this.createToolbar(this);
20865 if(!this.width && this.resizable){
20866 this.setSize(this.wrap.getSize());
20868 if (this.resizeEl) {
20869 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20870 // should trigger onReize..
20876 onResize : function(w, h)
20878 Roo.log('resize: ' +w + ',' + h );
20879 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20883 if(this.inputEl() ){
20884 if(typeof w == 'number'){
20885 var aw = w - this.wrap.getFrameWidth('lr');
20886 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20889 if(typeof h == 'number'){
20890 var tbh = -11; // fixme it needs to tool bar size!
20891 for (var i =0; i < this.toolbars.length;i++) {
20892 // fixme - ask toolbars for heights?
20893 tbh += this.toolbars[i].el.getHeight();
20894 //if (this.toolbars[i].footer) {
20895 // tbh += this.toolbars[i].footer.el.getHeight();
20903 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20904 ah -= 5; // knock a few pixes off for look..
20905 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20909 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20910 this.editorcore.onResize(ew,eh);
20915 * Toggles the editor between standard and source edit mode.
20916 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20918 toggleSourceEdit : function(sourceEditMode)
20920 this.editorcore.toggleSourceEdit(sourceEditMode);
20922 if(this.editorcore.sourceEditMode){
20923 Roo.log('editor - showing textarea');
20926 // Roo.log(this.syncValue());
20928 this.inputEl().removeClass(['hide', 'x-hidden']);
20929 this.inputEl().dom.removeAttribute('tabIndex');
20930 this.inputEl().focus();
20932 Roo.log('editor - hiding textarea');
20934 // Roo.log(this.pushValue());
20937 this.inputEl().addClass(['hide', 'x-hidden']);
20938 this.inputEl().dom.setAttribute('tabIndex', -1);
20939 //this.deferFocus();
20942 if(this.resizable){
20943 this.setSize(this.wrap.getSize());
20946 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20949 // private (for BoxComponent)
20950 adjustSize : Roo.BoxComponent.prototype.adjustSize,
20952 // private (for BoxComponent)
20953 getResizeEl : function(){
20957 // private (for BoxComponent)
20958 getPositionEl : function(){
20963 initEvents : function(){
20964 this.originalValue = this.getValue();
20968 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20971 // markInvalid : Roo.emptyFn,
20973 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20976 // clearInvalid : Roo.emptyFn,
20978 setValue : function(v){
20979 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20980 this.editorcore.pushValue();
20985 deferFocus : function(){
20986 this.focus.defer(10, this);
20990 focus : function(){
20991 this.editorcore.focus();
20997 onDestroy : function(){
21003 for (var i =0; i < this.toolbars.length;i++) {
21004 // fixme - ask toolbars for heights?
21005 this.toolbars[i].onDestroy();
21008 this.wrap.dom.innerHTML = '';
21009 this.wrap.remove();
21014 onFirstFocus : function(){
21015 //Roo.log("onFirstFocus");
21016 this.editorcore.onFirstFocus();
21017 for (var i =0; i < this.toolbars.length;i++) {
21018 this.toolbars[i].onFirstFocus();
21024 syncValue : function()
21026 this.editorcore.syncValue();
21029 pushValue : function()
21031 this.editorcore.pushValue();
21035 // hide stuff that is not compatible
21049 * @event specialkey
21053 * @cfg {String} fieldClass @hide
21056 * @cfg {String} focusClass @hide
21059 * @cfg {String} autoCreate @hide
21062 * @cfg {String} inputType @hide
21065 * @cfg {String} invalidClass @hide
21068 * @cfg {String} invalidText @hide
21071 * @cfg {String} msgFx @hide
21074 * @cfg {String} validateOnBlur @hide
21083 Roo.namespace('Roo.bootstrap.htmleditor');
21085 * @class Roo.bootstrap.HtmlEditorToolbar1
21090 new Roo.bootstrap.HtmlEditor({
21093 new Roo.bootstrap.HtmlEditorToolbar1({
21094 disable : { fonts: 1 , format: 1, ..., ... , ...],
21100 * @cfg {Object} disable List of elements to disable..
21101 * @cfg {Array} btns List of additional buttons.
21105 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21108 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21111 Roo.apply(this, config);
21113 // default disabled, based on 'good practice'..
21114 this.disable = this.disable || {};
21115 Roo.applyIf(this.disable, {
21118 specialElements : true
21120 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21122 this.editor = config.editor;
21123 this.editorcore = config.editor.editorcore;
21125 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21127 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21128 // dont call parent... till later.
21130 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
21135 editorcore : false,
21140 "h1","h2","h3","h4","h5","h6",
21142 "abbr", "acronym", "address", "cite", "samp", "var",
21146 onRender : function(ct, position)
21148 // Roo.log("Call onRender: " + this.xtype);
21150 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21152 this.el.dom.style.marginBottom = '0';
21154 var editorcore = this.editorcore;
21155 var editor= this.editor;
21158 var btn = function(id,cmd , toggle, handler){
21160 var event = toggle ? 'toggle' : 'click';
21165 xns: Roo.bootstrap,
21168 enableToggle:toggle !== false,
21170 pressed : toggle ? false : null,
21173 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21174 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21183 xns: Roo.bootstrap,
21184 glyphicon : 'font',
21188 xns: Roo.bootstrap,
21192 Roo.each(this.formats, function(f) {
21193 style.menu.items.push({
21195 xns: Roo.bootstrap,
21196 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21201 editorcore.insertTag(this.tagname);
21208 children.push(style);
21211 btn('bold',false,true);
21212 btn('italic',false,true);
21213 btn('align-left', 'justifyleft',true);
21214 btn('align-center', 'justifycenter',true);
21215 btn('align-right' , 'justifyright',true);
21216 btn('link', false, false, function(btn) {
21217 //Roo.log("create link?");
21218 var url = prompt(this.createLinkText, this.defaultLinkValue);
21219 if(url && url != 'http:/'+'/'){
21220 this.editorcore.relayCmd('createlink', url);
21223 btn('list','insertunorderedlist',true);
21224 btn('pencil', false,true, function(btn){
21227 this.toggleSourceEdit(btn.pressed);
21233 xns: Roo.bootstrap,
21238 xns: Roo.bootstrap,
21243 cog.menu.items.push({
21245 xns: Roo.bootstrap,
21246 html : Clean styles,
21251 editorcore.insertTag(this.tagname);
21260 this.xtype = 'NavSimplebar';
21262 for(var i=0;i< children.length;i++) {
21264 this.buttons.add(this.addxtypeChild(children[i]));
21268 editor.on('editorevent', this.updateToolbar, this);
21270 onBtnClick : function(id)
21272 this.editorcore.relayCmd(id);
21273 this.editorcore.focus();
21277 * Protected method that will not generally be called directly. It triggers
21278 * a toolbar update by reading the markup state of the current selection in the editor.
21280 updateToolbar: function(){
21282 if(!this.editorcore.activated){
21283 this.editor.onFirstFocus(); // is this neeed?
21287 var btns = this.buttons;
21288 var doc = this.editorcore.doc;
21289 btns.get('bold').setActive(doc.queryCommandState('bold'));
21290 btns.get('italic').setActive(doc.queryCommandState('italic'));
21291 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21293 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21294 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21295 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21297 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21298 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21301 var ans = this.editorcore.getAllAncestors();
21302 if (this.formatCombo) {
21305 var store = this.formatCombo.store;
21306 this.formatCombo.setValue("");
21307 for (var i =0; i < ans.length;i++) {
21308 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21310 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21318 // hides menus... - so this cant be on a menu...
21319 Roo.bootstrap.MenuMgr.hideAll();
21321 Roo.bootstrap.MenuMgr.hideAll();
21322 //this.editorsyncValue();
21324 onFirstFocus: function() {
21325 this.buttons.each(function(item){
21329 toggleSourceEdit : function(sourceEditMode){
21332 if(sourceEditMode){
21333 Roo.log("disabling buttons");
21334 this.buttons.each( function(item){
21335 if(item.cmd != 'pencil'){
21341 Roo.log("enabling buttons");
21342 if(this.editorcore.initialized){
21343 this.buttons.each( function(item){
21349 Roo.log("calling toggole on editor");
21350 // tell the editor that it's been pressed..
21351 this.editor.toggleSourceEdit(sourceEditMode);
21361 * @class Roo.bootstrap.Table.AbstractSelectionModel
21362 * @extends Roo.util.Observable
21363 * Abstract base class for grid SelectionModels. It provides the interface that should be
21364 * implemented by descendant classes. This class should not be directly instantiated.
21367 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21368 this.locked = false;
21369 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21373 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21374 /** @ignore Called by the grid automatically. Do not call directly. */
21375 init : function(grid){
21381 * Locks the selections.
21384 this.locked = true;
21388 * Unlocks the selections.
21390 unlock : function(){
21391 this.locked = false;
21395 * Returns true if the selections are locked.
21396 * @return {Boolean}
21398 isLocked : function(){
21399 return this.locked;
21403 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21404 * @class Roo.bootstrap.Table.RowSelectionModel
21405 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21406 * It supports multiple selections and keyboard selection/navigation.
21408 * @param {Object} config
21411 Roo.bootstrap.Table.RowSelectionModel = function(config){
21412 Roo.apply(this, config);
21413 this.selections = new Roo.util.MixedCollection(false, function(o){
21418 this.lastActive = false;
21422 * @event selectionchange
21423 * Fires when the selection changes
21424 * @param {SelectionModel} this
21426 "selectionchange" : true,
21428 * @event afterselectionchange
21429 * Fires after the selection changes (eg. by key press or clicking)
21430 * @param {SelectionModel} this
21432 "afterselectionchange" : true,
21434 * @event beforerowselect
21435 * Fires when a row is selected being selected, return false to cancel.
21436 * @param {SelectionModel} this
21437 * @param {Number} rowIndex The selected index
21438 * @param {Boolean} keepExisting False if other selections will be cleared
21440 "beforerowselect" : true,
21443 * Fires when a row is selected.
21444 * @param {SelectionModel} this
21445 * @param {Number} rowIndex The selected index
21446 * @param {Roo.data.Record} r The record
21448 "rowselect" : true,
21450 * @event rowdeselect
21451 * Fires when a row is deselected.
21452 * @param {SelectionModel} this
21453 * @param {Number} rowIndex The selected index
21455 "rowdeselect" : true
21457 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21458 this.locked = false;
21461 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21463 * @cfg {Boolean} singleSelect
21464 * True to allow selection of only one row at a time (defaults to false)
21466 singleSelect : false,
21469 initEvents : function(){
21471 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21472 this.grid.on("mousedown", this.handleMouseDown, this);
21473 }else{ // allow click to work like normal
21474 this.grid.on("rowclick", this.handleDragableRowClick, this);
21477 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21478 "up" : function(e){
21480 this.selectPrevious(e.shiftKey);
21481 }else if(this.last !== false && this.lastActive !== false){
21482 var last = this.last;
21483 this.selectRange(this.last, this.lastActive-1);
21484 this.grid.getView().focusRow(this.lastActive);
21485 if(last !== false){
21489 this.selectFirstRow();
21491 this.fireEvent("afterselectionchange", this);
21493 "down" : function(e){
21495 this.selectNext(e.shiftKey);
21496 }else if(this.last !== false && this.lastActive !== false){
21497 var last = this.last;
21498 this.selectRange(this.last, this.lastActive+1);
21499 this.grid.getView().focusRow(this.lastActive);
21500 if(last !== false){
21504 this.selectFirstRow();
21506 this.fireEvent("afterselectionchange", this);
21511 var view = this.grid.view;
21512 view.on("refresh", this.onRefresh, this);
21513 view.on("rowupdated", this.onRowUpdated, this);
21514 view.on("rowremoved", this.onRemove, this);
21518 onRefresh : function(){
21519 var ds = this.grid.dataSource, i, v = this.grid.view;
21520 var s = this.selections;
21521 s.each(function(r){
21522 if((i = ds.indexOfId(r.id)) != -1){
21531 onRemove : function(v, index, r){
21532 this.selections.remove(r);
21536 onRowUpdated : function(v, index, r){
21537 if(this.isSelected(r)){
21538 v.onRowSelect(index);
21544 * @param {Array} records The records to select
21545 * @param {Boolean} keepExisting (optional) True to keep existing selections
21547 selectRecords : function(records, keepExisting){
21549 this.clearSelections();
21551 var ds = this.grid.dataSource;
21552 for(var i = 0, len = records.length; i < len; i++){
21553 this.selectRow(ds.indexOf(records[i]), true);
21558 * Gets the number of selected rows.
21561 getCount : function(){
21562 return this.selections.length;
21566 * Selects the first row in the grid.
21568 selectFirstRow : function(){
21573 * Select the last row.
21574 * @param {Boolean} keepExisting (optional) True to keep existing selections
21576 selectLastRow : function(keepExisting){
21577 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21581 * Selects the row immediately following the last selected row.
21582 * @param {Boolean} keepExisting (optional) True to keep existing selections
21584 selectNext : function(keepExisting){
21585 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21586 this.selectRow(this.last+1, keepExisting);
21587 this.grid.getView().focusRow(this.last);
21592 * Selects the row that precedes the last selected row.
21593 * @param {Boolean} keepExisting (optional) True to keep existing selections
21595 selectPrevious : function(keepExisting){
21597 this.selectRow(this.last-1, keepExisting);
21598 this.grid.getView().focusRow(this.last);
21603 * Returns the selected records
21604 * @return {Array} Array of selected records
21606 getSelections : function(){
21607 return [].concat(this.selections.items);
21611 * Returns the first selected record.
21614 getSelected : function(){
21615 return this.selections.itemAt(0);
21620 * Clears all selections.
21622 clearSelections : function(fast){
21623 if(this.locked) return;
21625 var ds = this.grid.dataSource;
21626 var s = this.selections;
21627 s.each(function(r){
21628 this.deselectRow(ds.indexOfId(r.id));
21632 this.selections.clear();
21639 * Selects all rows.
21641 selectAll : function(){
21642 if(this.locked) return;
21643 this.selections.clear();
21644 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21645 this.selectRow(i, true);
21650 * Returns True if there is a selection.
21651 * @return {Boolean}
21653 hasSelection : function(){
21654 return this.selections.length > 0;
21658 * Returns True if the specified row is selected.
21659 * @param {Number/Record} record The record or index of the record to check
21660 * @return {Boolean}
21662 isSelected : function(index){
21663 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21664 return (r && this.selections.key(r.id) ? true : false);
21668 * Returns True if the specified record id is selected.
21669 * @param {String} id The id of record to check
21670 * @return {Boolean}
21672 isIdSelected : function(id){
21673 return (this.selections.key(id) ? true : false);
21677 handleMouseDown : function(e, t){
21678 var view = this.grid.getView(), rowIndex;
21679 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21682 if(e.shiftKey && this.last !== false){
21683 var last = this.last;
21684 this.selectRange(last, rowIndex, e.ctrlKey);
21685 this.last = last; // reset the last
21686 view.focusRow(rowIndex);
21688 var isSelected = this.isSelected(rowIndex);
21689 if(e.button !== 0 && isSelected){
21690 view.focusRow(rowIndex);
21691 }else if(e.ctrlKey && isSelected){
21692 this.deselectRow(rowIndex);
21693 }else if(!isSelected){
21694 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21695 view.focusRow(rowIndex);
21698 this.fireEvent("afterselectionchange", this);
21701 handleDragableRowClick : function(grid, rowIndex, e)
21703 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21704 this.selectRow(rowIndex, false);
21705 grid.view.focusRow(rowIndex);
21706 this.fireEvent("afterselectionchange", this);
21711 * Selects multiple rows.
21712 * @param {Array} rows Array of the indexes of the row to select
21713 * @param {Boolean} keepExisting (optional) True to keep existing selections
21715 selectRows : function(rows, keepExisting){
21717 this.clearSelections();
21719 for(var i = 0, len = rows.length; i < len; i++){
21720 this.selectRow(rows[i], true);
21725 * Selects a range of rows. All rows in between startRow and endRow are also selected.
21726 * @param {Number} startRow The index of the first row in the range
21727 * @param {Number} endRow The index of the last row in the range
21728 * @param {Boolean} keepExisting (optional) True to retain existing selections
21730 selectRange : function(startRow, endRow, keepExisting){
21731 if(this.locked) return;
21733 this.clearSelections();
21735 if(startRow <= endRow){
21736 for(var i = startRow; i <= endRow; i++){
21737 this.selectRow(i, true);
21740 for(var i = startRow; i >= endRow; i--){
21741 this.selectRow(i, true);
21747 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21748 * @param {Number} startRow The index of the first row in the range
21749 * @param {Number} endRow The index of the last row in the range
21751 deselectRange : function(startRow, endRow, preventViewNotify){
21752 if(this.locked) return;
21753 for(var i = startRow; i <= endRow; i++){
21754 this.deselectRow(i, preventViewNotify);
21760 * @param {Number} row The index of the row to select
21761 * @param {Boolean} keepExisting (optional) True to keep existing selections
21763 selectRow : function(index, keepExisting, preventViewNotify){
21764 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21765 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21766 if(!keepExisting || this.singleSelect){
21767 this.clearSelections();
21769 var r = this.grid.dataSource.getAt(index);
21770 this.selections.add(r);
21771 this.last = this.lastActive = index;
21772 if(!preventViewNotify){
21773 this.grid.getView().onRowSelect(index);
21775 this.fireEvent("rowselect", this, index, r);
21776 this.fireEvent("selectionchange", this);
21782 * @param {Number} row The index of the row to deselect
21784 deselectRow : function(index, preventViewNotify){
21785 if(this.locked) return;
21786 if(this.last == index){
21789 if(this.lastActive == index){
21790 this.lastActive = false;
21792 var r = this.grid.dataSource.getAt(index);
21793 this.selections.remove(r);
21794 if(!preventViewNotify){
21795 this.grid.getView().onRowDeselect(index);
21797 this.fireEvent("rowdeselect", this, index);
21798 this.fireEvent("selectionchange", this);
21802 restoreLast : function(){
21804 this.last = this._last;
21809 acceptsNav : function(row, col, cm){
21810 return !cm.isHidden(col) && cm.isCellEditable(col, row);
21814 onEditorKey : function(field, e){
21815 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21820 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21822 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21824 }else if(k == e.ENTER && !e.ctrlKey){
21828 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21830 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21832 }else if(k == e.ESC){
21836 g.startEditing(newCell[0], newCell[1]);
21841 * Ext JS Library 1.1.1
21842 * Copyright(c) 2006-2007, Ext JS, LLC.
21844 * Originally Released Under LGPL - original licence link has changed is not relivant.
21847 * <script type="text/javascript">
21851 * @class Roo.bootstrap.PagingToolbar
21852 * @extends Roo.bootstrap.NavSimplebar
21853 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21855 * Create a new PagingToolbar
21856 * @param {Object} config The config object
21857 * @param {Roo.data.Store} store
21859 Roo.bootstrap.PagingToolbar = function(config)
21861 // old args format still supported... - xtype is prefered..
21862 // created from xtype...
21864 this.ds = config.dataSource;
21866 if (config.store && !this.ds) {
21867 this.store= Roo.factory(config.store, Roo.data);
21868 this.ds = this.store;
21869 this.ds.xmodule = this.xmodule || false;
21872 this.toolbarItems = [];
21873 if (config.items) {
21874 this.toolbarItems = config.items;
21877 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21882 this.bind(this.ds);
21885 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21889 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21891 * @cfg {Roo.data.Store} dataSource
21892 * The underlying data store providing the paged data
21895 * @cfg {String/HTMLElement/Element} container
21896 * container The id or element that will contain the toolbar
21899 * @cfg {Boolean} displayInfo
21900 * True to display the displayMsg (defaults to false)
21903 * @cfg {Number} pageSize
21904 * The number of records to display per page (defaults to 20)
21908 * @cfg {String} displayMsg
21909 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21911 displayMsg : 'Displaying {0} - {1} of {2}',
21913 * @cfg {String} emptyMsg
21914 * The message to display when no records are found (defaults to "No data to display")
21916 emptyMsg : 'No data to display',
21918 * Customizable piece of the default paging text (defaults to "Page")
21921 beforePageText : "Page",
21923 * Customizable piece of the default paging text (defaults to "of %0")
21926 afterPageText : "of {0}",
21928 * Customizable piece of the default paging text (defaults to "First Page")
21931 firstText : "First Page",
21933 * Customizable piece of the default paging text (defaults to "Previous Page")
21936 prevText : "Previous Page",
21938 * Customizable piece of the default paging text (defaults to "Next Page")
21941 nextText : "Next Page",
21943 * Customizable piece of the default paging text (defaults to "Last Page")
21946 lastText : "Last Page",
21948 * Customizable piece of the default paging text (defaults to "Refresh")
21951 refreshText : "Refresh",
21955 onRender : function(ct, position)
21957 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21958 this.navgroup.parentId = this.id;
21959 this.navgroup.onRender(this.el, null);
21960 // add the buttons to the navgroup
21962 if(this.displayInfo){
21963 Roo.log(this.el.select('ul.navbar-nav',true).first());
21964 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21965 this.displayEl = this.el.select('.x-paging-info', true).first();
21966 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21967 // this.displayEl = navel.el.select('span',true).first();
21973 Roo.each(_this.buttons, function(e){ // this might need to use render????
21974 Roo.factory(e).onRender(_this.el, null);
21978 Roo.each(_this.toolbarItems, function(e) {
21979 _this.navgroup.addItem(e);
21983 this.first = this.navgroup.addItem({
21984 tooltip: this.firstText,
21986 icon : 'fa fa-backward',
21988 preventDefault: true,
21989 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21992 this.prev = this.navgroup.addItem({
21993 tooltip: this.prevText,
21995 icon : 'fa fa-step-backward',
21997 preventDefault: true,
21998 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
22000 //this.addSeparator();
22003 var field = this.navgroup.addItem( {
22005 cls : 'x-paging-position',
22007 html : this.beforePageText +
22008 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22009 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
22012 this.field = field.el.select('input', true).first();
22013 this.field.on("keydown", this.onPagingKeydown, this);
22014 this.field.on("focus", function(){this.dom.select();});
22017 this.afterTextEl = field.el.select('.x-paging-after',true).first();
22018 //this.field.setHeight(18);
22019 //this.addSeparator();
22020 this.next = this.navgroup.addItem({
22021 tooltip: this.nextText,
22023 html : ' <i class="fa fa-step-forward">',
22025 preventDefault: true,
22026 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
22028 this.last = this.navgroup.addItem({
22029 tooltip: this.lastText,
22030 icon : 'fa fa-forward',
22033 preventDefault: true,
22034 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
22036 //this.addSeparator();
22037 this.loading = this.navgroup.addItem({
22038 tooltip: this.refreshText,
22039 icon: 'fa fa-refresh',
22040 preventDefault: true,
22041 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22047 updateInfo : function(){
22048 if(this.displayEl){
22049 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22050 var msg = count == 0 ?
22054 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
22056 this.displayEl.update(msg);
22061 onLoad : function(ds, r, o){
22062 this.cursor = o.params ? o.params.start : 0;
22063 var d = this.getPageData(),
22067 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22068 this.field.dom.value = ap;
22069 this.first.setDisabled(ap == 1);
22070 this.prev.setDisabled(ap == 1);
22071 this.next.setDisabled(ap == ps);
22072 this.last.setDisabled(ap == ps);
22073 this.loading.enable();
22078 getPageData : function(){
22079 var total = this.ds.getTotalCount();
22082 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22083 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22088 onLoadError : function(){
22089 this.loading.enable();
22093 onPagingKeydown : function(e){
22094 var k = e.getKey();
22095 var d = this.getPageData();
22097 var v = this.field.dom.value, pageNum;
22098 if(!v || isNaN(pageNum = parseInt(v, 10))){
22099 this.field.dom.value = d.activePage;
22102 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22103 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22106 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))
22108 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22109 this.field.dom.value = pageNum;
22110 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22113 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22115 var v = this.field.dom.value, pageNum;
22116 var increment = (e.shiftKey) ? 10 : 1;
22117 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22119 if(!v || isNaN(pageNum = parseInt(v, 10))) {
22120 this.field.dom.value = d.activePage;
22123 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22125 this.field.dom.value = parseInt(v, 10) + increment;
22126 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22127 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22134 beforeLoad : function(){
22136 this.loading.disable();
22141 onClick : function(which){
22150 ds.load({params:{start: 0, limit: this.pageSize}});
22153 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22156 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22159 var total = ds.getTotalCount();
22160 var extra = total % this.pageSize;
22161 var lastStart = extra ? (total - extra) : total-this.pageSize;
22162 ds.load({params:{start: lastStart, limit: this.pageSize}});
22165 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22171 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22172 * @param {Roo.data.Store} store The data store to unbind
22174 unbind : function(ds){
22175 ds.un("beforeload", this.beforeLoad, this);
22176 ds.un("load", this.onLoad, this);
22177 ds.un("loadexception", this.onLoadError, this);
22178 ds.un("remove", this.updateInfo, this);
22179 ds.un("add", this.updateInfo, this);
22180 this.ds = undefined;
22184 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22185 * @param {Roo.data.Store} store The data store to bind
22187 bind : function(ds){
22188 ds.on("beforeload", this.beforeLoad, this);
22189 ds.on("load", this.onLoad, this);
22190 ds.on("loadexception", this.onLoadError, this);
22191 ds.on("remove", this.updateInfo, this);
22192 ds.on("add", this.updateInfo, this);
22203 * @class Roo.bootstrap.MessageBar
22204 * @extends Roo.bootstrap.Component
22205 * Bootstrap MessageBar class
22206 * @cfg {String} html contents of the MessageBar
22207 * @cfg {String} weight (info | success | warning | danger) default info
22208 * @cfg {String} beforeClass insert the bar before the given class
22209 * @cfg {Boolean} closable (true | false) default false
22210 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22213 * Create a new Element
22214 * @param {Object} config The config object
22217 Roo.bootstrap.MessageBar = function(config){
22218 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22221 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22227 beforeClass: 'bootstrap-sticky-wrap',
22229 getAutoCreate : function(){
22233 cls: 'alert alert-dismissable alert-' + this.weight,
22238 html: this.html || ''
22244 cfg.cls += ' alert-messages-fixed';
22258 onRender : function(ct, position)
22260 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22263 var cfg = Roo.apply({}, this.getAutoCreate());
22267 cfg.cls += ' ' + this.cls;
22270 cfg.style = this.style;
22272 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22274 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22277 this.el.select('>button.close').on('click', this.hide, this);
22283 if (!this.rendered) {
22289 this.fireEvent('show', this);
22295 if (!this.rendered) {
22301 this.fireEvent('hide', this);
22304 update : function()
22306 // var e = this.el.dom.firstChild;
22308 // if(this.closable){
22309 // e = e.nextSibling;
22312 // e.data = this.html || '';
22314 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22330 * @class Roo.bootstrap.Graph
22331 * @extends Roo.bootstrap.Component
22332 * Bootstrap Graph class
22336 @cfg {String} graphtype bar | vbar | pie
22337 @cfg {number} g_x coodinator | centre x (pie)
22338 @cfg {number} g_y coodinator | centre y (pie)
22339 @cfg {number} g_r radius (pie)
22340 @cfg {number} g_height height of the chart (respected by all elements in the set)
22341 @cfg {number} g_width width of the chart (respected by all elements in the set)
22342 @cfg {Object} title The title of the chart
22345 -opts (object) options for the chart
22347 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22348 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22350 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.
22351 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22353 o stretch (boolean)
22355 -opts (object) options for the pie
22358 o startAngle (number)
22359 o endAngle (number)
22363 * Create a new Input
22364 * @param {Object} config The config object
22367 Roo.bootstrap.Graph = function(config){
22368 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22374 * The img click event for the img.
22375 * @param {Roo.EventObject} e
22381 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22392 //g_colors: this.colors,
22399 getAutoCreate : function(){
22410 onRender : function(ct,position){
22411 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22412 this.raphael = Raphael(this.el.dom);
22414 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22415 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22416 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22417 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22419 r.text(160, 10, "Single Series Chart").attr(txtattr);
22420 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22421 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22422 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22424 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22425 r.barchart(330, 10, 300, 220, data1);
22426 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22427 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22430 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22431 // r.barchart(30, 30, 560, 250, xdata, {
22432 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22433 // axis : "0 0 1 1",
22434 // axisxlabels : xdata
22435 // //yvalues : cols,
22438 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22440 // this.load(null,xdata,{
22441 // axis : "0 0 1 1",
22442 // axisxlabels : xdata
22447 load : function(graphtype,xdata,opts){
22448 this.raphael.clear();
22450 graphtype = this.graphtype;
22455 var r = this.raphael,
22456 fin = function () {
22457 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22459 fout = function () {
22460 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22462 pfin = function() {
22463 this.sector.stop();
22464 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22467 this.label[0].stop();
22468 this.label[0].attr({ r: 7.5 });
22469 this.label[1].attr({ "font-weight": 800 });
22472 pfout = function() {
22473 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22476 this.label[0].animate({ r: 5 }, 500, "bounce");
22477 this.label[1].attr({ "font-weight": 400 });
22483 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22486 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22489 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
22490 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22492 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22499 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22504 setTitle: function(o)
22509 initEvents: function() {
22512 this.el.on('click', this.onClick, this);
22516 onClick : function(e)
22518 Roo.log('img onclick');
22519 this.fireEvent('click', this, e);
22531 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22534 * @class Roo.bootstrap.dash.NumberBox
22535 * @extends Roo.bootstrap.Component
22536 * Bootstrap NumberBox class
22537 * @cfg {String} headline Box headline
22538 * @cfg {String} content Box content
22539 * @cfg {String} icon Box icon
22540 * @cfg {String} footer Footer text
22541 * @cfg {String} fhref Footer href
22544 * Create a new NumberBox
22545 * @param {Object} config The config object
22549 Roo.bootstrap.dash.NumberBox = function(config){
22550 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22554 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
22563 getAutoCreate : function(){
22567 cls : 'small-box ',
22575 cls : 'roo-headline',
22576 html : this.headline
22580 cls : 'roo-content',
22581 html : this.content
22595 cls : 'ion ' + this.icon
22604 cls : 'small-box-footer',
22605 href : this.fhref || '#',
22609 cfg.cn.push(footer);
22616 onRender : function(ct,position){
22617 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22624 setHeadline: function (value)
22626 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22629 setFooter: function (value, href)
22631 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22634 this.el.select('a.small-box-footer',true).first().attr('href', href);
22639 setContent: function (value)
22641 this.el.select('.roo-content',true).first().dom.innerHTML = value;
22644 initEvents: function()
22658 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22661 * @class Roo.bootstrap.dash.TabBox
22662 * @extends Roo.bootstrap.Component
22663 * Bootstrap TabBox class
22664 * @cfg {String} title Title of the TabBox
22665 * @cfg {String} icon Icon of the TabBox
22666 * @cfg {Boolean} showtabs (true|false) show the tabs default true
22667 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22670 * Create a new TabBox
22671 * @param {Object} config The config object
22675 Roo.bootstrap.dash.TabBox = function(config){
22676 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22681 * When a pane is added
22682 * @param {Roo.bootstrap.dash.TabPane} pane
22686 * @event activatepane
22687 * When a pane is activated
22688 * @param {Roo.bootstrap.dash.TabPane} pane
22690 "activatepane" : true
22698 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
22703 tabScrollable : false,
22705 getChildContainer : function()
22707 return this.el.select('.tab-content', true).first();
22710 getAutoCreate : function(){
22714 cls: 'pull-left header',
22722 cls: 'fa ' + this.icon
22728 cls: 'nav nav-tabs pull-right',
22734 if(this.tabScrollable){
22741 cls: 'nav nav-tabs pull-right',
22752 cls: 'nav-tabs-custom',
22757 cls: 'tab-content no-padding',
22765 initEvents : function()
22767 //Roo.log('add add pane handler');
22768 this.on('addpane', this.onAddPane, this);
22771 * Updates the box title
22772 * @param {String} html to set the title to.
22774 setTitle : function(value)
22776 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22778 onAddPane : function(pane)
22780 this.panes.push(pane);
22781 //Roo.log('addpane');
22783 // tabs are rendere left to right..
22784 if(!this.showtabs){
22788 var ctr = this.el.select('.nav-tabs', true).first();
22791 var existing = ctr.select('.nav-tab',true);
22792 var qty = existing.getCount();;
22795 var tab = ctr.createChild({
22797 cls : 'nav-tab' + (qty ? '' : ' active'),
22805 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22808 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22810 pane.el.addClass('active');
22815 onTabClick : function(ev,un,ob,pane)
22817 //Roo.log('tab - prev default');
22818 ev.preventDefault();
22821 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22822 pane.tab.addClass('active');
22823 //Roo.log(pane.title);
22824 this.getChildContainer().select('.tab-pane',true).removeClass('active');
22825 // technically we should have a deactivate event.. but maybe add later.
22826 // and it should not de-activate the selected tab...
22827 this.fireEvent('activatepane', pane);
22828 pane.el.addClass('active');
22829 pane.fireEvent('activate');
22834 getActivePane : function()
22837 Roo.each(this.panes, function(p) {
22838 if(p.el.hasClass('active')){
22859 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22861 * @class Roo.bootstrap.TabPane
22862 * @extends Roo.bootstrap.Component
22863 * Bootstrap TabPane class
22864 * @cfg {Boolean} active (false | true) Default false
22865 * @cfg {String} title title of panel
22869 * Create a new TabPane
22870 * @param {Object} config The config object
22873 Roo.bootstrap.dash.TabPane = function(config){
22874 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22880 * When a pane is activated
22881 * @param {Roo.bootstrap.dash.TabPane} pane
22888 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
22893 // the tabBox that this is attached to.
22896 getAutoCreate : function()
22904 cfg.cls += ' active';
22909 initEvents : function()
22911 //Roo.log('trigger add pane handler');
22912 this.parent().fireEvent('addpane', this)
22916 * Updates the tab title
22917 * @param {String} html to set the title to.
22919 setTitle: function(str)
22925 this.tab.select('a', true).first().dom.innerHTML = str;
22942 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22945 * @class Roo.bootstrap.menu.Menu
22946 * @extends Roo.bootstrap.Component
22947 * Bootstrap Menu class - container for Menu
22948 * @cfg {String} html Text of the menu
22949 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22950 * @cfg {String} icon Font awesome icon
22951 * @cfg {String} pos Menu align to (top | bottom) default bottom
22955 * Create a new Menu
22956 * @param {Object} config The config object
22960 Roo.bootstrap.menu.Menu = function(config){
22961 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22965 * @event beforeshow
22966 * Fires before this menu is displayed
22967 * @param {Roo.bootstrap.menu.Menu} this
22971 * @event beforehide
22972 * Fires before this menu is hidden
22973 * @param {Roo.bootstrap.menu.Menu} this
22978 * Fires after this menu is displayed
22979 * @param {Roo.bootstrap.menu.Menu} this
22984 * Fires after this menu is hidden
22985 * @param {Roo.bootstrap.menu.Menu} this
22990 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22991 * @param {Roo.bootstrap.menu.Menu} this
22992 * @param {Roo.EventObject} e
22999 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
23003 weight : 'default',
23008 getChildContainer : function() {
23009 if(this.isSubMenu){
23013 return this.el.select('ul.dropdown-menu', true).first();
23016 getAutoCreate : function()
23021 cls : 'roo-menu-text',
23029 cls : 'fa ' + this.icon
23040 cls : 'dropdown-button btn btn-' + this.weight,
23045 cls : 'dropdown-toggle btn btn-' + this.weight,
23055 cls : 'dropdown-menu'
23061 if(this.pos == 'top'){
23062 cfg.cls += ' dropup';
23065 if(this.isSubMenu){
23068 cls : 'dropdown-menu'
23075 onRender : function(ct, position)
23077 this.isSubMenu = ct.hasClass('dropdown-submenu');
23079 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23082 initEvents : function()
23084 if(this.isSubMenu){
23088 this.hidden = true;
23090 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23091 this.triggerEl.on('click', this.onTriggerPress, this);
23093 this.buttonEl = this.el.select('button.dropdown-button', true).first();
23094 this.buttonEl.on('click', this.onClick, this);
23100 if(this.isSubMenu){
23104 return this.el.select('ul.dropdown-menu', true).first();
23107 onClick : function(e)
23109 this.fireEvent("click", this, e);
23112 onTriggerPress : function(e)
23114 if (this.isVisible()) {
23121 isVisible : function(){
23122 return !this.hidden;
23127 this.fireEvent("beforeshow", this);
23129 this.hidden = false;
23130 this.el.addClass('open');
23132 Roo.get(document).on("mouseup", this.onMouseUp, this);
23134 this.fireEvent("show", this);
23141 this.fireEvent("beforehide", this);
23143 this.hidden = true;
23144 this.el.removeClass('open');
23146 Roo.get(document).un("mouseup", this.onMouseUp);
23148 this.fireEvent("hide", this);
23151 onMouseUp : function()
23165 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23168 * @class Roo.bootstrap.menu.Item
23169 * @extends Roo.bootstrap.Component
23170 * Bootstrap MenuItem class
23171 * @cfg {Boolean} submenu (true | false) default false
23172 * @cfg {String} html text of the item
23173 * @cfg {String} href the link
23174 * @cfg {Boolean} disable (true | false) default false
23175 * @cfg {Boolean} preventDefault (true | false) default true
23176 * @cfg {String} icon Font awesome icon
23177 * @cfg {String} pos Submenu align to (left | right) default right
23181 * Create a new Item
23182 * @param {Object} config The config object
23186 Roo.bootstrap.menu.Item = function(config){
23187 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23191 * Fires when the mouse is hovering over this menu
23192 * @param {Roo.bootstrap.menu.Item} this
23193 * @param {Roo.EventObject} e
23198 * Fires when the mouse exits this menu
23199 * @param {Roo.bootstrap.menu.Item} this
23200 * @param {Roo.EventObject} e
23206 * The raw click event for the entire grid.
23207 * @param {Roo.EventObject} e
23213 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23218 preventDefault: true,
23223 getAutoCreate : function()
23228 cls : 'roo-menu-item-text',
23236 cls : 'fa ' + this.icon
23245 href : this.href || '#',
23252 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23256 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23258 if(this.pos == 'left'){
23259 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23266 initEvents : function()
23268 this.el.on('mouseover', this.onMouseOver, this);
23269 this.el.on('mouseout', this.onMouseOut, this);
23271 this.el.select('a', true).first().on('click', this.onClick, this);
23275 onClick : function(e)
23277 if(this.preventDefault){
23278 e.preventDefault();
23281 this.fireEvent("click", this, e);
23284 onMouseOver : function(e)
23286 if(this.submenu && this.pos == 'left'){
23287 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23290 this.fireEvent("mouseover", this, e);
23293 onMouseOut : function(e)
23295 this.fireEvent("mouseout", this, e);
23307 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23310 * @class Roo.bootstrap.menu.Separator
23311 * @extends Roo.bootstrap.Component
23312 * Bootstrap Separator class
23315 * Create a new Separator
23316 * @param {Object} config The config object
23320 Roo.bootstrap.menu.Separator = function(config){
23321 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23324 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23326 getAutoCreate : function(){
23347 * @class Roo.bootstrap.Tooltip
23348 * Bootstrap Tooltip class
23349 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23350 * to determine which dom element triggers the tooltip.
23352 * It needs to add support for additional attributes like tooltip-position
23355 * Create a new Toolti
23356 * @param {Object} config The config object
23359 Roo.bootstrap.Tooltip = function(config){
23360 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23363 Roo.apply(Roo.bootstrap.Tooltip, {
23365 * @function init initialize tooltip monitoring.
23369 currentTip : false,
23370 currentRegion : false,
23376 Roo.get(document).on('mouseover', this.enter ,this);
23377 Roo.get(document).on('mouseout', this.leave, this);
23380 this.currentTip = new Roo.bootstrap.Tooltip();
23383 enter : function(ev)
23385 var dom = ev.getTarget();
23387 //Roo.log(['enter',dom]);
23388 var el = Roo.fly(dom);
23389 if (this.currentEl) {
23391 //Roo.log(this.currentEl);
23392 //Roo.log(this.currentEl.contains(dom));
23393 if (this.currentEl == el) {
23396 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23402 if (this.currentTip.el) {
23403 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23408 // you can not look for children, as if el is the body.. then everythign is the child..
23409 if (!el.attr('tooltip')) { //
23410 if (!el.select("[tooltip]").elements.length) {
23413 // is the mouse over this child...?
23414 bindEl = el.select("[tooltip]").first();
23415 var xy = ev.getXY();
23416 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23417 //Roo.log("not in region.");
23420 //Roo.log("child element over..");
23423 this.currentEl = bindEl;
23424 this.currentTip.bind(bindEl);
23425 this.currentRegion = Roo.lib.Region.getRegion(dom);
23426 this.currentTip.enter();
23429 leave : function(ev)
23431 var dom = ev.getTarget();
23432 //Roo.log(['leave',dom]);
23433 if (!this.currentEl) {
23438 if (dom != this.currentEl.dom) {
23441 var xy = ev.getXY();
23442 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23445 // only activate leave if mouse cursor is outside... bounding box..
23450 if (this.currentTip) {
23451 this.currentTip.leave();
23453 //Roo.log('clear currentEl');
23454 this.currentEl = false;
23459 'left' : ['r-l', [-2,0], 'right'],
23460 'right' : ['l-r', [2,0], 'left'],
23461 'bottom' : ['t-b', [0,2], 'top'],
23462 'top' : [ 'b-t', [0,-2], 'bottom']
23468 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
23473 delay : null, // can be { show : 300 , hide: 500}
23477 hoverState : null, //???
23479 placement : 'bottom',
23481 getAutoCreate : function(){
23488 cls : 'tooltip-arrow'
23491 cls : 'tooltip-inner'
23498 bind : function(el)
23504 enter : function () {
23506 if (this.timeout != null) {
23507 clearTimeout(this.timeout);
23510 this.hoverState = 'in';
23511 //Roo.log("enter - show");
23512 if (!this.delay || !this.delay.show) {
23517 this.timeout = setTimeout(function () {
23518 if (_t.hoverState == 'in') {
23521 }, this.delay.show);
23525 clearTimeout(this.timeout);
23527 this.hoverState = 'out';
23528 if (!this.delay || !this.delay.hide) {
23534 this.timeout = setTimeout(function () {
23535 //Roo.log("leave - timeout");
23537 if (_t.hoverState == 'out') {
23539 Roo.bootstrap.Tooltip.currentEl = false;
23547 this.render(document.body);
23550 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23552 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23554 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23556 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23558 var placement = typeof this.placement == 'function' ?
23559 this.placement.call(this, this.el, on_el) :
23562 var autoToken = /\s?auto?\s?/i;
23563 var autoPlace = autoToken.test(placement);
23565 placement = placement.replace(autoToken, '') || 'top';
23569 //this.el.setXY([0,0]);
23571 //this.el.dom.style.display='block';
23573 //this.el.appendTo(on_el);
23575 var p = this.getPosition();
23576 var box = this.el.getBox();
23582 var align = Roo.bootstrap.Tooltip.alignment[placement];
23584 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23586 if(placement == 'top' || placement == 'bottom'){
23588 placement = 'right';
23591 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23592 placement = 'left';
23596 align = Roo.bootstrap.Tooltip.alignment[placement];
23598 this.el.alignTo(this.bindEl, align[0],align[1]);
23599 //var arrow = this.el.select('.arrow',true).first();
23600 //arrow.set(align[2],
23602 this.el.addClass(placement);
23604 this.el.addClass('in fade');
23606 this.hoverState = null;
23608 if (this.el.hasClass('fade')) {
23619 //this.el.setXY([0,0]);
23620 this.el.removeClass('in');
23636 * @class Roo.bootstrap.LocationPicker
23637 * @extends Roo.bootstrap.Component
23638 * Bootstrap LocationPicker class
23639 * @cfg {Number} latitude Position when init default 0
23640 * @cfg {Number} longitude Position when init default 0
23641 * @cfg {Number} zoom default 15
23642 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23643 * @cfg {Boolean} mapTypeControl default false
23644 * @cfg {Boolean} disableDoubleClickZoom default false
23645 * @cfg {Boolean} scrollwheel default true
23646 * @cfg {Boolean} streetViewControl default false
23647 * @cfg {Number} radius default 0
23648 * @cfg {String} locationName
23649 * @cfg {Boolean} draggable default true
23650 * @cfg {Boolean} enableAutocomplete default false
23651 * @cfg {Boolean} enableReverseGeocode default true
23652 * @cfg {String} markerTitle
23655 * Create a new LocationPicker
23656 * @param {Object} config The config object
23660 Roo.bootstrap.LocationPicker = function(config){
23662 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23667 * Fires when the picker initialized.
23668 * @param {Roo.bootstrap.LocationPicker} this
23669 * @param {Google Location} location
23673 * @event positionchanged
23674 * Fires when the picker position changed.
23675 * @param {Roo.bootstrap.LocationPicker} this
23676 * @param {Google Location} location
23678 positionchanged : true,
23681 * Fires when the map resize.
23682 * @param {Roo.bootstrap.LocationPicker} this
23687 * Fires when the map show.
23688 * @param {Roo.bootstrap.LocationPicker} this
23693 * Fires when the map hide.
23694 * @param {Roo.bootstrap.LocationPicker} this
23699 * Fires when click the map.
23700 * @param {Roo.bootstrap.LocationPicker} this
23701 * @param {Map event} e
23705 * @event mapRightClick
23706 * Fires when right click the map.
23707 * @param {Roo.bootstrap.LocationPicker} this
23708 * @param {Map event} e
23710 mapRightClick : true,
23712 * @event markerClick
23713 * Fires when click the marker.
23714 * @param {Roo.bootstrap.LocationPicker} this
23715 * @param {Map event} e
23717 markerClick : true,
23719 * @event markerRightClick
23720 * Fires when right click the marker.
23721 * @param {Roo.bootstrap.LocationPicker} this
23722 * @param {Map event} e
23724 markerRightClick : true,
23726 * @event OverlayViewDraw
23727 * Fires when OverlayView Draw
23728 * @param {Roo.bootstrap.LocationPicker} this
23730 OverlayViewDraw : true,
23732 * @event OverlayViewOnAdd
23733 * Fires when OverlayView Draw
23734 * @param {Roo.bootstrap.LocationPicker} this
23736 OverlayViewOnAdd : true,
23738 * @event OverlayViewOnRemove
23739 * Fires when OverlayView Draw
23740 * @param {Roo.bootstrap.LocationPicker} this
23742 OverlayViewOnRemove : true,
23744 * @event OverlayViewShow
23745 * Fires when OverlayView Draw
23746 * @param {Roo.bootstrap.LocationPicker} this
23747 * @param {Pixel} cpx
23749 OverlayViewShow : true,
23751 * @event OverlayViewHide
23752 * Fires when OverlayView Draw
23753 * @param {Roo.bootstrap.LocationPicker} this
23755 OverlayViewHide : true,
23757 * @event loadexception
23758 * Fires when load google lib failed.
23759 * @param {Roo.bootstrap.LocationPicker} this
23761 loadexception : true
23766 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
23768 gMapContext: false,
23774 mapTypeControl: false,
23775 disableDoubleClickZoom: false,
23777 streetViewControl: false,
23781 enableAutocomplete: false,
23782 enableReverseGeocode: true,
23785 getAutoCreate: function()
23790 cls: 'roo-location-picker'
23796 initEvents: function(ct, position)
23798 if(!this.el.getWidth() || this.isApplied()){
23802 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23807 initial: function()
23809 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
23810 this.fireEvent('loadexception', this);
23814 if(!this.mapTypeId){
23815 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23818 this.gMapContext = this.GMapContext();
23820 this.initOverlayView();
23822 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23826 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23827 _this.setPosition(_this.gMapContext.marker.position);
23830 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23831 _this.fireEvent('mapClick', this, event);
23835 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23836 _this.fireEvent('mapRightClick', this, event);
23840 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23841 _this.fireEvent('markerClick', this, event);
23845 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23846 _this.fireEvent('markerRightClick', this, event);
23850 this.setPosition(this.gMapContext.location);
23852 this.fireEvent('initial', this, this.gMapContext.location);
23855 initOverlayView: function()
23859 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23863 _this.fireEvent('OverlayViewDraw', _this);
23868 _this.fireEvent('OverlayViewOnAdd', _this);
23871 onRemove: function()
23873 _this.fireEvent('OverlayViewOnRemove', _this);
23876 show: function(cpx)
23878 _this.fireEvent('OverlayViewShow', _this, cpx);
23883 _this.fireEvent('OverlayViewHide', _this);
23889 fromLatLngToContainerPixel: function(event)
23891 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23894 isApplied: function()
23896 return this.getGmapContext() == false ? false : true;
23899 getGmapContext: function()
23901 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
23904 GMapContext: function()
23906 var position = new google.maps.LatLng(this.latitude, this.longitude);
23908 var _map = new google.maps.Map(this.el.dom, {
23911 mapTypeId: this.mapTypeId,
23912 mapTypeControl: this.mapTypeControl,
23913 disableDoubleClickZoom: this.disableDoubleClickZoom,
23914 scrollwheel: this.scrollwheel,
23915 streetViewControl: this.streetViewControl,
23916 locationName: this.locationName,
23917 draggable: this.draggable,
23918 enableAutocomplete: this.enableAutocomplete,
23919 enableReverseGeocode: this.enableReverseGeocode
23922 var _marker = new google.maps.Marker({
23923 position: position,
23925 title: this.markerTitle,
23926 draggable: this.draggable
23933 location: position,
23934 radius: this.radius,
23935 locationName: this.locationName,
23936 addressComponents: {
23937 formatted_address: null,
23938 addressLine1: null,
23939 addressLine2: null,
23941 streetNumber: null,
23945 stateOrProvince: null
23948 domContainer: this.el.dom,
23949 geodecoder: new google.maps.Geocoder()
23953 drawCircle: function(center, radius, options)
23955 if (this.gMapContext.circle != null) {
23956 this.gMapContext.circle.setMap(null);
23960 options = Roo.apply({}, options, {
23961 strokeColor: "#0000FF",
23962 strokeOpacity: .35,
23964 fillColor: "#0000FF",
23968 options.map = this.gMapContext.map;
23969 options.radius = radius;
23970 options.center = center;
23971 this.gMapContext.circle = new google.maps.Circle(options);
23972 return this.gMapContext.circle;
23978 setPosition: function(location)
23980 this.gMapContext.location = location;
23981 this.gMapContext.marker.setPosition(location);
23982 this.gMapContext.map.panTo(location);
23983 this.drawCircle(location, this.gMapContext.radius, {});
23987 if (this.gMapContext.settings.enableReverseGeocode) {
23988 this.gMapContext.geodecoder.geocode({
23989 latLng: this.gMapContext.location
23990 }, function(results, status) {
23992 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23993 _this.gMapContext.locationName = results[0].formatted_address;
23994 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23996 _this.fireEvent('positionchanged', this, location);
24003 this.fireEvent('positionchanged', this, location);
24008 google.maps.event.trigger(this.gMapContext.map, "resize");
24010 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24012 this.fireEvent('resize', this);
24015 setPositionByLatLng: function(latitude, longitude)
24017 this.setPosition(new google.maps.LatLng(latitude, longitude));
24020 getCurrentPosition: function()
24023 latitude: this.gMapContext.location.lat(),
24024 longitude: this.gMapContext.location.lng()
24028 getAddressName: function()
24030 return this.gMapContext.locationName;
24033 getAddressComponents: function()
24035 return this.gMapContext.addressComponents;
24038 address_component_from_google_geocode: function(address_components)
24042 for (var i = 0; i < address_components.length; i++) {
24043 var component = address_components[i];
24044 if (component.types.indexOf("postal_code") >= 0) {
24045 result.postalCode = component.short_name;
24046 } else if (component.types.indexOf("street_number") >= 0) {
24047 result.streetNumber = component.short_name;
24048 } else if (component.types.indexOf("route") >= 0) {
24049 result.streetName = component.short_name;
24050 } else if (component.types.indexOf("neighborhood") >= 0) {
24051 result.city = component.short_name;
24052 } else if (component.types.indexOf("locality") >= 0) {
24053 result.city = component.short_name;
24054 } else if (component.types.indexOf("sublocality") >= 0) {
24055 result.district = component.short_name;
24056 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24057 result.stateOrProvince = component.short_name;
24058 } else if (component.types.indexOf("country") >= 0) {
24059 result.country = component.short_name;
24063 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24064 result.addressLine2 = "";
24068 setZoomLevel: function(zoom)
24070 this.gMapContext.map.setZoom(zoom);
24083 this.fireEvent('show', this);
24094 this.fireEvent('hide', this);
24099 Roo.apply(Roo.bootstrap.LocationPicker, {
24101 OverlayView : function(map, options)
24103 options = options || {};
24117 * @class Roo.bootstrap.Alert
24118 * @extends Roo.bootstrap.Component
24119 * Bootstrap Alert class
24120 * @cfg {String} title The title of alert
24121 * @cfg {String} html The content of alert
24122 * @cfg {String} weight ( success | info | warning | danger )
24123 * @cfg {String} faicon font-awesomeicon
24126 * Create a new alert
24127 * @param {Object} config The config object
24131 Roo.bootstrap.Alert = function(config){
24132 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24136 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
24143 getAutoCreate : function()
24152 cls : 'roo-alert-icon'
24157 cls : 'roo-alert-title',
24162 cls : 'roo-alert-text',
24169 cfg.cn[0].cls += ' fa ' + this.faicon;
24173 cfg.cls += ' alert-' + this.weight;
24179 initEvents: function()
24181 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24184 setTitle : function(str)
24186 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24189 setText : function(str)
24191 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24194 setWeight : function(weight)
24197 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24200 this.weight = weight;
24202 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24205 setIcon : function(icon)
24208 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24211 this.faicon = icon;
24213 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24234 * @class Roo.bootstrap.UploadCropbox
24235 * @extends Roo.bootstrap.Component
24236 * Bootstrap UploadCropbox class
24237 * @cfg {String} emptyText show when image has been loaded
24238 * @cfg {String} rotateNotify show when image too small to rotate
24239 * @cfg {Number} errorTimeout default 3000
24240 * @cfg {Number} minWidth default 300
24241 * @cfg {Number} minHeight default 300
24242 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24243 * @cfg {Boolean} isDocument (true|false) default false
24244 * @cfg {String} url action url
24245 * @cfg {String} paramName default 'imageUpload'
24246 * @cfg {String} method default POST
24247 * @cfg {Boolean} loadMask (true|false) default true
24248 * @cfg {Boolean} loadingText default 'Loading...'
24251 * Create a new UploadCropbox
24252 * @param {Object} config The config object
24255 Roo.bootstrap.UploadCropbox = function(config){
24256 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24260 * @event beforeselectfile
24261 * Fire before select file
24262 * @param {Roo.bootstrap.UploadCropbox} this
24264 "beforeselectfile" : true,
24267 * Fire after initEvent
24268 * @param {Roo.bootstrap.UploadCropbox} this
24273 * Fire after initEvent
24274 * @param {Roo.bootstrap.UploadCropbox} this
24275 * @param {String} data
24280 * Fire when preparing the file data
24281 * @param {Roo.bootstrap.UploadCropbox} this
24282 * @param {Object} file
24287 * Fire when get exception
24288 * @param {Roo.bootstrap.UploadCropbox} this
24289 * @param {XMLHttpRequest} xhr
24291 "exception" : true,
24293 * @event beforeloadcanvas
24294 * Fire before load the canvas
24295 * @param {Roo.bootstrap.UploadCropbox} this
24296 * @param {String} src
24298 "beforeloadcanvas" : true,
24301 * Fire when trash image
24302 * @param {Roo.bootstrap.UploadCropbox} this
24307 * Fire when download the image
24308 * @param {Roo.bootstrap.UploadCropbox} this
24312 * @event footerbuttonclick
24313 * Fire when footerbuttonclick
24314 * @param {Roo.bootstrap.UploadCropbox} this
24315 * @param {String} type
24317 "footerbuttonclick" : true,
24321 * @param {Roo.bootstrap.UploadCropbox} this
24326 * Fire when rotate the image
24327 * @param {Roo.bootstrap.UploadCropbox} this
24328 * @param {String} pos
24333 * Fire when inspect the file
24334 * @param {Roo.bootstrap.UploadCropbox} this
24335 * @param {Object} file
24340 * Fire when xhr upload the file
24341 * @param {Roo.bootstrap.UploadCropbox} this
24342 * @param {Object} data
24347 * Fire when arrange the file data
24348 * @param {Roo.bootstrap.UploadCropbox} this
24349 * @param {Object} formData
24354 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24357 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24359 emptyText : 'Click to upload image',
24360 rotateNotify : 'Image is too small to rotate',
24361 errorTimeout : 3000,
24375 cropType : 'image/jpeg',
24377 canvasLoaded : false,
24378 isDocument : false,
24380 paramName : 'imageUpload',
24382 loadingText : 'Loading...',
24385 getAutoCreate : function()
24389 cls : 'roo-upload-cropbox',
24393 cls : 'roo-upload-cropbox-selector',
24398 cls : 'roo-upload-cropbox-body',
24399 style : 'cursor:pointer',
24403 cls : 'roo-upload-cropbox-preview'
24407 cls : 'roo-upload-cropbox-thumb'
24411 cls : 'roo-upload-cropbox-empty-notify',
24412 html : this.emptyText
24416 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24417 html : this.rotateNotify
24423 cls : 'roo-upload-cropbox-footer',
24426 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24436 onRender : function(ct, position)
24438 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24440 if (this.buttons.length) {
24442 Roo.each(this.buttons, function(bb) {
24444 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24446 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24452 this.maskEl = this.el;
24456 initEvents : function()
24458 this.urlAPI = (window.createObjectURL && window) ||
24459 (window.URL && URL.revokeObjectURL && URL) ||
24460 (window.webkitURL && webkitURL);
24462 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24463 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24465 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24466 this.selectorEl.hide();
24468 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24469 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24471 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24472 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24473 this.thumbEl.hide();
24475 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24476 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24478 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24479 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24480 this.errorEl.hide();
24482 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24483 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24484 this.footerEl.hide();
24486 this.setThumbBoxSize();
24492 this.fireEvent('initial', this);
24499 window.addEventListener("resize", function() { _this.resize(); } );
24501 this.bodyEl.on('click', this.beforeSelectFile, this);
24504 this.bodyEl.on('touchstart', this.onTouchStart, this);
24505 this.bodyEl.on('touchmove', this.onTouchMove, this);
24506 this.bodyEl.on('touchend', this.onTouchEnd, this);
24510 this.bodyEl.on('mousedown', this.onMouseDown, this);
24511 this.bodyEl.on('mousemove', this.onMouseMove, this);
24512 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24513 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24514 Roo.get(document).on('mouseup', this.onMouseUp, this);
24517 this.selectorEl.on('change', this.onFileSelected, this);
24523 this.baseScale = 1;
24525 this.baseRotate = 1;
24526 this.dragable = false;
24527 this.pinching = false;
24530 this.cropData = false;
24531 this.notifyEl.dom.innerHTML = this.emptyText;
24533 this.selectorEl.dom.value = '';
24537 resize : function()
24539 if(this.fireEvent('resize', this) != false){
24540 this.setThumbBoxPosition();
24541 this.setCanvasPosition();
24545 onFooterButtonClick : function(e, el, o, type)
24548 case 'rotate-left' :
24549 this.onRotateLeft(e);
24551 case 'rotate-right' :
24552 this.onRotateRight(e);
24555 this.beforeSelectFile(e);
24570 this.fireEvent('footerbuttonclick', this, type);
24573 beforeSelectFile : function(e)
24575 e.preventDefault();
24577 if(this.fireEvent('beforeselectfile', this) != false){
24578 this.selectorEl.dom.click();
24582 onFileSelected : function(e)
24584 e.preventDefault();
24586 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24590 var file = this.selectorEl.dom.files[0];
24592 if(this.fireEvent('inspect', this, file) != false){
24593 this.prepare(file);
24598 trash : function(e)
24600 this.fireEvent('trash', this);
24603 download : function(e)
24605 this.fireEvent('download', this);
24608 loadCanvas : function(src)
24610 if(this.fireEvent('beforeloadcanvas', this, src) != false){
24614 this.imageEl = document.createElement('img');
24618 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24620 this.imageEl.src = src;
24624 onLoadCanvas : function()
24626 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24627 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24629 this.bodyEl.un('click', this.beforeSelectFile, this);
24631 this.notifyEl.hide();
24632 this.thumbEl.show();
24633 this.footerEl.show();
24635 this.baseRotateLevel();
24637 if(this.isDocument){
24638 this.setThumbBoxSize();
24641 this.setThumbBoxPosition();
24643 this.baseScaleLevel();
24649 this.canvasLoaded = true;
24652 this.maskEl.unmask();
24657 setCanvasPosition : function()
24659 if(!this.canvasEl){
24663 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24664 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24666 this.previewEl.setLeft(pw);
24667 this.previewEl.setTop(ph);
24671 onMouseDown : function(e)
24675 this.dragable = true;
24676 this.pinching = false;
24678 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24679 this.dragable = false;
24683 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24684 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24688 onMouseMove : function(e)
24692 if(!this.canvasLoaded){
24696 if (!this.dragable){
24700 var minX = Math.ceil(this.thumbEl.getLeft(true));
24701 var minY = Math.ceil(this.thumbEl.getTop(true));
24703 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24704 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24706 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24707 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24709 x = x - this.mouseX;
24710 y = y - this.mouseY;
24712 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24713 var bgY = Math.ceil(y + this.previewEl.getTop(true));
24715 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24716 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24718 this.previewEl.setLeft(bgX);
24719 this.previewEl.setTop(bgY);
24721 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24722 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24725 onMouseUp : function(e)
24729 this.dragable = false;
24732 onMouseWheel : function(e)
24736 this.startScale = this.scale;
24738 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24740 if(!this.zoomable()){
24741 this.scale = this.startScale;
24750 zoomable : function()
24752 var minScale = this.thumbEl.getWidth() / this.minWidth;
24754 if(this.minWidth < this.minHeight){
24755 minScale = this.thumbEl.getHeight() / this.minHeight;
24758 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
24759 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
24763 (this.rotate == 0 || this.rotate == 180) &&
24765 width > this.imageEl.OriginWidth ||
24766 height > this.imageEl.OriginHeight ||
24767 (width < this.minWidth && height < this.minHeight)
24775 (this.rotate == 90 || this.rotate == 270) &&
24777 width > this.imageEl.OriginWidth ||
24778 height > this.imageEl.OriginHeight ||
24779 (width < this.minHeight && height < this.minWidth)
24786 !this.isDocument &&
24787 (this.rotate == 0 || this.rotate == 180) &&
24789 width < this.minWidth ||
24790 width > this.imageEl.OriginWidth ||
24791 height < this.minHeight ||
24792 height > this.imageEl.OriginHeight
24799 !this.isDocument &&
24800 (this.rotate == 90 || this.rotate == 270) &&
24802 width < this.minHeight ||
24803 width > this.imageEl.OriginWidth ||
24804 height < this.minWidth ||
24805 height > this.imageEl.OriginHeight
24815 onRotateLeft : function(e)
24817 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24819 var minScale = this.thumbEl.getWidth() / this.minWidth;
24821 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24822 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24824 this.startScale = this.scale;
24826 while (this.getScaleLevel() < minScale){
24828 this.scale = this.scale + 1;
24830 if(!this.zoomable()){
24835 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24836 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24841 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24848 this.scale = this.startScale;
24850 this.onRotateFail();
24855 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24857 if(this.isDocument){
24858 this.setThumbBoxSize();
24859 this.setThumbBoxPosition();
24860 this.setCanvasPosition();
24865 this.fireEvent('rotate', this, 'left');
24869 onRotateRight : function(e)
24871 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24873 var minScale = this.thumbEl.getWidth() / this.minWidth;
24875 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24876 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24878 this.startScale = this.scale;
24880 while (this.getScaleLevel() < minScale){
24882 this.scale = this.scale + 1;
24884 if(!this.zoomable()){
24889 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24890 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24895 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24902 this.scale = this.startScale;
24904 this.onRotateFail();
24909 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24911 if(this.isDocument){
24912 this.setThumbBoxSize();
24913 this.setThumbBoxPosition();
24914 this.setCanvasPosition();
24919 this.fireEvent('rotate', this, 'right');
24922 onRotateFail : function()
24924 this.errorEl.show(true);
24928 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
24933 this.previewEl.dom.innerHTML = '';
24935 var canvasEl = document.createElement("canvas");
24937 var contextEl = canvasEl.getContext("2d");
24939 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24940 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24941 var center = this.imageEl.OriginWidth / 2;
24943 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
24944 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24945 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24946 center = this.imageEl.OriginHeight / 2;
24949 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
24951 contextEl.translate(center, center);
24952 contextEl.rotate(this.rotate * Math.PI / 180);
24954 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24956 this.canvasEl = document.createElement("canvas");
24958 this.contextEl = this.canvasEl.getContext("2d");
24960 switch (this.rotate) {
24963 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24964 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24966 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24971 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24972 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24974 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24975 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);
24979 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24984 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24985 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24987 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24988 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);
24992 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);
24997 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24998 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25000 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25001 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25005 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);
25012 this.previewEl.appendChild(this.canvasEl);
25014 this.setCanvasPosition();
25019 if(!this.canvasLoaded){
25023 var imageCanvas = document.createElement("canvas");
25025 var imageContext = imageCanvas.getContext("2d");
25027 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25028 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25030 var center = imageCanvas.width / 2;
25032 imageContext.translate(center, center);
25034 imageContext.rotate(this.rotate * Math.PI / 180);
25036 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25038 var canvas = document.createElement("canvas");
25040 var context = canvas.getContext("2d");
25042 canvas.width = this.minWidth;
25043 canvas.height = this.minHeight;
25045 switch (this.rotate) {
25048 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25049 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25051 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25052 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25054 var targetWidth = this.minWidth - 2 * x;
25055 var targetHeight = this.minHeight - 2 * y;
25059 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25060 scale = targetWidth / width;
25063 if(x > 0 && y == 0){
25064 scale = targetHeight / height;
25067 if(x > 0 && y > 0){
25068 scale = targetWidth / width;
25070 if(width < height){
25071 scale = targetHeight / height;
25075 context.scale(scale, scale);
25077 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25078 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25080 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25081 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25083 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25088 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25089 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25091 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25092 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25094 var targetWidth = this.minWidth - 2 * x;
25095 var targetHeight = this.minHeight - 2 * y;
25099 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25100 scale = targetWidth / width;
25103 if(x > 0 && y == 0){
25104 scale = targetHeight / height;
25107 if(x > 0 && y > 0){
25108 scale = targetWidth / width;
25110 if(width < height){
25111 scale = targetHeight / height;
25115 context.scale(scale, scale);
25117 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25118 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25120 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25121 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25123 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25125 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25130 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25131 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25133 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25134 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25136 var targetWidth = this.minWidth - 2 * x;
25137 var targetHeight = this.minHeight - 2 * y;
25141 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25142 scale = targetWidth / width;
25145 if(x > 0 && y == 0){
25146 scale = targetHeight / height;
25149 if(x > 0 && y > 0){
25150 scale = targetWidth / width;
25152 if(width < height){
25153 scale = targetHeight / height;
25157 context.scale(scale, scale);
25159 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25160 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25162 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25163 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25165 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25166 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25168 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25173 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25174 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25176 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25177 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25179 var targetWidth = this.minWidth - 2 * x;
25180 var targetHeight = this.minHeight - 2 * y;
25184 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25185 scale = targetWidth / width;
25188 if(x > 0 && y == 0){
25189 scale = targetHeight / height;
25192 if(x > 0 && y > 0){
25193 scale = targetWidth / width;
25195 if(width < height){
25196 scale = targetHeight / height;
25200 context.scale(scale, scale);
25202 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25203 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25205 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25206 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25208 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25210 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25217 this.cropData = canvas.toDataURL(this.cropType);
25219 if(this.fireEvent('crop', this, this.cropData) !== false){
25220 this.process(this.file, this.cropData);
25227 setThumbBoxSize : function()
25231 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25232 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25233 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25235 this.minWidth = width;
25236 this.minHeight = height;
25238 if(this.rotate == 90 || this.rotate == 270){
25239 this.minWidth = height;
25240 this.minHeight = width;
25245 width = Math.ceil(this.minWidth * height / this.minHeight);
25247 if(this.minWidth > this.minHeight){
25249 height = Math.ceil(this.minHeight * width / this.minWidth);
25252 this.thumbEl.setStyle({
25253 width : width + 'px',
25254 height : height + 'px'
25261 setThumbBoxPosition : function()
25263 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25264 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25266 this.thumbEl.setLeft(x);
25267 this.thumbEl.setTop(y);
25271 baseRotateLevel : function()
25273 this.baseRotate = 1;
25276 typeof(this.exif) != 'undefined' &&
25277 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25278 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25280 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25283 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25287 baseScaleLevel : function()
25291 if(this.isDocument){
25293 if(this.baseRotate == 6 || this.baseRotate == 8){
25295 height = this.thumbEl.getHeight();
25296 this.baseScale = height / this.imageEl.OriginWidth;
25298 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25299 width = this.thumbEl.getWidth();
25300 this.baseScale = width / this.imageEl.OriginHeight;
25306 height = this.thumbEl.getHeight();
25307 this.baseScale = height / this.imageEl.OriginHeight;
25309 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25310 width = this.thumbEl.getWidth();
25311 this.baseScale = width / this.imageEl.OriginWidth;
25317 if(this.baseRotate == 6 || this.baseRotate == 8){
25319 width = this.thumbEl.getHeight();
25320 this.baseScale = width / this.imageEl.OriginHeight;
25322 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25323 height = this.thumbEl.getWidth();
25324 this.baseScale = height / this.imageEl.OriginHeight;
25327 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25328 height = this.thumbEl.getWidth();
25329 this.baseScale = height / this.imageEl.OriginHeight;
25331 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25332 width = this.thumbEl.getHeight();
25333 this.baseScale = width / this.imageEl.OriginWidth;
25340 width = this.thumbEl.getWidth();
25341 this.baseScale = width / this.imageEl.OriginWidth;
25343 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25344 height = this.thumbEl.getHeight();
25345 this.baseScale = height / this.imageEl.OriginHeight;
25348 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25350 height = this.thumbEl.getHeight();
25351 this.baseScale = height / this.imageEl.OriginHeight;
25353 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25354 width = this.thumbEl.getWidth();
25355 this.baseScale = width / this.imageEl.OriginWidth;
25363 getScaleLevel : function()
25365 return this.baseScale * Math.pow(1.1, this.scale);
25368 onTouchStart : function(e)
25370 if(!this.canvasLoaded){
25371 this.beforeSelectFile(e);
25375 var touches = e.browserEvent.touches;
25381 if(touches.length == 1){
25382 this.onMouseDown(e);
25386 if(touches.length != 2){
25392 for(var i = 0, finger; finger = touches[i]; i++){
25393 coords.push(finger.pageX, finger.pageY);
25396 var x = Math.pow(coords[0] - coords[2], 2);
25397 var y = Math.pow(coords[1] - coords[3], 2);
25399 this.startDistance = Math.sqrt(x + y);
25401 this.startScale = this.scale;
25403 this.pinching = true;
25404 this.dragable = false;
25408 onTouchMove : function(e)
25410 if(!this.pinching && !this.dragable){
25414 var touches = e.browserEvent.touches;
25421 this.onMouseMove(e);
25427 for(var i = 0, finger; finger = touches[i]; i++){
25428 coords.push(finger.pageX, finger.pageY);
25431 var x = Math.pow(coords[0] - coords[2], 2);
25432 var y = Math.pow(coords[1] - coords[3], 2);
25434 this.endDistance = Math.sqrt(x + y);
25436 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25438 if(!this.zoomable()){
25439 this.scale = this.startScale;
25447 onTouchEnd : function(e)
25449 this.pinching = false;
25450 this.dragable = false;
25454 process : function(file, crop)
25457 this.maskEl.mask(this.loadingText);
25460 this.xhr = new XMLHttpRequest();
25462 file.xhr = this.xhr;
25464 this.xhr.open(this.method, this.url, true);
25467 "Accept": "application/json",
25468 "Cache-Control": "no-cache",
25469 "X-Requested-With": "XMLHttpRequest"
25472 for (var headerName in headers) {
25473 var headerValue = headers[headerName];
25475 this.xhr.setRequestHeader(headerName, headerValue);
25481 this.xhr.onload = function()
25483 _this.xhrOnLoad(_this.xhr);
25486 this.xhr.onerror = function()
25488 _this.xhrOnError(_this.xhr);
25491 var formData = new FormData();
25493 formData.append('returnHTML', 'NO');
25496 formData.append('crop', crop);
25499 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25500 formData.append(this.paramName, file, file.name);
25503 if(typeof(file.filename) != 'undefined'){
25504 formData.append('filename', file.filename);
25507 if(typeof(file.mimetype) != 'undefined'){
25508 formData.append('mimetype', file.mimetype);
25511 if(this.fireEvent('arrange', this, formData) != false){
25512 this.xhr.send(formData);
25516 xhrOnLoad : function(xhr)
25519 this.maskEl.unmask();
25522 if (xhr.readyState !== 4) {
25523 this.fireEvent('exception', this, xhr);
25527 var response = Roo.decode(xhr.responseText);
25529 if(!response.success){
25530 this.fireEvent('exception', this, xhr);
25534 var response = Roo.decode(xhr.responseText);
25536 this.fireEvent('upload', this, response);
25540 xhrOnError : function()
25543 this.maskEl.unmask();
25546 Roo.log('xhr on error');
25548 var response = Roo.decode(xhr.responseText);
25554 prepare : function(file)
25557 this.maskEl.mask(this.loadingText);
25563 if(typeof(file) === 'string'){
25564 this.loadCanvas(file);
25568 if(!file || !this.urlAPI){
25573 this.cropType = file.type;
25577 if(this.fireEvent('prepare', this, this.file) != false){
25579 var reader = new FileReader();
25581 reader.onload = function (e) {
25582 if (e.target.error) {
25583 Roo.log(e.target.error);
25587 var buffer = e.target.result,
25588 dataView = new DataView(buffer),
25590 maxOffset = dataView.byteLength - 4,
25594 if (dataView.getUint16(0) === 0xffd8) {
25595 while (offset < maxOffset) {
25596 markerBytes = dataView.getUint16(offset);
25598 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25599 markerLength = dataView.getUint16(offset + 2) + 2;
25600 if (offset + markerLength > dataView.byteLength) {
25601 Roo.log('Invalid meta data: Invalid segment size.');
25605 if(markerBytes == 0xffe1){
25606 _this.parseExifData(
25613 offset += markerLength;
25623 var url = _this.urlAPI.createObjectURL(_this.file);
25625 _this.loadCanvas(url);
25630 reader.readAsArrayBuffer(this.file);
25636 parseExifData : function(dataView, offset, length)
25638 var tiffOffset = offset + 10,
25642 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25643 // No Exif data, might be XMP data instead
25647 // Check for the ASCII code for "Exif" (0x45786966):
25648 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25649 // No Exif data, might be XMP data instead
25652 if (tiffOffset + 8 > dataView.byteLength) {
25653 Roo.log('Invalid Exif data: Invalid segment size.');
25656 // Check for the two null bytes:
25657 if (dataView.getUint16(offset + 8) !== 0x0000) {
25658 Roo.log('Invalid Exif data: Missing byte alignment offset.');
25661 // Check the byte alignment:
25662 switch (dataView.getUint16(tiffOffset)) {
25664 littleEndian = true;
25667 littleEndian = false;
25670 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25673 // Check for the TIFF tag marker (0x002A):
25674 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25675 Roo.log('Invalid Exif data: Missing TIFF marker.');
25678 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25679 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25681 this.parseExifTags(
25684 tiffOffset + dirOffset,
25689 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25694 if (dirOffset + 6 > dataView.byteLength) {
25695 Roo.log('Invalid Exif data: Invalid directory offset.');
25698 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25699 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25700 if (dirEndOffset + 4 > dataView.byteLength) {
25701 Roo.log('Invalid Exif data: Invalid directory size.');
25704 for (i = 0; i < tagsNumber; i += 1) {
25708 dirOffset + 2 + 12 * i, // tag offset
25712 // Return the offset to the next directory:
25713 return dataView.getUint32(dirEndOffset, littleEndian);
25716 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
25718 var tag = dataView.getUint16(offset, littleEndian);
25720 this.exif[tag] = this.getExifValue(
25724 dataView.getUint16(offset + 2, littleEndian), // tag type
25725 dataView.getUint32(offset + 4, littleEndian), // tag length
25730 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25732 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25741 Roo.log('Invalid Exif data: Invalid tag type.');
25745 tagSize = tagType.size * length;
25746 // Determine if the value is contained in the dataOffset bytes,
25747 // or if the value at the dataOffset is a pointer to the actual data:
25748 dataOffset = tagSize > 4 ?
25749 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25750 if (dataOffset + tagSize > dataView.byteLength) {
25751 Roo.log('Invalid Exif data: Invalid data offset.');
25754 if (length === 1) {
25755 return tagType.getValue(dataView, dataOffset, littleEndian);
25758 for (i = 0; i < length; i += 1) {
25759 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
25762 if (tagType.ascii) {
25764 // Concatenate the chars:
25765 for (i = 0; i < values.length; i += 1) {
25767 // Ignore the terminating NULL byte(s):
25768 if (c === '\u0000') {
25780 Roo.apply(Roo.bootstrap.UploadCropbox, {
25782 'Orientation': 0x0112
25786 1: 0, //'top-left',
25788 3: 180, //'bottom-right',
25789 // 4: 'bottom-left',
25791 6: 90, //'right-top',
25792 // 7: 'right-bottom',
25793 8: 270 //'left-bottom'
25797 // byte, 8-bit unsigned int:
25799 getValue: function (dataView, dataOffset) {
25800 return dataView.getUint8(dataOffset);
25804 // ascii, 8-bit byte:
25806 getValue: function (dataView, dataOffset) {
25807 return String.fromCharCode(dataView.getUint8(dataOffset));
25812 // short, 16 bit int:
25814 getValue: function (dataView, dataOffset, littleEndian) {
25815 return dataView.getUint16(dataOffset, littleEndian);
25819 // long, 32 bit int:
25821 getValue: function (dataView, dataOffset, littleEndian) {
25822 return dataView.getUint32(dataOffset, littleEndian);
25826 // rational = two long values, first is numerator, second is denominator:
25828 getValue: function (dataView, dataOffset, littleEndian) {
25829 return dataView.getUint32(dataOffset, littleEndian) /
25830 dataView.getUint32(dataOffset + 4, littleEndian);
25834 // slong, 32 bit signed int:
25836 getValue: function (dataView, dataOffset, littleEndian) {
25837 return dataView.getInt32(dataOffset, littleEndian);
25841 // srational, two slongs, first is numerator, second is denominator:
25843 getValue: function (dataView, dataOffset, littleEndian) {
25844 return dataView.getInt32(dataOffset, littleEndian) /
25845 dataView.getInt32(dataOffset + 4, littleEndian);
25855 cls : 'btn-group roo-upload-cropbox-rotate-left',
25856 action : 'rotate-left',
25860 cls : 'btn btn-default',
25861 html : '<i class="fa fa-undo"></i>'
25867 cls : 'btn-group roo-upload-cropbox-picture',
25868 action : 'picture',
25872 cls : 'btn btn-default',
25873 html : '<i class="fa fa-picture-o"></i>'
25879 cls : 'btn-group roo-upload-cropbox-rotate-right',
25880 action : 'rotate-right',
25884 cls : 'btn btn-default',
25885 html : '<i class="fa fa-repeat"></i>'
25893 cls : 'btn-group roo-upload-cropbox-rotate-left',
25894 action : 'rotate-left',
25898 cls : 'btn btn-default',
25899 html : '<i class="fa fa-undo"></i>'
25905 cls : 'btn-group roo-upload-cropbox-download',
25906 action : 'download',
25910 cls : 'btn btn-default',
25911 html : '<i class="fa fa-download"></i>'
25917 cls : 'btn-group roo-upload-cropbox-crop',
25922 cls : 'btn btn-default',
25923 html : '<i class="fa fa-crop"></i>'
25929 cls : 'btn-group roo-upload-cropbox-trash',
25934 cls : 'btn btn-default',
25935 html : '<i class="fa fa-trash"></i>'
25941 cls : 'btn-group roo-upload-cropbox-rotate-right',
25942 action : 'rotate-right',
25946 cls : 'btn btn-default',
25947 html : '<i class="fa fa-repeat"></i>'
25955 cls : 'btn-group roo-upload-cropbox-rotate-left',
25956 action : 'rotate-left',
25960 cls : 'btn btn-default',
25961 html : '<i class="fa fa-undo"></i>'
25967 cls : 'btn-group roo-upload-cropbox-rotate-right',
25968 action : 'rotate-right',
25972 cls : 'btn btn-default',
25973 html : '<i class="fa fa-repeat"></i>'
25986 * @class Roo.bootstrap.DocumentManager
25987 * @extends Roo.bootstrap.Component
25988 * Bootstrap DocumentManager class
25989 * @cfg {String} paramName default 'imageUpload'
25990 * @cfg {String} method default POST
25991 * @cfg {String} url action url
25992 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
25993 * @cfg {Boolean} multiple multiple upload default true
25994 * @cfg {Number} thumbSize default 300
25995 * @cfg {String} fieldLabel
25996 * @cfg {Number} labelWidth default 4
25997 * @cfg {String} labelAlign (left|top) default left
25998 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26001 * Create a new DocumentManager
26002 * @param {Object} config The config object
26005 Roo.bootstrap.DocumentManager = function(config){
26006 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26011 * Fire when initial the DocumentManager
26012 * @param {Roo.bootstrap.DocumentManager} this
26017 * inspect selected file
26018 * @param {Roo.bootstrap.DocumentManager} this
26019 * @param {File} file
26024 * Fire when xhr load exception
26025 * @param {Roo.bootstrap.DocumentManager} this
26026 * @param {XMLHttpRequest} xhr
26028 "exception" : true,
26031 * prepare the form data
26032 * @param {Roo.bootstrap.DocumentManager} this
26033 * @param {Object} formData
26038 * Fire when remove the file
26039 * @param {Roo.bootstrap.DocumentManager} this
26040 * @param {Object} file
26045 * Fire after refresh the file
26046 * @param {Roo.bootstrap.DocumentManager} this
26051 * Fire after click the image
26052 * @param {Roo.bootstrap.DocumentManager} this
26053 * @param {Object} file
26058 * Fire when upload a image and editable set to true
26059 * @param {Roo.bootstrap.DocumentManager} this
26060 * @param {Object} file
26064 * @event beforeselectfile
26065 * Fire before select file
26066 * @param {Roo.bootstrap.DocumentManager} this
26068 "beforeselectfile" : true,
26071 * Fire before process file
26072 * @param {Roo.bootstrap.DocumentManager} this
26073 * @param {Object} file
26080 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
26089 paramName : 'imageUpload',
26092 labelAlign : 'left',
26099 getAutoCreate : function()
26101 var managerWidget = {
26103 cls : 'roo-document-manager',
26107 cls : 'roo-document-manager-selector',
26112 cls : 'roo-document-manager-uploader',
26116 cls : 'roo-document-manager-upload-btn',
26117 html : '<i class="fa fa-plus"></i>'
26128 cls : 'column col-md-12',
26133 if(this.fieldLabel.length){
26138 cls : 'column col-md-12',
26139 html : this.fieldLabel
26143 cls : 'column col-md-12',
26148 if(this.labelAlign == 'left'){
26152 cls : 'column col-md-' + this.labelWidth,
26153 html : this.fieldLabel
26157 cls : 'column col-md-' + (12 - this.labelWidth),
26167 cls : 'row clearfix',
26175 initEvents : function()
26177 this.managerEl = this.el.select('.roo-document-manager', true).first();
26178 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26180 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26181 this.selectorEl.hide();
26184 this.selectorEl.attr('multiple', 'multiple');
26187 this.selectorEl.on('change', this.onFileSelected, this);
26189 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26190 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26192 this.uploader.on('click', this.onUploaderClick, this);
26194 this.renderProgressDialog();
26198 window.addEventListener("resize", function() { _this.refresh(); } );
26200 this.fireEvent('initial', this);
26203 renderProgressDialog : function()
26207 this.progressDialog = new Roo.bootstrap.Modal({
26208 cls : 'roo-document-manager-progress-dialog',
26209 allow_close : false,
26219 btnclick : function() {
26220 _this.uploadCancel();
26226 this.progressDialog.render(Roo.get(document.body));
26228 this.progress = new Roo.bootstrap.Progress({
26229 cls : 'roo-document-manager-progress',
26234 this.progress.render(this.progressDialog.getChildContainer());
26236 this.progressBar = new Roo.bootstrap.ProgressBar({
26237 cls : 'roo-document-manager-progress-bar',
26240 aria_valuemax : 12,
26244 this.progressBar.render(this.progress.getChildContainer());
26247 onUploaderClick : function(e)
26249 e.preventDefault();
26251 if(this.fireEvent('beforeselectfile', this) != false){
26252 this.selectorEl.dom.click();
26257 onFileSelected : function(e)
26259 e.preventDefault();
26261 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26265 Roo.each(this.selectorEl.dom.files, function(file){
26266 if(this.fireEvent('inspect', this, file) != false){
26267 this.files.push(file);
26277 this.selectorEl.dom.value = '';
26279 if(!this.files.length){
26283 if(this.boxes > 0 && this.files.length > this.boxes){
26284 this.files = this.files.slice(0, this.boxes);
26287 this.uploader.show();
26289 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26290 this.uploader.hide();
26299 Roo.each(this.files, function(file){
26301 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26302 var f = this.renderPreview(file);
26307 if(file.type.indexOf('image') != -1){
26308 this.delegates.push(
26310 _this.process(file);
26311 }).createDelegate(this)
26319 _this.process(file);
26320 }).createDelegate(this)
26325 this.files = files;
26327 this.delegates = this.delegates.concat(docs);
26329 if(!this.delegates.length){
26334 this.progressBar.aria_valuemax = this.delegates.length;
26341 arrange : function()
26343 if(!this.delegates.length){
26344 this.progressDialog.hide();
26349 var delegate = this.delegates.shift();
26351 this.progressDialog.show();
26353 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26355 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26360 refresh : function()
26362 this.uploader.show();
26364 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26365 this.uploader.hide();
26368 Roo.isTouch ? this.closable(false) : this.closable(true);
26370 this.fireEvent('refresh', this);
26373 onRemove : function(e, el, o)
26375 e.preventDefault();
26377 this.fireEvent('remove', this, o);
26381 remove : function(o)
26385 Roo.each(this.files, function(file){
26386 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26395 this.files = files;
26402 Roo.each(this.files, function(file){
26407 file.target.remove();
26416 onClick : function(e, el, o)
26418 e.preventDefault();
26420 this.fireEvent('click', this, o);
26424 closable : function(closable)
26426 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26428 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26440 xhrOnLoad : function(xhr)
26442 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26446 if (xhr.readyState !== 4) {
26448 this.fireEvent('exception', this, xhr);
26452 var response = Roo.decode(xhr.responseText);
26454 if(!response.success){
26456 this.fireEvent('exception', this, xhr);
26460 var file = this.renderPreview(response.data);
26462 this.files.push(file);
26468 xhrOnError : function()
26470 Roo.log('xhr on error');
26472 var response = Roo.decode(xhr.responseText);
26479 process : function(file)
26481 if(this.fireEvent('process', this, file) !== false){
26482 if(this.editable && file.type.indexOf('image') != -1){
26483 this.fireEvent('edit', this, file);
26487 this.uploadStart(file, false);
26494 uploadStart : function(file, crop)
26496 this.xhr = new XMLHttpRequest();
26498 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26503 file.xhr = this.xhr;
26505 this.managerEl.createChild({
26507 cls : 'roo-document-manager-loading',
26511 tooltip : file.name,
26512 cls : 'roo-document-manager-thumb',
26513 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26519 this.xhr.open(this.method, this.url, true);
26522 "Accept": "application/json",
26523 "Cache-Control": "no-cache",
26524 "X-Requested-With": "XMLHttpRequest"
26527 for (var headerName in headers) {
26528 var headerValue = headers[headerName];
26530 this.xhr.setRequestHeader(headerName, headerValue);
26536 this.xhr.onload = function()
26538 _this.xhrOnLoad(_this.xhr);
26541 this.xhr.onerror = function()
26543 _this.xhrOnError(_this.xhr);
26546 var formData = new FormData();
26548 formData.append('returnHTML', 'NO');
26551 formData.append('crop', crop);
26554 formData.append(this.paramName, file, file.name);
26556 if(this.fireEvent('prepare', this, formData) != false){
26557 this.xhr.send(formData);
26561 uploadCancel : function()
26568 this.delegates = [];
26570 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26577 renderPreview : function(file)
26579 if(typeof(file.target) != 'undefined' && file.target){
26583 var previewEl = this.managerEl.createChild({
26585 cls : 'roo-document-manager-preview',
26589 tooltip : file.filename,
26590 cls : 'roo-document-manager-thumb',
26591 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26596 html : '<i class="fa fa-times-circle"></i>'
26601 var close = previewEl.select('button.close', true).first();
26603 close.on('click', this.onRemove, this, file);
26605 file.target = previewEl;
26607 var image = previewEl.select('img', true).first();
26611 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26613 image.on('click', this.onClick, this, file);
26619 onPreviewLoad : function(file, image)
26621 if(typeof(file.target) == 'undefined' || !file.target){
26625 var width = image.dom.naturalWidth || image.dom.width;
26626 var height = image.dom.naturalHeight || image.dom.height;
26628 if(width > height){
26629 file.target.addClass('wide');
26633 file.target.addClass('tall');
26638 uploadFromSource : function(file, crop)
26640 this.xhr = new XMLHttpRequest();
26642 this.managerEl.createChild({
26644 cls : 'roo-document-manager-loading',
26648 tooltip : file.name,
26649 cls : 'roo-document-manager-thumb',
26650 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26656 this.xhr.open(this.method, this.url, true);
26659 "Accept": "application/json",
26660 "Cache-Control": "no-cache",
26661 "X-Requested-With": "XMLHttpRequest"
26664 for (var headerName in headers) {
26665 var headerValue = headers[headerName];
26667 this.xhr.setRequestHeader(headerName, headerValue);
26673 this.xhr.onload = function()
26675 _this.xhrOnLoad(_this.xhr);
26678 this.xhr.onerror = function()
26680 _this.xhrOnError(_this.xhr);
26683 var formData = new FormData();
26685 formData.append('returnHTML', 'NO');
26687 formData.append('crop', crop);
26689 if(typeof(file.filename) != 'undefined'){
26690 formData.append('filename', file.filename);
26693 if(typeof(file.mimetype) != 'undefined'){
26694 formData.append('mimetype', file.mimetype);
26697 if(this.fireEvent('prepare', this, formData) != false){
26698 this.xhr.send(formData);
26708 * @class Roo.bootstrap.DocumentViewer
26709 * @extends Roo.bootstrap.Component
26710 * Bootstrap DocumentViewer class
26713 * Create a new DocumentViewer
26714 * @param {Object} config The config object
26717 Roo.bootstrap.DocumentViewer = function(config){
26718 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26723 * Fire after initEvent
26724 * @param {Roo.bootstrap.DocumentViewer} this
26730 * @param {Roo.bootstrap.DocumentViewer} this
26735 * Fire after trash button
26736 * @param {Roo.bootstrap.DocumentViewer} this
26743 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
26745 getAutoCreate : function()
26749 cls : 'roo-document-viewer',
26753 cls : 'roo-document-viewer-body',
26757 cls : 'roo-document-viewer-thumb',
26761 cls : 'roo-document-viewer-image'
26769 cls : 'roo-document-viewer-footer',
26772 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
26780 cls : 'btn btn-default roo-document-viewer-trash',
26781 html : '<i class="fa fa-trash"></i>'
26794 initEvents : function()
26797 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
26798 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26800 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
26801 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26803 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
26804 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26806 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
26807 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26809 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
26810 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26812 this.bodyEl.on('click', this.onClick, this);
26814 this.trashBtn.on('click', this.onTrash, this);
26818 initial : function()
26820 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
26823 this.fireEvent('initial', this);
26827 onClick : function(e)
26829 e.preventDefault();
26831 this.fireEvent('click', this);
26834 onTrash : function(e)
26836 e.preventDefault();
26838 this.fireEvent('trash', this);
26850 * @class Roo.bootstrap.NavProgressBar
26851 * @extends Roo.bootstrap.Component
26852 * Bootstrap NavProgressBar class
26855 * Create a new nav progress bar
26856 * @param {Object} config The config object
26859 Roo.bootstrap.NavProgressBar = function(config){
26860 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
26862 this.bullets = this.bullets || [];
26864 // Roo.bootstrap.NavProgressBar.register(this);
26868 * Fires when the active item changes
26869 * @param {Roo.bootstrap.NavProgressBar} this
26870 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
26871 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
26878 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
26883 getAutoCreate : function()
26885 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
26889 cls : 'roo-navigation-bar-group',
26893 cls : 'roo-navigation-top-bar'
26897 cls : 'roo-navigation-bullets-bar',
26901 cls : 'roo-navigation-bar'
26908 cls : 'roo-navigation-bottom-bar'
26918 initEvents: function()
26923 onRender : function(ct, position)
26925 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
26927 if(this.bullets.length){
26928 Roo.each(this.bullets, function(b){
26937 addItem : function(cfg)
26939 var item = new Roo.bootstrap.NavProgressItem(cfg);
26941 item.parentId = this.id;
26942 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
26945 var top = new Roo.bootstrap.Element({
26947 cls : 'roo-navigation-bar-text'
26950 var bottom = new Roo.bootstrap.Element({
26952 cls : 'roo-navigation-bar-text'
26955 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
26956 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
26958 var topText = new Roo.bootstrap.Element({
26960 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
26963 var bottomText = new Roo.bootstrap.Element({
26965 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
26968 topText.onRender(top.el, null);
26969 bottomText.onRender(bottom.el, null);
26972 item.bottomEl = bottom;
26975 this.barItems.push(item);
26980 getActive : function()
26982 var active = false;
26984 Roo.each(this.barItems, function(v){
26986 if (!v.isActive()) {
26998 setActiveItem : function(item)
27002 Roo.each(this.barItems, function(v){
27003 if (v.rid == item.rid) {
27007 if (v.isActive()) {
27008 v.setActive(false);
27013 item.setActive(true);
27015 this.fireEvent('changed', this, item, prev);
27018 getBarItem: function(rid)
27022 Roo.each(this.barItems, function(e) {
27023 if (e.rid != rid) {
27034 indexOfItem : function(item)
27038 Roo.each(this.barItems, function(v, i){
27040 if (v.rid != item.rid) {
27051 setActiveNext : function()
27053 var i = this.indexOfItem(this.getActive());
27055 if (i > this.barItems.length) {
27059 this.setActiveItem(this.barItems[i+1]);
27062 setActivePrev : function()
27064 var i = this.indexOfItem(this.getActive());
27070 this.setActiveItem(this.barItems[i-1]);
27073 format : function()
27075 if(!this.barItems.length){
27079 var width = 100 / this.barItems.length;
27081 Roo.each(this.barItems, function(i){
27082 i.el.setStyle('width', width + '%');
27083 i.topEl.el.setStyle('width', width + '%');
27084 i.bottomEl.el.setStyle('width', width + '%');
27093 * Nav Progress Item
27098 * @class Roo.bootstrap.NavProgressItem
27099 * @extends Roo.bootstrap.Component
27100 * Bootstrap NavProgressItem class
27101 * @cfg {String} rid the reference id
27102 * @cfg {Boolean} active (true|false) Is item active default false
27103 * @cfg {Boolean} disabled (true|false) Is item active default false
27104 * @cfg {String} html
27105 * @cfg {String} position (top|bottom) text position default bottom
27106 * @cfg {String} icon show icon instead of number
27109 * Create a new NavProgressItem
27110 * @param {Object} config The config object
27112 Roo.bootstrap.NavProgressItem = function(config){
27113 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27118 * The raw click event for the entire grid.
27119 * @param {Roo.bootstrap.NavProgressItem} this
27120 * @param {Roo.EventObject} e
27127 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
27133 position : 'bottom',
27136 getAutoCreate : function()
27138 var iconCls = 'roo-navigation-bar-item-icon';
27140 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27144 cls: 'roo-navigation-bar-item',
27154 cfg.cls += ' active';
27157 cfg.cls += ' disabled';
27163 disable : function()
27165 this.setDisabled(true);
27168 enable : function()
27170 this.setDisabled(false);
27173 initEvents: function()
27175 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27177 this.iconEl.on('click', this.onClick, this);
27180 onClick : function(e)
27182 e.preventDefault();
27188 if(this.fireEvent('click', this, e) === false){
27192 this.parent().setActiveItem(this);
27195 isActive: function ()
27197 return this.active;
27200 setActive : function(state)
27202 if(this.active == state){
27206 this.active = state;
27209 this.el.addClass('active');
27213 this.el.removeClass('active');
27218 setDisabled : function(state)
27220 if(this.disabled == state){
27224 this.disabled = state;
27227 this.el.addClass('disabled');
27231 this.el.removeClass('disabled');
27234 tooltipEl : function()
27236 return this.el.select('.roo-navigation-bar-item-icon', true).first();;