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(){
4085 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4087 if (this.disabled) {
4088 cfg.cls += ' disabled';
4091 if (this.href || this.html || this.glyphicon || this.icon) {
4095 href : this.href || "#",
4096 html: this.html || ''
4101 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4104 if(this.glyphicon) {
4105 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4110 cfg.cn[0].html += " <span class='caret'></span>";
4114 if (this.badge !== '') {
4116 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4124 initEvents: function()
4126 if (typeof (this.menu) != 'undefined') {
4127 this.menu.parentType = this.xtype;
4128 this.menu.triggerEl = this.el;
4129 this.menu = this.addxtype(Roo.apply({}, this.menu));
4132 this.el.select('a',true).on('click', this.onClick, this);
4134 if(this.tagtype == 'span'){
4135 this.el.select('span',true).on('click', this.onClick, this);
4138 // at this point parent should be available..
4139 this.parent().register(this);
4142 onClick : function(e)
4145 this.preventDefault ||
4152 if (this.disabled) {
4156 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4157 if (tg && tg.transition) {
4158 Roo.log("waiting for the transitionend");
4164 //Roo.log("fire event clicked");
4165 if(this.fireEvent('click', this, e) === false){
4169 if(this.tagtype == 'span'){
4173 //Roo.log(this.href);
4174 var ael = this.el.select('a',true).first();
4177 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4178 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4179 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4180 return; // ignore... - it's a 'hash' to another page.
4184 this.scrollToElement(e);
4188 var p = this.parent();
4190 if (['tabs','pills'].indexOf(p.type)!==-1) {
4191 if (typeof(p.setActiveItem) !== 'undefined') {
4192 p.setActiveItem(this);
4196 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4197 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4198 // remove the collapsed menu expand...
4199 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4203 isActive: function () {
4206 setActive : function(state, fire, is_was_active)
4208 if (this.active && !state && this.navId) {
4209 this.was_active = true;
4210 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4212 nv.clearWasActive(this);
4216 this.active = state;
4219 this.el.removeClass('active');
4220 } else if (!this.el.hasClass('active')) {
4221 this.el.addClass('active');
4224 this.fireEvent('changed', this, state);
4227 // show a panel if it's registered and related..
4229 if (!this.navId || !this.tabId || !state || is_was_active) {
4233 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4237 var pan = tg.getPanelByName(this.tabId);
4241 // if we can not flip to new panel - go back to old nav highlight..
4242 if (false == tg.showPanel(pan)) {
4243 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4245 var onav = nv.getWasActive();
4247 onav.setActive(true, false, true);
4256 // this should not be here...
4257 setDisabled : function(state)
4259 this.disabled = state;
4261 this.el.removeClass('disabled');
4262 } else if (!this.el.hasClass('disabled')) {
4263 this.el.addClass('disabled');
4269 * Fetch the element to display the tooltip on.
4270 * @return {Roo.Element} defaults to this.el
4272 tooltipEl : function()
4274 return this.el.select('' + this.tagtype + '', true).first();
4277 scrollToElement : function(e)
4279 var c = document.body;
4282 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4284 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4285 c = document.documentElement;
4288 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4294 var o = target.calcOffsetsTo(c);
4301 this.fireEvent('scrollto', this, options, e);
4303 Roo.get(c).scrollTo('top', options.value, true);
4316 * <span> icon </span>
4317 * <span> text </span>
4318 * <span>badge </span>
4322 * @class Roo.bootstrap.NavSidebarItem
4323 * @extends Roo.bootstrap.NavItem
4324 * Bootstrap Navbar.NavSidebarItem class
4325 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4327 * Create a new Navbar Button
4328 * @param {Object} config The config object
4330 Roo.bootstrap.NavSidebarItem = function(config){
4331 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4336 * The raw click event for the entire grid.
4337 * @param {Roo.EventObject} e
4342 * Fires when the active item active state changes
4343 * @param {Roo.bootstrap.NavSidebarItem} this
4344 * @param {boolean} state the new state
4352 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4354 badgeWeight : 'default',
4356 getAutoCreate : function(){
4361 href : this.href || '#',
4373 html : this.html || ''
4378 cfg.cls += ' active';
4381 if (this.disabled) {
4382 cfg.cls += ' disabled';
4386 if (this.glyphicon || this.icon) {
4387 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4388 a.cn.push({ tag : 'i', cls : c }) ;
4393 if (this.badge !== '') {
4395 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4399 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4400 a.cls += 'dropdown-toggle treeview' ;
4411 initEvents : function()
4413 this.el.on('click', this.onClick, this);
4416 if(this.badge !== ''){
4418 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4423 onClick : function(e)
4430 if(this.preventDefault){
4434 this.fireEvent('click', this);
4437 disable : function()
4439 this.setDisabled(true);
4444 this.setDisabled(false);
4447 setDisabled : function(state)
4449 if(this.disabled == state){
4453 this.disabled = state;
4456 this.el.addClass('disabled');
4460 this.el.removeClass('disabled');
4465 setActive : function(state)
4467 if(this.active == state){
4471 this.active = state;
4474 this.el.addClass('active');
4478 this.el.removeClass('active');
4483 isActive: function ()
4488 setBadge : function(str)
4494 this.badgeEl.dom.innerHTML = str;
4511 * @class Roo.bootstrap.Row
4512 * @extends Roo.bootstrap.Component
4513 * Bootstrap Row class (contains columns...)
4517 * @param {Object} config The config object
4520 Roo.bootstrap.Row = function(config){
4521 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4524 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4526 getAutoCreate : function(){
4545 * @class Roo.bootstrap.Element
4546 * @extends Roo.bootstrap.Component
4547 * Bootstrap Element class
4548 * @cfg {String} html contents of the element
4549 * @cfg {String} tag tag of the element
4550 * @cfg {String} cls class of the element
4551 * @cfg {Boolean} preventDefault (true|false) default false
4552 * @cfg {Boolean} clickable (true|false) default false
4555 * Create a new Element
4556 * @param {Object} config The config object
4559 Roo.bootstrap.Element = function(config){
4560 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4566 * When a element is chick
4567 * @param {Roo.bootstrap.Element} this
4568 * @param {Roo.EventObject} e
4574 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4579 preventDefault: false,
4582 getAutoCreate : function(){
4593 initEvents: function()
4595 Roo.bootstrap.Element.superclass.initEvents.call(this);
4598 this.el.on('click', this.onClick, this);
4603 onClick : function(e)
4605 if(this.preventDefault){
4609 this.fireEvent('click', this, e);
4612 getValue : function()
4614 return this.el.dom.innerHTML;
4617 setValue : function(value)
4619 this.el.dom.innerHTML = value;
4634 * @class Roo.bootstrap.Pagination
4635 * @extends Roo.bootstrap.Component
4636 * Bootstrap Pagination class
4637 * @cfg {String} size xs | sm | md | lg
4638 * @cfg {Boolean} inverse false | true
4641 * Create a new Pagination
4642 * @param {Object} config The config object
4645 Roo.bootstrap.Pagination = function(config){
4646 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4649 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4655 getAutoCreate : function(){
4661 cfg.cls += ' inverse';
4667 cfg.cls += " " + this.cls;
4685 * @class Roo.bootstrap.PaginationItem
4686 * @extends Roo.bootstrap.Component
4687 * Bootstrap PaginationItem class
4688 * @cfg {String} html text
4689 * @cfg {String} href the link
4690 * @cfg {Boolean} preventDefault (true | false) default true
4691 * @cfg {Boolean} active (true | false) default false
4692 * @cfg {Boolean} disabled default false
4696 * Create a new PaginationItem
4697 * @param {Object} config The config object
4701 Roo.bootstrap.PaginationItem = function(config){
4702 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4707 * The raw click event for the entire grid.
4708 * @param {Roo.EventObject} e
4714 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4718 preventDefault: true,
4723 getAutoCreate : function(){
4729 href : this.href ? this.href : '#',
4730 html : this.html ? this.html : ''
4740 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4744 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4750 initEvents: function() {
4752 this.el.on('click', this.onClick, this);
4755 onClick : function(e)
4757 Roo.log('PaginationItem on click ');
4758 if(this.preventDefault){
4766 this.fireEvent('click', this, e);
4782 * @class Roo.bootstrap.Slider
4783 * @extends Roo.bootstrap.Component
4784 * Bootstrap Slider class
4787 * Create a new Slider
4788 * @param {Object} config The config object
4791 Roo.bootstrap.Slider = function(config){
4792 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4795 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4797 getAutoCreate : function(){
4801 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4805 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4817 * Ext JS Library 1.1.1
4818 * Copyright(c) 2006-2007, Ext JS, LLC.
4820 * Originally Released Under LGPL - original licence link has changed is not relivant.
4823 * <script type="text/javascript">
4828 * @class Roo.grid.ColumnModel
4829 * @extends Roo.util.Observable
4830 * This is the default implementation of a ColumnModel used by the Grid. It defines
4831 * the columns in the grid.
4834 var colModel = new Roo.grid.ColumnModel([
4835 {header: "Ticker", width: 60, sortable: true, locked: true},
4836 {header: "Company Name", width: 150, sortable: true},
4837 {header: "Market Cap.", width: 100, sortable: true},
4838 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4839 {header: "Employees", width: 100, sortable: true, resizable: false}
4844 * The config options listed for this class are options which may appear in each
4845 * individual column definition.
4846 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4848 * @param {Object} config An Array of column config objects. See this class's
4849 * config objects for details.
4851 Roo.grid.ColumnModel = function(config){
4853 * The config passed into the constructor
4855 this.config = config;
4858 // if no id, create one
4859 // if the column does not have a dataIndex mapping,
4860 // map it to the order it is in the config
4861 for(var i = 0, len = config.length; i < len; i++){
4863 if(typeof c.dataIndex == "undefined"){
4866 if(typeof c.renderer == "string"){
4867 c.renderer = Roo.util.Format[c.renderer];
4869 if(typeof c.id == "undefined"){
4872 if(c.editor && c.editor.xtype){
4873 c.editor = Roo.factory(c.editor, Roo.grid);
4875 if(c.editor && c.editor.isFormField){
4876 c.editor = new Roo.grid.GridEditor(c.editor);
4878 this.lookup[c.id] = c;
4882 * The width of columns which have no width specified (defaults to 100)
4885 this.defaultWidth = 100;
4888 * Default sortable of columns which have no sortable specified (defaults to false)
4891 this.defaultSortable = false;
4895 * @event widthchange
4896 * Fires when the width of a column changes.
4897 * @param {ColumnModel} this
4898 * @param {Number} columnIndex The column index
4899 * @param {Number} newWidth The new width
4901 "widthchange": true,
4903 * @event headerchange
4904 * Fires when the text of a header changes.
4905 * @param {ColumnModel} this
4906 * @param {Number} columnIndex The column index
4907 * @param {Number} newText The new header text
4909 "headerchange": true,
4911 * @event hiddenchange
4912 * Fires when a column is hidden or "unhidden".
4913 * @param {ColumnModel} this
4914 * @param {Number} columnIndex The column index
4915 * @param {Boolean} hidden true if hidden, false otherwise
4917 "hiddenchange": true,
4919 * @event columnmoved
4920 * Fires when a column is moved.
4921 * @param {ColumnModel} this
4922 * @param {Number} oldIndex
4923 * @param {Number} newIndex
4925 "columnmoved" : true,
4927 * @event columlockchange
4928 * Fires when a column's locked state is changed
4929 * @param {ColumnModel} this
4930 * @param {Number} colIndex
4931 * @param {Boolean} locked true if locked
4933 "columnlockchange" : true
4935 Roo.grid.ColumnModel.superclass.constructor.call(this);
4937 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4939 * @cfg {String} header The header text to display in the Grid view.
4942 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4943 * {@link Roo.data.Record} definition from which to draw the column's value. If not
4944 * specified, the column's index is used as an index into the Record's data Array.
4947 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4948 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4951 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4952 * Defaults to the value of the {@link #defaultSortable} property.
4953 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4956 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
4959 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
4962 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4965 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4968 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4969 * given the cell's data value. See {@link #setRenderer}. If not specified, the
4970 * default renderer uses the raw data value. If an object is returned (bootstrap only)
4971 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4974 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
4977 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
4980 * @cfg {String} cursor (Optional)
4983 * @cfg {String} tooltip (Optional)
4986 * @cfg {Number} xs (Optional)
4989 * @cfg {Number} sm (Optional)
4992 * @cfg {Number} md (Optional)
4995 * @cfg {Number} lg (Optional)
4998 * Returns the id of the column at the specified index.
4999 * @param {Number} index The column index
5000 * @return {String} the id
5002 getColumnId : function(index){
5003 return this.config[index].id;
5007 * Returns the column for a specified id.
5008 * @param {String} id The column id
5009 * @return {Object} the column
5011 getColumnById : function(id){
5012 return this.lookup[id];
5017 * Returns the column for a specified dataIndex.
5018 * @param {String} dataIndex The column dataIndex
5019 * @return {Object|Boolean} the column or false if not found
5021 getColumnByDataIndex: function(dataIndex){
5022 var index = this.findColumnIndex(dataIndex);
5023 return index > -1 ? this.config[index] : false;
5027 * Returns the index for a specified column id.
5028 * @param {String} id The column id
5029 * @return {Number} the index, or -1 if not found
5031 getIndexById : function(id){
5032 for(var i = 0, len = this.config.length; i < len; i++){
5033 if(this.config[i].id == id){
5041 * Returns the index for a specified column dataIndex.
5042 * @param {String} dataIndex The column dataIndex
5043 * @return {Number} the index, or -1 if not found
5046 findColumnIndex : function(dataIndex){
5047 for(var i = 0, len = this.config.length; i < len; i++){
5048 if(this.config[i].dataIndex == dataIndex){
5056 moveColumn : function(oldIndex, newIndex){
5057 var c = this.config[oldIndex];
5058 this.config.splice(oldIndex, 1);
5059 this.config.splice(newIndex, 0, c);
5060 this.dataMap = null;
5061 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5064 isLocked : function(colIndex){
5065 return this.config[colIndex].locked === true;
5068 setLocked : function(colIndex, value, suppressEvent){
5069 if(this.isLocked(colIndex) == value){
5072 this.config[colIndex].locked = value;
5074 this.fireEvent("columnlockchange", this, colIndex, value);
5078 getTotalLockedWidth : function(){
5080 for(var i = 0; i < this.config.length; i++){
5081 if(this.isLocked(i) && !this.isHidden(i)){
5082 this.totalWidth += this.getColumnWidth(i);
5088 getLockedCount : function(){
5089 for(var i = 0, len = this.config.length; i < len; i++){
5090 if(!this.isLocked(i)){
5097 * Returns the number of columns.
5100 getColumnCount : function(visibleOnly){
5101 if(visibleOnly === true){
5103 for(var i = 0, len = this.config.length; i < len; i++){
5104 if(!this.isHidden(i)){
5110 return this.config.length;
5114 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5115 * @param {Function} fn
5116 * @param {Object} scope (optional)
5117 * @return {Array} result
5119 getColumnsBy : function(fn, scope){
5121 for(var i = 0, len = this.config.length; i < len; i++){
5122 var c = this.config[i];
5123 if(fn.call(scope||this, c, i) === true){
5131 * Returns true if the specified column is sortable.
5132 * @param {Number} col The column index
5135 isSortable : function(col){
5136 if(typeof this.config[col].sortable == "undefined"){
5137 return this.defaultSortable;
5139 return this.config[col].sortable;
5143 * Returns the rendering (formatting) function defined for the column.
5144 * @param {Number} col The column index.
5145 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5147 getRenderer : function(col){
5148 if(!this.config[col].renderer){
5149 return Roo.grid.ColumnModel.defaultRenderer;
5151 return this.config[col].renderer;
5155 * Sets the rendering (formatting) function for a column.
5156 * @param {Number} col The column index
5157 * @param {Function} fn The function to use to process the cell's raw data
5158 * to return HTML markup for the grid view. The render function is called with
5159 * the following parameters:<ul>
5160 * <li>Data value.</li>
5161 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5162 * <li>css A CSS style string to apply to the table cell.</li>
5163 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5164 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5165 * <li>Row index</li>
5166 * <li>Column index</li>
5167 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5169 setRenderer : function(col, fn){
5170 this.config[col].renderer = fn;
5174 * Returns the width for the specified column.
5175 * @param {Number} col The column index
5178 getColumnWidth : function(col){
5179 return this.config[col].width * 1 || this.defaultWidth;
5183 * Sets the width for a column.
5184 * @param {Number} col The column index
5185 * @param {Number} width The new width
5187 setColumnWidth : function(col, width, suppressEvent){
5188 this.config[col].width = width;
5189 this.totalWidth = null;
5191 this.fireEvent("widthchange", this, col, width);
5196 * Returns the total width of all columns.
5197 * @param {Boolean} includeHidden True to include hidden column widths
5200 getTotalWidth : function(includeHidden){
5201 if(!this.totalWidth){
5202 this.totalWidth = 0;
5203 for(var i = 0, len = this.config.length; i < len; i++){
5204 if(includeHidden || !this.isHidden(i)){
5205 this.totalWidth += this.getColumnWidth(i);
5209 return this.totalWidth;
5213 * Returns the header for the specified column.
5214 * @param {Number} col The column index
5217 getColumnHeader : function(col){
5218 return this.config[col].header;
5222 * Sets the header for a column.
5223 * @param {Number} col The column index
5224 * @param {String} header The new header
5226 setColumnHeader : function(col, header){
5227 this.config[col].header = header;
5228 this.fireEvent("headerchange", this, col, header);
5232 * Returns the tooltip for the specified column.
5233 * @param {Number} col The column index
5236 getColumnTooltip : function(col){
5237 return this.config[col].tooltip;
5240 * Sets the tooltip for a column.
5241 * @param {Number} col The column index
5242 * @param {String} tooltip The new tooltip
5244 setColumnTooltip : function(col, tooltip){
5245 this.config[col].tooltip = tooltip;
5249 * Returns the dataIndex for the specified column.
5250 * @param {Number} col The column index
5253 getDataIndex : function(col){
5254 return this.config[col].dataIndex;
5258 * Sets the dataIndex for a column.
5259 * @param {Number} col The column index
5260 * @param {Number} dataIndex The new dataIndex
5262 setDataIndex : function(col, dataIndex){
5263 this.config[col].dataIndex = dataIndex;
5269 * Returns true if the cell is editable.
5270 * @param {Number} colIndex The column index
5271 * @param {Number} rowIndex The row index
5274 isCellEditable : function(colIndex, rowIndex){
5275 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5279 * Returns the editor defined for the cell/column.
5280 * return false or null to disable editing.
5281 * @param {Number} colIndex The column index
5282 * @param {Number} rowIndex The row index
5285 getCellEditor : function(colIndex, rowIndex){
5286 return this.config[colIndex].editor;
5290 * Sets if a column is editable.
5291 * @param {Number} col The column index
5292 * @param {Boolean} editable True if the column is editable
5294 setEditable : function(col, editable){
5295 this.config[col].editable = editable;
5300 * Returns true if the column is hidden.
5301 * @param {Number} colIndex The column index
5304 isHidden : function(colIndex){
5305 return this.config[colIndex].hidden;
5310 * Returns true if the column width cannot be changed
5312 isFixed : function(colIndex){
5313 return this.config[colIndex].fixed;
5317 * Returns true if the column can be resized
5320 isResizable : function(colIndex){
5321 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5324 * Sets if a column is hidden.
5325 * @param {Number} colIndex The column index
5326 * @param {Boolean} hidden True if the column is hidden
5328 setHidden : function(colIndex, hidden){
5329 this.config[colIndex].hidden = hidden;
5330 this.totalWidth = null;
5331 this.fireEvent("hiddenchange", this, colIndex, hidden);
5335 * Sets the editor for a column.
5336 * @param {Number} col The column index
5337 * @param {Object} editor The editor object
5339 setEditor : function(col, editor){
5340 this.config[col].editor = editor;
5344 Roo.grid.ColumnModel.defaultRenderer = function(value){
5345 if(typeof value == "string" && value.length < 1){
5351 // Alias for backwards compatibility
5352 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5355 * Ext JS Library 1.1.1
5356 * Copyright(c) 2006-2007, Ext JS, LLC.
5358 * Originally Released Under LGPL - original licence link has changed is not relivant.
5361 * <script type="text/javascript">
5365 * @class Roo.LoadMask
5366 * A simple utility class for generically masking elements while loading data. If the element being masked has
5367 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5368 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5369 * element's UpdateManager load indicator and will be destroyed after the initial load.
5371 * Create a new LoadMask
5372 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5373 * @param {Object} config The config object
5375 Roo.LoadMask = function(el, config){
5376 this.el = Roo.get(el);
5377 Roo.apply(this, config);
5379 this.store.on('beforeload', this.onBeforeLoad, this);
5380 this.store.on('load', this.onLoad, this);
5381 this.store.on('loadexception', this.onLoadException, this);
5382 this.removeMask = false;
5384 var um = this.el.getUpdateManager();
5385 um.showLoadIndicator = false; // disable the default indicator
5386 um.on('beforeupdate', this.onBeforeLoad, this);
5387 um.on('update', this.onLoad, this);
5388 um.on('failure', this.onLoad, this);
5389 this.removeMask = true;
5393 Roo.LoadMask.prototype = {
5395 * @cfg {Boolean} removeMask
5396 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5397 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5401 * The text to display in a centered loading message box (defaults to 'Loading...')
5405 * @cfg {String} msgCls
5406 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5408 msgCls : 'x-mask-loading',
5411 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5417 * Disables the mask to prevent it from being displayed
5419 disable : function(){
5420 this.disabled = true;
5424 * Enables the mask so that it can be displayed
5426 enable : function(){
5427 this.disabled = false;
5430 onLoadException : function()
5434 if (typeof(arguments[3]) != 'undefined') {
5435 Roo.MessageBox.alert("Error loading",arguments[3]);
5439 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5440 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5449 this.el.unmask(this.removeMask);
5454 this.el.unmask(this.removeMask);
5458 onBeforeLoad : function(){
5460 this.el.mask(this.msg, this.msgCls);
5465 destroy : function(){
5467 this.store.un('beforeload', this.onBeforeLoad, this);
5468 this.store.un('load', this.onLoad, this);
5469 this.store.un('loadexception', this.onLoadException, this);
5471 var um = this.el.getUpdateManager();
5472 um.un('beforeupdate', this.onBeforeLoad, this);
5473 um.un('update', this.onLoad, this);
5474 um.un('failure', this.onLoad, this);
5485 * @class Roo.bootstrap.Table
5486 * @extends Roo.bootstrap.Component
5487 * Bootstrap Table class
5488 * @cfg {String} cls table class
5489 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5490 * @cfg {String} bgcolor Specifies the background color for a table
5491 * @cfg {Number} border Specifies whether the table cells should have borders or not
5492 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5493 * @cfg {Number} cellspacing Specifies the space between cells
5494 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5495 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5496 * @cfg {String} sortable Specifies that the table should be sortable
5497 * @cfg {String} summary Specifies a summary of the content of a table
5498 * @cfg {Number} width Specifies the width of a table
5499 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5501 * @cfg {boolean} striped Should the rows be alternative striped
5502 * @cfg {boolean} bordered Add borders to the table
5503 * @cfg {boolean} hover Add hover highlighting
5504 * @cfg {boolean} condensed Format condensed
5505 * @cfg {boolean} responsive Format condensed
5506 * @cfg {Boolean} loadMask (true|false) default false
5507 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5508 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5509 * @cfg {Boolean} rowSelection (true|false) default false
5510 * @cfg {Boolean} cellSelection (true|false) default false
5511 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5515 * Create a new Table
5516 * @param {Object} config The config object
5519 Roo.bootstrap.Table = function(config){
5520 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5523 this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5524 this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5525 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5526 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5530 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5531 this.sm = this.selModel;
5532 this.sm.xmodule = this.xmodule || false;
5534 if (this.cm && typeof(this.cm.config) == 'undefined') {
5535 this.colModel = new Roo.grid.ColumnModel(this.cm);
5536 this.cm = this.colModel;
5537 this.cm.xmodule = this.xmodule || false;
5540 this.store= Roo.factory(this.store, Roo.data);
5541 this.ds = this.store;
5542 this.ds.xmodule = this.xmodule || false;
5545 if (this.footer && this.store) {
5546 this.footer.dataSource = this.ds;
5547 this.footer = Roo.factory(this.footer);
5554 * Fires when a cell is clicked
5555 * @param {Roo.bootstrap.Table} this
5556 * @param {Roo.Element} el
5557 * @param {Number} rowIndex
5558 * @param {Number} columnIndex
5559 * @param {Roo.EventObject} e
5563 * @event celldblclick
5564 * Fires when a cell is double clicked
5565 * @param {Roo.bootstrap.Table} this
5566 * @param {Roo.Element} el
5567 * @param {Number} rowIndex
5568 * @param {Number} columnIndex
5569 * @param {Roo.EventObject} e
5571 "celldblclick" : true,
5574 * Fires when a row is clicked
5575 * @param {Roo.bootstrap.Table} this
5576 * @param {Roo.Element} el
5577 * @param {Number} rowIndex
5578 * @param {Roo.EventObject} e
5582 * @event rowdblclick
5583 * Fires when a row is double clicked
5584 * @param {Roo.bootstrap.Table} this
5585 * @param {Roo.Element} el
5586 * @param {Number} rowIndex
5587 * @param {Roo.EventObject} e
5589 "rowdblclick" : true,
5592 * Fires when a mouseover occur
5593 * @param {Roo.bootstrap.Table} this
5594 * @param {Roo.Element} el
5595 * @param {Number} rowIndex
5596 * @param {Number} columnIndex
5597 * @param {Roo.EventObject} e
5602 * Fires when a mouseout occur
5603 * @param {Roo.bootstrap.Table} this
5604 * @param {Roo.Element} el
5605 * @param {Number} rowIndex
5606 * @param {Number} columnIndex
5607 * @param {Roo.EventObject} e
5612 * Fires when a row is rendered, so you can change add a style to it.
5613 * @param {Roo.bootstrap.Table} this
5614 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5618 * @event rowsrendered
5619 * Fires when all the rows have been rendered
5620 * @param {Roo.bootstrap.Table} this
5622 'rowsrendered' : true
5627 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5652 rowSelection : false,
5653 cellSelection : false,
5656 // Roo.Element - the tbody
5659 getAutoCreate : function(){
5660 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5669 cfg.cls += ' table-striped';
5673 cfg.cls += ' table-hover';
5675 if (this.bordered) {
5676 cfg.cls += ' table-bordered';
5678 if (this.condensed) {
5679 cfg.cls += ' table-condensed';
5681 if (this.responsive) {
5682 cfg.cls += ' table-responsive';
5686 cfg.cls+= ' ' +this.cls;
5689 // this lot should be simplifed...
5692 cfg.align=this.align;
5695 cfg.bgcolor=this.bgcolor;
5698 cfg.border=this.border;
5700 if (this.cellpadding) {
5701 cfg.cellpadding=this.cellpadding;
5703 if (this.cellspacing) {
5704 cfg.cellspacing=this.cellspacing;
5707 cfg.frame=this.frame;
5710 cfg.rules=this.rules;
5712 if (this.sortable) {
5713 cfg.sortable=this.sortable;
5716 cfg.summary=this.summary;
5719 cfg.width=this.width;
5722 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5725 if(this.store || this.cm){
5726 if(this.headerShow){
5727 cfg.cn.push(this.renderHeader());
5730 cfg.cn.push(this.renderBody());
5732 if(this.footerShow){
5733 cfg.cn.push(this.renderFooter());
5736 cfg.cls+= ' TableGrid';
5739 return { cn : [ cfg ] };
5742 initEvents : function()
5744 if(!this.store || !this.cm){
5748 //Roo.log('initEvents with ds!!!!');
5750 this.mainBody = this.el.select('tbody', true).first();
5755 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5756 e.on('click', _this.sort, _this);
5759 this.el.on("click", this.onClick, this);
5760 this.el.on("dblclick", this.onDblClick, this);
5762 // why is this done????? = it breaks dialogs??
5763 //this.parent().el.setStyle('position', 'relative');
5767 this.footer.parentId = this.id;
5768 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5771 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5773 this.store.on('load', this.onLoad, this);
5774 this.store.on('beforeload', this.onBeforeLoad, this);
5775 this.store.on('update', this.onUpdate, this);
5776 this.store.on('add', this.onAdd, this);
5780 onMouseover : function(e, el)
5782 var cell = Roo.get(el);
5788 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5789 cell = cell.findParent('td', false, true);
5792 var row = cell.findParent('tr', false, true);
5793 var cellIndex = cell.dom.cellIndex;
5794 var rowIndex = row.dom.rowIndex - 1; // start from 0
5796 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5800 onMouseout : function(e, el)
5802 var cell = Roo.get(el);
5808 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5809 cell = cell.findParent('td', false, true);
5812 var row = cell.findParent('tr', false, true);
5813 var cellIndex = cell.dom.cellIndex;
5814 var rowIndex = row.dom.rowIndex - 1; // start from 0
5816 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5820 onClick : function(e, el)
5822 var cell = Roo.get(el);
5824 if(!cell || (!this.cellSelection && !this.rowSelection)){
5828 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5829 cell = cell.findParent('td', false, true);
5832 if(!cell || typeof(cell) == 'undefined'){
5836 var row = cell.findParent('tr', false, true);
5838 if(!row || typeof(row) == 'undefined'){
5842 var cellIndex = cell.dom.cellIndex;
5843 var rowIndex = this.getRowIndex(row);
5845 // why??? - should these not be based on SelectionModel?
5846 if(this.cellSelection){
5847 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5850 if(this.rowSelection){
5851 this.fireEvent('rowclick', this, row, rowIndex, e);
5857 onDblClick : function(e,el)
5859 var cell = Roo.get(el);
5861 if(!cell || (!this.CellSelection && !this.RowSelection)){
5865 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5866 cell = cell.findParent('td', false, true);
5869 if(!cell || typeof(cell) == 'undefined'){
5873 var row = cell.findParent('tr', false, true);
5875 if(!row || typeof(row) == 'undefined'){
5879 var cellIndex = cell.dom.cellIndex;
5880 var rowIndex = this.getRowIndex(row);
5882 if(this.CellSelection){
5883 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5886 if(this.RowSelection){
5887 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5891 sort : function(e,el)
5893 var col = Roo.get(el);
5895 if(!col.hasClass('sortable')){
5899 var sort = col.attr('sort');
5902 if(col.hasClass('glyphicon-arrow-up')){
5906 this.store.sortInfo = {field : sort, direction : dir};
5909 Roo.log("calling footer first");
5910 this.footer.onClick('first');
5913 this.store.load({ params : { start : 0 } });
5917 renderHeader : function()
5926 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5928 var config = cm.config[i];
5933 html: cm.getColumnHeader(i)
5938 if(typeof(config.lgHeader) != 'undefined'){
5939 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5942 if(typeof(config.mdHeader) != 'undefined'){
5943 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5946 if(typeof(config.smHeader) != 'undefined'){
5947 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5950 if(typeof(config.xsHeader) != 'undefined'){
5951 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5958 if(typeof(config.tooltip) != 'undefined'){
5959 c.tooltip = config.tooltip;
5962 if(typeof(config.colspan) != 'undefined'){
5963 c.colspan = config.colspan;
5966 if(typeof(config.hidden) != 'undefined' && config.hidden){
5967 c.style += ' display:none;';
5970 if(typeof(config.dataIndex) != 'undefined'){
5971 c.sort = config.dataIndex;
5974 if(typeof(config.sortable) != 'undefined' && config.sortable){
5978 if(typeof(config.align) != 'undefined' && config.align.length){
5979 c.style += ' text-align:' + config.align + ';';
5982 if(typeof(config.width) != 'undefined'){
5983 c.style += ' width:' + config.width + 'px;';
5986 if(typeof(config.cls) != 'undefined'){
5987 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5990 ['xs','sm','md','lg'].map(function(size){
5992 if(typeof(config[size]) == 'undefined'){
5996 if (!config[size]) { // 0 = hidden
5997 cfg.cls += ' hidden-' + size;
6001 cfg.cls += ' col-' + size + '-' + config[size];
6011 renderBody : function()
6021 colspan : this.cm.getColumnCount()
6031 renderFooter : function()
6041 colspan : this.cm.getColumnCount()
6055 Roo.log('ds onload');
6060 var ds = this.store;
6062 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6063 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6065 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6066 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6069 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6070 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6074 var tbody = this.mainBody;
6076 if(ds.getCount() > 0){
6077 ds.data.each(function(d,rowIndex){
6078 var row = this.renderRow(cm, ds, rowIndex);
6080 tbody.createChild(row);
6084 if(row.cellObjects.length){
6085 Roo.each(row.cellObjects, function(r){
6086 _this.renderCellObject(r);
6093 Roo.each(this.el.select('tbody td', true).elements, function(e){
6094 e.on('mouseover', _this.onMouseover, _this);
6097 Roo.each(this.el.select('tbody td', true).elements, function(e){
6098 e.on('mouseout', _this.onMouseout, _this);
6100 this.fireEvent('rowsrendered', this);
6101 //if(this.loadMask){
6102 // this.maskEl.hide();
6107 onUpdate : function(ds,record)
6109 this.refreshRow(record);
6112 onRemove : function(ds, record, index, isUpdate){
6113 if(isUpdate !== true){
6114 this.fireEvent("beforerowremoved", this, index, record);
6116 var bt = this.mainBody.dom;
6118 var rows = this.el.select('tbody > tr', true).elements;
6120 if(typeof(rows[index]) != 'undefined'){
6121 bt.removeChild(rows[index].dom);
6124 // if(bt.rows[index]){
6125 // bt.removeChild(bt.rows[index]);
6128 if(isUpdate !== true){
6129 //this.stripeRows(index);
6130 //this.syncRowHeights(index, index);
6132 this.fireEvent("rowremoved", this, index, record);
6136 onAdd : function(ds, records, rowIndex)
6138 //Roo.log('on Add called');
6139 // - note this does not handle multiple adding very well..
6140 var bt = this.mainBody.dom;
6141 for (var i =0 ; i < records.length;i++) {
6142 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6143 //Roo.log(records[i]);
6144 //Roo.log(this.store.getAt(rowIndex+i));
6145 this.insertRow(this.store, rowIndex + i, false);
6152 refreshRow : function(record){
6153 var ds = this.store, index;
6154 if(typeof record == 'number'){
6156 record = ds.getAt(index);
6158 index = ds.indexOf(record);
6160 this.insertRow(ds, index, true);
6161 this.onRemove(ds, record, index+1, true);
6162 //this.syncRowHeights(index, index);
6164 this.fireEvent("rowupdated", this, index, record);
6167 insertRow : function(dm, rowIndex, isUpdate){
6170 this.fireEvent("beforerowsinserted", this, rowIndex);
6172 //var s = this.getScrollState();
6173 var row = this.renderRow(this.cm, this.store, rowIndex);
6174 // insert before rowIndex..
6175 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6179 if(row.cellObjects.length){
6180 Roo.each(row.cellObjects, function(r){
6181 _this.renderCellObject(r);
6186 this.fireEvent("rowsinserted", this, rowIndex);
6187 //this.syncRowHeights(firstRow, lastRow);
6188 //this.stripeRows(firstRow);
6195 getRowDom : function(rowIndex)
6197 var rows = this.el.select('tbody > tr', true).elements;
6199 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6202 // returns the object tree for a tr..
6205 renderRow : function(cm, ds, rowIndex)
6208 var d = ds.getAt(rowIndex);
6215 var cellObjects = [];
6217 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6218 var config = cm.config[i];
6220 var renderer = cm.getRenderer(i);
6224 if(typeof(renderer) !== 'undefined'){
6225 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6227 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6228 // and are rendered into the cells after the row is rendered - using the id for the element.
6230 if(typeof(value) === 'object'){
6240 rowIndex : rowIndex,
6245 this.fireEvent('rowclass', this, rowcfg);
6249 cls : rowcfg.rowClass,
6251 html: (typeof(value) === 'object') ? '' : value
6258 if(typeof(config.colspan) != 'undefined'){
6259 td.colspan = config.colspan;
6262 if(typeof(config.hidden) != 'undefined' && config.hidden){
6263 td.style += ' display:none;';
6266 if(typeof(config.align) != 'undefined' && config.align.length){
6267 td.style += ' text-align:' + config.align + ';';
6270 if(typeof(config.width) != 'undefined'){
6271 td.style += ' width:' + config.width + 'px;';
6274 if(typeof(config.cursor) != 'undefined'){
6275 td.style += ' cursor:' + config.cursor + ';';
6278 if(typeof(config.cls) != 'undefined'){
6279 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6282 ['xs','sm','md','lg'].map(function(size){
6284 if(typeof(config[size]) == 'undefined'){
6288 if (!config[size]) { // 0 = hidden
6289 td.cls += ' hidden-' + size;
6293 td.cls += ' col-' + size + '-' + config[size];
6301 row.cellObjects = cellObjects;
6309 onBeforeLoad : function()
6311 //Roo.log('ds onBeforeLoad');
6315 //if(this.loadMask){
6316 // this.maskEl.show();
6324 this.el.select('tbody', true).first().dom.innerHTML = '';
6327 * Show or hide a row.
6328 * @param {Number} rowIndex to show or hide
6329 * @param {Boolean} state hide
6331 setRowVisibility : function(rowIndex, state)
6333 var bt = this.mainBody.dom;
6335 var rows = this.el.select('tbody > tr', true).elements;
6337 if(typeof(rows[rowIndex]) == 'undefined'){
6340 rows[rowIndex].dom.style.display = state ? '' : 'none';
6344 getSelectionModel : function(){
6346 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6348 return this.selModel;
6351 * Render the Roo.bootstrap object from renderder
6353 renderCellObject : function(r)
6357 var t = r.cfg.render(r.container);
6360 Roo.each(r.cfg.cn, function(c){
6362 container: t.getChildContainer(),
6365 _this.renderCellObject(child);
6370 getRowIndex : function(row)
6374 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6397 * @class Roo.bootstrap.TableCell
6398 * @extends Roo.bootstrap.Component
6399 * Bootstrap TableCell class
6400 * @cfg {String} html cell contain text
6401 * @cfg {String} cls cell class
6402 * @cfg {String} tag cell tag (td|th) default td
6403 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6404 * @cfg {String} align Aligns the content in a cell
6405 * @cfg {String} axis Categorizes cells
6406 * @cfg {String} bgcolor Specifies the background color of a cell
6407 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6408 * @cfg {Number} colspan Specifies the number of columns a cell should span
6409 * @cfg {String} headers Specifies one or more header cells a cell is related to
6410 * @cfg {Number} height Sets the height of a cell
6411 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6412 * @cfg {Number} rowspan Sets the number of rows a cell should span
6413 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6414 * @cfg {String} valign Vertical aligns the content in a cell
6415 * @cfg {Number} width Specifies the width of a cell
6418 * Create a new TableCell
6419 * @param {Object} config The config object
6422 Roo.bootstrap.TableCell = function(config){
6423 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6426 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6446 getAutoCreate : function(){
6447 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6467 cfg.align=this.align
6473 cfg.bgcolor=this.bgcolor
6476 cfg.charoff=this.charoff
6479 cfg.colspan=this.colspan
6482 cfg.headers=this.headers
6485 cfg.height=this.height
6488 cfg.nowrap=this.nowrap
6491 cfg.rowspan=this.rowspan
6494 cfg.scope=this.scope
6497 cfg.valign=this.valign
6500 cfg.width=this.width
6519 * @class Roo.bootstrap.TableRow
6520 * @extends Roo.bootstrap.Component
6521 * Bootstrap TableRow class
6522 * @cfg {String} cls row class
6523 * @cfg {String} align Aligns the content in a table row
6524 * @cfg {String} bgcolor Specifies a background color for a table row
6525 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6526 * @cfg {String} valign Vertical aligns the content in a table row
6529 * Create a new TableRow
6530 * @param {Object} config The config object
6533 Roo.bootstrap.TableRow = function(config){
6534 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6537 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6545 getAutoCreate : function(){
6546 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6556 cfg.align = this.align;
6559 cfg.bgcolor = this.bgcolor;
6562 cfg.charoff = this.charoff;
6565 cfg.valign = this.valign;
6583 * @class Roo.bootstrap.TableBody
6584 * @extends Roo.bootstrap.Component
6585 * Bootstrap TableBody class
6586 * @cfg {String} cls element class
6587 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6588 * @cfg {String} align Aligns the content inside the element
6589 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6590 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6593 * Create a new TableBody
6594 * @param {Object} config The config object
6597 Roo.bootstrap.TableBody = function(config){
6598 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6601 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6609 getAutoCreate : function(){
6610 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6624 cfg.align = this.align;
6627 cfg.charoff = this.charoff;
6630 cfg.valign = this.valign;
6637 // initEvents : function()
6644 // this.store = Roo.factory(this.store, Roo.data);
6645 // this.store.on('load', this.onLoad, this);
6647 // this.store.load();
6651 // onLoad: function ()
6653 // this.fireEvent('load', this);
6663 * Ext JS Library 1.1.1
6664 * Copyright(c) 2006-2007, Ext JS, LLC.
6666 * Originally Released Under LGPL - original licence link has changed is not relivant.
6669 * <script type="text/javascript">
6672 // as we use this in bootstrap.
6673 Roo.namespace('Roo.form');
6675 * @class Roo.form.Action
6676 * Internal Class used to handle form actions
6678 * @param {Roo.form.BasicForm} el The form element or its id
6679 * @param {Object} config Configuration options
6684 // define the action interface
6685 Roo.form.Action = function(form, options){
6687 this.options = options || {};
6690 * Client Validation Failed
6693 Roo.form.Action.CLIENT_INVALID = 'client';
6695 * Server Validation Failed
6698 Roo.form.Action.SERVER_INVALID = 'server';
6700 * Connect to Server Failed
6703 Roo.form.Action.CONNECT_FAILURE = 'connect';
6705 * Reading Data from Server Failed
6708 Roo.form.Action.LOAD_FAILURE = 'load';
6710 Roo.form.Action.prototype = {
6712 failureType : undefined,
6713 response : undefined,
6717 run : function(options){
6722 success : function(response){
6727 handleResponse : function(response){
6731 // default connection failure
6732 failure : function(response){
6734 this.response = response;
6735 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6736 this.form.afterAction(this, false);
6739 processResponse : function(response){
6740 this.response = response;
6741 if(!response.responseText){
6744 this.result = this.handleResponse(response);
6748 // utility functions used internally
6749 getUrl : function(appendParams){
6750 var url = this.options.url || this.form.url || this.form.el.dom.action;
6752 var p = this.getParams();
6754 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6760 getMethod : function(){
6761 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6764 getParams : function(){
6765 var bp = this.form.baseParams;
6766 var p = this.options.params;
6768 if(typeof p == "object"){
6769 p = Roo.urlEncode(Roo.applyIf(p, bp));
6770 }else if(typeof p == 'string' && bp){
6771 p += '&' + Roo.urlEncode(bp);
6774 p = Roo.urlEncode(bp);
6779 createCallback : function(){
6781 success: this.success,
6782 failure: this.failure,
6784 timeout: (this.form.timeout*1000),
6785 upload: this.form.fileUpload ? this.success : undefined
6790 Roo.form.Action.Submit = function(form, options){
6791 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6794 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6797 haveProgress : false,
6798 uploadComplete : false,
6800 // uploadProgress indicator.
6801 uploadProgress : function()
6803 if (!this.form.progressUrl) {
6807 if (!this.haveProgress) {
6808 Roo.MessageBox.progress("Uploading", "Uploading");
6810 if (this.uploadComplete) {
6811 Roo.MessageBox.hide();
6815 this.haveProgress = true;
6817 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6819 var c = new Roo.data.Connection();
6821 url : this.form.progressUrl,
6826 success : function(req){
6827 //console.log(data);
6831 rdata = Roo.decode(req.responseText)
6833 Roo.log("Invalid data from server..");
6837 if (!rdata || !rdata.success) {
6839 Roo.MessageBox.alert(Roo.encode(rdata));
6842 var data = rdata.data;
6844 if (this.uploadComplete) {
6845 Roo.MessageBox.hide();
6850 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6851 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6854 this.uploadProgress.defer(2000,this);
6857 failure: function(data) {
6858 Roo.log('progress url failed ');
6869 // run get Values on the form, so it syncs any secondary forms.
6870 this.form.getValues();
6872 var o = this.options;
6873 var method = this.getMethod();
6874 var isPost = method == 'POST';
6875 if(o.clientValidation === false || this.form.isValid()){
6877 if (this.form.progressUrl) {
6878 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6879 (new Date() * 1) + '' + Math.random());
6884 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6885 form:this.form.el.dom,
6886 url:this.getUrl(!isPost),
6888 params:isPost ? this.getParams() : null,
6889 isUpload: this.form.fileUpload
6892 this.uploadProgress();
6894 }else if (o.clientValidation !== false){ // client validation failed
6895 this.failureType = Roo.form.Action.CLIENT_INVALID;
6896 this.form.afterAction(this, false);
6900 success : function(response)
6902 this.uploadComplete= true;
6903 if (this.haveProgress) {
6904 Roo.MessageBox.hide();
6908 var result = this.processResponse(response);
6909 if(result === true || result.success){
6910 this.form.afterAction(this, true);
6914 this.form.markInvalid(result.errors);
6915 this.failureType = Roo.form.Action.SERVER_INVALID;
6917 this.form.afterAction(this, false);
6919 failure : function(response)
6921 this.uploadComplete= true;
6922 if (this.haveProgress) {
6923 Roo.MessageBox.hide();
6926 this.response = response;
6927 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6928 this.form.afterAction(this, false);
6931 handleResponse : function(response){
6932 if(this.form.errorReader){
6933 var rs = this.form.errorReader.read(response);
6936 for(var i = 0, len = rs.records.length; i < len; i++) {
6937 var r = rs.records[i];
6941 if(errors.length < 1){
6945 success : rs.success,
6951 ret = Roo.decode(response.responseText);
6955 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6965 Roo.form.Action.Load = function(form, options){
6966 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6967 this.reader = this.form.reader;
6970 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6975 Roo.Ajax.request(Roo.apply(
6976 this.createCallback(), {
6977 method:this.getMethod(),
6978 url:this.getUrl(false),
6979 params:this.getParams()
6983 success : function(response){
6985 var result = this.processResponse(response);
6986 if(result === true || !result.success || !result.data){
6987 this.failureType = Roo.form.Action.LOAD_FAILURE;
6988 this.form.afterAction(this, false);
6991 this.form.clearInvalid();
6992 this.form.setValues(result.data);
6993 this.form.afterAction(this, true);
6996 handleResponse : function(response){
6997 if(this.form.reader){
6998 var rs = this.form.reader.read(response);
6999 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7001 success : rs.success,
7005 return Roo.decode(response.responseText);
7009 Roo.form.Action.ACTION_TYPES = {
7010 'load' : Roo.form.Action.Load,
7011 'submit' : Roo.form.Action.Submit
7020 * @class Roo.bootstrap.Form
7021 * @extends Roo.bootstrap.Component
7022 * Bootstrap Form class
7023 * @cfg {String} method GET | POST (default POST)
7024 * @cfg {String} labelAlign top | left (default top)
7025 * @cfg {String} align left | right - for navbars
7026 * @cfg {Boolean} loadMask load mask when submit (default true)
7031 * @param {Object} config The config object
7035 Roo.bootstrap.Form = function(config){
7036 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7039 * @event clientvalidation
7040 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7041 * @param {Form} this
7042 * @param {Boolean} valid true if the form has passed client-side validation
7044 clientvalidation: true,
7046 * @event beforeaction
7047 * Fires before any action is performed. Return false to cancel the action.
7048 * @param {Form} this
7049 * @param {Action} action The action to be performed
7053 * @event actionfailed
7054 * Fires when an action fails.
7055 * @param {Form} this
7056 * @param {Action} action The action that failed
7058 actionfailed : true,
7060 * @event actioncomplete
7061 * Fires when an action is completed.
7062 * @param {Form} this
7063 * @param {Action} action The action that completed
7065 actioncomplete : true
7070 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7073 * @cfg {String} method
7074 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7079 * The URL to use for form actions if one isn't supplied in the action options.
7082 * @cfg {Boolean} fileUpload
7083 * Set to true if this form is a file upload.
7087 * @cfg {Object} baseParams
7088 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7092 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7096 * @cfg {Sting} align (left|right) for navbar forms
7101 activeAction : null,
7104 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7105 * element by passing it or its id or mask the form itself by passing in true.
7108 waitMsgTarget : false,
7112 getAutoCreate : function(){
7116 method : this.method || 'POST',
7117 id : this.id || Roo.id(),
7120 if (this.parent().xtype.match(/^Nav/)) {
7121 cfg.cls = 'navbar-form navbar-' + this.align;
7125 if (this.labelAlign == 'left' ) {
7126 cfg.cls += ' form-horizontal';
7132 initEvents : function()
7134 this.el.on('submit', this.onSubmit, this);
7135 // this was added as random key presses on the form where triggering form submit.
7136 this.el.on('keypress', function(e) {
7137 if (e.getCharCode() != 13) {
7140 // we might need to allow it for textareas.. and some other items.
7141 // check e.getTarget().
7143 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7147 Roo.log("keypress blocked");
7155 onSubmit : function(e){
7160 * Returns true if client-side validation on the form is successful.
7163 isValid : function(){
7164 var items = this.getItems();
7166 items.each(function(f){
7175 * Returns true if any fields in this form have changed since their original load.
7178 isDirty : function(){
7180 var items = this.getItems();
7181 items.each(function(f){
7191 * Performs a predefined action (submit or load) or custom actions you define on this form.
7192 * @param {String} actionName The name of the action type
7193 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7194 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7195 * accept other config options):
7197 Property Type Description
7198 ---------------- --------------- ----------------------------------------------------------------------------------
7199 url String The url for the action (defaults to the form's url)
7200 method String The form method to use (defaults to the form's method, or POST if not defined)
7201 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7202 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7203 validate the form on the client (defaults to false)
7205 * @return {BasicForm} this
7207 doAction : function(action, options){
7208 if(typeof action == 'string'){
7209 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7211 if(this.fireEvent('beforeaction', this, action) !== false){
7212 this.beforeAction(action);
7213 action.run.defer(100, action);
7219 beforeAction : function(action){
7220 var o = action.options;
7223 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7225 // not really supported yet.. ??
7227 //if(this.waitMsgTarget === true){
7228 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7229 //}else if(this.waitMsgTarget){
7230 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7231 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7233 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7239 afterAction : function(action, success){
7240 this.activeAction = null;
7241 var o = action.options;
7243 //if(this.waitMsgTarget === true){
7245 //}else if(this.waitMsgTarget){
7246 // this.waitMsgTarget.unmask();
7248 // Roo.MessageBox.updateProgress(1);
7249 // Roo.MessageBox.hide();
7256 Roo.callback(o.success, o.scope, [this, action]);
7257 this.fireEvent('actioncomplete', this, action);
7261 // failure condition..
7262 // we have a scenario where updates need confirming.
7263 // eg. if a locking scenario exists..
7264 // we look for { errors : { needs_confirm : true }} in the response.
7266 (typeof(action.result) != 'undefined') &&
7267 (typeof(action.result.errors) != 'undefined') &&
7268 (typeof(action.result.errors.needs_confirm) != 'undefined')
7271 Roo.log("not supported yet");
7274 Roo.MessageBox.confirm(
7275 "Change requires confirmation",
7276 action.result.errorMsg,
7281 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7291 Roo.callback(o.failure, o.scope, [this, action]);
7292 // show an error message if no failed handler is set..
7293 if (!this.hasListener('actionfailed')) {
7294 Roo.log("need to add dialog support");
7296 Roo.MessageBox.alert("Error",
7297 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7298 action.result.errorMsg :
7299 "Saving Failed, please check your entries or try again"
7304 this.fireEvent('actionfailed', this, action);
7309 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7310 * @param {String} id The value to search for
7313 findField : function(id){
7314 var items = this.getItems();
7315 var field = items.get(id);
7317 items.each(function(f){
7318 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7325 return field || null;
7328 * Mark fields in this form invalid in bulk.
7329 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7330 * @return {BasicForm} this
7332 markInvalid : function(errors){
7333 if(errors instanceof Array){
7334 for(var i = 0, len = errors.length; i < len; i++){
7335 var fieldError = errors[i];
7336 var f = this.findField(fieldError.id);
7338 f.markInvalid(fieldError.msg);
7344 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7345 field.markInvalid(errors[id]);
7349 //Roo.each(this.childForms || [], function (f) {
7350 // f.markInvalid(errors);
7357 * Set values for fields in this form in bulk.
7358 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7359 * @return {BasicForm} this
7361 setValues : function(values){
7362 if(values instanceof Array){ // array of objects
7363 for(var i = 0, len = values.length; i < len; i++){
7365 var f = this.findField(v.id);
7367 f.setValue(v.value);
7368 if(this.trackResetOnLoad){
7369 f.originalValue = f.getValue();
7373 }else{ // object hash
7376 if(typeof values[id] != 'function' && (field = this.findField(id))){
7378 if (field.setFromData &&
7380 field.displayField &&
7381 // combos' with local stores can
7382 // be queried via setValue()
7383 // to set their value..
7384 (field.store && !field.store.isLocal)
7388 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7389 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7390 field.setFromData(sd);
7393 field.setValue(values[id]);
7397 if(this.trackResetOnLoad){
7398 field.originalValue = field.getValue();
7404 //Roo.each(this.childForms || [], function (f) {
7405 // f.setValues(values);
7412 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7413 * they are returned as an array.
7414 * @param {Boolean} asString
7417 getValues : function(asString){
7418 //if (this.childForms) {
7419 // copy values from the child forms
7420 // Roo.each(this.childForms, function (f) {
7421 // this.setValues(f.getValues());
7427 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7428 if(asString === true){
7431 return Roo.urlDecode(fs);
7435 * Returns the fields in this form as an object with key/value pairs.
7436 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7439 getFieldValues : function(with_hidden)
7441 var items = this.getItems();
7443 items.each(function(f){
7447 var v = f.getValue();
7448 if (f.inputType =='radio') {
7449 if (typeof(ret[f.getName()]) == 'undefined') {
7450 ret[f.getName()] = ''; // empty..
7453 if (!f.el.dom.checked) {
7461 // not sure if this supported any more..
7462 if ((typeof(v) == 'object') && f.getRawValue) {
7463 v = f.getRawValue() ; // dates..
7465 // combo boxes where name != hiddenName...
7466 if (f.name != f.getName()) {
7467 ret[f.name] = f.getRawValue();
7469 ret[f.getName()] = v;
7476 * Clears all invalid messages in this form.
7477 * @return {BasicForm} this
7479 clearInvalid : function(){
7480 var items = this.getItems();
7482 items.each(function(f){
7493 * @return {BasicForm} this
7496 var items = this.getItems();
7497 items.each(function(f){
7501 Roo.each(this.childForms || [], function (f) {
7508 getItems : function()
7510 var r=new Roo.util.MixedCollection(false, function(o){
7511 return o.id || (o.id = Roo.id());
7513 var iter = function(el) {
7520 Roo.each(el.items,function(e) {
7540 * Ext JS Library 1.1.1
7541 * Copyright(c) 2006-2007, Ext JS, LLC.
7543 * Originally Released Under LGPL - original licence link has changed is not relivant.
7546 * <script type="text/javascript">
7549 * @class Roo.form.VTypes
7550 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7553 Roo.form.VTypes = function(){
7554 // closure these in so they are only created once.
7555 var alpha = /^[a-zA-Z_]+$/;
7556 var alphanum = /^[a-zA-Z0-9_]+$/;
7557 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7558 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7560 // All these messages and functions are configurable
7563 * The function used to validate email addresses
7564 * @param {String} value The email address
7566 'email' : function(v){
7567 return email.test(v);
7570 * The error text to display when the email validation function returns false
7573 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7575 * The keystroke filter mask to be applied on email input
7578 'emailMask' : /[a-z0-9_\.\-@]/i,
7581 * The function used to validate URLs
7582 * @param {String} value The URL
7584 'url' : function(v){
7588 * The error text to display when the url validation function returns false
7591 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7594 * The function used to validate alpha values
7595 * @param {String} value The value
7597 'alpha' : function(v){
7598 return alpha.test(v);
7601 * The error text to display when the alpha validation function returns false
7604 'alphaText' : 'This field should only contain letters and _',
7606 * The keystroke filter mask to be applied on alpha input
7609 'alphaMask' : /[a-z_]/i,
7612 * The function used to validate alphanumeric values
7613 * @param {String} value The value
7615 'alphanum' : function(v){
7616 return alphanum.test(v);
7619 * The error text to display when the alphanumeric validation function returns false
7622 'alphanumText' : 'This field should only contain letters, numbers and _',
7624 * The keystroke filter mask to be applied on alphanumeric input
7627 'alphanumMask' : /[a-z0-9_]/i
7637 * @class Roo.bootstrap.Input
7638 * @extends Roo.bootstrap.Component
7639 * Bootstrap Input class
7640 * @cfg {Boolean} disabled is it disabled
7641 * @cfg {String} fieldLabel - the label associated
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} inputType - input / file submit ...
7646 * @cfg {string} placeholder - placeholder to put in text.
7647 * @cfg {string} before - input group add on before
7648 * @cfg {string} after - input group add on after
7649 * @cfg {string} size - (lg|sm) or leave empty..
7650 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7651 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7652 * @cfg {Number} md colspan out of 12 for computer-sized screens
7653 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7654 * @cfg {string} value default value of the input
7655 * @cfg {Number} labelWidth set the width of label (0-12)
7656 * @cfg {String} labelAlign (top|left)
7657 * @cfg {Boolean} readOnly Specifies that the field should be read-only
7658 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7660 * @cfg {String} align (left|center|right) Default left
7661 * @cfg {Boolean} forceFeedback (true|false) Default false
7667 * Create a new Input
7668 * @param {Object} config The config object
7671 Roo.bootstrap.Input = function(config){
7672 Roo.bootstrap.Input.superclass.constructor.call(this, config);
7677 * Fires when this field receives input focus.
7678 * @param {Roo.form.Field} this
7683 * Fires when this field loses input focus.
7684 * @param {Roo.form.Field} this
7689 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
7690 * {@link Roo.EventObject#getKey} to determine which key was pressed.
7691 * @param {Roo.form.Field} this
7692 * @param {Roo.EventObject} e The event object
7697 * Fires just before the field blurs if the field value has changed.
7698 * @param {Roo.form.Field} this
7699 * @param {Mixed} newValue The new value
7700 * @param {Mixed} oldValue The original value
7705 * Fires after the field has been marked as invalid.
7706 * @param {Roo.form.Field} this
7707 * @param {String} msg The validation message
7712 * Fires after the field has been validated with no errors.
7713 * @param {Roo.form.Field} this
7718 * Fires after the key up
7719 * @param {Roo.form.Field} this
7720 * @param {Roo.EventObject} e The event Object
7726 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
7728 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7729 automatic validation (defaults to "keyup").
7731 validationEvent : "keyup",
7733 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7735 validateOnBlur : true,
7737 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7739 validationDelay : 250,
7741 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7743 focusClass : "x-form-focus", // not needed???
7747 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7749 invalidClass : "has-warning",
7752 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7754 validClass : "has-success",
7757 * @cfg {Boolean} hasFeedback (true|false) default true
7762 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7764 invalidFeedbackClass : "glyphicon-warning-sign",
7767 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7769 validFeedbackClass : "glyphicon-ok",
7772 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7774 selectOnFocus : false,
7777 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7781 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7786 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7788 disableKeyFilter : false,
7791 * @cfg {Boolean} disabled True to disable the field (defaults to false).
7795 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7799 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7801 blankText : "This field is required",
7804 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7808 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7810 maxLength : Number.MAX_VALUE,
7812 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7814 minLengthText : "The minimum length for this field is {0}",
7816 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7818 maxLengthText : "The maximum length for this field is {0}",
7822 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7823 * If available, this function will be called only after the basic validators all return true, and will be passed the
7824 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7828 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7829 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7830 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
7834 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7838 autocomplete: false,
7857 formatedValue : false,
7858 forceFeedback : false,
7860 parentLabelAlign : function()
7863 while (parent.parent()) {
7864 parent = parent.parent();
7865 if (typeof(parent.labelAlign) !='undefined') {
7866 return parent.labelAlign;
7873 getAutoCreate : function(){
7875 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7881 if(this.inputType != 'hidden'){
7882 cfg.cls = 'form-group' //input-group
7888 type : this.inputType,
7890 cls : 'form-control',
7891 placeholder : this.placeholder || '',
7892 autocomplete : this.autocomplete || 'new-password'
7897 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7900 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7901 input.maxLength = this.maxLength;
7904 if (this.disabled) {
7905 input.disabled=true;
7908 if (this.readOnly) {
7909 input.readonly=true;
7913 input.name = this.name;
7916 input.cls += ' input-' + this.size;
7919 ['xs','sm','md','lg'].map(function(size){
7920 if (settings[size]) {
7921 cfg.cls += ' col-' + size + '-' + settings[size];
7925 var inputblock = input;
7929 cls: 'glyphicon form-control-feedback'
7932 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7935 cls : 'has-feedback',
7943 if (this.before || this.after) {
7946 cls : 'input-group',
7950 if (this.before && typeof(this.before) == 'string') {
7952 inputblock.cn.push({
7954 cls : 'roo-input-before input-group-addon',
7958 if (this.before && typeof(this.before) == 'object') {
7959 this.before = Roo.factory(this.before);
7960 Roo.log(this.before);
7961 inputblock.cn.push({
7963 cls : 'roo-input-before input-group-' +
7964 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7968 inputblock.cn.push(input);
7970 if (this.after && typeof(this.after) == 'string') {
7971 inputblock.cn.push({
7973 cls : 'roo-input-after input-group-addon',
7977 if (this.after && typeof(this.after) == 'object') {
7978 this.after = Roo.factory(this.after);
7979 Roo.log(this.after);
7980 inputblock.cn.push({
7982 cls : 'roo-input-after input-group-' +
7983 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7987 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7988 inputblock.cls += ' has-feedback';
7989 inputblock.cn.push(feedback);
7993 if (align ==='left' && this.fieldLabel.length) {
7994 Roo.log("left and has label");
8000 cls : 'control-label col-sm-' + this.labelWidth,
8001 html : this.fieldLabel
8005 cls : "col-sm-" + (12 - this.labelWidth),
8012 } else if ( this.fieldLabel.length) {
8018 //cls : 'input-group-addon',
8019 html : this.fieldLabel
8029 Roo.log(" no label && no align");
8038 Roo.log('input-parentType: ' + this.parentType);
8040 if (this.parentType === 'Navbar' && this.parent().bar) {
8041 cfg.cls += ' navbar-form';
8049 * return the real input element.
8051 inputEl: function ()
8053 return this.el.select('input.form-control',true).first();
8056 tooltipEl : function()
8058 return this.inputEl();
8061 setDisabled : function(v)
8063 var i = this.inputEl().dom;
8065 i.removeAttribute('disabled');
8069 i.setAttribute('disabled','true');
8071 initEvents : function()
8074 this.inputEl().on("keydown" , this.fireKey, this);
8075 this.inputEl().on("focus", this.onFocus, this);
8076 this.inputEl().on("blur", this.onBlur, this);
8078 this.inputEl().relayEvent('keyup', this);
8080 // reference to original value for reset
8081 this.originalValue = this.getValue();
8082 //Roo.form.TextField.superclass.initEvents.call(this);
8083 if(this.validationEvent == 'keyup'){
8084 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8085 this.inputEl().on('keyup', this.filterValidation, this);
8087 else if(this.validationEvent !== false){
8088 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8091 if(this.selectOnFocus){
8092 this.on("focus", this.preFocus, this);
8095 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8096 this.inputEl().on("keypress", this.filterKeys, this);
8099 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8100 this.el.on("click", this.autoSize, this);
8103 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8104 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8107 if (typeof(this.before) == 'object') {
8108 this.before.render(this.el.select('.roo-input-before',true).first());
8110 if (typeof(this.after) == 'object') {
8111 this.after.render(this.el.select('.roo-input-after',true).first());
8116 filterValidation : function(e){
8117 if(!e.isNavKeyPress()){
8118 this.validationTask.delay(this.validationDelay);
8122 * Validates the field value
8123 * @return {Boolean} True if the value is valid, else false
8125 validate : function(){
8126 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8127 if(this.disabled || this.validateValue(this.getRawValue())){
8138 * Validates a value according to the field's validation rules and marks the field as invalid
8139 * if the validation fails
8140 * @param {Mixed} value The value to validate
8141 * @return {Boolean} True if the value is valid, else false
8143 validateValue : function(value){
8144 if(value.length < 1) { // if it's blank
8145 if(this.allowBlank){
8151 if(value.length < this.minLength){
8154 if(value.length > this.maxLength){
8158 var vt = Roo.form.VTypes;
8159 if(!vt[this.vtype](value, this)){
8163 if(typeof this.validator == "function"){
8164 var msg = this.validator(value);
8170 if(this.regex && !this.regex.test(value)){
8180 fireKey : function(e){
8181 //Roo.log('field ' + e.getKey());
8182 if(e.isNavKeyPress()){
8183 this.fireEvent("specialkey", this, e);
8186 focus : function (selectText){
8188 this.inputEl().focus();
8189 if(selectText === true){
8190 this.inputEl().dom.select();
8196 onFocus : function(){
8197 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8198 // this.el.addClass(this.focusClass);
8201 this.hasFocus = true;
8202 this.startValue = this.getValue();
8203 this.fireEvent("focus", this);
8207 beforeBlur : Roo.emptyFn,
8211 onBlur : function(){
8213 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8214 //this.el.removeClass(this.focusClass);
8216 this.hasFocus = false;
8217 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8220 var v = this.getValue();
8221 if(String(v) !== String(this.startValue)){
8222 this.fireEvent('change', this, v, this.startValue);
8224 this.fireEvent("blur", this);
8228 * Resets the current field value to the originally loaded value and clears any validation messages
8231 this.setValue(this.originalValue);
8235 * Returns the name of the field
8236 * @return {Mixed} name The name field
8238 getName: function(){
8242 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8243 * @return {Mixed} value The field value
8245 getValue : function(){
8247 var v = this.inputEl().getValue();
8252 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8253 * @return {Mixed} value The field value
8255 getRawValue : function(){
8256 var v = this.inputEl().getValue();
8262 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8263 * @param {Mixed} value The value to set
8265 setRawValue : function(v){
8266 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8269 selectText : function(start, end){
8270 var v = this.getRawValue();
8272 start = start === undefined ? 0 : start;
8273 end = end === undefined ? v.length : end;
8274 var d = this.inputEl().dom;
8275 if(d.setSelectionRange){
8276 d.setSelectionRange(start, end);
8277 }else if(d.createTextRange){
8278 var range = d.createTextRange();
8279 range.moveStart("character", start);
8280 range.moveEnd("character", v.length-end);
8287 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8288 * @param {Mixed} value The value to set
8290 setValue : function(v){
8293 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8299 processValue : function(value){
8300 if(this.stripCharsRe){
8301 var newValue = value.replace(this.stripCharsRe, '');
8302 if(newValue !== value){
8303 this.setRawValue(newValue);
8310 preFocus : function(){
8312 if(this.selectOnFocus){
8313 this.inputEl().dom.select();
8316 filterKeys : function(e){
8318 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8321 var c = e.getCharCode(), cc = String.fromCharCode(c);
8322 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8325 if(!this.maskRe.test(cc)){
8330 * Clear any invalid styles/messages for this field
8332 clearInvalid : function(){
8334 if(!this.el || this.preventMark){ // not rendered
8337 this.el.removeClass(this.invalidClass);
8339 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8341 var feedback = this.el.select('.form-control-feedback', true).first();
8344 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8349 this.fireEvent('valid', this);
8353 * Mark this field as valid
8355 markValid : function(){
8356 if(!this.el || this.preventMark){ // not rendered
8360 this.el.removeClass([this.invalidClass, this.validClass]);
8362 var feedback = this.el.select('.form-control-feedback', true).first();
8365 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8368 if(this.disabled || this.allowBlank){
8372 this.el.addClass(this.validClass);
8374 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8376 var feedback = this.el.select('.form-control-feedback', true).first();
8379 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8380 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8385 this.fireEvent('valid', this);
8389 * Mark this field as invalid
8390 * @param {String} msg The validation message
8392 markInvalid : function(msg)
8394 if(!this.el || this.preventMark){ // not rendered
8398 this.el.removeClass([this.invalidClass, this.validClass]);
8400 var feedback = this.el.select('.form-control-feedback', true).first();
8403 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8406 if(this.disabled || this.allowBlank){
8410 this.el.addClass(this.invalidClass);
8412 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8414 var feedback = this.el.select('.form-control-feedback', true).first();
8417 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8419 if(this.getValue().length || this.forceFeedback){
8420 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8427 this.fireEvent('invalid', this, msg);
8430 SafariOnKeyDown : function(event)
8432 // this is a workaround for a password hang bug on chrome/ webkit.
8434 var isSelectAll = false;
8436 if(this.inputEl().dom.selectionEnd > 0){
8437 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8439 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8440 event.preventDefault();
8445 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8447 event.preventDefault();
8448 // this is very hacky as keydown always get's upper case.
8450 var cc = String.fromCharCode(event.getCharCode());
8451 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8455 adjustWidth : function(tag, w){
8456 tag = tag.toLowerCase();
8457 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8458 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8462 if(tag == 'textarea'){
8465 }else if(Roo.isOpera){
8469 if(tag == 'textarea'){
8488 * @class Roo.bootstrap.TextArea
8489 * @extends Roo.bootstrap.Input
8490 * Bootstrap TextArea class
8491 * @cfg {Number} cols Specifies the visible width of a text area
8492 * @cfg {Number} rows Specifies the visible number of lines in a text area
8493 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8494 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8495 * @cfg {string} html text
8498 * Create a new TextArea
8499 * @param {Object} config The config object
8502 Roo.bootstrap.TextArea = function(config){
8503 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8507 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8517 getAutoCreate : function(){
8519 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8530 value : this.value || '',
8531 html: this.html || '',
8532 cls : 'form-control',
8533 placeholder : this.placeholder || ''
8537 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8538 input.maxLength = this.maxLength;
8542 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8546 input.cols = this.cols;
8549 if (this.readOnly) {
8550 input.readonly = true;
8554 input.name = this.name;
8558 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8562 ['xs','sm','md','lg'].map(function(size){
8563 if (settings[size]) {
8564 cfg.cls += ' col-' + size + '-' + settings[size];
8568 var inputblock = input;
8570 if(this.hasFeedback && !this.allowBlank){
8574 cls: 'glyphicon form-control-feedback'
8578 cls : 'has-feedback',
8587 if (this.before || this.after) {
8590 cls : 'input-group',
8594 inputblock.cn.push({
8596 cls : 'input-group-addon',
8601 inputblock.cn.push(input);
8603 if(this.hasFeedback && !this.allowBlank){
8604 inputblock.cls += ' has-feedback';
8605 inputblock.cn.push(feedback);
8609 inputblock.cn.push({
8611 cls : 'input-group-addon',
8618 if (align ==='left' && this.fieldLabel.length) {
8619 Roo.log("left and has label");
8625 cls : 'control-label col-sm-' + this.labelWidth,
8626 html : this.fieldLabel
8630 cls : "col-sm-" + (12 - this.labelWidth),
8637 } else if ( this.fieldLabel.length) {
8643 //cls : 'input-group-addon',
8644 html : this.fieldLabel
8654 Roo.log(" no label && no align");
8664 if (this.disabled) {
8665 input.disabled=true;
8672 * return the real textarea element.
8674 inputEl: function ()
8676 return this.el.select('textarea.form-control',true).first();
8684 * trigger field - base class for combo..
8689 * @class Roo.bootstrap.TriggerField
8690 * @extends Roo.bootstrap.Input
8691 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8692 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8693 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8694 * for which you can provide a custom implementation. For example:
8696 var trigger = new Roo.bootstrap.TriggerField();
8697 trigger.onTriggerClick = myTriggerFn;
8698 trigger.applyTo('my-field');
8701 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8702 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8703 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
8704 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8705 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8708 * Create a new TriggerField.
8709 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8710 * to the base TextField)
8712 Roo.bootstrap.TriggerField = function(config){
8713 this.mimicing = false;
8714 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8717 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
8719 * @cfg {String} triggerClass A CSS class to apply to the trigger
8722 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8727 * @cfg {Boolean} removable (true|false) special filter default false
8731 /** @cfg {Boolean} grow @hide */
8732 /** @cfg {Number} growMin @hide */
8733 /** @cfg {Number} growMax @hide */
8739 autoSize: Roo.emptyFn,
8746 actionMode : 'wrap',
8751 getAutoCreate : function(){
8753 var align = this.labelAlign || this.parentLabelAlign();
8758 cls: 'form-group' //input-group
8765 type : this.inputType,
8766 cls : 'form-control',
8767 autocomplete: 'new-password',
8768 placeholder : this.placeholder || ''
8772 input.name = this.name;
8775 input.cls += ' input-' + this.size;
8778 if (this.disabled) {
8779 input.disabled=true;
8782 var inputblock = input;
8784 if(this.hasFeedback && !this.allowBlank){
8788 cls: 'glyphicon form-control-feedback'
8791 if(this.removable && !this.editable && !this.tickable){
8793 cls : 'has-feedback',
8799 cls : 'roo-combo-removable-btn close'
8806 cls : 'has-feedback',
8815 if(this.removable && !this.editable && !this.tickable){
8817 cls : 'roo-removable',
8823 cls : 'roo-combo-removable-btn close'
8830 if (this.before || this.after) {
8833 cls : 'input-group',
8837 inputblock.cn.push({
8839 cls : 'input-group-addon',
8844 inputblock.cn.push(input);
8846 if(this.hasFeedback && !this.allowBlank){
8847 inputblock.cls += ' has-feedback';
8848 inputblock.cn.push(feedback);
8852 inputblock.cn.push({
8854 cls : 'input-group-addon',
8867 cls: 'form-hidden-field'
8875 Roo.log('multiple');
8883 cls: 'form-hidden-field'
8887 cls: 'select2-choices',
8891 cls: 'select2-search-field',
8904 cls: 'select2-container input-group',
8909 // cls: 'typeahead typeahead-long dropdown-menu',
8910 // style: 'display:none'
8915 if(!this.multiple && this.showToggleBtn){
8921 if (this.caret != false) {
8924 cls: 'fa fa-' + this.caret
8931 cls : 'input-group-addon btn dropdown-toggle',
8936 cls: 'combobox-clear',
8950 combobox.cls += ' select2-container-multi';
8953 if (align ==='left' && this.fieldLabel.length) {
8955 Roo.log("left and has label");
8961 cls : 'control-label col-sm-' + this.labelWidth,
8962 html : this.fieldLabel
8966 cls : "col-sm-" + (12 - this.labelWidth),
8973 } else if ( this.fieldLabel.length) {
8979 //cls : 'input-group-addon',
8980 html : this.fieldLabel
8990 Roo.log(" no label && no align");
8997 ['xs','sm','md','lg'].map(function(size){
8998 if (settings[size]) {
8999 cfg.cls += ' col-' + size + '-' + settings[size];
9010 onResize : function(w, h){
9011 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9012 // if(typeof w == 'number'){
9013 // var x = w - this.trigger.getWidth();
9014 // this.inputEl().setWidth(this.adjustWidth('input', x));
9015 // this.trigger.setStyle('left', x+'px');
9020 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9023 getResizeEl : function(){
9024 return this.inputEl();
9028 getPositionEl : function(){
9029 return this.inputEl();
9033 alignErrorIcon : function(){
9034 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9038 initEvents : function(){
9042 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9043 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9044 if(!this.multiple && this.showToggleBtn){
9045 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9046 if(this.hideTrigger){
9047 this.trigger.setDisplayed(false);
9049 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9053 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9056 if(this.removable && !this.editable && !this.tickable){
9057 var close = this.closeTriggerEl();
9060 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9061 close.on('click', this.removeBtnClick, this, close);
9065 //this.trigger.addClassOnOver('x-form-trigger-over');
9066 //this.trigger.addClassOnClick('x-form-trigger-click');
9069 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9073 closeTriggerEl : function()
9075 var close = this.el.select('.roo-combo-removable-btn', true).first();
9076 return close ? close : false;
9079 removeBtnClick : function(e, h, el)
9083 if(this.fireEvent("remove", this) !== false){
9088 createList : function()
9090 this.list = Roo.get(document.body).createChild({
9092 cls: 'typeahead typeahead-long dropdown-menu',
9093 style: 'display:none'
9096 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9101 initTrigger : function(){
9106 onDestroy : function(){
9108 this.trigger.removeAllListeners();
9109 // this.trigger.remove();
9112 // this.wrap.remove();
9114 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9118 onFocus : function(){
9119 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9122 this.wrap.addClass('x-trigger-wrap-focus');
9123 this.mimicing = true;
9124 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9125 if(this.monitorTab){
9126 this.el.on("keydown", this.checkTab, this);
9133 checkTab : function(e){
9134 if(e.getKey() == e.TAB){
9140 onBlur : function(){
9145 mimicBlur : function(e, t){
9147 if(!this.wrap.contains(t) && this.validateBlur()){
9154 triggerBlur : function(){
9155 this.mimicing = false;
9156 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9157 if(this.monitorTab){
9158 this.el.un("keydown", this.checkTab, this);
9160 //this.wrap.removeClass('x-trigger-wrap-focus');
9161 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9165 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9166 validateBlur : function(e, t){
9171 onDisable : function(){
9172 this.inputEl().dom.disabled = true;
9173 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9175 // this.wrap.addClass('x-item-disabled');
9180 onEnable : function(){
9181 this.inputEl().dom.disabled = false;
9182 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9184 // this.el.removeClass('x-item-disabled');
9189 onShow : function(){
9190 var ae = this.getActionEl();
9193 ae.dom.style.display = '';
9194 ae.dom.style.visibility = 'visible';
9200 onHide : function(){
9201 var ae = this.getActionEl();
9202 ae.dom.style.display = 'none';
9206 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9207 * by an implementing function.
9209 * @param {EventObject} e
9211 onTriggerClick : Roo.emptyFn
9215 * Ext JS Library 1.1.1
9216 * Copyright(c) 2006-2007, Ext JS, LLC.
9218 * Originally Released Under LGPL - original licence link has changed is not relivant.
9221 * <script type="text/javascript">
9226 * @class Roo.data.SortTypes
9228 * Defines the default sorting (casting?) comparison functions used when sorting data.
9230 Roo.data.SortTypes = {
9232 * Default sort that does nothing
9233 * @param {Mixed} s The value being converted
9234 * @return {Mixed} The comparison value
9241 * The regular expression used to strip tags
9245 stripTagsRE : /<\/?[^>]+>/gi,
9248 * Strips all HTML tags to sort on text only
9249 * @param {Mixed} s The value being converted
9250 * @return {String} The comparison value
9252 asText : function(s){
9253 return String(s).replace(this.stripTagsRE, "");
9257 * Strips all HTML tags to sort on text only - Case insensitive
9258 * @param {Mixed} s The value being converted
9259 * @return {String} The comparison value
9261 asUCText : function(s){
9262 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9266 * Case insensitive string
9267 * @param {Mixed} s The value being converted
9268 * @return {String} The comparison value
9270 asUCString : function(s) {
9271 return String(s).toUpperCase();
9276 * @param {Mixed} s The value being converted
9277 * @return {Number} The comparison value
9279 asDate : function(s) {
9283 if(s instanceof Date){
9286 return Date.parse(String(s));
9291 * @param {Mixed} s The value being converted
9292 * @return {Float} The comparison value
9294 asFloat : function(s) {
9295 var val = parseFloat(String(s).replace(/,/g, ""));
9296 if(isNaN(val)) val = 0;
9302 * @param {Mixed} s The value being converted
9303 * @return {Number} The comparison value
9305 asInt : function(s) {
9306 var val = parseInt(String(s).replace(/,/g, ""));
9307 if(isNaN(val)) val = 0;
9312 * Ext JS Library 1.1.1
9313 * Copyright(c) 2006-2007, Ext JS, LLC.
9315 * Originally Released Under LGPL - original licence link has changed is not relivant.
9318 * <script type="text/javascript">
9322 * @class Roo.data.Record
9323 * Instances of this class encapsulate both record <em>definition</em> information, and record
9324 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9325 * to access Records cached in an {@link Roo.data.Store} object.<br>
9327 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9328 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9331 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9333 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9334 * {@link #create}. The parameters are the same.
9335 * @param {Array} data An associative Array of data values keyed by the field name.
9336 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9337 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9338 * not specified an integer id is generated.
9340 Roo.data.Record = function(data, id){
9341 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9346 * Generate a constructor for a specific record layout.
9347 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9348 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9349 * Each field definition object may contain the following properties: <ul>
9350 * <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,
9351 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9352 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9353 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9354 * is being used, then this is a string containing the javascript expression to reference the data relative to
9355 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9356 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9357 * this may be omitted.</p></li>
9358 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9359 * <ul><li>auto (Default, implies no conversion)</li>
9364 * <li>date</li></ul></p></li>
9365 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9366 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9367 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9368 * by the Reader into an object that will be stored in the Record. It is passed the
9369 * following parameters:<ul>
9370 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9372 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9374 * <br>usage:<br><pre><code>
9375 var TopicRecord = Roo.data.Record.create(
9376 {name: 'title', mapping: 'topic_title'},
9377 {name: 'author', mapping: 'username'},
9378 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9379 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9380 {name: 'lastPoster', mapping: 'user2'},
9381 {name: 'excerpt', mapping: 'post_text'}
9384 var myNewRecord = new TopicRecord({
9385 title: 'Do my job please',
9388 lastPost: new Date(),
9389 lastPoster: 'Animal',
9390 excerpt: 'No way dude!'
9392 myStore.add(myNewRecord);
9397 Roo.data.Record.create = function(o){
9399 f.superclass.constructor.apply(this, arguments);
9401 Roo.extend(f, Roo.data.Record);
9402 var p = f.prototype;
9403 p.fields = new Roo.util.MixedCollection(false, function(field){
9406 for(var i = 0, len = o.length; i < len; i++){
9407 p.fields.add(new Roo.data.Field(o[i]));
9409 f.getField = function(name){
9410 return p.fields.get(name);
9415 Roo.data.Record.AUTO_ID = 1000;
9416 Roo.data.Record.EDIT = 'edit';
9417 Roo.data.Record.REJECT = 'reject';
9418 Roo.data.Record.COMMIT = 'commit';
9420 Roo.data.Record.prototype = {
9422 * Readonly flag - true if this record has been modified.
9431 join : function(store){
9436 * Set the named field to the specified value.
9437 * @param {String} name The name of the field to set.
9438 * @param {Object} value The value to set the field to.
9440 set : function(name, value){
9441 if(this.data[name] == value){
9448 if(typeof this.modified[name] == 'undefined'){
9449 this.modified[name] = this.data[name];
9451 this.data[name] = value;
9452 if(!this.editing && this.store){
9453 this.store.afterEdit(this);
9458 * Get the value of the named field.
9459 * @param {String} name The name of the field to get the value of.
9460 * @return {Object} The value of the field.
9462 get : function(name){
9463 return this.data[name];
9467 beginEdit : function(){
9468 this.editing = true;
9473 cancelEdit : function(){
9474 this.editing = false;
9475 delete this.modified;
9479 endEdit : function(){
9480 this.editing = false;
9481 if(this.dirty && this.store){
9482 this.store.afterEdit(this);
9487 * Usually called by the {@link Roo.data.Store} which owns the Record.
9488 * Rejects all changes made to the Record since either creation, or the last commit operation.
9489 * Modified fields are reverted to their original values.
9491 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9492 * of reject operations.
9494 reject : function(){
9495 var m = this.modified;
9497 if(typeof m[n] != "function"){
9498 this.data[n] = m[n];
9502 delete this.modified;
9503 this.editing = false;
9505 this.store.afterReject(this);
9510 * Usually called by the {@link Roo.data.Store} which owns the Record.
9511 * Commits all changes made to the Record since either creation, or the last commit operation.
9513 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9514 * of commit operations.
9516 commit : function(){
9518 delete this.modified;
9519 this.editing = false;
9521 this.store.afterCommit(this);
9526 hasError : function(){
9527 return this.error != null;
9531 clearError : function(){
9536 * Creates a copy of this record.
9537 * @param {String} id (optional) A new record id if you don't want to use this record's id
9540 copy : function(newId) {
9541 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9545 * Ext JS Library 1.1.1
9546 * Copyright(c) 2006-2007, Ext JS, LLC.
9548 * Originally Released Under LGPL - original licence link has changed is not relivant.
9551 * <script type="text/javascript">
9557 * @class Roo.data.Store
9558 * @extends Roo.util.Observable
9559 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9560 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9562 * 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
9563 * has no knowledge of the format of the data returned by the Proxy.<br>
9565 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9566 * instances from the data object. These records are cached and made available through accessor functions.
9568 * Creates a new Store.
9569 * @param {Object} config A config object containing the objects needed for the Store to access data,
9570 * and read the data into Records.
9572 Roo.data.Store = function(config){
9573 this.data = new Roo.util.MixedCollection(false);
9574 this.data.getKey = function(o){
9577 this.baseParams = {};
9584 "multisort" : "_multisort"
9587 if(config && config.data){
9588 this.inlineData = config.data;
9592 Roo.apply(this, config);
9594 if(this.reader){ // reader passed
9595 this.reader = Roo.factory(this.reader, Roo.data);
9596 this.reader.xmodule = this.xmodule || false;
9597 if(!this.recordType){
9598 this.recordType = this.reader.recordType;
9600 if(this.reader.onMetaChange){
9601 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9605 if(this.recordType){
9606 this.fields = this.recordType.prototype.fields;
9612 * @event datachanged
9613 * Fires when the data cache has changed, and a widget which is using this Store
9614 * as a Record cache should refresh its view.
9615 * @param {Store} this
9620 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9621 * @param {Store} this
9622 * @param {Object} meta The JSON metadata
9627 * Fires when Records have been added to the Store
9628 * @param {Store} this
9629 * @param {Roo.data.Record[]} records The array of Records added
9630 * @param {Number} index The index at which the record(s) were added
9635 * Fires when a Record has been removed from the Store
9636 * @param {Store} this
9637 * @param {Roo.data.Record} record The Record that was removed
9638 * @param {Number} index The index at which the record was removed
9643 * Fires when a Record has been updated
9644 * @param {Store} this
9645 * @param {Roo.data.Record} record The Record that was updated
9646 * @param {String} operation The update operation being performed. Value may be one of:
9648 Roo.data.Record.EDIT
9649 Roo.data.Record.REJECT
9650 Roo.data.Record.COMMIT
9656 * Fires when the data cache has been cleared.
9657 * @param {Store} this
9662 * Fires before a request is made for a new data object. If the beforeload handler returns false
9663 * the load action will be canceled.
9664 * @param {Store} this
9665 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9669 * @event beforeloadadd
9670 * Fires after a new set of Records has been loaded.
9671 * @param {Store} this
9672 * @param {Roo.data.Record[]} records The Records that were loaded
9673 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9675 beforeloadadd : true,
9678 * Fires after a new set of Records has been loaded, before they are added to the store.
9679 * @param {Store} this
9680 * @param {Roo.data.Record[]} records The Records that were loaded
9681 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9682 * @params {Object} return from reader
9686 * @event loadexception
9687 * Fires if an exception occurs in the Proxy during loading.
9688 * Called with the signature of the Proxy's "loadexception" event.
9689 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9692 * @param {Object} return from JsonData.reader() - success, totalRecords, records
9693 * @param {Object} load options
9694 * @param {Object} jsonData from your request (normally this contains the Exception)
9696 loadexception : true
9700 this.proxy = Roo.factory(this.proxy, Roo.data);
9701 this.proxy.xmodule = this.xmodule || false;
9702 this.relayEvents(this.proxy, ["loadexception"]);
9704 this.sortToggle = {};
9705 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9707 Roo.data.Store.superclass.constructor.call(this);
9709 if(this.inlineData){
9710 this.loadData(this.inlineData);
9711 delete this.inlineData;
9715 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9717 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
9718 * without a remote query - used by combo/forms at present.
9722 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9725 * @cfg {Array} data Inline data to be loaded when the store is initialized.
9728 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9729 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9732 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9733 * on any HTTP request
9736 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9739 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9743 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9744 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9749 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9750 * loaded or when a record is removed. (defaults to false).
9752 pruneModifiedRecords : false,
9758 * Add Records to the Store and fires the add event.
9759 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9761 add : function(records){
9762 records = [].concat(records);
9763 for(var i = 0, len = records.length; i < len; i++){
9764 records[i].join(this);
9766 var index = this.data.length;
9767 this.data.addAll(records);
9768 this.fireEvent("add", this, records, index);
9772 * Remove a Record from the Store and fires the remove event.
9773 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9775 remove : function(record){
9776 var index = this.data.indexOf(record);
9777 this.data.removeAt(index);
9778 if(this.pruneModifiedRecords){
9779 this.modified.remove(record);
9781 this.fireEvent("remove", this, record, index);
9785 * Remove all Records from the Store and fires the clear event.
9787 removeAll : function(){
9789 if(this.pruneModifiedRecords){
9792 this.fireEvent("clear", this);
9796 * Inserts Records to the Store at the given index and fires the add event.
9797 * @param {Number} index The start index at which to insert the passed Records.
9798 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9800 insert : function(index, records){
9801 records = [].concat(records);
9802 for(var i = 0, len = records.length; i < len; i++){
9803 this.data.insert(index, records[i]);
9804 records[i].join(this);
9806 this.fireEvent("add", this, records, index);
9810 * Get the index within the cache of the passed Record.
9811 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9812 * @return {Number} The index of the passed Record. Returns -1 if not found.
9814 indexOf : function(record){
9815 return this.data.indexOf(record);
9819 * Get the index within the cache of the Record with the passed id.
9820 * @param {String} id The id of the Record to find.
9821 * @return {Number} The index of the Record. Returns -1 if not found.
9823 indexOfId : function(id){
9824 return this.data.indexOfKey(id);
9828 * Get the Record with the specified id.
9829 * @param {String} id The id of the Record to find.
9830 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9832 getById : function(id){
9833 return this.data.key(id);
9837 * Get the Record at the specified index.
9838 * @param {Number} index The index of the Record to find.
9839 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9841 getAt : function(index){
9842 return this.data.itemAt(index);
9846 * Returns a range of Records between specified indices.
9847 * @param {Number} startIndex (optional) The starting index (defaults to 0)
9848 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9849 * @return {Roo.data.Record[]} An array of Records
9851 getRange : function(start, end){
9852 return this.data.getRange(start, end);
9856 storeOptions : function(o){
9857 o = Roo.apply({}, o);
9860 this.lastOptions = o;
9864 * Loads the Record cache from the configured Proxy using the configured Reader.
9866 * If using remote paging, then the first load call must specify the <em>start</em>
9867 * and <em>limit</em> properties in the options.params property to establish the initial
9868 * position within the dataset, and the number of Records to cache on each read from the Proxy.
9870 * <strong>It is important to note that for remote data sources, loading is asynchronous,
9871 * and this call will return before the new data has been loaded. Perform any post-processing
9872 * in a callback function, or in a "load" event handler.</strong>
9874 * @param {Object} options An object containing properties which control loading options:<ul>
9875 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9876 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9877 * passed the following arguments:<ul>
9878 * <li>r : Roo.data.Record[]</li>
9879 * <li>options: Options object from the load call</li>
9880 * <li>success: Boolean success indicator</li></ul></li>
9881 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9882 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9885 load : function(options){
9886 options = options || {};
9887 if(this.fireEvent("beforeload", this, options) !== false){
9888 this.storeOptions(options);
9889 var p = Roo.apply(options.params || {}, this.baseParams);
9890 // if meta was not loaded from remote source.. try requesting it.
9891 if (!this.reader.metaFromRemote) {
9894 if(this.sortInfo && this.remoteSort){
9895 var pn = this.paramNames;
9896 p[pn["sort"]] = this.sortInfo.field;
9897 p[pn["dir"]] = this.sortInfo.direction;
9899 if (this.multiSort) {
9900 var pn = this.paramNames;
9901 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9904 this.proxy.load(p, this.reader, this.loadRecords, this, options);
9909 * Reloads the Record cache from the configured Proxy using the configured Reader and
9910 * the options from the last load operation performed.
9911 * @param {Object} options (optional) An object containing properties which may override the options
9912 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9913 * the most recently used options are reused).
9915 reload : function(options){
9916 this.load(Roo.applyIf(options||{}, this.lastOptions));
9920 // Called as a callback by the Reader during a load operation.
9921 loadRecords : function(o, options, success){
9922 if(!o || success === false){
9923 if(success !== false){
9924 this.fireEvent("load", this, [], options, o);
9926 if(options.callback){
9927 options.callback.call(options.scope || this, [], options, false);
9931 // if data returned failure - throw an exception.
9932 if (o.success === false) {
9933 // show a message if no listener is registered.
9934 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9935 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9937 // loadmask wil be hooked into this..
9938 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9941 var r = o.records, t = o.totalRecords || r.length;
9943 this.fireEvent("beforeloadadd", this, r, options, o);
9945 if(!options || options.add !== true){
9946 if(this.pruneModifiedRecords){
9949 for(var i = 0, len = r.length; i < len; i++){
9953 this.data = this.snapshot;
9954 delete this.snapshot;
9957 this.data.addAll(r);
9958 this.totalLength = t;
9960 this.fireEvent("datachanged", this);
9962 this.totalLength = Math.max(t, this.data.length+r.length);
9965 this.fireEvent("load", this, r, options, o);
9966 if(options.callback){
9967 options.callback.call(options.scope || this, r, options, true);
9973 * Loads data from a passed data block. A Reader which understands the format of the data
9974 * must have been configured in the constructor.
9975 * @param {Object} data The data block from which to read the Records. The format of the data expected
9976 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9977 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9979 loadData : function(o, append){
9980 var r = this.reader.readRecords(o);
9981 this.loadRecords(r, {add: append}, true);
9985 * Gets the number of cached records.
9987 * <em>If using paging, this may not be the total size of the dataset. If the data object
9988 * used by the Reader contains the dataset size, then the getTotalCount() function returns
9989 * the data set size</em>
9991 getCount : function(){
9992 return this.data.length || 0;
9996 * Gets the total number of records in the dataset as returned by the server.
9998 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9999 * the dataset size</em>
10001 getTotalCount : function(){
10002 return this.totalLength || 0;
10006 * Returns the sort state of the Store as an object with two properties:
10008 field {String} The name of the field by which the Records are sorted
10009 direction {String} The sort order, "ASC" or "DESC"
10012 getSortState : function(){
10013 return this.sortInfo;
10017 applySort : function(){
10018 if(this.sortInfo && !this.remoteSort){
10019 var s = this.sortInfo, f = s.field;
10020 var st = this.fields.get(f).sortType;
10021 var fn = function(r1, r2){
10022 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10023 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10025 this.data.sort(s.direction, fn);
10026 if(this.snapshot && this.snapshot != this.data){
10027 this.snapshot.sort(s.direction, fn);
10033 * Sets the default sort column and order to be used by the next load operation.
10034 * @param {String} fieldName The name of the field to sort by.
10035 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10037 setDefaultSort : function(field, dir){
10038 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10042 * Sort the Records.
10043 * If remote sorting is used, the sort is performed on the server, and the cache is
10044 * reloaded. If local sorting is used, the cache is sorted internally.
10045 * @param {String} fieldName The name of the field to sort by.
10046 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10048 sort : function(fieldName, dir){
10049 var f = this.fields.get(fieldName);
10051 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10053 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10054 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10059 this.sortToggle[f.name] = dir;
10060 this.sortInfo = {field: f.name, direction: dir};
10061 if(!this.remoteSort){
10063 this.fireEvent("datachanged", this);
10065 this.load(this.lastOptions);
10070 * Calls the specified function for each of the Records in the cache.
10071 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10072 * Returning <em>false</em> aborts and exits the iteration.
10073 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10075 each : function(fn, scope){
10076 this.data.each(fn, scope);
10080 * Gets all records modified since the last commit. Modified records are persisted across load operations
10081 * (e.g., during paging).
10082 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10084 getModifiedRecords : function(){
10085 return this.modified;
10089 createFilterFn : function(property, value, anyMatch){
10090 if(!value.exec){ // not a regex
10091 value = String(value);
10092 if(value.length == 0){
10095 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10097 return function(r){
10098 return value.test(r.data[property]);
10103 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10104 * @param {String} property A field on your records
10105 * @param {Number} start The record index to start at (defaults to 0)
10106 * @param {Number} end The last record index to include (defaults to length - 1)
10107 * @return {Number} The sum
10109 sum : function(property, start, end){
10110 var rs = this.data.items, v = 0;
10111 start = start || 0;
10112 end = (end || end === 0) ? end : rs.length-1;
10114 for(var i = start; i <= end; i++){
10115 v += (rs[i].data[property] || 0);
10121 * Filter the records by a specified property.
10122 * @param {String} field A field on your records
10123 * @param {String/RegExp} value Either a string that the field
10124 * should start with or a RegExp to test against the field
10125 * @param {Boolean} anyMatch True to match any part not just the beginning
10127 filter : function(property, value, anyMatch){
10128 var fn = this.createFilterFn(property, value, anyMatch);
10129 return fn ? this.filterBy(fn) : this.clearFilter();
10133 * Filter by a function. The specified function will be called with each
10134 * record in this data source. If the function returns true the record is included,
10135 * otherwise it is filtered.
10136 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10137 * @param {Object} scope (optional) The scope of the function (defaults to this)
10139 filterBy : function(fn, scope){
10140 this.snapshot = this.snapshot || this.data;
10141 this.data = this.queryBy(fn, scope||this);
10142 this.fireEvent("datachanged", this);
10146 * Query the records by a specified property.
10147 * @param {String} field A field on your records
10148 * @param {String/RegExp} value Either a string that the field
10149 * should start with or a RegExp to test against the field
10150 * @param {Boolean} anyMatch True to match any part not just the beginning
10151 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10153 query : function(property, value, anyMatch){
10154 var fn = this.createFilterFn(property, value, anyMatch);
10155 return fn ? this.queryBy(fn) : this.data.clone();
10159 * Query by a function. The specified function will be called with each
10160 * record in this data source. If the function returns true the record is included
10162 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10163 * @param {Object} scope (optional) The scope of the function (defaults to this)
10164 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10166 queryBy : function(fn, scope){
10167 var data = this.snapshot || this.data;
10168 return data.filterBy(fn, scope||this);
10172 * Collects unique values for a particular dataIndex from this store.
10173 * @param {String} dataIndex The property to collect
10174 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10175 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10176 * @return {Array} An array of the unique values
10178 collect : function(dataIndex, allowNull, bypassFilter){
10179 var d = (bypassFilter === true && this.snapshot) ?
10180 this.snapshot.items : this.data.items;
10181 var v, sv, r = [], l = {};
10182 for(var i = 0, len = d.length; i < len; i++){
10183 v = d[i].data[dataIndex];
10185 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10194 * Revert to a view of the Record cache with no filtering applied.
10195 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10197 clearFilter : function(suppressEvent){
10198 if(this.snapshot && this.snapshot != this.data){
10199 this.data = this.snapshot;
10200 delete this.snapshot;
10201 if(suppressEvent !== true){
10202 this.fireEvent("datachanged", this);
10208 afterEdit : function(record){
10209 if(this.modified.indexOf(record) == -1){
10210 this.modified.push(record);
10212 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10216 afterReject : function(record){
10217 this.modified.remove(record);
10218 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10222 afterCommit : function(record){
10223 this.modified.remove(record);
10224 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10228 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10229 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10231 commitChanges : function(){
10232 var m = this.modified.slice(0);
10233 this.modified = [];
10234 for(var i = 0, len = m.length; i < len; i++){
10240 * Cancel outstanding changes on all changed records.
10242 rejectChanges : function(){
10243 var m = this.modified.slice(0);
10244 this.modified = [];
10245 for(var i = 0, len = m.length; i < len; i++){
10250 onMetaChange : function(meta, rtype, o){
10251 this.recordType = rtype;
10252 this.fields = rtype.prototype.fields;
10253 delete this.snapshot;
10254 this.sortInfo = meta.sortInfo || this.sortInfo;
10255 this.modified = [];
10256 this.fireEvent('metachange', this, this.reader.meta);
10259 moveIndex : function(data, type)
10261 var index = this.indexOf(data);
10263 var newIndex = index + type;
10267 this.insert(newIndex, data);
10272 * Ext JS Library 1.1.1
10273 * Copyright(c) 2006-2007, Ext JS, LLC.
10275 * Originally Released Under LGPL - original licence link has changed is not relivant.
10278 * <script type="text/javascript">
10282 * @class Roo.data.SimpleStore
10283 * @extends Roo.data.Store
10284 * Small helper class to make creating Stores from Array data easier.
10285 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10286 * @cfg {Array} fields An array of field definition objects, or field name strings.
10287 * @cfg {Array} data The multi-dimensional array of data
10289 * @param {Object} config
10291 Roo.data.SimpleStore = function(config){
10292 Roo.data.SimpleStore.superclass.constructor.call(this, {
10294 reader: new Roo.data.ArrayReader({
10297 Roo.data.Record.create(config.fields)
10299 proxy : new Roo.data.MemoryProxy(config.data)
10303 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10305 * Ext JS Library 1.1.1
10306 * Copyright(c) 2006-2007, Ext JS, LLC.
10308 * Originally Released Under LGPL - original licence link has changed is not relivant.
10311 * <script type="text/javascript">
10316 * @extends Roo.data.Store
10317 * @class Roo.data.JsonStore
10318 * Small helper class to make creating Stores for JSON data easier. <br/>
10320 var store = new Roo.data.JsonStore({
10321 url: 'get-images.php',
10323 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10326 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10327 * JsonReader and HttpProxy (unless inline data is provided).</b>
10328 * @cfg {Array} fields An array of field definition objects, or field name strings.
10330 * @param {Object} config
10332 Roo.data.JsonStore = function(c){
10333 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10334 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10335 reader: new Roo.data.JsonReader(c, c.fields)
10338 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10340 * Ext JS Library 1.1.1
10341 * Copyright(c) 2006-2007, Ext JS, LLC.
10343 * Originally Released Under LGPL - original licence link has changed is not relivant.
10346 * <script type="text/javascript">
10350 Roo.data.Field = function(config){
10351 if(typeof config == "string"){
10352 config = {name: config};
10354 Roo.apply(this, config);
10357 this.type = "auto";
10360 var st = Roo.data.SortTypes;
10361 // named sortTypes are supported, here we look them up
10362 if(typeof this.sortType == "string"){
10363 this.sortType = st[this.sortType];
10366 // set default sortType for strings and dates
10367 if(!this.sortType){
10370 this.sortType = st.asUCString;
10373 this.sortType = st.asDate;
10376 this.sortType = st.none;
10381 var stripRe = /[\$,%]/g;
10383 // prebuilt conversion function for this field, instead of
10384 // switching every time we're reading a value
10386 var cv, dateFormat = this.dateFormat;
10391 cv = function(v){ return v; };
10394 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10398 return v !== undefined && v !== null && v !== '' ?
10399 parseInt(String(v).replace(stripRe, ""), 10) : '';
10404 return v !== undefined && v !== null && v !== '' ?
10405 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10410 cv = function(v){ return v === true || v === "true" || v == 1; };
10417 if(v instanceof Date){
10421 if(dateFormat == "timestamp"){
10422 return new Date(v*1000);
10424 return Date.parseDate(v, dateFormat);
10426 var parsed = Date.parse(v);
10427 return parsed ? new Date(parsed) : null;
10436 Roo.data.Field.prototype = {
10444 * Ext JS Library 1.1.1
10445 * Copyright(c) 2006-2007, Ext JS, LLC.
10447 * Originally Released Under LGPL - original licence link has changed is not relivant.
10450 * <script type="text/javascript">
10453 // Base class for reading structured data from a data source. This class is intended to be
10454 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10457 * @class Roo.data.DataReader
10458 * Base class for reading structured data from a data source. This class is intended to be
10459 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10462 Roo.data.DataReader = function(meta, recordType){
10466 this.recordType = recordType instanceof Array ?
10467 Roo.data.Record.create(recordType) : recordType;
10470 Roo.data.DataReader.prototype = {
10472 * Create an empty record
10473 * @param {Object} data (optional) - overlay some values
10474 * @return {Roo.data.Record} record created.
10476 newRow : function(d) {
10478 this.recordType.prototype.fields.each(function(c) {
10480 case 'int' : da[c.name] = 0; break;
10481 case 'date' : da[c.name] = new Date(); break;
10482 case 'float' : da[c.name] = 0.0; break;
10483 case 'boolean' : da[c.name] = false; break;
10484 default : da[c.name] = ""; break;
10488 return new this.recordType(Roo.apply(da, d));
10493 * Ext JS Library 1.1.1
10494 * Copyright(c) 2006-2007, Ext JS, LLC.
10496 * Originally Released Under LGPL - original licence link has changed is not relivant.
10499 * <script type="text/javascript">
10503 * @class Roo.data.DataProxy
10504 * @extends Roo.data.Observable
10505 * This class is an abstract base class for implementations which provide retrieval of
10506 * unformatted data objects.<br>
10508 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10509 * (of the appropriate type which knows how to parse the data object) to provide a block of
10510 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10512 * Custom implementations must implement the load method as described in
10513 * {@link Roo.data.HttpProxy#load}.
10515 Roo.data.DataProxy = function(){
10518 * @event beforeload
10519 * Fires before a network request is made to retrieve a data object.
10520 * @param {Object} This DataProxy object.
10521 * @param {Object} params The params parameter to the load function.
10526 * Fires before the load method's callback is called.
10527 * @param {Object} This DataProxy object.
10528 * @param {Object} o The data object.
10529 * @param {Object} arg The callback argument object passed to the load function.
10533 * @event loadexception
10534 * Fires if an Exception occurs during data retrieval.
10535 * @param {Object} This DataProxy object.
10536 * @param {Object} o The data object.
10537 * @param {Object} arg The callback argument object passed to the load function.
10538 * @param {Object} e The Exception.
10540 loadexception : true
10542 Roo.data.DataProxy.superclass.constructor.call(this);
10545 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10548 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10552 * Ext JS Library 1.1.1
10553 * Copyright(c) 2006-2007, Ext JS, LLC.
10555 * Originally Released Under LGPL - original licence link has changed is not relivant.
10558 * <script type="text/javascript">
10561 * @class Roo.data.MemoryProxy
10562 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10563 * to the Reader when its load method is called.
10565 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10567 Roo.data.MemoryProxy = function(data){
10571 Roo.data.MemoryProxy.superclass.constructor.call(this);
10575 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10577 * Load data from the requested source (in this case an in-memory
10578 * data object passed to the constructor), read the data object into
10579 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10580 * process that block using the passed callback.
10581 * @param {Object} params This parameter is not used by the MemoryProxy class.
10582 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10583 * object into a block of Roo.data.Records.
10584 * @param {Function} callback The function into which to pass the block of Roo.data.records.
10585 * The function must be passed <ul>
10586 * <li>The Record block object</li>
10587 * <li>The "arg" argument from the load function</li>
10588 * <li>A boolean success indicator</li>
10590 * @param {Object} scope The scope in which to call the callback
10591 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10593 load : function(params, reader, callback, scope, arg){
10594 params = params || {};
10597 result = reader.readRecords(this.data);
10599 this.fireEvent("loadexception", this, arg, null, e);
10600 callback.call(scope, null, arg, false);
10603 callback.call(scope, result, arg, true);
10607 update : function(params, records){
10612 * Ext JS Library 1.1.1
10613 * Copyright(c) 2006-2007, Ext JS, LLC.
10615 * Originally Released Under LGPL - original licence link has changed is not relivant.
10618 * <script type="text/javascript">
10621 * @class Roo.data.HttpProxy
10622 * @extends Roo.data.DataProxy
10623 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10624 * configured to reference a certain URL.<br><br>
10626 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10627 * from which the running page was served.<br><br>
10629 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10631 * Be aware that to enable the browser to parse an XML document, the server must set
10632 * the Content-Type header in the HTTP response to "text/xml".
10634 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10635 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
10636 * will be used to make the request.
10638 Roo.data.HttpProxy = function(conn){
10639 Roo.data.HttpProxy.superclass.constructor.call(this);
10640 // is conn a conn config or a real conn?
10642 this.useAjax = !conn || !conn.events;
10646 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10647 // thse are take from connection...
10650 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10653 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10654 * extra parameters to each request made by this object. (defaults to undefined)
10657 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10658 * to each request made by this object. (defaults to undefined)
10661 * @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)
10664 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10667 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10673 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10677 * Return the {@link Roo.data.Connection} object being used by this Proxy.
10678 * @return {Connection} The Connection object. This object may be used to subscribe to events on
10679 * a finer-grained basis than the DataProxy events.
10681 getConnection : function(){
10682 return this.useAjax ? Roo.Ajax : this.conn;
10686 * Load data from the configured {@link Roo.data.Connection}, read the data object into
10687 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10688 * process that block using the passed callback.
10689 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10690 * for the request to the remote server.
10691 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10692 * object into a block of Roo.data.Records.
10693 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10694 * The function must be passed <ul>
10695 * <li>The Record block object</li>
10696 * <li>The "arg" argument from the load function</li>
10697 * <li>A boolean success indicator</li>
10699 * @param {Object} scope The scope in which to call the callback
10700 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10702 load : function(params, reader, callback, scope, arg){
10703 if(this.fireEvent("beforeload", this, params) !== false){
10705 params : params || {},
10707 callback : callback,
10712 callback : this.loadResponse,
10716 Roo.applyIf(o, this.conn);
10717 if(this.activeRequest){
10718 Roo.Ajax.abort(this.activeRequest);
10720 this.activeRequest = Roo.Ajax.request(o);
10722 this.conn.request(o);
10725 callback.call(scope||this, null, arg, false);
10730 loadResponse : function(o, success, response){
10731 delete this.activeRequest;
10733 this.fireEvent("loadexception", this, o, response);
10734 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10739 result = o.reader.read(response);
10741 this.fireEvent("loadexception", this, o, response, e);
10742 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10746 this.fireEvent("load", this, o, o.request.arg);
10747 o.request.callback.call(o.request.scope, result, o.request.arg, true);
10751 update : function(dataSet){
10756 updateResponse : function(dataSet){
10761 * Ext JS Library 1.1.1
10762 * Copyright(c) 2006-2007, Ext JS, LLC.
10764 * Originally Released Under LGPL - original licence link has changed is not relivant.
10767 * <script type="text/javascript">
10771 * @class Roo.data.ScriptTagProxy
10772 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10773 * other than the originating domain of the running page.<br><br>
10775 * <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
10776 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10778 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10779 * source code that is used as the source inside a <script> tag.<br><br>
10781 * In order for the browser to process the returned data, the server must wrap the data object
10782 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10783 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10784 * depending on whether the callback name was passed:
10787 boolean scriptTag = false;
10788 String cb = request.getParameter("callback");
10791 response.setContentType("text/javascript");
10793 response.setContentType("application/x-json");
10795 Writer out = response.getWriter();
10797 out.write(cb + "(");
10799 out.print(dataBlock.toJsonString());
10806 * @param {Object} config A configuration object.
10808 Roo.data.ScriptTagProxy = function(config){
10809 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10810 Roo.apply(this, config);
10811 this.head = document.getElementsByTagName("head")[0];
10814 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10816 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10818 * @cfg {String} url The URL from which to request the data object.
10821 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10825 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10826 * the server the name of the callback function set up by the load call to process the returned data object.
10827 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10828 * javascript output which calls this named function passing the data object as its only parameter.
10830 callbackParam : "callback",
10832 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10833 * name to the request.
10838 * Load data from the configured URL, read the data object into
10839 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10840 * process that block using the passed callback.
10841 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10842 * for the request to the remote server.
10843 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10844 * object into a block of Roo.data.Records.
10845 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10846 * The function must be passed <ul>
10847 * <li>The Record block object</li>
10848 * <li>The "arg" argument from the load function</li>
10849 * <li>A boolean success indicator</li>
10851 * @param {Object} scope The scope in which to call the callback
10852 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10854 load : function(params, reader, callback, scope, arg){
10855 if(this.fireEvent("beforeload", this, params) !== false){
10857 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10859 var url = this.url;
10860 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10862 url += "&_dc=" + (new Date().getTime());
10864 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10867 cb : "stcCallback"+transId,
10868 scriptId : "stcScript"+transId,
10872 callback : callback,
10878 window[trans.cb] = function(o){
10879 conn.handleResponse(o, trans);
10882 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10884 if(this.autoAbort !== false){
10888 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10890 var script = document.createElement("script");
10891 script.setAttribute("src", url);
10892 script.setAttribute("type", "text/javascript");
10893 script.setAttribute("id", trans.scriptId);
10894 this.head.appendChild(script);
10896 this.trans = trans;
10898 callback.call(scope||this, null, arg, false);
10903 isLoading : function(){
10904 return this.trans ? true : false;
10908 * Abort the current server request.
10910 abort : function(){
10911 if(this.isLoading()){
10912 this.destroyTrans(this.trans);
10917 destroyTrans : function(trans, isLoaded){
10918 this.head.removeChild(document.getElementById(trans.scriptId));
10919 clearTimeout(trans.timeoutId);
10921 window[trans.cb] = undefined;
10923 delete window[trans.cb];
10926 // if hasn't been loaded, wait for load to remove it to prevent script error
10927 window[trans.cb] = function(){
10928 window[trans.cb] = undefined;
10930 delete window[trans.cb];
10937 handleResponse : function(o, trans){
10938 this.trans = false;
10939 this.destroyTrans(trans, true);
10942 result = trans.reader.readRecords(o);
10944 this.fireEvent("loadexception", this, o, trans.arg, e);
10945 trans.callback.call(trans.scope||window, null, trans.arg, false);
10948 this.fireEvent("load", this, o, trans.arg);
10949 trans.callback.call(trans.scope||window, result, trans.arg, true);
10953 handleFailure : function(trans){
10954 this.trans = false;
10955 this.destroyTrans(trans, false);
10956 this.fireEvent("loadexception", this, null, trans.arg);
10957 trans.callback.call(trans.scope||window, null, trans.arg, false);
10961 * Ext JS Library 1.1.1
10962 * Copyright(c) 2006-2007, Ext JS, LLC.
10964 * Originally Released Under LGPL - original licence link has changed is not relivant.
10967 * <script type="text/javascript">
10971 * @class Roo.data.JsonReader
10972 * @extends Roo.data.DataReader
10973 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10974 * based on mappings in a provided Roo.data.Record constructor.
10976 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10977 * in the reply previously.
10982 var RecordDef = Roo.data.Record.create([
10983 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
10984 {name: 'occupation'} // This field will use "occupation" as the mapping.
10986 var myReader = new Roo.data.JsonReader({
10987 totalProperty: "results", // The property which contains the total dataset size (optional)
10988 root: "rows", // The property which contains an Array of row objects
10989 id: "id" // The property within each row object that provides an ID for the record (optional)
10993 * This would consume a JSON file like this:
10995 { 'results': 2, 'rows': [
10996 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10997 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11000 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11001 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11002 * paged from the remote server.
11003 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11004 * @cfg {String} root name of the property which contains the Array of row objects.
11005 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11006 * @cfg {Array} fields Array of field definition objects
11008 * Create a new JsonReader
11009 * @param {Object} meta Metadata configuration options
11010 * @param {Object} recordType Either an Array of field definition objects,
11011 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11013 Roo.data.JsonReader = function(meta, recordType){
11016 // set some defaults:
11017 Roo.applyIf(meta, {
11018 totalProperty: 'total',
11019 successProperty : 'success',
11024 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11026 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11029 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11030 * Used by Store query builder to append _requestMeta to params.
11033 metaFromRemote : false,
11035 * This method is only used by a DataProxy which has retrieved data from a remote server.
11036 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11037 * @return {Object} data A data block which is used by an Roo.data.Store object as
11038 * a cache of Roo.data.Records.
11040 read : function(response){
11041 var json = response.responseText;
11043 var o = /* eval:var:o */ eval("("+json+")");
11045 throw {message: "JsonReader.read: Json object not found"};
11051 this.metaFromRemote = true;
11052 this.meta = o.metaData;
11053 this.recordType = Roo.data.Record.create(o.metaData.fields);
11054 this.onMetaChange(this.meta, this.recordType, o);
11056 return this.readRecords(o);
11059 // private function a store will implement
11060 onMetaChange : function(meta, recordType, o){
11067 simpleAccess: function(obj, subsc) {
11074 getJsonAccessor: function(){
11076 return function(expr) {
11078 return(re.test(expr))
11079 ? new Function("obj", "return obj." + expr)
11084 return Roo.emptyFn;
11089 * Create a data block containing Roo.data.Records from an XML document.
11090 * @param {Object} o An object which contains an Array of row objects in the property specified
11091 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11092 * which contains the total size of the dataset.
11093 * @return {Object} data A data block which is used by an Roo.data.Store object as
11094 * a cache of Roo.data.Records.
11096 readRecords : function(o){
11098 * After any data loads, the raw JSON data is available for further custom processing.
11102 var s = this.meta, Record = this.recordType,
11103 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11105 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11107 if(s.totalProperty) {
11108 this.getTotal = this.getJsonAccessor(s.totalProperty);
11110 if(s.successProperty) {
11111 this.getSuccess = this.getJsonAccessor(s.successProperty);
11113 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11115 var g = this.getJsonAccessor(s.id);
11116 this.getId = function(rec) {
11118 return (r === undefined || r === "") ? null : r;
11121 this.getId = function(){return null;};
11124 for(var jj = 0; jj < fl; jj++){
11126 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11127 this.ef[jj] = this.getJsonAccessor(map);
11131 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11132 if(s.totalProperty){
11133 var vt = parseInt(this.getTotal(o), 10);
11138 if(s.successProperty){
11139 var vs = this.getSuccess(o);
11140 if(vs === false || vs === 'false'){
11145 for(var i = 0; i < c; i++){
11148 var id = this.getId(n);
11149 for(var j = 0; j < fl; j++){
11151 var v = this.ef[j](n);
11153 Roo.log('missing convert for ' + f.name);
11157 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11159 var record = new Record(values, id);
11161 records[i] = record;
11167 totalRecords : totalRecords
11172 * Ext JS Library 1.1.1
11173 * Copyright(c) 2006-2007, Ext JS, LLC.
11175 * Originally Released Under LGPL - original licence link has changed is not relivant.
11178 * <script type="text/javascript">
11182 * @class Roo.data.ArrayReader
11183 * @extends Roo.data.DataReader
11184 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11185 * Each element of that Array represents a row of data fields. The
11186 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11187 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11191 var RecordDef = Roo.data.Record.create([
11192 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11193 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11195 var myReader = new Roo.data.ArrayReader({
11196 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11200 * This would consume an Array like this:
11202 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11204 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11206 * Create a new JsonReader
11207 * @param {Object} meta Metadata configuration options.
11208 * @param {Object} recordType Either an Array of field definition objects
11209 * as specified to {@link Roo.data.Record#create},
11210 * or an {@link Roo.data.Record} object
11211 * created using {@link Roo.data.Record#create}.
11213 Roo.data.ArrayReader = function(meta, recordType){
11214 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11217 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11219 * Create a data block containing Roo.data.Records from an XML document.
11220 * @param {Object} o An Array of row objects which represents the dataset.
11221 * @return {Object} data A data block which is used by an Roo.data.Store object as
11222 * a cache of Roo.data.Records.
11224 readRecords : function(o){
11225 var sid = this.meta ? this.meta.id : null;
11226 var recordType = this.recordType, fields = recordType.prototype.fields;
11229 for(var i = 0; i < root.length; i++){
11232 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11233 for(var j = 0, jlen = fields.length; j < jlen; j++){
11234 var f = fields.items[j];
11235 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11236 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11238 values[f.name] = v;
11240 var record = new recordType(values, id);
11242 records[records.length] = record;
11246 totalRecords : records.length
11255 * @class Roo.bootstrap.ComboBox
11256 * @extends Roo.bootstrap.TriggerField
11257 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11258 * @cfg {Boolean} append (true|false) default false
11259 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11260 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11261 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11262 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11263 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11264 * @cfg {Boolean} animate default true
11265 * @cfg {Boolean} emptyResultText only for touch device
11266 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11268 * Create a new ComboBox.
11269 * @param {Object} config Configuration options
11271 Roo.bootstrap.ComboBox = function(config){
11272 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11276 * Fires when the dropdown list is expanded
11277 * @param {Roo.bootstrap.ComboBox} combo This combo box
11282 * Fires when the dropdown list is collapsed
11283 * @param {Roo.bootstrap.ComboBox} combo This combo box
11287 * @event beforeselect
11288 * Fires before a list item is selected. Return false to cancel the selection.
11289 * @param {Roo.bootstrap.ComboBox} combo This combo box
11290 * @param {Roo.data.Record} record The data record returned from the underlying store
11291 * @param {Number} index The index of the selected item in the dropdown list
11293 'beforeselect' : true,
11296 * Fires when a list item is selected
11297 * @param {Roo.bootstrap.ComboBox} combo This combo box
11298 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11299 * @param {Number} index The index of the selected item in the dropdown list
11303 * @event beforequery
11304 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11305 * The event object passed has these properties:
11306 * @param {Roo.bootstrap.ComboBox} combo This combo box
11307 * @param {String} query The query
11308 * @param {Boolean} forceAll true to force "all" query
11309 * @param {Boolean} cancel true to cancel the query
11310 * @param {Object} e The query event object
11312 'beforequery': true,
11315 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11316 * @param {Roo.bootstrap.ComboBox} combo This combo box
11321 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11322 * @param {Roo.bootstrap.ComboBox} combo This combo box
11323 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11328 * Fires when the remove value from the combobox array
11329 * @param {Roo.bootstrap.ComboBox} combo This combo box
11333 * @event specialfilter
11334 * Fires when specialfilter
11335 * @param {Roo.bootstrap.ComboBox} combo This combo box
11337 'specialfilter' : true
11342 this.tickItems = [];
11344 this.selectedIndex = -1;
11345 if(this.mode == 'local'){
11346 if(config.queryDelay === undefined){
11347 this.queryDelay = 10;
11349 if(config.minChars === undefined){
11355 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11358 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11359 * rendering into an Roo.Editor, defaults to false)
11362 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11363 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11366 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11369 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11370 * the dropdown list (defaults to undefined, with no header element)
11374 * @cfg {String/Roo.Template} tpl The template to use to render the output
11378 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11380 listWidth: undefined,
11382 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11383 * mode = 'remote' or 'text' if mode = 'local')
11385 displayField: undefined,
11388 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11389 * mode = 'remote' or 'value' if mode = 'local').
11390 * Note: use of a valueField requires the user make a selection
11391 * in order for a value to be mapped.
11393 valueField: undefined,
11397 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11398 * field's data value (defaults to the underlying DOM element's name)
11400 hiddenName: undefined,
11402 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11406 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11408 selectedClass: 'active',
11411 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11415 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11416 * anchor positions (defaults to 'tl-bl')
11418 listAlign: 'tl-bl?',
11420 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11424 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11425 * query specified by the allQuery config option (defaults to 'query')
11427 triggerAction: 'query',
11429 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11430 * (defaults to 4, does not apply if editable = false)
11434 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11435 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11439 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11440 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11444 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11445 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11449 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11450 * when editable = true (defaults to false)
11452 selectOnFocus:false,
11454 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11456 queryParam: 'query',
11458 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
11459 * when mode = 'remote' (defaults to 'Loading...')
11461 loadingText: 'Loading...',
11463 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11467 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11471 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11472 * traditional select (defaults to true)
11476 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11480 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11484 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11485 * listWidth has a higher value)
11489 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11490 * allow the user to set arbitrary text into the field (defaults to false)
11492 forceSelection:false,
11494 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11495 * if typeAhead = true (defaults to 250)
11497 typeAheadDelay : 250,
11499 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11500 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11502 valueNotFoundText : undefined,
11504 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11506 blockFocus : false,
11509 * @cfg {Boolean} disableClear Disable showing of clear button.
11511 disableClear : false,
11513 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
11515 alwaysQuery : false,
11518 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
11523 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11525 invalidClass : "has-warning",
11528 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11530 validClass : "has-success",
11533 * @cfg {Boolean} specialFilter (true|false) special filter default false
11535 specialFilter : false,
11538 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11540 mobileTouchView : true,
11552 btnPosition : 'right',
11553 triggerList : true,
11554 showToggleBtn : true,
11556 emptyResultText: 'Empty',
11557 triggerText : 'Select',
11559 // element that contains real text value.. (when hidden is used..)
11561 getAutoCreate : function()
11569 if(Roo.isTouch && this.mobileTouchView){
11570 cfg = this.getAutoCreateTouchView();
11577 if(!this.tickable){
11578 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11583 * ComboBox with tickable selections
11586 var align = this.labelAlign || this.parentLabelAlign();
11589 cls : 'form-group roo-combobox-tickable' //input-group
11594 cls : 'tickable-buttons',
11599 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11600 html : this.triggerText
11606 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11613 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11620 buttons.cn.unshift({
11622 cls: 'select2-search-field-input'
11628 Roo.each(buttons.cn, function(c){
11630 c.cls += ' btn-' + _this.size;
11633 if (_this.disabled) {
11644 cls: 'form-hidden-field'
11648 cls: 'select2-choices',
11652 cls: 'select2-search-field',
11664 cls: 'select2-container input-group select2-container-multi',
11669 // cls: 'typeahead typeahead-long dropdown-menu',
11670 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
11675 if(this.hasFeedback && !this.allowBlank){
11679 cls: 'glyphicon form-control-feedback'
11682 combobox.cn.push(feedback);
11685 if (align ==='left' && this.fieldLabel.length) {
11687 Roo.log("left and has label");
11693 cls : 'control-label col-sm-' + this.labelWidth,
11694 html : this.fieldLabel
11698 cls : "col-sm-" + (12 - this.labelWidth),
11705 } else if ( this.fieldLabel.length) {
11711 //cls : 'input-group-addon',
11712 html : this.fieldLabel
11722 Roo.log(" no label && no align");
11729 ['xs','sm','md','lg'].map(function(size){
11730 if (settings[size]) {
11731 cfg.cls += ' col-' + size + '-' + settings[size];
11739 _initEventsCalled : false,
11742 initEvents: function()
11745 if (this._initEventsCalled) { // as we call render... prevent looping...
11748 this._initEventsCalled = true;
11751 throw "can not find store for combo";
11754 this.store = Roo.factory(this.store, Roo.data);
11756 // if we are building from html. then this element is so complex, that we can not really
11757 // use the rendered HTML.
11758 // so we have to trash and replace the previous code.
11759 if (Roo.XComponent.build_from_html) {
11761 // remove this element....
11762 var e = this.el.dom, k=0;
11763 while (e ) { e = e.previousSibling; ++k;}
11768 this.rendered = false;
11770 this.render(this.parent().getChildContainer(true), k);
11781 if(Roo.isTouch && this.mobileTouchView){
11782 this.initTouchView();
11787 this.initTickableEvents();
11791 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11793 if(this.hiddenName){
11795 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11797 this.hiddenField.dom.value =
11798 this.hiddenValue !== undefined ? this.hiddenValue :
11799 this.value !== undefined ? this.value : '';
11801 // prevent input submission
11802 this.el.dom.removeAttribute('name');
11803 this.hiddenField.dom.setAttribute('name', this.hiddenName);
11808 // this.el.dom.setAttribute('autocomplete', 'off');
11811 var cls = 'x-combo-list';
11813 //this.list = new Roo.Layer({
11814 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11820 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11821 _this.list.setWidth(lw);
11824 this.list.on('mouseover', this.onViewOver, this);
11825 this.list.on('mousemove', this.onViewMove, this);
11827 this.list.on('scroll', this.onViewScroll, this);
11830 this.list.swallowEvent('mousewheel');
11831 this.assetHeight = 0;
11834 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11835 this.assetHeight += this.header.getHeight();
11838 this.innerList = this.list.createChild({cls:cls+'-inner'});
11839 this.innerList.on('mouseover', this.onViewOver, this);
11840 this.innerList.on('mousemove', this.onViewMove, this);
11841 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11843 if(this.allowBlank && !this.pageSize && !this.disableClear){
11844 this.footer = this.list.createChild({cls:cls+'-ft'});
11845 this.pageTb = new Roo.Toolbar(this.footer);
11849 this.footer = this.list.createChild({cls:cls+'-ft'});
11850 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11851 {pageSize: this.pageSize});
11855 if (this.pageTb && this.allowBlank && !this.disableClear) {
11857 this.pageTb.add(new Roo.Toolbar.Fill(), {
11858 cls: 'x-btn-icon x-btn-clear',
11860 handler: function()
11863 _this.clearValue();
11864 _this.onSelect(false, -1);
11869 this.assetHeight += this.footer.getHeight();
11874 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11877 this.view = new Roo.View(this.list, this.tpl, {
11878 singleSelect:true, store: this.store, selectedClass: this.selectedClass
11880 //this.view.wrapEl.setDisplayed(false);
11881 this.view.on('click', this.onViewClick, this);
11885 this.store.on('beforeload', this.onBeforeLoad, this);
11886 this.store.on('load', this.onLoad, this);
11887 this.store.on('loadexception', this.onLoadException, this);
11889 if(this.resizable){
11890 this.resizer = new Roo.Resizable(this.list, {
11891 pinned:true, handles:'se'
11893 this.resizer.on('resize', function(r, w, h){
11894 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11895 this.listWidth = w;
11896 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11897 this.restrictHeight();
11899 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11902 if(!this.editable){
11903 this.editable = true;
11904 this.setEditable(false);
11909 if (typeof(this.events.add.listeners) != 'undefined') {
11911 this.addicon = this.wrap.createChild(
11912 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
11914 this.addicon.on('click', function(e) {
11915 this.fireEvent('add', this);
11918 if (typeof(this.events.edit.listeners) != 'undefined') {
11920 this.editicon = this.wrap.createChild(
11921 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
11922 if (this.addicon) {
11923 this.editicon.setStyle('margin-left', '40px');
11925 this.editicon.on('click', function(e) {
11927 // we fire even if inothing is selected..
11928 this.fireEvent('edit', this, this.lastData );
11934 this.keyNav = new Roo.KeyNav(this.inputEl(), {
11935 "up" : function(e){
11936 this.inKeyMode = true;
11940 "down" : function(e){
11941 if(!this.isExpanded()){
11942 this.onTriggerClick();
11944 this.inKeyMode = true;
11949 "enter" : function(e){
11950 // this.onViewClick();
11954 if(this.fireEvent("specialkey", this, e)){
11955 this.onViewClick(false);
11961 "esc" : function(e){
11965 "tab" : function(e){
11968 if(this.fireEvent("specialkey", this, e)){
11969 this.onViewClick(false);
11977 doRelay : function(foo, bar, hname){
11978 if(hname == 'down' || this.scope.isExpanded()){
11979 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11988 this.queryDelay = Math.max(this.queryDelay || 10,
11989 this.mode == 'local' ? 10 : 250);
11992 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11994 if(this.typeAhead){
11995 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11997 if(this.editable !== false){
11998 this.inputEl().on("keyup", this.onKeyUp, this);
12000 if(this.forceSelection){
12001 this.inputEl().on('blur', this.doForce, this);
12005 this.choices = this.el.select('ul.select2-choices', true).first();
12006 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12010 initTickableEvents: function()
12014 if(this.hiddenName){
12016 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12018 this.hiddenField.dom.value =
12019 this.hiddenValue !== undefined ? this.hiddenValue :
12020 this.value !== undefined ? this.value : '';
12022 // prevent input submission
12023 this.el.dom.removeAttribute('name');
12024 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12029 // this.list = this.el.select('ul.dropdown-menu',true).first();
12031 this.choices = this.el.select('ul.select2-choices', true).first();
12032 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12033 if(this.triggerList){
12034 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12037 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12038 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12040 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12041 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12043 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12044 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12046 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12047 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12048 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12051 this.cancelBtn.hide();
12056 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12057 _this.list.setWidth(lw);
12060 this.list.on('mouseover', this.onViewOver, this);
12061 this.list.on('mousemove', this.onViewMove, this);
12063 this.list.on('scroll', this.onViewScroll, this);
12066 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>';
12069 this.view = new Roo.View(this.list, this.tpl, {
12070 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12073 //this.view.wrapEl.setDisplayed(false);
12074 this.view.on('click', this.onViewClick, this);
12078 this.store.on('beforeload', this.onBeforeLoad, this);
12079 this.store.on('load', this.onLoad, this);
12080 this.store.on('loadexception', this.onLoadException, this);
12083 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12084 "up" : function(e){
12085 this.inKeyMode = true;
12089 "down" : function(e){
12090 this.inKeyMode = true;
12094 "enter" : function(e){
12095 if(this.fireEvent("specialkey", this, e)){
12096 this.onViewClick(false);
12102 "esc" : function(e){
12103 this.onTickableFooterButtonClick(e, false, false);
12106 "tab" : function(e){
12107 this.fireEvent("specialkey", this, e);
12109 this.onTickableFooterButtonClick(e, false, false);
12116 doRelay : function(e, fn, key){
12117 if(this.scope.isExpanded()){
12118 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12127 this.queryDelay = Math.max(this.queryDelay || 10,
12128 this.mode == 'local' ? 10 : 250);
12131 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12133 if(this.typeAhead){
12134 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12137 if(this.editable !== false){
12138 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12143 onDestroy : function(){
12145 this.view.setStore(null);
12146 this.view.el.removeAllListeners();
12147 this.view.el.remove();
12148 this.view.purgeListeners();
12151 this.list.dom.innerHTML = '';
12155 this.store.un('beforeload', this.onBeforeLoad, this);
12156 this.store.un('load', this.onLoad, this);
12157 this.store.un('loadexception', this.onLoadException, this);
12159 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12163 fireKey : function(e){
12164 if(e.isNavKeyPress() && !this.list.isVisible()){
12165 this.fireEvent("specialkey", this, e);
12170 onResize: function(w, h){
12171 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12173 // if(typeof w != 'number'){
12174 // // we do not handle it!?!?
12177 // var tw = this.trigger.getWidth();
12178 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12179 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12181 // this.inputEl().setWidth( this.adjustWidth('input', x));
12183 // //this.trigger.setStyle('left', x+'px');
12185 // if(this.list && this.listWidth === undefined){
12186 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12187 // this.list.setWidth(lw);
12188 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12196 * Allow or prevent the user from directly editing the field text. If false is passed,
12197 * the user will only be able to select from the items defined in the dropdown list. This method
12198 * is the runtime equivalent of setting the 'editable' config option at config time.
12199 * @param {Boolean} value True to allow the user to directly edit the field text
12201 setEditable : function(value){
12202 if(value == this.editable){
12205 this.editable = value;
12207 this.inputEl().dom.setAttribute('readOnly', true);
12208 this.inputEl().on('mousedown', this.onTriggerClick, this);
12209 this.inputEl().addClass('x-combo-noedit');
12211 this.inputEl().dom.setAttribute('readOnly', false);
12212 this.inputEl().un('mousedown', this.onTriggerClick, this);
12213 this.inputEl().removeClass('x-combo-noedit');
12219 onBeforeLoad : function(combo,opts){
12220 if(!this.hasFocus){
12224 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12226 this.restrictHeight();
12227 this.selectedIndex = -1;
12231 onLoad : function(){
12233 this.hasQuery = false;
12235 if(!this.hasFocus){
12239 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12240 this.loading.hide();
12243 if(this.store.getCount() > 0){
12245 this.restrictHeight();
12246 if(this.lastQuery == this.allQuery){
12247 if(this.editable && !this.tickable){
12248 this.inputEl().dom.select();
12252 !this.selectByValue(this.value, true) &&
12255 !this.store.lastOptions ||
12256 typeof(this.store.lastOptions.add) == 'undefined' ||
12257 this.store.lastOptions.add != true
12260 this.select(0, true);
12263 if(this.autoFocus){
12266 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12267 this.taTask.delay(this.typeAheadDelay);
12271 this.onEmptyResults();
12277 onLoadException : function()
12279 this.hasQuery = false;
12281 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12282 this.loading.hide();
12285 if(this.tickable && this.editable){
12291 Roo.log(this.store.reader.jsonData);
12292 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12294 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12300 onTypeAhead : function(){
12301 if(this.store.getCount() > 0){
12302 var r = this.store.getAt(0);
12303 var newValue = r.data[this.displayField];
12304 var len = newValue.length;
12305 var selStart = this.getRawValue().length;
12307 if(selStart != len){
12308 this.setRawValue(newValue);
12309 this.selectText(selStart, newValue.length);
12315 onSelect : function(record, index){
12317 if(this.fireEvent('beforeselect', this, record, index) !== false){
12319 this.setFromData(index > -1 ? record.data : false);
12322 this.fireEvent('select', this, record, index);
12327 * Returns the currently selected field value or empty string if no value is set.
12328 * @return {String} value The selected value
12330 getValue : function(){
12333 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12336 if(this.valueField){
12337 return typeof this.value != 'undefined' ? this.value : '';
12339 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12344 * Clears any text/value currently set in the field
12346 clearValue : function(){
12347 if(this.hiddenField){
12348 this.hiddenField.dom.value = '';
12351 this.setRawValue('');
12352 this.lastSelectionText = '';
12353 this.lastData = false;
12355 var close = this.closeTriggerEl();
12364 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12365 * will be displayed in the field. If the value does not match the data value of an existing item,
12366 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12367 * Otherwise the field will be blank (although the value will still be set).
12368 * @param {String} value The value to match
12370 setValue : function(v){
12377 if(this.valueField){
12378 var r = this.findRecord(this.valueField, v);
12380 text = r.data[this.displayField];
12381 }else if(this.valueNotFoundText !== undefined){
12382 text = this.valueNotFoundText;
12385 this.lastSelectionText = text;
12386 if(this.hiddenField){
12387 this.hiddenField.dom.value = v;
12389 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12392 var close = this.closeTriggerEl();
12395 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12399 * @property {Object} the last set data for the element
12404 * Sets the value of the field based on a object which is related to the record format for the store.
12405 * @param {Object} value the value to set as. or false on reset?
12407 setFromData : function(o){
12414 var dv = ''; // display value
12415 var vv = ''; // value value..
12417 if (this.displayField) {
12418 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12420 // this is an error condition!!!
12421 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12424 if(this.valueField){
12425 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12428 var close = this.closeTriggerEl();
12431 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12434 if(this.hiddenField){
12435 this.hiddenField.dom.value = vv;
12437 this.lastSelectionText = dv;
12438 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12442 // no hidden field.. - we store the value in 'value', but still display
12443 // display field!!!!
12444 this.lastSelectionText = dv;
12445 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12452 reset : function(){
12453 // overridden so that last data is reset..
12460 this.setValue(this.originalValue);
12461 this.clearInvalid();
12462 this.lastData = false;
12464 this.view.clearSelections();
12468 findRecord : function(prop, value){
12470 if(this.store.getCount() > 0){
12471 this.store.each(function(r){
12472 if(r.data[prop] == value){
12482 getName: function()
12484 // returns hidden if it's set..
12485 if (!this.rendered) {return ''};
12486 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
12490 onViewMove : function(e, t){
12491 this.inKeyMode = false;
12495 onViewOver : function(e, t){
12496 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12499 var item = this.view.findItemFromChild(t);
12502 var index = this.view.indexOf(item);
12503 this.select(index, false);
12508 onViewClick : function(view, doFocus, el, e)
12510 var index = this.view.getSelectedIndexes()[0];
12512 var r = this.store.getAt(index);
12516 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12523 Roo.each(this.tickItems, function(v,k){
12525 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12526 _this.tickItems.splice(k, 1);
12528 if(typeof(e) == 'undefined' && view == false){
12529 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12541 this.tickItems.push(r.data);
12543 if(typeof(e) == 'undefined' && view == false){
12544 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12551 this.onSelect(r, index);
12553 if(doFocus !== false && !this.blockFocus){
12554 this.inputEl().focus();
12559 restrictHeight : function(){
12560 //this.innerList.dom.style.height = '';
12561 //var inner = this.innerList.dom;
12562 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12563 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12564 //this.list.beginUpdate();
12565 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12566 this.list.alignTo(this.inputEl(), this.listAlign);
12567 this.list.alignTo(this.inputEl(), this.listAlign);
12568 //this.list.endUpdate();
12572 onEmptyResults : function(){
12574 if(this.tickable && this.editable){
12575 this.restrictHeight();
12583 * Returns true if the dropdown list is expanded, else false.
12585 isExpanded : function(){
12586 return this.list.isVisible();
12590 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12591 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12592 * @param {String} value The data value of the item to select
12593 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12594 * selected item if it is not currently in view (defaults to true)
12595 * @return {Boolean} True if the value matched an item in the list, else false
12597 selectByValue : function(v, scrollIntoView){
12598 if(v !== undefined && v !== null){
12599 var r = this.findRecord(this.valueField || this.displayField, v);
12601 this.select(this.store.indexOf(r), scrollIntoView);
12609 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12610 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12611 * @param {Number} index The zero-based index of the list item to select
12612 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12613 * selected item if it is not currently in view (defaults to true)
12615 select : function(index, scrollIntoView){
12616 this.selectedIndex = index;
12617 this.view.select(index);
12618 if(scrollIntoView !== false){
12619 var el = this.view.getNode(index);
12621 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12624 this.list.scrollChildIntoView(el, false);
12630 selectNext : function(){
12631 var ct = this.store.getCount();
12633 if(this.selectedIndex == -1){
12635 }else if(this.selectedIndex < ct-1){
12636 this.select(this.selectedIndex+1);
12642 selectPrev : function(){
12643 var ct = this.store.getCount();
12645 if(this.selectedIndex == -1){
12647 }else if(this.selectedIndex != 0){
12648 this.select(this.selectedIndex-1);
12654 onKeyUp : function(e){
12655 if(this.editable !== false && !e.isSpecialKey()){
12656 this.lastKey = e.getKey();
12657 this.dqTask.delay(this.queryDelay);
12662 validateBlur : function(){
12663 return !this.list || !this.list.isVisible();
12667 initQuery : function(){
12669 var v = this.getRawValue();
12671 if(this.tickable && this.editable){
12672 v = this.tickableInputEl().getValue();
12679 doForce : function(){
12680 if(this.inputEl().dom.value.length > 0){
12681 this.inputEl().dom.value =
12682 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12688 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
12689 * query allowing the query action to be canceled if needed.
12690 * @param {String} query The SQL query to execute
12691 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12692 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
12693 * saved in the current store (defaults to false)
12695 doQuery : function(q, forceAll){
12697 if(q === undefined || q === null){
12702 forceAll: forceAll,
12706 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12711 forceAll = qe.forceAll;
12712 if(forceAll === true || (q.length >= this.minChars)){
12714 this.hasQuery = true;
12716 if(this.lastQuery != q || this.alwaysQuery){
12717 this.lastQuery = q;
12718 if(this.mode == 'local'){
12719 this.selectedIndex = -1;
12721 this.store.clearFilter();
12724 if(this.specialFilter){
12725 this.fireEvent('specialfilter', this);
12730 this.store.filter(this.displayField, q);
12733 this.store.fireEvent("datachanged", this.store);
12740 this.store.baseParams[this.queryParam] = q;
12742 var options = {params : this.getParams(q)};
12745 options.add = true;
12746 options.params.start = this.page * this.pageSize;
12749 this.store.load(options);
12752 * this code will make the page width larger, at the beginning, the list not align correctly,
12753 * we should expand the list on onLoad
12754 * so command out it
12759 this.selectedIndex = -1;
12764 this.loadNext = false;
12768 getParams : function(q){
12770 //p[this.queryParam] = q;
12774 p.limit = this.pageSize;
12780 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12782 collapse : function(){
12783 if(!this.isExpanded()){
12790 this.hasFocus = false;
12792 this.cancelBtn.hide();
12793 this.trigger.show();
12796 this.tickableInputEl().dom.value = '';
12797 this.tickableInputEl().blur();
12802 Roo.get(document).un('mousedown', this.collapseIf, this);
12803 Roo.get(document).un('mousewheel', this.collapseIf, this);
12804 if (!this.editable) {
12805 Roo.get(document).un('keydown', this.listKeyPress, this);
12807 this.fireEvent('collapse', this);
12811 collapseIf : function(e){
12812 var in_combo = e.within(this.el);
12813 var in_list = e.within(this.list);
12814 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12816 if (in_combo || in_list || is_list) {
12817 //e.stopPropagation();
12822 this.onTickableFooterButtonClick(e, false, false);
12830 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12832 expand : function(){
12834 if(this.isExpanded() || !this.hasFocus){
12838 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12839 this.list.setWidth(lw);
12846 this.restrictHeight();
12850 this.tickItems = Roo.apply([], this.item);
12853 this.cancelBtn.show();
12854 this.trigger.hide();
12857 this.tickableInputEl().focus();
12862 Roo.get(document).on('mousedown', this.collapseIf, this);
12863 Roo.get(document).on('mousewheel', this.collapseIf, this);
12864 if (!this.editable) {
12865 Roo.get(document).on('keydown', this.listKeyPress, this);
12868 this.fireEvent('expand', this);
12872 // Implements the default empty TriggerField.onTriggerClick function
12873 onTriggerClick : function(e)
12875 Roo.log('trigger click');
12877 if(this.disabled || !this.triggerList){
12882 this.loadNext = false;
12884 if(this.isExpanded()){
12886 if (!this.blockFocus) {
12887 this.inputEl().focus();
12891 this.hasFocus = true;
12892 if(this.triggerAction == 'all') {
12893 this.doQuery(this.allQuery, true);
12895 this.doQuery(this.getRawValue());
12897 if (!this.blockFocus) {
12898 this.inputEl().focus();
12903 onTickableTriggerClick : function(e)
12910 this.loadNext = false;
12911 this.hasFocus = true;
12913 if(this.triggerAction == 'all') {
12914 this.doQuery(this.allQuery, true);
12916 this.doQuery(this.getRawValue());
12920 onSearchFieldClick : function(e)
12922 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12923 this.onTickableFooterButtonClick(e, false, false);
12927 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12932 this.loadNext = false;
12933 this.hasFocus = true;
12935 if(this.triggerAction == 'all') {
12936 this.doQuery(this.allQuery, true);
12938 this.doQuery(this.getRawValue());
12942 listKeyPress : function(e)
12944 //Roo.log('listkeypress');
12945 // scroll to first matching element based on key pres..
12946 if (e.isSpecialKey()) {
12949 var k = String.fromCharCode(e.getKey()).toUpperCase();
12952 var csel = this.view.getSelectedNodes();
12953 var cselitem = false;
12955 var ix = this.view.indexOf(csel[0]);
12956 cselitem = this.store.getAt(ix);
12957 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12963 this.store.each(function(v) {
12965 // start at existing selection.
12966 if (cselitem.id == v.id) {
12972 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12973 match = this.store.indexOf(v);
12979 if (match === false) {
12980 return true; // no more action?
12983 this.view.select(match);
12984 var sn = Roo.get(this.view.getSelectedNodes()[0])
12985 sn.scrollIntoView(sn.dom.parentNode, false);
12988 onViewScroll : function(e, t){
12990 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){
12994 this.hasQuery = true;
12996 this.loading = this.list.select('.loading', true).first();
12998 if(this.loading === null){
12999 this.list.createChild({
13001 cls: 'loading select2-more-results select2-active',
13002 html: 'Loading more results...'
13005 this.loading = this.list.select('.loading', true).first();
13007 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13009 this.loading.hide();
13012 this.loading.show();
13017 this.loadNext = true;
13019 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13024 addItem : function(o)
13026 var dv = ''; // display value
13028 if (this.displayField) {
13029 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13031 // this is an error condition!!!
13032 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13039 var choice = this.choices.createChild({
13041 cls: 'select2-search-choice',
13050 cls: 'select2-search-choice-close',
13055 }, this.searchField);
13057 var close = choice.select('a.select2-search-choice-close', true).first()
13059 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13067 this.inputEl().dom.value = '';
13072 onRemoveItem : function(e, _self, o)
13074 e.preventDefault();
13076 this.lastItem = Roo.apply([], this.item);
13078 var index = this.item.indexOf(o.data) * 1;
13081 Roo.log('not this item?!');
13085 this.item.splice(index, 1);
13090 this.fireEvent('remove', this, e);
13096 syncValue : function()
13098 if(!this.item.length){
13105 Roo.each(this.item, function(i){
13106 if(_this.valueField){
13107 value.push(i[_this.valueField]);
13114 this.value = value.join(',');
13116 if(this.hiddenField){
13117 this.hiddenField.dom.value = this.value;
13120 this.store.fireEvent("datachanged", this.store);
13123 clearItem : function()
13125 if(!this.multiple){
13131 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13139 if(this.tickable && !Roo.isTouch){
13140 this.view.refresh();
13144 inputEl: function ()
13146 if(Roo.isTouch && this.mobileTouchView){
13147 return this.el.select('input.form-control',true).first();
13151 return this.searchField;
13154 return this.el.select('input.form-control',true).first();
13158 onTickableFooterButtonClick : function(e, btn, el)
13160 e.preventDefault();
13162 this.lastItem = Roo.apply([], this.item);
13164 if(btn && btn.name == 'cancel'){
13165 this.tickItems = Roo.apply([], this.item);
13174 Roo.each(this.tickItems, function(o){
13182 validate : function()
13184 var v = this.getRawValue();
13187 v = this.getValue();
13190 if(this.disabled || this.allowBlank || v.length){
13195 this.markInvalid();
13199 tickableInputEl : function()
13201 if(!this.tickable || !this.editable){
13202 return this.inputEl();
13205 return this.inputEl().select('.select2-search-field-input', true).first();
13209 getAutoCreateTouchView : function()
13214 cls: 'form-group' //input-group
13220 type : this.inputType,
13221 cls : 'form-control x-combo-noedit',
13222 autocomplete: 'new-password',
13223 placeholder : this.placeholder || '',
13228 input.name = this.name;
13232 input.cls += ' input-' + this.size;
13235 if (this.disabled) {
13236 input.disabled = true;
13247 inputblock.cls += ' input-group';
13249 inputblock.cn.unshift({
13251 cls : 'input-group-addon',
13256 if(this.removable && !this.multiple){
13257 inputblock.cls += ' roo-removable';
13259 inputblock.cn.push({
13262 cls : 'roo-combo-removable-btn close'
13266 if(this.hasFeedback && !this.allowBlank){
13268 inputblock.cls += ' has-feedback';
13270 inputblock.cn.push({
13272 cls: 'glyphicon form-control-feedback'
13279 inputblock.cls += (this.before) ? '' : ' input-group';
13281 inputblock.cn.push({
13283 cls : 'input-group-addon',
13294 cls: 'form-hidden-field'
13308 cls: 'form-hidden-field'
13312 cls: 'select2-choices',
13316 cls: 'select2-search-field',
13329 cls: 'select2-container input-group',
13336 combobox.cls += ' select2-container-multi';
13339 var align = this.labelAlign || this.parentLabelAlign();
13343 if(this.fieldLabel.length){
13345 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13346 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13351 cls : 'control-label ' + lw,
13352 html : this.fieldLabel
13364 var settings = this;
13366 ['xs','sm','md','lg'].map(function(size){
13367 if (settings[size]) {
13368 cfg.cls += ' col-' + size + '-' + settings[size];
13375 initTouchView : function()
13377 this.renderTouchView();
13379 this.touchViewEl.on('scroll', function(){
13380 this.el.dom.scrollTop = 0;
13383 this.inputEl().on("click", this.showTouchView, this);
13384 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13385 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13387 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13389 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13390 this.store.on('load', this.onTouchViewLoad, this);
13391 this.store.on('loadexception', this.onTouchViewLoadException, this);
13393 if(this.hiddenName){
13395 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13397 this.hiddenField.dom.value =
13398 this.hiddenValue !== undefined ? this.hiddenValue :
13399 this.value !== undefined ? this.value : '';
13401 this.el.dom.removeAttribute('name');
13402 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13406 this.choices = this.el.select('ul.select2-choices', true).first();
13407 this.searchField = this.el.select('ul li.select2-search-field', true).first();
13410 if(this.removable && !this.multiple){
13411 var close = this.closeTriggerEl();
13413 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13414 close.on('click', this.removeBtnClick, this, close);
13423 renderTouchView : function()
13425 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13426 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13428 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13429 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13431 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13432 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13433 this.touchViewBodyEl.setStyle('overflow', 'auto');
13435 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13436 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13438 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13439 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13443 showTouchView : function()
13445 this.touchViewHeaderEl.hide();
13447 if(this.fieldLabel.length){
13448 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13449 this.touchViewHeaderEl.show();
13452 this.touchViewEl.show();
13454 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13455 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13457 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13459 if(this.fieldLabel.length){
13460 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13463 this.touchViewBodyEl.setHeight(bodyHeight);
13467 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13469 this.touchViewEl.addClass('in');
13472 this.doTouchViewQuery();
13476 hideTouchView : function()
13478 this.touchViewEl.removeClass('in');
13482 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13484 this.touchViewEl.setStyle('display', 'none');
13489 setTouchViewValue : function()
13496 Roo.each(this.tickItems, function(o){
13501 this.hideTouchView();
13504 doTouchViewQuery : function()
13513 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13517 if(!this.alwaysQuery || this.mode == 'local'){
13518 this.onTouchViewLoad();
13525 onTouchViewBeforeLoad : function(combo,opts)
13531 onTouchViewLoad : function()
13533 if(this.store.getCount() < 1){
13534 this.onTouchViewEmptyResults();
13538 this.clearTouchView();
13540 var rawValue = this.getRawValue();
13542 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13544 this.tickItems = [];
13546 this.store.data.each(function(d, rowIndex){
13547 var row = this.touchViewListGroup.createChild(template);
13549 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13550 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13553 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13554 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13557 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13558 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13559 this.tickItems.push(d.data);
13562 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13566 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13568 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13570 if(this.fieldLabel.length){
13571 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13574 var listHeight = this.touchViewListGroup.getHeight();
13578 if(firstChecked && listHeight > bodyHeight){
13579 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13584 onTouchViewLoadException : function()
13586 this.hideTouchView();
13589 onTouchViewEmptyResults : function()
13591 this.clearTouchView();
13593 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13595 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13599 clearTouchView : function()
13601 this.touchViewListGroup.dom.innerHTML = '';
13604 onTouchViewClick : function(e, el, o)
13606 e.preventDefault();
13609 var rowIndex = o.rowIndex;
13611 var r = this.store.getAt(rowIndex);
13613 if(!this.multiple){
13614 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13615 c.dom.removeAttribute('checked');
13618 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13620 this.setFromData(r.data);
13622 var close = this.closeTriggerEl();
13628 this.hideTouchView();
13630 this.fireEvent('select', this, r, rowIndex);
13635 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13636 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13637 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13641 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13642 this.addItem(r.data);
13643 this.tickItems.push(r.data);
13649 * @cfg {Boolean} grow
13653 * @cfg {Number} growMin
13657 * @cfg {Number} growMax
13666 Roo.apply(Roo.bootstrap.ComboBox, {
13670 cls: 'modal-header',
13692 cls: 'list-group-item',
13696 cls: 'roo-combobox-list-group-item-value'
13700 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13714 listItemCheckbox : {
13716 cls: 'list-group-item',
13720 cls: 'roo-combobox-list-group-item-value'
13724 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13740 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13745 cls: 'modal-footer',
13753 cls: 'col-xs-6 text-left',
13756 cls: 'btn btn-danger roo-touch-view-cancel',
13762 cls: 'col-xs-6 text-right',
13765 cls: 'btn btn-success roo-touch-view-ok',
13776 Roo.apply(Roo.bootstrap.ComboBox, {
13778 touchViewTemplate : {
13780 cls: 'modal fade roo-combobox-touch-view',
13784 cls: 'modal-dialog',
13788 cls: 'modal-content',
13790 Roo.bootstrap.ComboBox.header,
13791 Roo.bootstrap.ComboBox.body,
13792 Roo.bootstrap.ComboBox.footer
13801 * Ext JS Library 1.1.1
13802 * Copyright(c) 2006-2007, Ext JS, LLC.
13804 * Originally Released Under LGPL - original licence link has changed is not relivant.
13807 * <script type="text/javascript">
13812 * @extends Roo.util.Observable
13813 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
13814 * This class also supports single and multi selection modes. <br>
13815 * Create a data model bound view:
13817 var store = new Roo.data.Store(...);
13819 var view = new Roo.View({
13821 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
13823 singleSelect: true,
13824 selectedClass: "ydataview-selected",
13828 // listen for node click?
13829 view.on("click", function(vw, index, node, e){
13830 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13834 dataModel.load("foobar.xml");
13836 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13838 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13839 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13841 * Note: old style constructor is still suported (container, template, config)
13844 * Create a new View
13845 * @param {Object} config The config object
13848 Roo.View = function(config, depreciated_tpl, depreciated_config){
13850 this.parent = false;
13852 if (typeof(depreciated_tpl) == 'undefined') {
13853 // new way.. - universal constructor.
13854 Roo.apply(this, config);
13855 this.el = Roo.get(this.el);
13858 this.el = Roo.get(config);
13859 this.tpl = depreciated_tpl;
13860 Roo.apply(this, depreciated_config);
13862 this.wrapEl = this.el.wrap().wrap();
13863 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13866 if(typeof(this.tpl) == "string"){
13867 this.tpl = new Roo.Template(this.tpl);
13869 // support xtype ctors..
13870 this.tpl = new Roo.factory(this.tpl, Roo);
13874 this.tpl.compile();
13879 * @event beforeclick
13880 * Fires before a click is processed. Returns false to cancel the default action.
13881 * @param {Roo.View} this
13882 * @param {Number} index The index of the target node
13883 * @param {HTMLElement} node The target node
13884 * @param {Roo.EventObject} e The raw event object
13886 "beforeclick" : true,
13889 * Fires when a template node is clicked.
13890 * @param {Roo.View} this
13891 * @param {Number} index The index of the target node
13892 * @param {HTMLElement} node The target node
13893 * @param {Roo.EventObject} e The raw event object
13898 * Fires when a template node is double clicked.
13899 * @param {Roo.View} this
13900 * @param {Number} index The index of the target node
13901 * @param {HTMLElement} node The target node
13902 * @param {Roo.EventObject} e The raw event object
13906 * @event contextmenu
13907 * Fires when a template node is right clicked.
13908 * @param {Roo.View} this
13909 * @param {Number} index The index of the target node
13910 * @param {HTMLElement} node The target node
13911 * @param {Roo.EventObject} e The raw event object
13913 "contextmenu" : true,
13915 * @event selectionchange
13916 * Fires when the selected nodes change.
13917 * @param {Roo.View} this
13918 * @param {Array} selections Array of the selected nodes
13920 "selectionchange" : true,
13923 * @event beforeselect
13924 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13925 * @param {Roo.View} this
13926 * @param {HTMLElement} node The node to be selected
13927 * @param {Array} selections Array of currently selected nodes
13929 "beforeselect" : true,
13931 * @event preparedata
13932 * Fires on every row to render, to allow you to change the data.
13933 * @param {Roo.View} this
13934 * @param {Object} data to be rendered (change this)
13936 "preparedata" : true
13944 "click": this.onClick,
13945 "dblclick": this.onDblClick,
13946 "contextmenu": this.onContextMenu,
13950 this.selections = [];
13952 this.cmp = new Roo.CompositeElementLite([]);
13954 this.store = Roo.factory(this.store, Roo.data);
13955 this.setStore(this.store, true);
13958 if ( this.footer && this.footer.xtype) {
13960 var fctr = this.wrapEl.appendChild(document.createElement("div"));
13962 this.footer.dataSource = this.store;
13963 this.footer.container = fctr;
13964 this.footer = Roo.factory(this.footer, Roo);
13965 fctr.insertFirst(this.el);
13967 // this is a bit insane - as the paging toolbar seems to detach the el..
13968 // dom.parentNode.parentNode.parentNode
13969 // they get detached?
13973 Roo.View.superclass.constructor.call(this);
13978 Roo.extend(Roo.View, Roo.util.Observable, {
13981 * @cfg {Roo.data.Store} store Data store to load data from.
13986 * @cfg {String|Roo.Element} el The container element.
13991 * @cfg {String|Roo.Template} tpl The template used by this View
13995 * @cfg {String} dataName the named area of the template to use as the data area
13996 * Works with domtemplates roo-name="name"
14000 * @cfg {String} selectedClass The css class to add to selected nodes
14002 selectedClass : "x-view-selected",
14004 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14009 * @cfg {String} text to display on mask (default Loading)
14013 * @cfg {Boolean} multiSelect Allow multiple selection
14015 multiSelect : false,
14017 * @cfg {Boolean} singleSelect Allow single selection
14019 singleSelect: false,
14022 * @cfg {Boolean} toggleSelect - selecting
14024 toggleSelect : false,
14027 * @cfg {Boolean} tickable - selecting
14032 * Returns the element this view is bound to.
14033 * @return {Roo.Element}
14035 getEl : function(){
14036 return this.wrapEl;
14042 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14044 refresh : function(){
14045 //Roo.log('refresh');
14048 // if we are using something like 'domtemplate', then
14049 // the what gets used is:
14050 // t.applySubtemplate(NAME, data, wrapping data..)
14051 // the outer template then get' applied with
14052 // the store 'extra data'
14053 // and the body get's added to the
14054 // roo-name="data" node?
14055 // <span class='roo-tpl-{name}'></span> ?????
14059 this.clearSelections();
14060 this.el.update("");
14062 var records = this.store.getRange();
14063 if(records.length < 1) {
14065 // is this valid?? = should it render a template??
14067 this.el.update(this.emptyText);
14071 if (this.dataName) {
14072 this.el.update(t.apply(this.store.meta)); //????
14073 el = this.el.child('.roo-tpl-' + this.dataName);
14076 for(var i = 0, len = records.length; i < len; i++){
14077 var data = this.prepareData(records[i].data, i, records[i]);
14078 this.fireEvent("preparedata", this, data, i, records[i]);
14080 var d = Roo.apply({}, data);
14083 Roo.apply(d, {'roo-id' : Roo.id()});
14087 Roo.each(this.parent.item, function(item){
14088 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14091 Roo.apply(d, {'roo-data-checked' : 'checked'});
14095 html[html.length] = Roo.util.Format.trim(
14097 t.applySubtemplate(this.dataName, d, this.store.meta) :
14104 el.update(html.join(""));
14105 this.nodes = el.dom.childNodes;
14106 this.updateIndexes(0);
14111 * Function to override to reformat the data that is sent to
14112 * the template for each node.
14113 * DEPRICATED - use the preparedata event handler.
14114 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14115 * a JSON object for an UpdateManager bound view).
14117 prepareData : function(data, index, record)
14119 this.fireEvent("preparedata", this, data, index, record);
14123 onUpdate : function(ds, record){
14124 // Roo.log('on update');
14125 this.clearSelections();
14126 var index = this.store.indexOf(record);
14127 var n = this.nodes[index];
14128 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14129 n.parentNode.removeChild(n);
14130 this.updateIndexes(index, index);
14136 onAdd : function(ds, records, index)
14138 //Roo.log(['on Add', ds, records, index] );
14139 this.clearSelections();
14140 if(this.nodes.length == 0){
14144 var n = this.nodes[index];
14145 for(var i = 0, len = records.length; i < len; i++){
14146 var d = this.prepareData(records[i].data, i, records[i]);
14148 this.tpl.insertBefore(n, d);
14151 this.tpl.append(this.el, d);
14154 this.updateIndexes(index);
14157 onRemove : function(ds, record, index){
14158 // Roo.log('onRemove');
14159 this.clearSelections();
14160 var el = this.dataName ?
14161 this.el.child('.roo-tpl-' + this.dataName) :
14164 el.dom.removeChild(this.nodes[index]);
14165 this.updateIndexes(index);
14169 * Refresh an individual node.
14170 * @param {Number} index
14172 refreshNode : function(index){
14173 this.onUpdate(this.store, this.store.getAt(index));
14176 updateIndexes : function(startIndex, endIndex){
14177 var ns = this.nodes;
14178 startIndex = startIndex || 0;
14179 endIndex = endIndex || ns.length - 1;
14180 for(var i = startIndex; i <= endIndex; i++){
14181 ns[i].nodeIndex = i;
14186 * Changes the data store this view uses and refresh the view.
14187 * @param {Store} store
14189 setStore : function(store, initial){
14190 if(!initial && this.store){
14191 this.store.un("datachanged", this.refresh);
14192 this.store.un("add", this.onAdd);
14193 this.store.un("remove", this.onRemove);
14194 this.store.un("update", this.onUpdate);
14195 this.store.un("clear", this.refresh);
14196 this.store.un("beforeload", this.onBeforeLoad);
14197 this.store.un("load", this.onLoad);
14198 this.store.un("loadexception", this.onLoad);
14202 store.on("datachanged", this.refresh, this);
14203 store.on("add", this.onAdd, this);
14204 store.on("remove", this.onRemove, this);
14205 store.on("update", this.onUpdate, this);
14206 store.on("clear", this.refresh, this);
14207 store.on("beforeload", this.onBeforeLoad, this);
14208 store.on("load", this.onLoad, this);
14209 store.on("loadexception", this.onLoad, this);
14217 * onbeforeLoad - masks the loading area.
14220 onBeforeLoad : function(store,opts)
14222 //Roo.log('onBeforeLoad');
14224 this.el.update("");
14226 this.el.mask(this.mask ? this.mask : "Loading" );
14228 onLoad : function ()
14235 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14236 * @param {HTMLElement} node
14237 * @return {HTMLElement} The template node
14239 findItemFromChild : function(node){
14240 var el = this.dataName ?
14241 this.el.child('.roo-tpl-' + this.dataName,true) :
14244 if(!node || node.parentNode == el){
14247 var p = node.parentNode;
14248 while(p && p != el){
14249 if(p.parentNode == el){
14258 onClick : function(e){
14259 var item = this.findItemFromChild(e.getTarget());
14261 var index = this.indexOf(item);
14262 if(this.onItemClick(item, index, e) !== false){
14263 this.fireEvent("click", this, index, item, e);
14266 this.clearSelections();
14271 onContextMenu : function(e){
14272 var item = this.findItemFromChild(e.getTarget());
14274 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14279 onDblClick : function(e){
14280 var item = this.findItemFromChild(e.getTarget());
14282 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14286 onItemClick : function(item, index, e)
14288 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14291 if (this.toggleSelect) {
14292 var m = this.isSelected(item) ? 'unselect' : 'select';
14295 _t[m](item, true, false);
14298 if(this.multiSelect || this.singleSelect){
14299 if(this.multiSelect && e.shiftKey && this.lastSelection){
14300 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14302 this.select(item, this.multiSelect && e.ctrlKey);
14303 this.lastSelection = item;
14306 if(!this.tickable){
14307 e.preventDefault();
14315 * Get the number of selected nodes.
14318 getSelectionCount : function(){
14319 return this.selections.length;
14323 * Get the currently selected nodes.
14324 * @return {Array} An array of HTMLElements
14326 getSelectedNodes : function(){
14327 return this.selections;
14331 * Get the indexes of the selected nodes.
14334 getSelectedIndexes : function(){
14335 var indexes = [], s = this.selections;
14336 for(var i = 0, len = s.length; i < len; i++){
14337 indexes.push(s[i].nodeIndex);
14343 * Clear all selections
14344 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14346 clearSelections : function(suppressEvent){
14347 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14348 this.cmp.elements = this.selections;
14349 this.cmp.removeClass(this.selectedClass);
14350 this.selections = [];
14351 if(!suppressEvent){
14352 this.fireEvent("selectionchange", this, this.selections);
14358 * Returns true if the passed node is selected
14359 * @param {HTMLElement/Number} node The node or node index
14360 * @return {Boolean}
14362 isSelected : function(node){
14363 var s = this.selections;
14367 node = this.getNode(node);
14368 return s.indexOf(node) !== -1;
14373 * @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
14374 * @param {Boolean} keepExisting (optional) true to keep existing selections
14375 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14377 select : function(nodeInfo, keepExisting, suppressEvent){
14378 if(nodeInfo instanceof Array){
14380 this.clearSelections(true);
14382 for(var i = 0, len = nodeInfo.length; i < len; i++){
14383 this.select(nodeInfo[i], true, true);
14387 var node = this.getNode(nodeInfo);
14388 if(!node || this.isSelected(node)){
14389 return; // already selected.
14392 this.clearSelections(true);
14395 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14396 Roo.fly(node).addClass(this.selectedClass);
14397 this.selections.push(node);
14398 if(!suppressEvent){
14399 this.fireEvent("selectionchange", this, this.selections);
14407 * @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
14408 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14409 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14411 unselect : function(nodeInfo, keepExisting, suppressEvent)
14413 if(nodeInfo instanceof Array){
14414 Roo.each(this.selections, function(s) {
14415 this.unselect(s, nodeInfo);
14419 var node = this.getNode(nodeInfo);
14420 if(!node || !this.isSelected(node)){
14421 //Roo.log("not selected");
14422 return; // not selected.
14426 Roo.each(this.selections, function(s) {
14428 Roo.fly(node).removeClass(this.selectedClass);
14435 this.selections= ns;
14436 this.fireEvent("selectionchange", this, this.selections);
14440 * Gets a template node.
14441 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14442 * @return {HTMLElement} The node or null if it wasn't found
14444 getNode : function(nodeInfo){
14445 if(typeof nodeInfo == "string"){
14446 return document.getElementById(nodeInfo);
14447 }else if(typeof nodeInfo == "number"){
14448 return this.nodes[nodeInfo];
14454 * Gets a range template nodes.
14455 * @param {Number} startIndex
14456 * @param {Number} endIndex
14457 * @return {Array} An array of nodes
14459 getNodes : function(start, end){
14460 var ns = this.nodes;
14461 start = start || 0;
14462 end = typeof end == "undefined" ? ns.length - 1 : end;
14465 for(var i = start; i <= end; i++){
14469 for(var i = start; i >= end; i--){
14477 * Finds the index of the passed node
14478 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14479 * @return {Number} The index of the node or -1
14481 indexOf : function(node){
14482 node = this.getNode(node);
14483 if(typeof node.nodeIndex == "number"){
14484 return node.nodeIndex;
14486 var ns = this.nodes;
14487 for(var i = 0, len = ns.length; i < len; i++){
14498 * based on jquery fullcalendar
14502 Roo.bootstrap = Roo.bootstrap || {};
14504 * @class Roo.bootstrap.Calendar
14505 * @extends Roo.bootstrap.Component
14506 * Bootstrap Calendar class
14507 * @cfg {Boolean} loadMask (true|false) default false
14508 * @cfg {Object} header generate the user specific header of the calendar, default false
14511 * Create a new Container
14512 * @param {Object} config The config object
14517 Roo.bootstrap.Calendar = function(config){
14518 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14522 * Fires when a date is selected
14523 * @param {DatePicker} this
14524 * @param {Date} date The selected date
14528 * @event monthchange
14529 * Fires when the displayed month changes
14530 * @param {DatePicker} this
14531 * @param {Date} date The selected month
14533 'monthchange': true,
14535 * @event evententer
14536 * Fires when mouse over an event
14537 * @param {Calendar} this
14538 * @param {event} Event
14540 'evententer': true,
14542 * @event eventleave
14543 * Fires when the mouse leaves an
14544 * @param {Calendar} this
14547 'eventleave': true,
14549 * @event eventclick
14550 * Fires when the mouse click an
14551 * @param {Calendar} this
14560 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
14563 * @cfg {Number} startDay
14564 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14572 getAutoCreate : function(){
14575 var fc_button = function(name, corner, style, content ) {
14576 return Roo.apply({},{
14578 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
14580 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14583 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14594 style : 'width:100%',
14601 cls : 'fc-header-left',
14603 fc_button('prev', 'left', 'arrow', '‹' ),
14604 fc_button('next', 'right', 'arrow', '›' ),
14605 { tag: 'span', cls: 'fc-header-space' },
14606 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
14614 cls : 'fc-header-center',
14618 cls: 'fc-header-title',
14621 html : 'month / year'
14629 cls : 'fc-header-right',
14631 /* fc_button('month', 'left', '', 'month' ),
14632 fc_button('week', '', '', 'week' ),
14633 fc_button('day', 'right', '', 'day' )
14645 header = this.header;
14648 var cal_heads = function() {
14650 // fixme - handle this.
14652 for (var i =0; i < Date.dayNames.length; i++) {
14653 var d = Date.dayNames[i];
14656 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14657 html : d.substring(0,3)
14661 ret[0].cls += ' fc-first';
14662 ret[6].cls += ' fc-last';
14665 var cal_cell = function(n) {
14668 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14673 cls: 'fc-day-number',
14677 cls: 'fc-day-content',
14681 style: 'position: relative;' // height: 17px;
14693 var cal_rows = function() {
14696 for (var r = 0; r < 6; r++) {
14703 for (var i =0; i < Date.dayNames.length; i++) {
14704 var d = Date.dayNames[i];
14705 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14708 row.cn[0].cls+=' fc-first';
14709 row.cn[0].cn[0].style = 'min-height:90px';
14710 row.cn[6].cls+=' fc-last';
14714 ret[0].cls += ' fc-first';
14715 ret[4].cls += ' fc-prev-last';
14716 ret[5].cls += ' fc-last';
14723 cls: 'fc-border-separate',
14724 style : 'width:100%',
14732 cls : 'fc-first fc-last',
14750 cls : 'fc-content',
14751 style : "position: relative;",
14754 cls : 'fc-view fc-view-month fc-grid',
14755 style : 'position: relative',
14756 unselectable : 'on',
14759 cls : 'fc-event-container',
14760 style : 'position:absolute;z-index:8;top:0;left:0;'
14778 initEvents : function()
14781 throw "can not find store for calendar";
14787 style: "text-align:center",
14791 style: "background-color:white;width:50%;margin:250 auto",
14795 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
14806 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14808 var size = this.el.select('.fc-content', true).first().getSize();
14809 this.maskEl.setSize(size.width, size.height);
14810 this.maskEl.enableDisplayMode("block");
14811 if(!this.loadMask){
14812 this.maskEl.hide();
14815 this.store = Roo.factory(this.store, Roo.data);
14816 this.store.on('load', this.onLoad, this);
14817 this.store.on('beforeload', this.onBeforeLoad, this);
14821 this.cells = this.el.select('.fc-day',true);
14822 //Roo.log(this.cells);
14823 this.textNodes = this.el.query('.fc-day-number');
14824 this.cells.addClassOnOver('fc-state-hover');
14826 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14827 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14828 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14829 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14831 this.on('monthchange', this.onMonthChange, this);
14833 this.update(new Date().clearTime());
14836 resize : function() {
14837 var sz = this.el.getSize();
14839 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14840 this.el.select('.fc-day-content div',true).setHeight(34);
14845 showPrevMonth : function(e){
14846 this.update(this.activeDate.add("mo", -1));
14848 showToday : function(e){
14849 this.update(new Date().clearTime());
14852 showNextMonth : function(e){
14853 this.update(this.activeDate.add("mo", 1));
14857 showPrevYear : function(){
14858 this.update(this.activeDate.add("y", -1));
14862 showNextYear : function(){
14863 this.update(this.activeDate.add("y", 1));
14868 update : function(date)
14870 var vd = this.activeDate;
14871 this.activeDate = date;
14872 // if(vd && this.el){
14873 // var t = date.getTime();
14874 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14875 // Roo.log('using add remove');
14877 // this.fireEvent('monthchange', this, date);
14879 // this.cells.removeClass("fc-state-highlight");
14880 // this.cells.each(function(c){
14881 // if(c.dateValue == t){
14882 // c.addClass("fc-state-highlight");
14883 // setTimeout(function(){
14884 // try{c.dom.firstChild.focus();}catch(e){}
14894 var days = date.getDaysInMonth();
14896 var firstOfMonth = date.getFirstDateOfMonth();
14897 var startingPos = firstOfMonth.getDay()-this.startDay;
14899 if(startingPos < this.startDay){
14903 var pm = date.add(Date.MONTH, -1);
14904 var prevStart = pm.getDaysInMonth()-startingPos;
14906 this.cells = this.el.select('.fc-day',true);
14907 this.textNodes = this.el.query('.fc-day-number');
14908 this.cells.addClassOnOver('fc-state-hover');
14910 var cells = this.cells.elements;
14911 var textEls = this.textNodes;
14913 Roo.each(cells, function(cell){
14914 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14917 days += startingPos;
14919 // convert everything to numbers so it's fast
14920 var day = 86400000;
14921 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14924 //Roo.log(prevStart);
14926 var today = new Date().clearTime().getTime();
14927 var sel = date.clearTime().getTime();
14928 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14929 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14930 var ddMatch = this.disabledDatesRE;
14931 var ddText = this.disabledDatesText;
14932 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14933 var ddaysText = this.disabledDaysText;
14934 var format = this.format;
14936 var setCellClass = function(cal, cell){
14940 //Roo.log('set Cell Class');
14942 var t = d.getTime();
14946 cell.dateValue = t;
14948 cell.className += " fc-today";
14949 cell.className += " fc-state-highlight";
14950 cell.title = cal.todayText;
14953 // disable highlight in other month..
14954 //cell.className += " fc-state-highlight";
14959 cell.className = " fc-state-disabled";
14960 cell.title = cal.minText;
14964 cell.className = " fc-state-disabled";
14965 cell.title = cal.maxText;
14969 if(ddays.indexOf(d.getDay()) != -1){
14970 cell.title = ddaysText;
14971 cell.className = " fc-state-disabled";
14974 if(ddMatch && format){
14975 var fvalue = d.dateFormat(format);
14976 if(ddMatch.test(fvalue)){
14977 cell.title = ddText.replace("%0", fvalue);
14978 cell.className = " fc-state-disabled";
14982 if (!cell.initialClassName) {
14983 cell.initialClassName = cell.dom.className;
14986 cell.dom.className = cell.initialClassName + ' ' + cell.className;
14991 for(; i < startingPos; i++) {
14992 textEls[i].innerHTML = (++prevStart);
14993 d.setDate(d.getDate()+1);
14995 cells[i].className = "fc-past fc-other-month";
14996 setCellClass(this, cells[i]);
15001 for(; i < days; i++){
15002 intDay = i - startingPos + 1;
15003 textEls[i].innerHTML = (intDay);
15004 d.setDate(d.getDate()+1);
15006 cells[i].className = ''; // "x-date-active";
15007 setCellClass(this, cells[i]);
15011 for(; i < 42; i++) {
15012 textEls[i].innerHTML = (++extraDays);
15013 d.setDate(d.getDate()+1);
15015 cells[i].className = "fc-future fc-other-month";
15016 setCellClass(this, cells[i]);
15019 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15021 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15023 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15024 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15026 if(totalRows != 6){
15027 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15028 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15031 this.fireEvent('monthchange', this, date);
15035 if(!this.internalRender){
15036 var main = this.el.dom.firstChild;
15037 var w = main.offsetWidth;
15038 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15039 Roo.fly(main).setWidth(w);
15040 this.internalRender = true;
15041 // opera does not respect the auto grow header center column
15042 // then, after it gets a width opera refuses to recalculate
15043 // without a second pass
15044 if(Roo.isOpera && !this.secondPass){
15045 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15046 this.secondPass = true;
15047 this.update.defer(10, this, [date]);
15054 findCell : function(dt) {
15055 dt = dt.clearTime().getTime();
15057 this.cells.each(function(c){
15058 //Roo.log("check " +c.dateValue + '?=' + dt);
15059 if(c.dateValue == dt){
15069 findCells : function(ev) {
15070 var s = ev.start.clone().clearTime().getTime();
15072 var e= ev.end.clone().clearTime().getTime();
15075 this.cells.each(function(c){
15076 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15078 if(c.dateValue > e){
15081 if(c.dateValue < s){
15090 // findBestRow: function(cells)
15094 // for (var i =0 ; i < cells.length;i++) {
15095 // ret = Math.max(cells[i].rows || 0,ret);
15102 addItem : function(ev)
15104 // look for vertical location slot in
15105 var cells = this.findCells(ev);
15107 // ev.row = this.findBestRow(cells);
15109 // work out the location.
15113 for(var i =0; i < cells.length; i++) {
15115 cells[i].row = cells[0].row;
15118 cells[i].row = cells[i].row + 1;
15128 if (crow.start.getY() == cells[i].getY()) {
15130 crow.end = cells[i];
15147 cells[0].events.push(ev);
15149 this.calevents.push(ev);
15152 clearEvents: function() {
15154 if(!this.calevents){
15158 Roo.each(this.cells.elements, function(c){
15164 Roo.each(this.calevents, function(e) {
15165 Roo.each(e.els, function(el) {
15166 el.un('mouseenter' ,this.onEventEnter, this);
15167 el.un('mouseleave' ,this.onEventLeave, this);
15172 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15178 renderEvents: function()
15182 this.cells.each(function(c) {
15191 if(c.row != c.events.length){
15192 r = 4 - (4 - (c.row - c.events.length));
15195 c.events = ev.slice(0, r);
15196 c.more = ev.slice(r);
15198 if(c.more.length && c.more.length == 1){
15199 c.events.push(c.more.pop());
15202 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15206 this.cells.each(function(c) {
15208 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15211 for (var e = 0; e < c.events.length; e++){
15212 var ev = c.events[e];
15213 var rows = ev.rows;
15215 for(var i = 0; i < rows.length; i++) {
15217 // how many rows should it span..
15220 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15221 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15223 unselectable : "on",
15226 cls: 'fc-event-inner',
15230 // cls: 'fc-event-time',
15231 // html : cells.length > 1 ? '' : ev.time
15235 cls: 'fc-event-title',
15236 html : String.format('{0}', ev.title)
15243 cls: 'ui-resizable-handle ui-resizable-e',
15244 html : '  '
15251 cfg.cls += ' fc-event-start';
15253 if ((i+1) == rows.length) {
15254 cfg.cls += ' fc-event-end';
15257 var ctr = _this.el.select('.fc-event-container',true).first();
15258 var cg = ctr.createChild(cfg);
15260 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15261 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15263 var r = (c.more.length) ? 1 : 0;
15264 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15265 cg.setWidth(ebox.right - sbox.x -2);
15267 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15268 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15269 cg.on('click', _this.onEventClick, _this, ev);
15280 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15281 style : 'position: absolute',
15282 unselectable : "on",
15285 cls: 'fc-event-inner',
15289 cls: 'fc-event-title',
15297 cls: 'ui-resizable-handle ui-resizable-e',
15298 html : '  '
15304 var ctr = _this.el.select('.fc-event-container',true).first();
15305 var cg = ctr.createChild(cfg);
15307 var sbox = c.select('.fc-day-content',true).first().getBox();
15308 var ebox = c.select('.fc-day-content',true).first().getBox();
15310 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15311 cg.setWidth(ebox.right - sbox.x -2);
15313 cg.on('click', _this.onMoreEventClick, _this, c.more);
15323 onEventEnter: function (e, el,event,d) {
15324 this.fireEvent('evententer', this, el, event);
15327 onEventLeave: function (e, el,event,d) {
15328 this.fireEvent('eventleave', this, el, event);
15331 onEventClick: function (e, el,event,d) {
15332 this.fireEvent('eventclick', this, el, event);
15335 onMonthChange: function () {
15339 onMoreEventClick: function(e, el, more)
15343 this.calpopover.placement = 'right';
15344 this.calpopover.setTitle('More');
15346 this.calpopover.setContent('');
15348 var ctr = this.calpopover.el.select('.popover-content', true).first();
15350 Roo.each(more, function(m){
15352 cls : 'fc-event-hori fc-event-draggable',
15355 var cg = ctr.createChild(cfg);
15357 cg.on('click', _this.onEventClick, _this, m);
15360 this.calpopover.show(el);
15365 onLoad: function ()
15367 this.calevents = [];
15370 if(this.store.getCount() > 0){
15371 this.store.data.each(function(d){
15374 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15375 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15376 time : d.data.start_time,
15377 title : d.data.title,
15378 description : d.data.description,
15379 venue : d.data.venue
15384 this.renderEvents();
15386 if(this.calevents.length && this.loadMask){
15387 this.maskEl.hide();
15391 onBeforeLoad: function()
15393 this.clearEvents();
15395 this.maskEl.show();
15409 * @class Roo.bootstrap.Popover
15410 * @extends Roo.bootstrap.Component
15411 * Bootstrap Popover class
15412 * @cfg {String} html contents of the popover (or false to use children..)
15413 * @cfg {String} title of popover (or false to hide)
15414 * @cfg {String} placement how it is placed
15415 * @cfg {String} trigger click || hover (or false to trigger manually)
15416 * @cfg {String} over what (parent or false to trigger manually.)
15417 * @cfg {Number} delay - delay before showing
15420 * Create a new Popover
15421 * @param {Object} config The config object
15424 Roo.bootstrap.Popover = function(config){
15425 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15428 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
15430 title: 'Fill in a title',
15433 placement : 'right',
15434 trigger : 'hover', // hover
15440 can_build_overlaid : false,
15442 getChildContainer : function()
15444 return this.el.select('.popover-content',true).first();
15447 getAutoCreate : function(){
15448 Roo.log('make popover?');
15450 cls : 'popover roo-dynamic',
15451 style: 'display:block',
15457 cls : 'popover-inner',
15461 cls: 'popover-title',
15465 cls : 'popover-content',
15476 setTitle: function(str)
15479 this.el.select('.popover-title',true).first().dom.innerHTML = str;
15481 setContent: function(str)
15484 this.el.select('.popover-content',true).first().dom.innerHTML = str;
15486 // as it get's added to the bottom of the page.
15487 onRender : function(ct, position)
15489 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15491 var cfg = Roo.apply({}, this.getAutoCreate());
15495 cfg.cls += ' ' + this.cls;
15498 cfg.style = this.style;
15500 Roo.log("adding to ")
15501 this.el = Roo.get(document.body).createChild(cfg, position);
15507 initEvents : function()
15509 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15510 this.el.enableDisplayMode('block');
15512 if (this.over === false) {
15515 if (this.triggers === false) {
15518 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15519 var triggers = this.trigger ? this.trigger.split(' ') : [];
15520 Roo.each(triggers, function(trigger) {
15522 if (trigger == 'click') {
15523 on_el.on('click', this.toggle, this);
15524 } else if (trigger != 'manual') {
15525 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
15526 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15528 on_el.on(eventIn ,this.enter, this);
15529 on_el.on(eventOut, this.leave, this);
15540 toggle : function () {
15541 this.hoverState == 'in' ? this.leave() : this.enter();
15544 enter : function () {
15547 clearTimeout(this.timeout);
15549 this.hoverState = 'in';
15551 if (!this.delay || !this.delay.show) {
15556 this.timeout = setTimeout(function () {
15557 if (_t.hoverState == 'in') {
15560 }, this.delay.show)
15562 leave : function() {
15563 clearTimeout(this.timeout);
15565 this.hoverState = 'out';
15567 if (!this.delay || !this.delay.hide) {
15572 this.timeout = setTimeout(function () {
15573 if (_t.hoverState == 'out') {
15576 }, this.delay.hide)
15579 show : function (on_el)
15582 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15585 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15586 if (this.html !== false) {
15587 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15589 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15590 if (!this.title.length) {
15591 this.el.select('.popover-title',true).hide();
15594 var placement = typeof this.placement == 'function' ?
15595 this.placement.call(this, this.el, on_el) :
15598 var autoToken = /\s?auto?\s?/i;
15599 var autoPlace = autoToken.test(placement);
15601 placement = placement.replace(autoToken, '') || 'top';
15605 //this.el.setXY([0,0]);
15607 this.el.dom.style.display='block';
15608 this.el.addClass(placement);
15610 //this.el.appendTo(on_el);
15612 var p = this.getPosition();
15613 var box = this.el.getBox();
15618 var align = Roo.bootstrap.Popover.alignment[placement];
15619 this.el.alignTo(on_el, align[0],align[1]);
15620 //var arrow = this.el.select('.arrow',true).first();
15621 //arrow.set(align[2],
15623 this.el.addClass('in');
15626 if (this.el.hasClass('fade')) {
15633 this.el.setXY([0,0]);
15634 this.el.removeClass('in');
15636 this.hoverState = null;
15642 Roo.bootstrap.Popover.alignment = {
15643 'left' : ['r-l', [-10,0], 'right'],
15644 'right' : ['l-r', [10,0], 'left'],
15645 'bottom' : ['t-b', [0,10], 'top'],
15646 'top' : [ 'b-t', [0,-10], 'bottom']
15657 * @class Roo.bootstrap.Progress
15658 * @extends Roo.bootstrap.Component
15659 * Bootstrap Progress class
15660 * @cfg {Boolean} striped striped of the progress bar
15661 * @cfg {Boolean} active animated of the progress bar
15665 * Create a new Progress
15666 * @param {Object} config The config object
15669 Roo.bootstrap.Progress = function(config){
15670 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15673 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
15678 getAutoCreate : function(){
15686 cfg.cls += ' progress-striped';
15690 cfg.cls += ' active';
15709 * @class Roo.bootstrap.ProgressBar
15710 * @extends Roo.bootstrap.Component
15711 * Bootstrap ProgressBar class
15712 * @cfg {Number} aria_valuenow aria-value now
15713 * @cfg {Number} aria_valuemin aria-value min
15714 * @cfg {Number} aria_valuemax aria-value max
15715 * @cfg {String} label label for the progress bar
15716 * @cfg {String} panel (success | info | warning | danger )
15717 * @cfg {String} role role of the progress bar
15718 * @cfg {String} sr_only text
15722 * Create a new ProgressBar
15723 * @param {Object} config The config object
15726 Roo.bootstrap.ProgressBar = function(config){
15727 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15730 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
15734 aria_valuemax : 100,
15740 getAutoCreate : function()
15745 cls: 'progress-bar',
15746 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15758 cfg.role = this.role;
15761 if(this.aria_valuenow){
15762 cfg['aria-valuenow'] = this.aria_valuenow;
15765 if(this.aria_valuemin){
15766 cfg['aria-valuemin'] = this.aria_valuemin;
15769 if(this.aria_valuemax){
15770 cfg['aria-valuemax'] = this.aria_valuemax;
15773 if(this.label && !this.sr_only){
15774 cfg.html = this.label;
15778 cfg.cls += ' progress-bar-' + this.panel;
15784 update : function(aria_valuenow)
15786 this.aria_valuenow = aria_valuenow;
15788 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15803 * @class Roo.bootstrap.TabGroup
15804 * @extends Roo.bootstrap.Column
15805 * Bootstrap Column class
15806 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15807 * @cfg {Boolean} carousel true to make the group behave like a carousel
15808 * @cfg {Number} bullets show the panel pointer.. default 0
15809 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15810 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15811 * @cfg {Number} timer auto slide timer .. default 0 millisecond
15814 * Create a new TabGroup
15815 * @param {Object} config The config object
15818 Roo.bootstrap.TabGroup = function(config){
15819 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15821 this.navId = Roo.id();
15824 Roo.bootstrap.TabGroup.register(this);
15828 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
15831 transition : false,
15836 slideOnTouch : false,
15838 getAutoCreate : function()
15840 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15842 cfg.cls += ' tab-content';
15844 Roo.log('get auto create...............');
15846 if (this.carousel) {
15847 cfg.cls += ' carousel slide';
15850 cls : 'carousel-inner'
15853 if(this.bullets > 0 && !Roo.isTouch){
15856 cls : 'carousel-bullets',
15860 if(this.bullets_cls){
15861 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15864 for (var i = 0; i < this.bullets; i++){
15866 cls : 'bullet bullet-' + i
15874 cfg.cn[0].cn = bullets;
15881 initEvents: function()
15883 Roo.log('-------- init events on tab group ---------');
15885 if(this.bullets > 0 && !Roo.isTouch){
15891 if(Roo.isTouch && this.slideOnTouch){
15892 this.el.on("touchstart", this.onTouchStart, this);
15895 if(this.autoslide){
15898 this.slideFn = window.setInterval(function() {
15899 _this.showPanelNext();
15905 onTouchStart : function(e, el, o)
15907 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15911 this.showPanelNext();
15914 getChildContainer : function()
15916 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15920 * register a Navigation item
15921 * @param {Roo.bootstrap.NavItem} the navitem to add
15923 register : function(item)
15925 this.tabs.push( item);
15926 item.navId = this.navId; // not really needed..
15930 getActivePanel : function()
15933 Roo.each(this.tabs, function(t) {
15943 getPanelByName : function(n)
15946 Roo.each(this.tabs, function(t) {
15947 if (t.tabId == n) {
15955 indexOfPanel : function(p)
15958 Roo.each(this.tabs, function(t,i) {
15959 if (t.tabId == p.tabId) {
15968 * show a specific panel
15969 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15970 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15972 showPanel : function (pan)
15974 if(this.transition){
15975 Roo.log("waiting for the transitionend");
15979 if (typeof(pan) == 'number') {
15980 pan = this.tabs[pan];
15982 if (typeof(pan) == 'string') {
15983 pan = this.getPanelByName(pan);
15985 if (pan.tabId == this.getActivePanel().tabId) {
15988 var cur = this.getActivePanel();
15990 if (false === cur.fireEvent('beforedeactivate')) {
15994 if(this.bullets > 0 && !Roo.isTouch){
15995 this.setActiveBullet(this.indexOfPanel(pan));
15998 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16000 this.transition = true;
16001 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16002 var lr = dir == 'next' ? 'left' : 'right';
16003 pan.el.addClass(dir); // or prev
16004 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16005 cur.el.addClass(lr); // or right
16006 pan.el.addClass(lr);
16009 cur.el.on('transitionend', function() {
16010 Roo.log("trans end?");
16012 pan.el.removeClass([lr,dir]);
16013 pan.setActive(true);
16015 cur.el.removeClass([lr]);
16016 cur.setActive(false);
16018 _this.transition = false;
16020 }, this, { single: true } );
16025 cur.setActive(false);
16026 pan.setActive(true);
16031 showPanelNext : function()
16033 var i = this.indexOfPanel(this.getActivePanel());
16035 if (i >= this.tabs.length - 1 && !this.autoslide) {
16039 if (i >= this.tabs.length - 1 && this.autoslide) {
16043 this.showPanel(this.tabs[i+1]);
16046 showPanelPrev : function()
16048 var i = this.indexOfPanel(this.getActivePanel());
16050 if (i < 1 && !this.autoslide) {
16054 if (i < 1 && this.autoslide) {
16055 i = this.tabs.length;
16058 this.showPanel(this.tabs[i-1]);
16061 initBullet : function()
16069 for (var i = 0; i < this.bullets; i++){
16070 var bullet = this.el.select('.bullet-' + i, true).first();
16076 bullet.on('click', (function(e, el, o, ii, t){
16078 e.preventDefault();
16080 _this.showPanel(ii);
16082 if(_this.autoslide && _this.slideFn){
16083 clearInterval(_this.slideFn);
16084 _this.slideFn = window.setInterval(function() {
16085 _this.showPanelNext();
16089 }).createDelegate(this, [i, bullet], true));
16093 setActiveBullet : function(i)
16099 Roo.each(this.el.select('.bullet', true).elements, function(el){
16100 el.removeClass('selected');
16103 var bullet = this.el.select('.bullet-' + i, true).first();
16109 bullet.addClass('selected');
16120 Roo.apply(Roo.bootstrap.TabGroup, {
16124 * register a Navigation Group
16125 * @param {Roo.bootstrap.NavGroup} the navgroup to add
16127 register : function(navgrp)
16129 this.groups[navgrp.navId] = navgrp;
16133 * fetch a Navigation Group based on the navigation ID
16134 * if one does not exist , it will get created.
16135 * @param {string} the navgroup to add
16136 * @returns {Roo.bootstrap.NavGroup} the navgroup
16138 get: function(navId) {
16139 if (typeof(this.groups[navId]) == 'undefined') {
16140 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16142 return this.groups[navId] ;
16157 * @class Roo.bootstrap.TabPanel
16158 * @extends Roo.bootstrap.Component
16159 * Bootstrap TabPanel class
16160 * @cfg {Boolean} active panel active
16161 * @cfg {String} html panel content
16162 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16163 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16167 * Create a new TabPanel
16168 * @param {Object} config The config object
16171 Roo.bootstrap.TabPanel = function(config){
16172 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16176 * Fires when the active status changes
16177 * @param {Roo.bootstrap.TabPanel} this
16178 * @param {Boolean} state the new state
16183 * @event beforedeactivate
16184 * Fires before a tab is de-activated - can be used to do validation on a form.
16185 * @param {Roo.bootstrap.TabPanel} this
16186 * @return {Boolean} false if there is an error
16189 'beforedeactivate': true
16192 this.tabId = this.tabId || Roo.id();
16196 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16203 getAutoCreate : function(){
16206 // item is needed for carousel - not sure if it has any effect otherwise
16207 cls: 'tab-pane item',
16208 html: this.html || ''
16212 cfg.cls += ' active';
16216 cfg.tabId = this.tabId;
16223 initEvents: function()
16225 Roo.log('-------- init events on tab panel ---------');
16227 var p = this.parent();
16228 this.navId = this.navId || p.navId;
16230 if (typeof(this.navId) != 'undefined') {
16231 // not really needed.. but just in case.. parent should be a NavGroup.
16232 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16233 Roo.log(['register', tg, this]);
16236 var i = tg.tabs.length - 1;
16238 if(this.active && tg.bullets > 0 && i < tg.bullets){
16239 tg.setActiveBullet(i);
16246 onRender : function(ct, position)
16248 // Roo.log("Call onRender: " + this.xtype);
16250 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16258 setActive: function(state)
16260 Roo.log("panel - set active " + this.tabId + "=" + state);
16262 this.active = state;
16264 this.el.removeClass('active');
16266 } else if (!this.el.hasClass('active')) {
16267 this.el.addClass('active');
16270 this.fireEvent('changed', this, state);
16287 * @class Roo.bootstrap.DateField
16288 * @extends Roo.bootstrap.Input
16289 * Bootstrap DateField class
16290 * @cfg {Number} weekStart default 0
16291 * @cfg {String} viewMode default empty, (months|years)
16292 * @cfg {String} minViewMode default empty, (months|years)
16293 * @cfg {Number} startDate default -Infinity
16294 * @cfg {Number} endDate default Infinity
16295 * @cfg {Boolean} todayHighlight default false
16296 * @cfg {Boolean} todayBtn default false
16297 * @cfg {Boolean} calendarWeeks default false
16298 * @cfg {Object} daysOfWeekDisabled default empty
16299 * @cfg {Boolean} singleMode default false (true | false)
16301 * @cfg {Boolean} keyboardNavigation default true
16302 * @cfg {String} language default en
16305 * Create a new DateField
16306 * @param {Object} config The config object
16309 Roo.bootstrap.DateField = function(config){
16310 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16314 * Fires when this field show.
16315 * @param {Roo.bootstrap.DateField} this
16316 * @param {Mixed} date The date value
16321 * Fires when this field hide.
16322 * @param {Roo.bootstrap.DateField} this
16323 * @param {Mixed} date The date value
16328 * Fires when select a date.
16329 * @param {Roo.bootstrap.DateField} this
16330 * @param {Mixed} date The date value
16336 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16339 * @cfg {String} format
16340 * The default date format string which can be overriden for localization support. The format must be
16341 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16345 * @cfg {String} altFormats
16346 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16347 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16349 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16357 todayHighlight : false,
16363 keyboardNavigation: true,
16365 calendarWeeks: false,
16367 startDate: -Infinity,
16371 daysOfWeekDisabled: [],
16375 singleMode : false,
16377 UTCDate: function()
16379 return new Date(Date.UTC.apply(Date, arguments));
16382 UTCToday: function()
16384 var today = new Date();
16385 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16388 getDate: function() {
16389 var d = this.getUTCDate();
16390 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16393 getUTCDate: function() {
16397 setDate: function(d) {
16398 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16401 setUTCDate: function(d) {
16403 this.setValue(this.formatDate(this.date));
16406 onRender: function(ct, position)
16409 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16411 this.language = this.language || 'en';
16412 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16413 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16415 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16416 this.format = this.format || 'm/d/y';
16417 this.isInline = false;
16418 this.isInput = true;
16419 this.component = this.el.select('.add-on', true).first() || false;
16420 this.component = (this.component && this.component.length === 0) ? false : this.component;
16421 this.hasInput = this.component && this.inputEL().length;
16423 if (typeof(this.minViewMode === 'string')) {
16424 switch (this.minViewMode) {
16426 this.minViewMode = 1;
16429 this.minViewMode = 2;
16432 this.minViewMode = 0;
16437 if (typeof(this.viewMode === 'string')) {
16438 switch (this.viewMode) {
16451 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16453 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16455 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16457 this.picker().on('mousedown', this.onMousedown, this);
16458 this.picker().on('click', this.onClick, this);
16460 this.picker().addClass('datepicker-dropdown');
16462 this.startViewMode = this.viewMode;
16464 if(this.singleMode){
16465 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16466 v.setVisibilityMode(Roo.Element.DISPLAY)
16470 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16471 v.setStyle('width', '189px');
16475 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16476 if(!this.calendarWeeks){
16481 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16482 v.attr('colspan', function(i, val){
16483 return parseInt(val) + 1;
16488 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16490 this.setStartDate(this.startDate);
16491 this.setEndDate(this.endDate);
16493 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16500 if(this.isInline) {
16505 picker : function()
16507 return this.pickerEl;
16508 // return this.el.select('.datepicker', true).first();
16511 fillDow: function()
16513 var dowCnt = this.weekStart;
16522 if(this.calendarWeeks){
16530 while (dowCnt < this.weekStart + 7) {
16534 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16538 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16541 fillMonths: function()
16544 var months = this.picker().select('>.datepicker-months td', true).first();
16546 months.dom.innerHTML = '';
16552 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16555 months.createChild(month);
16562 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;
16564 if (this.date < this.startDate) {
16565 this.viewDate = new Date(this.startDate);
16566 } else if (this.date > this.endDate) {
16567 this.viewDate = new Date(this.endDate);
16569 this.viewDate = new Date(this.date);
16577 var d = new Date(this.viewDate),
16578 year = d.getUTCFullYear(),
16579 month = d.getUTCMonth(),
16580 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16581 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16582 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16583 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16584 currentDate = this.date && this.date.valueOf(),
16585 today = this.UTCToday();
16587 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16589 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16591 // this.picker.select('>tfoot th.today').
16592 // .text(dates[this.language].today)
16593 // .toggle(this.todayBtn !== false);
16595 this.updateNavArrows();
16598 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16600 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16602 prevMonth.setUTCDate(day);
16604 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16606 var nextMonth = new Date(prevMonth);
16608 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16610 nextMonth = nextMonth.valueOf();
16612 var fillMonths = false;
16614 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16616 while(prevMonth.valueOf() < nextMonth) {
16619 if (prevMonth.getUTCDay() === this.weekStart) {
16621 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16629 if(this.calendarWeeks){
16630 // ISO 8601: First week contains first thursday.
16631 // ISO also states week starts on Monday, but we can be more abstract here.
16633 // Start of current week: based on weekstart/current date
16634 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16635 // Thursday of this week
16636 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16637 // First Thursday of year, year from thursday
16638 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16639 // Calendar week: ms between thursdays, div ms per day, div 7 days
16640 calWeek = (th - yth) / 864e5 / 7 + 1;
16642 fillMonths.cn.push({
16650 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16652 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16655 if (this.todayHighlight &&
16656 prevMonth.getUTCFullYear() == today.getFullYear() &&
16657 prevMonth.getUTCMonth() == today.getMonth() &&
16658 prevMonth.getUTCDate() == today.getDate()) {
16659 clsName += ' today';
16662 if (currentDate && prevMonth.valueOf() === currentDate) {
16663 clsName += ' active';
16666 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16667 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16668 clsName += ' disabled';
16671 fillMonths.cn.push({
16673 cls: 'day ' + clsName,
16674 html: prevMonth.getDate()
16677 prevMonth.setDate(prevMonth.getDate()+1);
16680 var currentYear = this.date && this.date.getUTCFullYear();
16681 var currentMonth = this.date && this.date.getUTCMonth();
16683 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16685 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16686 v.removeClass('active');
16688 if(currentYear === year && k === currentMonth){
16689 v.addClass('active');
16692 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16693 v.addClass('disabled');
16699 year = parseInt(year/10, 10) * 10;
16701 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16703 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16706 for (var i = -1; i < 11; i++) {
16707 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16709 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16717 showMode: function(dir)
16720 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16723 Roo.each(this.picker().select('>div',true).elements, function(v){
16724 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16727 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16732 if(this.isInline) return;
16734 this.picker().removeClass(['bottom', 'top']);
16736 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16738 * place to the top of element!
16742 this.picker().addClass('top');
16743 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16748 this.picker().addClass('bottom');
16750 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16753 parseDate : function(value)
16755 if(!value || value instanceof Date){
16758 var v = Date.parseDate(value, this.format);
16759 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16760 v = Date.parseDate(value, 'Y-m-d');
16762 if(!v && this.altFormats){
16763 if(!this.altFormatsArray){
16764 this.altFormatsArray = this.altFormats.split("|");
16766 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16767 v = Date.parseDate(value, this.altFormatsArray[i]);
16773 formatDate : function(date, fmt)
16775 return (!date || !(date instanceof Date)) ?
16776 date : date.dateFormat(fmt || this.format);
16779 onFocus : function()
16781 Roo.bootstrap.DateField.superclass.onFocus.call(this);
16785 onBlur : function()
16787 Roo.bootstrap.DateField.superclass.onBlur.call(this);
16789 var d = this.inputEl().getValue();
16798 this.picker().show();
16802 this.fireEvent('show', this, this.date);
16807 if(this.isInline) return;
16808 this.picker().hide();
16809 this.viewMode = this.startViewMode;
16812 this.fireEvent('hide', this, this.date);
16816 onMousedown: function(e)
16818 e.stopPropagation();
16819 e.preventDefault();
16824 Roo.bootstrap.DateField.superclass.keyup.call(this);
16828 setValue: function(v)
16831 // v can be a string or a date..
16834 var d = new Date(this.parseDate(v) ).clearTime();
16836 if(isNaN(d.getTime())){
16837 this.date = this.viewDate = '';
16838 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16842 v = this.formatDate(d);
16844 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16846 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16850 this.fireEvent('select', this, this.date);
16854 getValue: function()
16856 return this.formatDate(this.date);
16859 fireKey: function(e)
16861 if (!this.picker().isVisible()){
16862 if (e.keyCode == 27) // allow escape to hide and re-show picker
16867 var dateChanged = false,
16869 newDate, newViewDate;
16874 e.preventDefault();
16878 if (!this.keyboardNavigation) break;
16879 dir = e.keyCode == 37 ? -1 : 1;
16882 newDate = this.moveYear(this.date, dir);
16883 newViewDate = this.moveYear(this.viewDate, dir);
16884 } else if (e.shiftKey){
16885 newDate = this.moveMonth(this.date, dir);
16886 newViewDate = this.moveMonth(this.viewDate, dir);
16888 newDate = new Date(this.date);
16889 newDate.setUTCDate(this.date.getUTCDate() + dir);
16890 newViewDate = new Date(this.viewDate);
16891 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16893 if (this.dateWithinRange(newDate)){
16894 this.date = newDate;
16895 this.viewDate = newViewDate;
16896 this.setValue(this.formatDate(this.date));
16898 e.preventDefault();
16899 dateChanged = true;
16904 if (!this.keyboardNavigation) break;
16905 dir = e.keyCode == 38 ? -1 : 1;
16907 newDate = this.moveYear(this.date, dir);
16908 newViewDate = this.moveYear(this.viewDate, dir);
16909 } else if (e.shiftKey){
16910 newDate = this.moveMonth(this.date, dir);
16911 newViewDate = this.moveMonth(this.viewDate, dir);
16913 newDate = new Date(this.date);
16914 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16915 newViewDate = new Date(this.viewDate);
16916 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16918 if (this.dateWithinRange(newDate)){
16919 this.date = newDate;
16920 this.viewDate = newViewDate;
16921 this.setValue(this.formatDate(this.date));
16923 e.preventDefault();
16924 dateChanged = true;
16928 this.setValue(this.formatDate(this.date));
16930 e.preventDefault();
16933 this.setValue(this.formatDate(this.date));
16947 onClick: function(e)
16949 e.stopPropagation();
16950 e.preventDefault();
16952 var target = e.getTarget();
16954 if(target.nodeName.toLowerCase() === 'i'){
16955 target = Roo.get(target).dom.parentNode;
16958 var nodeName = target.nodeName;
16959 var className = target.className;
16960 var html = target.innerHTML;
16961 //Roo.log(nodeName);
16963 switch(nodeName.toLowerCase()) {
16965 switch(className) {
16971 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16972 switch(this.viewMode){
16974 this.viewDate = this.moveMonth(this.viewDate, dir);
16978 this.viewDate = this.moveYear(this.viewDate, dir);
16984 var date = new Date();
16985 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16987 this.setValue(this.formatDate(this.date));
16994 if (className.indexOf('disabled') < 0) {
16995 this.viewDate.setUTCDate(1);
16996 if (className.indexOf('month') > -1) {
16997 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16999 var year = parseInt(html, 10) || 0;
17000 this.viewDate.setUTCFullYear(year);
17004 if(this.singleMode){
17005 this.setValue(this.formatDate(this.viewDate));
17016 //Roo.log(className);
17017 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17018 var day = parseInt(html, 10) || 1;
17019 var year = this.viewDate.getUTCFullYear(),
17020 month = this.viewDate.getUTCMonth();
17022 if (className.indexOf('old') > -1) {
17029 } else if (className.indexOf('new') > -1) {
17037 //Roo.log([year,month,day]);
17038 this.date = this.UTCDate(year, month, day,0,0,0,0);
17039 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17041 //Roo.log(this.formatDate(this.date));
17042 this.setValue(this.formatDate(this.date));
17049 setStartDate: function(startDate)
17051 this.startDate = startDate || -Infinity;
17052 if (this.startDate !== -Infinity) {
17053 this.startDate = this.parseDate(this.startDate);
17056 this.updateNavArrows();
17059 setEndDate: function(endDate)
17061 this.endDate = endDate || Infinity;
17062 if (this.endDate !== Infinity) {
17063 this.endDate = this.parseDate(this.endDate);
17066 this.updateNavArrows();
17069 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17071 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17072 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17073 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17075 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17076 return parseInt(d, 10);
17079 this.updateNavArrows();
17082 updateNavArrows: function()
17084 if(this.singleMode){
17088 var d = new Date(this.viewDate),
17089 year = d.getUTCFullYear(),
17090 month = d.getUTCMonth();
17092 Roo.each(this.picker().select('.prev', true).elements, function(v){
17094 switch (this.viewMode) {
17097 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17103 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17110 Roo.each(this.picker().select('.next', true).elements, function(v){
17112 switch (this.viewMode) {
17115 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17121 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17129 moveMonth: function(date, dir)
17131 if (!dir) return date;
17132 var new_date = new Date(date.valueOf()),
17133 day = new_date.getUTCDate(),
17134 month = new_date.getUTCMonth(),
17135 mag = Math.abs(dir),
17137 dir = dir > 0 ? 1 : -1;
17140 // If going back one month, make sure month is not current month
17141 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17143 return new_date.getUTCMonth() == month;
17145 // If going forward one month, make sure month is as expected
17146 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17148 return new_date.getUTCMonth() != new_month;
17150 new_month = month + dir;
17151 new_date.setUTCMonth(new_month);
17152 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17153 if (new_month < 0 || new_month > 11)
17154 new_month = (new_month + 12) % 12;
17156 // For magnitudes >1, move one month at a time...
17157 for (var i=0; i<mag; i++)
17158 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17159 new_date = this.moveMonth(new_date, dir);
17160 // ...then reset the day, keeping it in the new month
17161 new_month = new_date.getUTCMonth();
17162 new_date.setUTCDate(day);
17164 return new_month != new_date.getUTCMonth();
17167 // Common date-resetting loop -- if date is beyond end of month, make it
17170 new_date.setUTCDate(--day);
17171 new_date.setUTCMonth(new_month);
17176 moveYear: function(date, dir)
17178 return this.moveMonth(date, dir*12);
17181 dateWithinRange: function(date)
17183 return date >= this.startDate && date <= this.endDate;
17189 this.picker().remove();
17194 Roo.apply(Roo.bootstrap.DateField, {
17205 html: '<i class="fa fa-arrow-left"/>'
17215 html: '<i class="fa fa-arrow-right"/>'
17257 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17258 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17259 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17260 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17261 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17274 navFnc: 'FullYear',
17279 navFnc: 'FullYear',
17284 Roo.apply(Roo.bootstrap.DateField, {
17288 cls: 'datepicker dropdown-menu roo-dynamic',
17292 cls: 'datepicker-days',
17296 cls: 'table-condensed',
17298 Roo.bootstrap.DateField.head,
17302 Roo.bootstrap.DateField.footer
17309 cls: 'datepicker-months',
17313 cls: 'table-condensed',
17315 Roo.bootstrap.DateField.head,
17316 Roo.bootstrap.DateField.content,
17317 Roo.bootstrap.DateField.footer
17324 cls: 'datepicker-years',
17328 cls: 'table-condensed',
17330 Roo.bootstrap.DateField.head,
17331 Roo.bootstrap.DateField.content,
17332 Roo.bootstrap.DateField.footer
17351 * @class Roo.bootstrap.TimeField
17352 * @extends Roo.bootstrap.Input
17353 * Bootstrap DateField class
17357 * Create a new TimeField
17358 * @param {Object} config The config object
17361 Roo.bootstrap.TimeField = function(config){
17362 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17366 * Fires when this field show.
17367 * @param {Roo.bootstrap.DateField} thisthis
17368 * @param {Mixed} date The date value
17373 * Fires when this field hide.
17374 * @param {Roo.bootstrap.DateField} this
17375 * @param {Mixed} date The date value
17380 * Fires when select a date.
17381 * @param {Roo.bootstrap.DateField} this
17382 * @param {Mixed} date The date value
17388 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17391 * @cfg {String} format
17392 * The default time format string which can be overriden for localization support. The format must be
17393 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17397 onRender: function(ct, position)
17400 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17402 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17404 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17406 this.pop = this.picker().select('>.datepicker-time',true).first();
17407 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17409 this.picker().on('mousedown', this.onMousedown, this);
17410 this.picker().on('click', this.onClick, this);
17412 this.picker().addClass('datepicker-dropdown');
17417 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17418 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17419 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17420 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17421 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17422 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17426 fireKey: function(e){
17427 if (!this.picker().isVisible()){
17428 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17434 e.preventDefault();
17442 this.onTogglePeriod();
17445 this.onIncrementMinutes();
17448 this.onDecrementMinutes();
17457 onClick: function(e) {
17458 e.stopPropagation();
17459 e.preventDefault();
17462 picker : function()
17464 return this.el.select('.datepicker', true).first();
17467 fillTime: function()
17469 var time = this.pop.select('tbody', true).first();
17471 time.dom.innerHTML = '';
17486 cls: 'hours-up glyphicon glyphicon-chevron-up'
17506 cls: 'minutes-up glyphicon glyphicon-chevron-up'
17527 cls: 'timepicker-hour',
17542 cls: 'timepicker-minute',
17557 cls: 'btn btn-primary period',
17579 cls: 'hours-down glyphicon glyphicon-chevron-down'
17599 cls: 'minutes-down glyphicon glyphicon-chevron-down'
17617 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17624 var hours = this.time.getHours();
17625 var minutes = this.time.getMinutes();
17638 hours = hours - 12;
17642 hours = '0' + hours;
17646 minutes = '0' + minutes;
17649 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17650 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17651 this.pop.select('button', true).first().dom.innerHTML = period;
17657 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17659 var cls = ['bottom'];
17661 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17668 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17673 this.picker().addClass(cls.join('-'));
17677 Roo.each(cls, function(c){
17679 _this.picker().setTop(_this.inputEl().getHeight());
17683 _this.picker().setTop(0 - _this.picker().getHeight());
17688 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17692 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17699 onFocus : function()
17701 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17705 onBlur : function()
17707 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17713 this.picker().show();
17718 this.fireEvent('show', this, this.date);
17723 this.picker().hide();
17726 this.fireEvent('hide', this, this.date);
17729 setTime : function()
17732 this.setValue(this.time.format(this.format));
17734 this.fireEvent('select', this, this.date);
17739 onMousedown: function(e){
17740 e.stopPropagation();
17741 e.preventDefault();
17744 onIncrementHours: function()
17746 Roo.log('onIncrementHours');
17747 this.time = this.time.add(Date.HOUR, 1);
17752 onDecrementHours: function()
17754 Roo.log('onDecrementHours');
17755 this.time = this.time.add(Date.HOUR, -1);
17759 onIncrementMinutes: function()
17761 Roo.log('onIncrementMinutes');
17762 this.time = this.time.add(Date.MINUTE, 1);
17766 onDecrementMinutes: function()
17768 Roo.log('onDecrementMinutes');
17769 this.time = this.time.add(Date.MINUTE, -1);
17773 onTogglePeriod: function()
17775 Roo.log('onTogglePeriod');
17776 this.time = this.time.add(Date.HOUR, 12);
17783 Roo.apply(Roo.bootstrap.TimeField, {
17813 cls: 'btn btn-info ok',
17825 Roo.apply(Roo.bootstrap.TimeField, {
17829 cls: 'datepicker dropdown-menu',
17833 cls: 'datepicker-time',
17837 cls: 'table-condensed',
17839 Roo.bootstrap.TimeField.content,
17840 Roo.bootstrap.TimeField.footer
17859 * @class Roo.bootstrap.MonthField
17860 * @extends Roo.bootstrap.Input
17861 * Bootstrap MonthField class
17863 * @cfg {String} language default en
17866 * Create a new MonthField
17867 * @param {Object} config The config object
17870 Roo.bootstrap.MonthField = function(config){
17871 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17876 * Fires when this field show.
17877 * @param {Roo.bootstrap.MonthField} this
17878 * @param {Mixed} date The date value
17883 * Fires when this field hide.
17884 * @param {Roo.bootstrap.MonthField} this
17885 * @param {Mixed} date The date value
17890 * Fires when select a date.
17891 * @param {Roo.bootstrap.MonthField} this
17892 * @param {String} oldvalue The old value
17893 * @param {String} newvalue The new value
17899 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
17901 onRender: function(ct, position)
17904 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17906 this.language = this.language || 'en';
17907 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17908 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17910 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17911 this.isInline = false;
17912 this.isInput = true;
17913 this.component = this.el.select('.add-on', true).first() || false;
17914 this.component = (this.component && this.component.length === 0) ? false : this.component;
17915 this.hasInput = this.component && this.inputEL().length;
17917 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17919 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17921 this.picker().on('mousedown', this.onMousedown, this);
17922 this.picker().on('click', this.onClick, this);
17924 this.picker().addClass('datepicker-dropdown');
17926 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17927 v.setStyle('width', '189px');
17934 if(this.isInline) {
17940 setValue: function(v, suppressEvent)
17942 var o = this.getValue();
17944 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17948 if(suppressEvent !== true){
17949 this.fireEvent('select', this, o, v);
17954 getValue: function()
17959 onClick: function(e)
17961 e.stopPropagation();
17962 e.preventDefault();
17964 var target = e.getTarget();
17966 if(target.nodeName.toLowerCase() === 'i'){
17967 target = Roo.get(target).dom.parentNode;
17970 var nodeName = target.nodeName;
17971 var className = target.className;
17972 var html = target.innerHTML;
17974 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17978 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17980 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17986 picker : function()
17988 return this.pickerEl;
17991 fillMonths: function()
17994 var months = this.picker().select('>.datepicker-months td', true).first();
17996 months.dom.innerHTML = '';
18002 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18005 months.createChild(month);
18014 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18015 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18018 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18019 e.removeClass('active');
18021 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18022 e.addClass('active');
18029 if(this.isInline) return;
18031 this.picker().removeClass(['bottom', 'top']);
18033 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18035 * place to the top of element!
18039 this.picker().addClass('top');
18040 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18045 this.picker().addClass('bottom');
18047 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18050 onFocus : function()
18052 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18056 onBlur : function()
18058 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18060 var d = this.inputEl().getValue();
18069 this.picker().show();
18070 this.picker().select('>.datepicker-months', true).first().show();
18074 this.fireEvent('show', this, this.date);
18079 if(this.isInline) return;
18080 this.picker().hide();
18081 this.fireEvent('hide', this, this.date);
18085 onMousedown: function(e)
18087 e.stopPropagation();
18088 e.preventDefault();
18093 Roo.bootstrap.MonthField.superclass.keyup.call(this);
18097 fireKey: function(e)
18099 if (!this.picker().isVisible()){
18100 if (e.keyCode == 27) // allow escape to hide and re-show picker
18110 e.preventDefault();
18114 dir = e.keyCode == 37 ? -1 : 1;
18116 this.vIndex = this.vIndex + dir;
18118 if(this.vIndex < 0){
18122 if(this.vIndex > 11){
18126 if(isNaN(this.vIndex)){
18130 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18136 dir = e.keyCode == 38 ? -1 : 1;
18138 this.vIndex = this.vIndex + dir * 4;
18140 if(this.vIndex < 0){
18144 if(this.vIndex > 11){
18148 if(isNaN(this.vIndex)){
18152 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18157 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18158 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18162 e.preventDefault();
18165 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18166 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18182 this.picker().remove();
18187 Roo.apply(Roo.bootstrap.MonthField, {
18206 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18207 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18212 Roo.apply(Roo.bootstrap.MonthField, {
18216 cls: 'datepicker dropdown-menu roo-dynamic',
18220 cls: 'datepicker-months',
18224 cls: 'table-condensed',
18226 Roo.bootstrap.DateField.content
18246 * @class Roo.bootstrap.CheckBox
18247 * @extends Roo.bootstrap.Input
18248 * Bootstrap CheckBox class
18250 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18251 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18252 * @cfg {String} boxLabel The text that appears beside the checkbox
18253 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18254 * @cfg {Boolean} checked initnal the element
18255 * @cfg {Boolean} inline inline the element (default false)
18256 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18259 * Create a new CheckBox
18260 * @param {Object} config The config object
18263 Roo.bootstrap.CheckBox = function(config){
18264 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18269 * Fires when the element is checked or unchecked.
18270 * @param {Roo.bootstrap.CheckBox} this This input
18271 * @param {Boolean} checked The new checked value
18278 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18280 inputType: 'checkbox',
18288 getAutoCreate : function()
18290 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18296 cfg.cls = 'form-group ' + this.inputType; //input-group
18299 cfg.cls += ' ' + this.inputType + '-inline';
18305 type : this.inputType,
18306 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18307 cls : 'roo-' + this.inputType, //'form-box',
18308 placeholder : this.placeholder || ''
18312 if (this.weight) { // Validity check?
18313 cfg.cls += " " + this.inputType + "-" + this.weight;
18316 if (this.disabled) {
18317 input.disabled=true;
18321 input.checked = this.checked;
18325 input.name = this.name;
18329 input.cls += ' input-' + this.size;
18334 ['xs','sm','md','lg'].map(function(size){
18335 if (settings[size]) {
18336 cfg.cls += ' col-' + size + '-' + settings[size];
18340 var inputblock = input;
18342 if (this.before || this.after) {
18345 cls : 'input-group',
18350 inputblock.cn.push({
18352 cls : 'input-group-addon',
18357 inputblock.cn.push(input);
18360 inputblock.cn.push({
18362 cls : 'input-group-addon',
18369 if (align ==='left' && this.fieldLabel.length) {
18370 Roo.log("left and has label");
18376 cls : 'control-label col-md-' + this.labelWidth,
18377 html : this.fieldLabel
18381 cls : "col-md-" + (12 - this.labelWidth),
18388 } else if ( this.fieldLabel.length) {
18393 tag: this.boxLabel ? 'span' : 'label',
18395 cls: 'control-label box-input-label',
18396 //cls : 'input-group-addon',
18397 html : this.fieldLabel
18407 Roo.log(" no label && no align");
18408 cfg.cn = [ inputblock ] ;
18413 var boxLabelCfg = {
18415 //'for': id, // box label is handled by onclick - so no for...
18417 html: this.boxLabel
18421 boxLabelCfg.tooltip = this.tooltip;
18424 cfg.cn.push(boxLabelCfg);
18434 * return the real input element.
18436 inputEl: function ()
18438 return this.el.select('input.roo-' + this.inputType,true).first();
18441 labelEl: function()
18443 return this.el.select('label.control-label',true).first();
18445 /* depricated... */
18449 return this.labelEl();
18452 initEvents : function()
18454 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18456 this.inputEl().on('click', this.onClick, this);
18458 if (this.boxLabel) {
18459 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18462 this.startValue = this.getValue();
18465 Roo.bootstrap.CheckBox.register(this);
18469 onClick : function()
18471 this.setChecked(!this.checked);
18474 setChecked : function(state,suppressEvent)
18476 this.startValue = this.getValue();
18478 if(this.inputType == 'radio'){
18480 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18481 e.dom.checked = false;
18484 this.inputEl().dom.checked = true;
18486 this.inputEl().dom.value = this.inputValue;
18488 if(suppressEvent !== true){
18489 this.fireEvent('check', this, true);
18497 this.checked = state;
18499 this.inputEl().dom.checked = state;
18501 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18503 if(suppressEvent !== true){
18504 this.fireEvent('check', this, state);
18510 getValue : function()
18512 if(this.inputType == 'radio'){
18513 return this.getGroupValue();
18516 return this.inputEl().getValue();
18520 getGroupValue : function()
18522 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18526 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18529 setValue : function(v,suppressEvent)
18531 if(this.inputType == 'radio'){
18532 this.setGroupValue(v, suppressEvent);
18536 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18541 setGroupValue : function(v, suppressEvent)
18543 this.startValue = this.getValue();
18545 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18546 e.dom.checked = false;
18548 if(e.dom.value == v){
18549 e.dom.checked = true;
18553 if(suppressEvent !== true){
18554 this.fireEvent('check', this, true);
18562 validate : function()
18566 (this.inputType == 'radio' && this.validateRadio()) ||
18567 (this.inputType == 'checkbox' && this.validateCheckbox())
18573 this.markInvalid();
18577 validateRadio : function()
18581 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18582 if(!e.dom.checked){
18594 validateCheckbox : function()
18597 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18600 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18608 for(var i in group){
18613 r = (group[i].getValue() == group[i].inputValue) ? true : false;
18620 * Mark this field as valid
18622 markValid : function()
18624 if(this.allowBlank){
18630 this.fireEvent('valid', this);
18632 if(this.inputType == 'radio'){
18633 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18634 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18635 e.findParent('.form-group', false, true).addClass(_this.validClass);
18642 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18643 this.el.findParent('.form-group', false, true).addClass(this.validClass);
18647 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18653 for(var i in group){
18654 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18655 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18660 * Mark this field as invalid
18661 * @param {String} msg The validation message
18663 markInvalid : function(msg)
18665 if(this.allowBlank){
18671 this.fireEvent('invalid', this, msg);
18673 if(this.inputType == 'radio'){
18674 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18675 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18676 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18683 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18684 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18688 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18694 for(var i in group){
18695 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18696 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18703 Roo.apply(Roo.bootstrap.CheckBox, {
18708 * register a CheckBox Group
18709 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18711 register : function(checkbox)
18713 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18714 this.groups[checkbox.groupId] = {};
18717 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18721 this.groups[checkbox.groupId][checkbox.name] = checkbox;
18725 * fetch a CheckBox Group based on the group ID
18726 * @param {string} the group ID
18727 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18729 get: function(groupId) {
18730 if (typeof(this.groups[groupId]) == 'undefined') {
18734 return this.groups[groupId] ;
18746 *<div class="radio">
18748 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18749 Option one is this and that—be sure to include why it's great
18756 *<label class="radio-inline">fieldLabel</label>
18757 *<label class="radio-inline">
18758 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18766 * @class Roo.bootstrap.Radio
18767 * @extends Roo.bootstrap.CheckBox
18768 * Bootstrap Radio class
18771 * Create a new Radio
18772 * @param {Object} config The config object
18775 Roo.bootstrap.Radio = function(config){
18776 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18780 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
18782 inputType: 'radio',
18786 getAutoCreate : function()
18788 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18789 align = align || 'left'; // default...
18796 tag : this.inline ? 'span' : 'div',
18801 var inline = this.inline ? ' radio-inline' : '';
18805 // does not need for, as we wrap the input with it..
18807 cls : 'control-label box-label' + inline,
18810 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18814 //cls : 'control-label' + inline,
18815 html : this.fieldLabel,
18816 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18825 type : this.inputType,
18826 //value : (!this.checked) ? this.valueOff : this.inputValue,
18827 value : this.inputValue,
18829 placeholder : this.placeholder || '' // ?? needed????
18832 if (this.weight) { // Validity check?
18833 input.cls += " radio-" + this.weight;
18835 if (this.disabled) {
18836 input.disabled=true;
18840 input.checked = this.checked;
18844 input.name = this.name;
18848 input.cls += ' input-' + this.size;
18851 //?? can span's inline have a width??
18854 ['xs','sm','md','lg'].map(function(size){
18855 if (settings[size]) {
18856 cfg.cls += ' col-' + size + '-' + settings[size];
18860 var inputblock = input;
18862 if (this.before || this.after) {
18865 cls : 'input-group',
18870 inputblock.cn.push({
18872 cls : 'input-group-addon',
18876 inputblock.cn.push(input);
18878 inputblock.cn.push({
18880 cls : 'input-group-addon',
18888 if (this.fieldLabel && this.fieldLabel.length) {
18889 cfg.cn.push(fieldLabel);
18892 // normal bootstrap puts the input inside the label.
18893 // however with our styled version - it has to go after the input.
18895 //lbl.cn.push(inputblock);
18899 cls: 'radio' + inline,
18906 cfg.cn.push( lblwrap);
18911 html: this.boxLabel
18920 initEvents : function()
18922 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18924 this.inputEl().on('click', this.onClick, this);
18925 if (this.boxLabel) {
18926 Roo.log('find label')
18927 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
18932 inputEl: function ()
18934 return this.el.select('input.roo-radio',true).first();
18936 onClick : function()
18939 this.setChecked(true);
18942 setChecked : function(state,suppressEvent)
18945 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18946 v.dom.checked = false;
18949 Roo.log(this.inputEl().dom);
18950 this.checked = state;
18951 this.inputEl().dom.checked = state;
18953 if(suppressEvent !== true){
18954 this.fireEvent('check', this, state);
18957 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18961 getGroupValue : function()
18964 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18965 if(v.dom.checked == true){
18966 value = v.dom.value;
18974 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
18975 * @return {Mixed} value The field value
18977 getValue : function(){
18978 return this.getGroupValue();
18984 //<script type="text/javascript">
18987 * Based Ext JS Library 1.1.1
18988 * Copyright(c) 2006-2007, Ext JS, LLC.
18994 * @class Roo.HtmlEditorCore
18995 * @extends Roo.Component
18996 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18998 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19001 Roo.HtmlEditorCore = function(config){
19004 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19009 * @event initialize
19010 * Fires when the editor is fully initialized (including the iframe)
19011 * @param {Roo.HtmlEditorCore} this
19016 * Fires when the editor is first receives the focus. Any insertion must wait
19017 * until after this event.
19018 * @param {Roo.HtmlEditorCore} this
19022 * @event beforesync
19023 * Fires before the textarea is updated with content from the editor iframe. Return false
19024 * to cancel the sync.
19025 * @param {Roo.HtmlEditorCore} this
19026 * @param {String} html
19030 * @event beforepush
19031 * Fires before the iframe editor is updated with content from the textarea. Return false
19032 * to cancel the push.
19033 * @param {Roo.HtmlEditorCore} this
19034 * @param {String} html
19039 * Fires when the textarea is updated with content from the editor iframe.
19040 * @param {Roo.HtmlEditorCore} this
19041 * @param {String} html
19046 * Fires when the iframe editor is updated with content from the textarea.
19047 * @param {Roo.HtmlEditorCore} this
19048 * @param {String} html
19053 * @event editorevent
19054 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19055 * @param {Roo.HtmlEditorCore} this
19061 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19063 // defaults : white / black...
19064 this.applyBlacklists();
19071 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
19075 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
19081 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
19086 * @cfg {Number} height (in pixels)
19090 * @cfg {Number} width (in pixels)
19095 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19098 stylesheets: false,
19103 // private properties
19104 validationEvent : false,
19106 initialized : false,
19108 sourceEditMode : false,
19109 onFocus : Roo.emptyFn,
19111 hideMode:'offsets',
19115 // blacklist + whitelisted elements..
19122 * Protected method that will not generally be called directly. It
19123 * is called when the editor initializes the iframe with HTML contents. Override this method if you
19124 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19126 getDocMarkup : function(){
19130 // inherit styels from page...??
19131 if (this.stylesheets === false) {
19133 Roo.get(document.head).select('style').each(function(node) {
19134 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19137 Roo.get(document.head).select('link').each(function(node) {
19138 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19141 } else if (!this.stylesheets.length) {
19143 st = '<style type="text/css">' +
19144 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19150 st += '<style type="text/css">' +
19151 'IMG { cursor: pointer } ' +
19155 return '<html><head>' + st +
19156 //<style type="text/css">' +
19157 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19159 ' </head><body class="roo-htmleditor-body"></body></html>';
19163 onRender : function(ct, position)
19166 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19167 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19170 this.el.dom.style.border = '0 none';
19171 this.el.dom.setAttribute('tabIndex', -1);
19172 this.el.addClass('x-hidden hide');
19176 if(Roo.isIE){ // fix IE 1px bogus margin
19177 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19181 this.frameId = Roo.id();
19185 var iframe = this.owner.wrap.createChild({
19187 cls: 'form-control', // bootstrap..
19189 name: this.frameId,
19190 frameBorder : 'no',
19191 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19196 this.iframe = iframe.dom;
19198 this.assignDocWin();
19200 this.doc.designMode = 'on';
19203 this.doc.write(this.getDocMarkup());
19207 var task = { // must defer to wait for browser to be ready
19209 //console.log("run task?" + this.doc.readyState);
19210 this.assignDocWin();
19211 if(this.doc.body || this.doc.readyState == 'complete'){
19213 this.doc.designMode="on";
19217 Roo.TaskMgr.stop(task);
19218 this.initEditor.defer(10, this);
19225 Roo.TaskMgr.start(task);
19230 onResize : function(w, h)
19232 Roo.log('resize: ' +w + ',' + h );
19233 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19237 if(typeof w == 'number'){
19239 this.iframe.style.width = w + 'px';
19241 if(typeof h == 'number'){
19243 this.iframe.style.height = h + 'px';
19245 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19252 * Toggles the editor between standard and source edit mode.
19253 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19255 toggleSourceEdit : function(sourceEditMode){
19257 this.sourceEditMode = sourceEditMode === true;
19259 if(this.sourceEditMode){
19261 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19264 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19265 //this.iframe.className = '';
19268 //this.setSize(this.owner.wrap.getSize());
19269 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19276 * Protected method that will not generally be called directly. If you need/want
19277 * custom HTML cleanup, this is the method you should override.
19278 * @param {String} html The HTML to be cleaned
19279 * return {String} The cleaned HTML
19281 cleanHtml : function(html){
19282 html = String(html);
19283 if(html.length > 5){
19284 if(Roo.isSafari){ // strip safari nonsense
19285 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19288 if(html == ' '){
19295 * HTML Editor -> Textarea
19296 * Protected method that will not generally be called directly. Syncs the contents
19297 * of the editor iframe with the textarea.
19299 syncValue : function(){
19300 if(this.initialized){
19301 var bd = (this.doc.body || this.doc.documentElement);
19302 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19303 var html = bd.innerHTML;
19305 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19306 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19308 html = '<div style="'+m[0]+'">' + html + '</div>';
19311 html = this.cleanHtml(html);
19312 // fix up the special chars.. normaly like back quotes in word...
19313 // however we do not want to do this with chinese..
19314 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19315 var cc = b.charCodeAt();
19317 (cc >= 0x4E00 && cc < 0xA000 ) ||
19318 (cc >= 0x3400 && cc < 0x4E00 ) ||
19319 (cc >= 0xf900 && cc < 0xfb00 )
19325 if(this.owner.fireEvent('beforesync', this, html) !== false){
19326 this.el.dom.value = html;
19327 this.owner.fireEvent('sync', this, html);
19333 * Protected method that will not generally be called directly. Pushes the value of the textarea
19334 * into the iframe editor.
19336 pushValue : function(){
19337 if(this.initialized){
19338 var v = this.el.dom.value.trim();
19340 // if(v.length < 1){
19344 if(this.owner.fireEvent('beforepush', this, v) !== false){
19345 var d = (this.doc.body || this.doc.documentElement);
19347 this.cleanUpPaste();
19348 this.el.dom.value = d.innerHTML;
19349 this.owner.fireEvent('push', this, v);
19355 deferFocus : function(){
19356 this.focus.defer(10, this);
19360 focus : function(){
19361 if(this.win && !this.sourceEditMode){
19368 assignDocWin: function()
19370 var iframe = this.iframe;
19373 this.doc = iframe.contentWindow.document;
19374 this.win = iframe.contentWindow;
19376 // if (!Roo.get(this.frameId)) {
19379 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19380 // this.win = Roo.get(this.frameId).dom.contentWindow;
19382 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19386 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19387 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19392 initEditor : function(){
19393 //console.log("INIT EDITOR");
19394 this.assignDocWin();
19398 this.doc.designMode="on";
19400 this.doc.write(this.getDocMarkup());
19403 var dbody = (this.doc.body || this.doc.documentElement);
19404 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19405 // this copies styles from the containing element into thsi one..
19406 // not sure why we need all of this..
19407 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19409 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19410 //ss['background-attachment'] = 'fixed'; // w3c
19411 dbody.bgProperties = 'fixed'; // ie
19412 //Roo.DomHelper.applyStyles(dbody, ss);
19413 Roo.EventManager.on(this.doc, {
19414 //'mousedown': this.onEditorEvent,
19415 'mouseup': this.onEditorEvent,
19416 'dblclick': this.onEditorEvent,
19417 'click': this.onEditorEvent,
19418 'keyup': this.onEditorEvent,
19423 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19425 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19426 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19428 this.initialized = true;
19430 this.owner.fireEvent('initialize', this);
19435 onDestroy : function(){
19441 //for (var i =0; i < this.toolbars.length;i++) {
19442 // // fixme - ask toolbars for heights?
19443 // this.toolbars[i].onDestroy();
19446 //this.wrap.dom.innerHTML = '';
19447 //this.wrap.remove();
19452 onFirstFocus : function(){
19454 this.assignDocWin();
19457 this.activated = true;
19460 if(Roo.isGecko){ // prevent silly gecko errors
19462 var s = this.win.getSelection();
19463 if(!s.focusNode || s.focusNode.nodeType != 3){
19464 var r = s.getRangeAt(0);
19465 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19470 this.execCmd('useCSS', true);
19471 this.execCmd('styleWithCSS', false);
19474 this.owner.fireEvent('activate', this);
19478 adjustFont: function(btn){
19479 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19480 //if(Roo.isSafari){ // safari
19483 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19484 if(Roo.isSafari){ // safari
19485 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19486 v = (v < 10) ? 10 : v;
19487 v = (v > 48) ? 48 : v;
19488 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19493 v = Math.max(1, v+adjust);
19495 this.execCmd('FontSize', v );
19498 onEditorEvent : function(e)
19500 this.owner.fireEvent('editorevent', this, e);
19501 // this.updateToolbar();
19502 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19505 insertTag : function(tg)
19507 // could be a bit smarter... -> wrap the current selected tRoo..
19508 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19510 range = this.createRange(this.getSelection());
19511 var wrappingNode = this.doc.createElement(tg.toLowerCase());
19512 wrappingNode.appendChild(range.extractContents());
19513 range.insertNode(wrappingNode);
19520 this.execCmd("formatblock", tg);
19524 insertText : function(txt)
19528 var range = this.createRange();
19529 range.deleteContents();
19530 //alert(Sender.getAttribute('label'));
19532 range.insertNode(this.doc.createTextNode(txt));
19538 * Executes a Midas editor command on the editor document and performs necessary focus and
19539 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19540 * @param {String} cmd The Midas command
19541 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19543 relayCmd : function(cmd, value){
19545 this.execCmd(cmd, value);
19546 this.owner.fireEvent('editorevent', this);
19547 //this.updateToolbar();
19548 this.owner.deferFocus();
19552 * Executes a Midas editor command directly on the editor document.
19553 * For visual commands, you should use {@link #relayCmd} instead.
19554 * <b>This should only be called after the editor is initialized.</b>
19555 * @param {String} cmd The Midas command
19556 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19558 execCmd : function(cmd, value){
19559 this.doc.execCommand(cmd, false, value === undefined ? null : value);
19566 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19568 * @param {String} text | dom node..
19570 insertAtCursor : function(text)
19575 if(!this.activated){
19581 var r = this.doc.selection.createRange();
19592 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19596 // from jquery ui (MIT licenced)
19598 var win = this.win;
19600 if (win.getSelection && win.getSelection().getRangeAt) {
19601 range = win.getSelection().getRangeAt(0);
19602 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19603 range.insertNode(node);
19604 } else if (win.document.selection && win.document.selection.createRange) {
19605 // no firefox support
19606 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19607 win.document.selection.createRange().pasteHTML(txt);
19609 // no firefox support
19610 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19611 this.execCmd('InsertHTML', txt);
19620 mozKeyPress : function(e){
19622 var c = e.getCharCode(), cmd;
19625 c = String.fromCharCode(c).toLowerCase();
19639 this.cleanUpPaste.defer(100, this);
19647 e.preventDefault();
19655 fixKeys : function(){ // load time branching for fastest keydown performance
19657 return function(e){
19658 var k = e.getKey(), r;
19661 r = this.doc.selection.createRange();
19664 r.pasteHTML('    ');
19671 r = this.doc.selection.createRange();
19673 var target = r.parentElement();
19674 if(!target || target.tagName.toLowerCase() != 'li'){
19676 r.pasteHTML('<br />');
19682 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19683 this.cleanUpPaste.defer(100, this);
19689 }else if(Roo.isOpera){
19690 return function(e){
19691 var k = e.getKey();
19695 this.execCmd('InsertHTML','    ');
19698 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19699 this.cleanUpPaste.defer(100, this);
19704 }else if(Roo.isSafari){
19705 return function(e){
19706 var k = e.getKey();
19710 this.execCmd('InsertText','\t');
19714 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19715 this.cleanUpPaste.defer(100, this);
19723 getAllAncestors: function()
19725 var p = this.getSelectedNode();
19728 a.push(p); // push blank onto stack..
19729 p = this.getParentElement();
19733 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19737 a.push(this.doc.body);
19741 lastSelNode : false,
19744 getSelection : function()
19746 this.assignDocWin();
19747 return Roo.isIE ? this.doc.selection : this.win.getSelection();
19750 getSelectedNode: function()
19752 // this may only work on Gecko!!!
19754 // should we cache this!!!!
19759 var range = this.createRange(this.getSelection()).cloneRange();
19762 var parent = range.parentElement();
19764 var testRange = range.duplicate();
19765 testRange.moveToElementText(parent);
19766 if (testRange.inRange(range)) {
19769 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19772 parent = parent.parentElement;
19777 // is ancestor a text element.
19778 var ac = range.commonAncestorContainer;
19779 if (ac.nodeType == 3) {
19780 ac = ac.parentNode;
19783 var ar = ac.childNodes;
19786 var other_nodes = [];
19787 var has_other_nodes = false;
19788 for (var i=0;i<ar.length;i++) {
19789 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
19792 // fullly contained node.
19794 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19799 // probably selected..
19800 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19801 other_nodes.push(ar[i]);
19805 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
19810 has_other_nodes = true;
19812 if (!nodes.length && other_nodes.length) {
19813 nodes= other_nodes;
19815 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19821 createRange: function(sel)
19823 // this has strange effects when using with
19824 // top toolbar - not sure if it's a great idea.
19825 //this.editor.contentWindow.focus();
19826 if (typeof sel != "undefined") {
19828 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19830 return this.doc.createRange();
19833 return this.doc.createRange();
19836 getParentElement: function()
19839 this.assignDocWin();
19840 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19842 var range = this.createRange(sel);
19845 var p = range.commonAncestorContainer;
19846 while (p.nodeType == 3) { // text node
19857 * Range intersection.. the hard stuff...
19861 * [ -- selected range --- ]
19865 * if end is before start or hits it. fail.
19866 * if start is after end or hits it fail.
19868 * if either hits (but other is outside. - then it's not
19874 // @see http://www.thismuchiknow.co.uk/?p=64.
19875 rangeIntersectsNode : function(range, node)
19877 var nodeRange = node.ownerDocument.createRange();
19879 nodeRange.selectNode(node);
19881 nodeRange.selectNodeContents(node);
19884 var rangeStartRange = range.cloneRange();
19885 rangeStartRange.collapse(true);
19887 var rangeEndRange = range.cloneRange();
19888 rangeEndRange.collapse(false);
19890 var nodeStartRange = nodeRange.cloneRange();
19891 nodeStartRange.collapse(true);
19893 var nodeEndRange = nodeRange.cloneRange();
19894 nodeEndRange.collapse(false);
19896 return rangeStartRange.compareBoundaryPoints(
19897 Range.START_TO_START, nodeEndRange) == -1 &&
19898 rangeEndRange.compareBoundaryPoints(
19899 Range.START_TO_START, nodeStartRange) == 1;
19903 rangeCompareNode : function(range, node)
19905 var nodeRange = node.ownerDocument.createRange();
19907 nodeRange.selectNode(node);
19909 nodeRange.selectNodeContents(node);
19913 range.collapse(true);
19915 nodeRange.collapse(true);
19917 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19918 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
19920 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19922 var nodeIsBefore = ss == 1;
19923 var nodeIsAfter = ee == -1;
19925 if (nodeIsBefore && nodeIsAfter)
19927 if (!nodeIsBefore && nodeIsAfter)
19928 return 1; //right trailed.
19930 if (nodeIsBefore && !nodeIsAfter)
19931 return 2; // left trailed.
19936 // private? - in a new class?
19937 cleanUpPaste : function()
19939 // cleans up the whole document..
19940 Roo.log('cleanuppaste');
19942 this.cleanUpChildren(this.doc.body);
19943 var clean = this.cleanWordChars(this.doc.body.innerHTML);
19944 if (clean != this.doc.body.innerHTML) {
19945 this.doc.body.innerHTML = clean;
19950 cleanWordChars : function(input) {// change the chars to hex code
19951 var he = Roo.HtmlEditorCore;
19953 var output = input;
19954 Roo.each(he.swapCodes, function(sw) {
19955 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19957 output = output.replace(swapper, sw[1]);
19964 cleanUpChildren : function (n)
19966 if (!n.childNodes.length) {
19969 for (var i = n.childNodes.length-1; i > -1 ; i--) {
19970 this.cleanUpChild(n.childNodes[i]);
19977 cleanUpChild : function (node)
19980 //console.log(node);
19981 if (node.nodeName == "#text") {
19982 // clean up silly Windows -- stuff?
19985 if (node.nodeName == "#comment") {
19986 node.parentNode.removeChild(node);
19987 // clean up silly Windows -- stuff?
19990 var lcname = node.tagName.toLowerCase();
19991 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19992 // whitelist of tags..
19994 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19996 node.parentNode.removeChild(node);
20001 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20003 // remove <a name=....> as rendering on yahoo mailer is borked with this.
20004 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20006 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20007 // remove_keep_children = true;
20010 if (remove_keep_children) {
20011 this.cleanUpChildren(node);
20012 // inserts everything just before this node...
20013 while (node.childNodes.length) {
20014 var cn = node.childNodes[0];
20015 node.removeChild(cn);
20016 node.parentNode.insertBefore(cn, node);
20018 node.parentNode.removeChild(node);
20022 if (!node.attributes || !node.attributes.length) {
20023 this.cleanUpChildren(node);
20027 function cleanAttr(n,v)
20030 if (v.match(/^\./) || v.match(/^\//)) {
20033 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20036 if (v.match(/^#/)) {
20039 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20040 node.removeAttribute(n);
20044 var cwhite = this.cwhite;
20045 var cblack = this.cblack;
20047 function cleanStyle(n,v)
20049 if (v.match(/expression/)) { //XSS?? should we even bother..
20050 node.removeAttribute(n);
20054 var parts = v.split(/;/);
20057 Roo.each(parts, function(p) {
20058 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20062 var l = p.split(':').shift().replace(/\s+/g,'');
20063 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20065 if ( cwhite.length && cblack.indexOf(l) > -1) {
20066 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20067 //node.removeAttribute(n);
20071 // only allow 'c whitelisted system attributes'
20072 if ( cwhite.length && cwhite.indexOf(l) < 0) {
20073 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20074 //node.removeAttribute(n);
20084 if (clean.length) {
20085 node.setAttribute(n, clean.join(';'));
20087 node.removeAttribute(n);
20093 for (var i = node.attributes.length-1; i > -1 ; i--) {
20094 var a = node.attributes[i];
20097 if (a.name.toLowerCase().substr(0,2)=='on') {
20098 node.removeAttribute(a.name);
20101 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20102 node.removeAttribute(a.name);
20105 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20106 cleanAttr(a.name,a.value); // fixme..
20109 if (a.name == 'style') {
20110 cleanStyle(a.name,a.value);
20113 /// clean up MS crap..
20114 // tecnically this should be a list of valid class'es..
20117 if (a.name == 'class') {
20118 if (a.value.match(/^Mso/)) {
20119 node.className = '';
20122 if (a.value.match(/body/)) {
20123 node.className = '';
20134 this.cleanUpChildren(node);
20140 * Clean up MS wordisms...
20142 cleanWord : function(node)
20147 this.cleanWord(this.doc.body);
20150 if (node.nodeName == "#text") {
20151 // clean up silly Windows -- stuff?
20154 if (node.nodeName == "#comment") {
20155 node.parentNode.removeChild(node);
20156 // clean up silly Windows -- stuff?
20160 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20161 node.parentNode.removeChild(node);
20165 // remove - but keep children..
20166 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20167 while (node.childNodes.length) {
20168 var cn = node.childNodes[0];
20169 node.removeChild(cn);
20170 node.parentNode.insertBefore(cn, node);
20172 node.parentNode.removeChild(node);
20173 this.iterateChildren(node, this.cleanWord);
20177 if (node.className.length) {
20179 var cn = node.className.split(/\W+/);
20181 Roo.each(cn, function(cls) {
20182 if (cls.match(/Mso[a-zA-Z]+/)) {
20187 node.className = cna.length ? cna.join(' ') : '';
20189 node.removeAttribute("class");
20193 if (node.hasAttribute("lang")) {
20194 node.removeAttribute("lang");
20197 if (node.hasAttribute("style")) {
20199 var styles = node.getAttribute("style").split(";");
20201 Roo.each(styles, function(s) {
20202 if (!s.match(/:/)) {
20205 var kv = s.split(":");
20206 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20209 // what ever is left... we allow.
20212 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20213 if (!nstyle.length) {
20214 node.removeAttribute('style');
20217 this.iterateChildren(node, this.cleanWord);
20223 * iterateChildren of a Node, calling fn each time, using this as the scole..
20224 * @param {DomNode} node node to iterate children of.
20225 * @param {Function} fn method of this class to call on each item.
20227 iterateChildren : function(node, fn)
20229 if (!node.childNodes.length) {
20232 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20233 fn.call(this, node.childNodes[i])
20239 * cleanTableWidths.
20241 * Quite often pasting from word etc.. results in tables with column and widths.
20242 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20245 cleanTableWidths : function(node)
20250 this.cleanTableWidths(this.doc.body);
20255 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20258 Roo.log(node.tagName);
20259 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20260 this.iterateChildren(node, this.cleanTableWidths);
20263 if (node.hasAttribute('width')) {
20264 node.removeAttribute('width');
20268 if (node.hasAttribute("style")) {
20271 var styles = node.getAttribute("style").split(";");
20273 Roo.each(styles, function(s) {
20274 if (!s.match(/:/)) {
20277 var kv = s.split(":");
20278 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20281 // what ever is left... we allow.
20284 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20285 if (!nstyle.length) {
20286 node.removeAttribute('style');
20290 this.iterateChildren(node, this.cleanTableWidths);
20298 domToHTML : function(currentElement, depth, nopadtext) {
20300 depth = depth || 0;
20301 nopadtext = nopadtext || false;
20303 if (!currentElement) {
20304 return this.domToHTML(this.doc.body);
20307 //Roo.log(currentElement);
20309 var allText = false;
20310 var nodeName = currentElement.nodeName;
20311 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20313 if (nodeName == '#text') {
20315 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20320 if (nodeName != 'BODY') {
20323 // Prints the node tagName, such as <A>, <IMG>, etc
20326 for(i = 0; i < currentElement.attributes.length;i++) {
20328 var aname = currentElement.attributes.item(i).name;
20329 if (!currentElement.attributes.item(i).value.length) {
20332 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20335 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20344 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20347 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20352 // Traverse the tree
20354 var currentElementChild = currentElement.childNodes.item(i);
20355 var allText = true;
20356 var innerHTML = '';
20358 while (currentElementChild) {
20359 // Formatting code (indent the tree so it looks nice on the screen)
20360 var nopad = nopadtext;
20361 if (lastnode == 'SPAN') {
20365 if (currentElementChild.nodeName == '#text') {
20366 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20367 toadd = nopadtext ? toadd : toadd.trim();
20368 if (!nopad && toadd.length > 80) {
20369 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20371 innerHTML += toadd;
20374 currentElementChild = currentElement.childNodes.item(i);
20380 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20382 // Recursively traverse the tree structure of the child node
20383 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20384 lastnode = currentElementChild.nodeName;
20386 currentElementChild=currentElement.childNodes.item(i);
20392 // The remaining code is mostly for formatting the tree
20393 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20398 ret+= "</"+tagName+">";
20404 applyBlacklists : function()
20406 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20407 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20411 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20412 if (b.indexOf(tag) > -1) {
20415 this.white.push(tag);
20419 Roo.each(w, function(tag) {
20420 if (b.indexOf(tag) > -1) {
20423 if (this.white.indexOf(tag) > -1) {
20426 this.white.push(tag);
20431 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20432 if (w.indexOf(tag) > -1) {
20435 this.black.push(tag);
20439 Roo.each(b, function(tag) {
20440 if (w.indexOf(tag) > -1) {
20443 if (this.black.indexOf(tag) > -1) {
20446 this.black.push(tag);
20451 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20452 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20456 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20457 if (b.indexOf(tag) > -1) {
20460 this.cwhite.push(tag);
20464 Roo.each(w, function(tag) {
20465 if (b.indexOf(tag) > -1) {
20468 if (this.cwhite.indexOf(tag) > -1) {
20471 this.cwhite.push(tag);
20476 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20477 if (w.indexOf(tag) > -1) {
20480 this.cblack.push(tag);
20484 Roo.each(b, function(tag) {
20485 if (w.indexOf(tag) > -1) {
20488 if (this.cblack.indexOf(tag) > -1) {
20491 this.cblack.push(tag);
20496 setStylesheets : function(stylesheets)
20498 if(typeof(stylesheets) == 'string'){
20499 Roo.get(this.iframe.contentDocument.head).createChild({
20501 rel : 'stylesheet',
20510 Roo.each(stylesheets, function(s) {
20515 Roo.get(_this.iframe.contentDocument.head).createChild({
20517 rel : 'stylesheet',
20526 removeStylesheets : function()
20530 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20535 // hide stuff that is not compatible
20549 * @event specialkey
20553 * @cfg {String} fieldClass @hide
20556 * @cfg {String} focusClass @hide
20559 * @cfg {String} autoCreate @hide
20562 * @cfg {String} inputType @hide
20565 * @cfg {String} invalidClass @hide
20568 * @cfg {String} invalidText @hide
20571 * @cfg {String} msgFx @hide
20574 * @cfg {String} validateOnBlur @hide
20578 Roo.HtmlEditorCore.white = [
20579 'area', 'br', 'img', 'input', 'hr', 'wbr',
20581 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
20582 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
20583 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
20584 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
20585 'table', 'ul', 'xmp',
20587 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
20590 'dir', 'menu', 'ol', 'ul', 'dl',
20596 Roo.HtmlEditorCore.black = [
20597 // 'embed', 'object', // enable - backend responsiblity to clean thiese
20599 'base', 'basefont', 'bgsound', 'blink', 'body',
20600 'frame', 'frameset', 'head', 'html', 'ilayer',
20601 'iframe', 'layer', 'link', 'meta', 'object',
20602 'script', 'style' ,'title', 'xml' // clean later..
20604 Roo.HtmlEditorCore.clean = [
20605 'script', 'style', 'title', 'xml'
20607 Roo.HtmlEditorCore.remove = [
20612 Roo.HtmlEditorCore.ablack = [
20616 Roo.HtmlEditorCore.aclean = [
20617 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
20621 Roo.HtmlEditorCore.pwhite= [
20622 'http', 'https', 'mailto'
20625 // white listed style attributes.
20626 Roo.HtmlEditorCore.cwhite= [
20627 // 'text-align', /// default is to allow most things..
20633 // black listed style attributes.
20634 Roo.HtmlEditorCore.cblack= [
20635 // 'font-size' -- this can be set by the project
20639 Roo.HtmlEditorCore.swapCodes =[
20658 * @class Roo.bootstrap.HtmlEditor
20659 * @extends Roo.bootstrap.TextArea
20660 * Bootstrap HtmlEditor class
20663 * Create a new HtmlEditor
20664 * @param {Object} config The config object
20667 Roo.bootstrap.HtmlEditor = function(config){
20668 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20669 if (!this.toolbars) {
20670 this.toolbars = [];
20672 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20675 * @event initialize
20676 * Fires when the editor is fully initialized (including the iframe)
20677 * @param {HtmlEditor} this
20682 * Fires when the editor is first receives the focus. Any insertion must wait
20683 * until after this event.
20684 * @param {HtmlEditor} this
20688 * @event beforesync
20689 * Fires before the textarea is updated with content from the editor iframe. Return false
20690 * to cancel the sync.
20691 * @param {HtmlEditor} this
20692 * @param {String} html
20696 * @event beforepush
20697 * Fires before the iframe editor is updated with content from the textarea. Return false
20698 * to cancel the push.
20699 * @param {HtmlEditor} this
20700 * @param {String} html
20705 * Fires when the textarea is updated with content from the editor iframe.
20706 * @param {HtmlEditor} this
20707 * @param {String} html
20712 * Fires when the iframe editor is updated with content from the textarea.
20713 * @param {HtmlEditor} this
20714 * @param {String} html
20718 * @event editmodechange
20719 * Fires when the editor switches edit modes
20720 * @param {HtmlEditor} this
20721 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20723 editmodechange: true,
20725 * @event editorevent
20726 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20727 * @param {HtmlEditor} this
20731 * @event firstfocus
20732 * Fires when on first focus - needed by toolbars..
20733 * @param {HtmlEditor} this
20738 * Auto save the htmlEditor value as a file into Events
20739 * @param {HtmlEditor} this
20743 * @event savedpreview
20744 * preview the saved version of htmlEditor
20745 * @param {HtmlEditor} this
20752 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
20756 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20761 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20766 * @cfg {Number} height (in pixels)
20770 * @cfg {Number} width (in pixels)
20775 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20778 stylesheets: false,
20783 // private properties
20784 validationEvent : false,
20786 initialized : false,
20789 onFocus : Roo.emptyFn,
20791 hideMode:'offsets',
20794 tbContainer : false,
20796 toolbarContainer :function() {
20797 return this.wrap.select('.x-html-editor-tb',true).first();
20801 * Protected method that will not generally be called directly. It
20802 * is called when the editor creates its toolbar. Override this method if you need to
20803 * add custom toolbar buttons.
20804 * @param {HtmlEditor} editor
20806 createToolbar : function(){
20808 Roo.log("create toolbars");
20810 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20811 this.toolbars[0].render(this.toolbarContainer());
20815 // if (!editor.toolbars || !editor.toolbars.length) {
20816 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20819 // for (var i =0 ; i < editor.toolbars.length;i++) {
20820 // editor.toolbars[i] = Roo.factory(
20821 // typeof(editor.toolbars[i]) == 'string' ?
20822 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
20823 // Roo.bootstrap.HtmlEditor);
20824 // editor.toolbars[i].init(editor);
20830 onRender : function(ct, position)
20832 // Roo.log("Call onRender: " + this.xtype);
20834 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20836 this.wrap = this.inputEl().wrap({
20837 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20840 this.editorcore.onRender(ct, position);
20842 if (this.resizable) {
20843 this.resizeEl = new Roo.Resizable(this.wrap, {
20847 minHeight : this.height,
20848 height: this.height,
20849 handles : this.resizable,
20852 resize : function(r, w, h) {
20853 _t.onResize(w,h); // -something
20859 this.createToolbar(this);
20862 if(!this.width && this.resizable){
20863 this.setSize(this.wrap.getSize());
20865 if (this.resizeEl) {
20866 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20867 // should trigger onReize..
20873 onResize : function(w, h)
20875 Roo.log('resize: ' +w + ',' + h );
20876 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20880 if(this.inputEl() ){
20881 if(typeof w == 'number'){
20882 var aw = w - this.wrap.getFrameWidth('lr');
20883 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20886 if(typeof h == 'number'){
20887 var tbh = -11; // fixme it needs to tool bar size!
20888 for (var i =0; i < this.toolbars.length;i++) {
20889 // fixme - ask toolbars for heights?
20890 tbh += this.toolbars[i].el.getHeight();
20891 //if (this.toolbars[i].footer) {
20892 // tbh += this.toolbars[i].footer.el.getHeight();
20900 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20901 ah -= 5; // knock a few pixes off for look..
20902 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20906 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20907 this.editorcore.onResize(ew,eh);
20912 * Toggles the editor between standard and source edit mode.
20913 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20915 toggleSourceEdit : function(sourceEditMode)
20917 this.editorcore.toggleSourceEdit(sourceEditMode);
20919 if(this.editorcore.sourceEditMode){
20920 Roo.log('editor - showing textarea');
20923 // Roo.log(this.syncValue());
20925 this.inputEl().removeClass(['hide', 'x-hidden']);
20926 this.inputEl().dom.removeAttribute('tabIndex');
20927 this.inputEl().focus();
20929 Roo.log('editor - hiding textarea');
20931 // Roo.log(this.pushValue());
20934 this.inputEl().addClass(['hide', 'x-hidden']);
20935 this.inputEl().dom.setAttribute('tabIndex', -1);
20936 //this.deferFocus();
20939 if(this.resizable){
20940 this.setSize(this.wrap.getSize());
20943 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20946 // private (for BoxComponent)
20947 adjustSize : Roo.BoxComponent.prototype.adjustSize,
20949 // private (for BoxComponent)
20950 getResizeEl : function(){
20954 // private (for BoxComponent)
20955 getPositionEl : function(){
20960 initEvents : function(){
20961 this.originalValue = this.getValue();
20965 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20968 // markInvalid : Roo.emptyFn,
20970 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20973 // clearInvalid : Roo.emptyFn,
20975 setValue : function(v){
20976 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20977 this.editorcore.pushValue();
20982 deferFocus : function(){
20983 this.focus.defer(10, this);
20987 focus : function(){
20988 this.editorcore.focus();
20994 onDestroy : function(){
21000 for (var i =0; i < this.toolbars.length;i++) {
21001 // fixme - ask toolbars for heights?
21002 this.toolbars[i].onDestroy();
21005 this.wrap.dom.innerHTML = '';
21006 this.wrap.remove();
21011 onFirstFocus : function(){
21012 //Roo.log("onFirstFocus");
21013 this.editorcore.onFirstFocus();
21014 for (var i =0; i < this.toolbars.length;i++) {
21015 this.toolbars[i].onFirstFocus();
21021 syncValue : function()
21023 this.editorcore.syncValue();
21026 pushValue : function()
21028 this.editorcore.pushValue();
21032 // hide stuff that is not compatible
21046 * @event specialkey
21050 * @cfg {String} fieldClass @hide
21053 * @cfg {String} focusClass @hide
21056 * @cfg {String} autoCreate @hide
21059 * @cfg {String} inputType @hide
21062 * @cfg {String} invalidClass @hide
21065 * @cfg {String} invalidText @hide
21068 * @cfg {String} msgFx @hide
21071 * @cfg {String} validateOnBlur @hide
21080 Roo.namespace('Roo.bootstrap.htmleditor');
21082 * @class Roo.bootstrap.HtmlEditorToolbar1
21087 new Roo.bootstrap.HtmlEditor({
21090 new Roo.bootstrap.HtmlEditorToolbar1({
21091 disable : { fonts: 1 , format: 1, ..., ... , ...],
21097 * @cfg {Object} disable List of elements to disable..
21098 * @cfg {Array} btns List of additional buttons.
21102 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21105 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21108 Roo.apply(this, config);
21110 // default disabled, based on 'good practice'..
21111 this.disable = this.disable || {};
21112 Roo.applyIf(this.disable, {
21115 specialElements : true
21117 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21119 this.editor = config.editor;
21120 this.editorcore = config.editor.editorcore;
21122 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21124 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21125 // dont call parent... till later.
21127 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
21132 editorcore : false,
21137 "h1","h2","h3","h4","h5","h6",
21139 "abbr", "acronym", "address", "cite", "samp", "var",
21143 onRender : function(ct, position)
21145 // Roo.log("Call onRender: " + this.xtype);
21147 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21149 this.el.dom.style.marginBottom = '0';
21151 var editorcore = this.editorcore;
21152 var editor= this.editor;
21155 var btn = function(id,cmd , toggle, handler){
21157 var event = toggle ? 'toggle' : 'click';
21162 xns: Roo.bootstrap,
21165 enableToggle:toggle !== false,
21167 pressed : toggle ? false : null,
21170 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21171 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21180 xns: Roo.bootstrap,
21181 glyphicon : 'font',
21185 xns: Roo.bootstrap,
21189 Roo.each(this.formats, function(f) {
21190 style.menu.items.push({
21192 xns: Roo.bootstrap,
21193 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21198 editorcore.insertTag(this.tagname);
21205 children.push(style);
21208 btn('bold',false,true);
21209 btn('italic',false,true);
21210 btn('align-left', 'justifyleft',true);
21211 btn('align-center', 'justifycenter',true);
21212 btn('align-right' , 'justifyright',true);
21213 btn('link', false, false, function(btn) {
21214 //Roo.log("create link?");
21215 var url = prompt(this.createLinkText, this.defaultLinkValue);
21216 if(url && url != 'http:/'+'/'){
21217 this.editorcore.relayCmd('createlink', url);
21220 btn('list','insertunorderedlist',true);
21221 btn('pencil', false,true, function(btn){
21224 this.toggleSourceEdit(btn.pressed);
21230 xns: Roo.bootstrap,
21235 xns: Roo.bootstrap,
21240 cog.menu.items.push({
21242 xns: Roo.bootstrap,
21243 html : Clean styles,
21248 editorcore.insertTag(this.tagname);
21257 this.xtype = 'NavSimplebar';
21259 for(var i=0;i< children.length;i++) {
21261 this.buttons.add(this.addxtypeChild(children[i]));
21265 editor.on('editorevent', this.updateToolbar, this);
21267 onBtnClick : function(id)
21269 this.editorcore.relayCmd(id);
21270 this.editorcore.focus();
21274 * Protected method that will not generally be called directly. It triggers
21275 * a toolbar update by reading the markup state of the current selection in the editor.
21277 updateToolbar: function(){
21279 if(!this.editorcore.activated){
21280 this.editor.onFirstFocus(); // is this neeed?
21284 var btns = this.buttons;
21285 var doc = this.editorcore.doc;
21286 btns.get('bold').setActive(doc.queryCommandState('bold'));
21287 btns.get('italic').setActive(doc.queryCommandState('italic'));
21288 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21290 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21291 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21292 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21294 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21295 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21298 var ans = this.editorcore.getAllAncestors();
21299 if (this.formatCombo) {
21302 var store = this.formatCombo.store;
21303 this.formatCombo.setValue("");
21304 for (var i =0; i < ans.length;i++) {
21305 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21307 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21315 // hides menus... - so this cant be on a menu...
21316 Roo.bootstrap.MenuMgr.hideAll();
21318 Roo.bootstrap.MenuMgr.hideAll();
21319 //this.editorsyncValue();
21321 onFirstFocus: function() {
21322 this.buttons.each(function(item){
21326 toggleSourceEdit : function(sourceEditMode){
21329 if(sourceEditMode){
21330 Roo.log("disabling buttons");
21331 this.buttons.each( function(item){
21332 if(item.cmd != 'pencil'){
21338 Roo.log("enabling buttons");
21339 if(this.editorcore.initialized){
21340 this.buttons.each( function(item){
21346 Roo.log("calling toggole on editor");
21347 // tell the editor that it's been pressed..
21348 this.editor.toggleSourceEdit(sourceEditMode);
21358 * @class Roo.bootstrap.Table.AbstractSelectionModel
21359 * @extends Roo.util.Observable
21360 * Abstract base class for grid SelectionModels. It provides the interface that should be
21361 * implemented by descendant classes. This class should not be directly instantiated.
21364 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21365 this.locked = false;
21366 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21370 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21371 /** @ignore Called by the grid automatically. Do not call directly. */
21372 init : function(grid){
21378 * Locks the selections.
21381 this.locked = true;
21385 * Unlocks the selections.
21387 unlock : function(){
21388 this.locked = false;
21392 * Returns true if the selections are locked.
21393 * @return {Boolean}
21395 isLocked : function(){
21396 return this.locked;
21400 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21401 * @class Roo.bootstrap.Table.RowSelectionModel
21402 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21403 * It supports multiple selections and keyboard selection/navigation.
21405 * @param {Object} config
21408 Roo.bootstrap.Table.RowSelectionModel = function(config){
21409 Roo.apply(this, config);
21410 this.selections = new Roo.util.MixedCollection(false, function(o){
21415 this.lastActive = false;
21419 * @event selectionchange
21420 * Fires when the selection changes
21421 * @param {SelectionModel} this
21423 "selectionchange" : true,
21425 * @event afterselectionchange
21426 * Fires after the selection changes (eg. by key press or clicking)
21427 * @param {SelectionModel} this
21429 "afterselectionchange" : true,
21431 * @event beforerowselect
21432 * Fires when a row is selected being selected, return false to cancel.
21433 * @param {SelectionModel} this
21434 * @param {Number} rowIndex The selected index
21435 * @param {Boolean} keepExisting False if other selections will be cleared
21437 "beforerowselect" : true,
21440 * Fires when a row is selected.
21441 * @param {SelectionModel} this
21442 * @param {Number} rowIndex The selected index
21443 * @param {Roo.data.Record} r The record
21445 "rowselect" : true,
21447 * @event rowdeselect
21448 * Fires when a row is deselected.
21449 * @param {SelectionModel} this
21450 * @param {Number} rowIndex The selected index
21452 "rowdeselect" : true
21454 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21455 this.locked = false;
21458 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21460 * @cfg {Boolean} singleSelect
21461 * True to allow selection of only one row at a time (defaults to false)
21463 singleSelect : false,
21466 initEvents : function(){
21468 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21469 this.grid.on("mousedown", this.handleMouseDown, this);
21470 }else{ // allow click to work like normal
21471 this.grid.on("rowclick", this.handleDragableRowClick, this);
21474 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21475 "up" : function(e){
21477 this.selectPrevious(e.shiftKey);
21478 }else if(this.last !== false && this.lastActive !== false){
21479 var last = this.last;
21480 this.selectRange(this.last, this.lastActive-1);
21481 this.grid.getView().focusRow(this.lastActive);
21482 if(last !== false){
21486 this.selectFirstRow();
21488 this.fireEvent("afterselectionchange", this);
21490 "down" : function(e){
21492 this.selectNext(e.shiftKey);
21493 }else if(this.last !== false && this.lastActive !== false){
21494 var last = this.last;
21495 this.selectRange(this.last, this.lastActive+1);
21496 this.grid.getView().focusRow(this.lastActive);
21497 if(last !== false){
21501 this.selectFirstRow();
21503 this.fireEvent("afterselectionchange", this);
21508 var view = this.grid.view;
21509 view.on("refresh", this.onRefresh, this);
21510 view.on("rowupdated", this.onRowUpdated, this);
21511 view.on("rowremoved", this.onRemove, this);
21515 onRefresh : function(){
21516 var ds = this.grid.dataSource, i, v = this.grid.view;
21517 var s = this.selections;
21518 s.each(function(r){
21519 if((i = ds.indexOfId(r.id)) != -1){
21528 onRemove : function(v, index, r){
21529 this.selections.remove(r);
21533 onRowUpdated : function(v, index, r){
21534 if(this.isSelected(r)){
21535 v.onRowSelect(index);
21541 * @param {Array} records The records to select
21542 * @param {Boolean} keepExisting (optional) True to keep existing selections
21544 selectRecords : function(records, keepExisting){
21546 this.clearSelections();
21548 var ds = this.grid.dataSource;
21549 for(var i = 0, len = records.length; i < len; i++){
21550 this.selectRow(ds.indexOf(records[i]), true);
21555 * Gets the number of selected rows.
21558 getCount : function(){
21559 return this.selections.length;
21563 * Selects the first row in the grid.
21565 selectFirstRow : function(){
21570 * Select the last row.
21571 * @param {Boolean} keepExisting (optional) True to keep existing selections
21573 selectLastRow : function(keepExisting){
21574 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21578 * Selects the row immediately following the last selected row.
21579 * @param {Boolean} keepExisting (optional) True to keep existing selections
21581 selectNext : function(keepExisting){
21582 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21583 this.selectRow(this.last+1, keepExisting);
21584 this.grid.getView().focusRow(this.last);
21589 * Selects the row that precedes the last selected row.
21590 * @param {Boolean} keepExisting (optional) True to keep existing selections
21592 selectPrevious : function(keepExisting){
21594 this.selectRow(this.last-1, keepExisting);
21595 this.grid.getView().focusRow(this.last);
21600 * Returns the selected records
21601 * @return {Array} Array of selected records
21603 getSelections : function(){
21604 return [].concat(this.selections.items);
21608 * Returns the first selected record.
21611 getSelected : function(){
21612 return this.selections.itemAt(0);
21617 * Clears all selections.
21619 clearSelections : function(fast){
21620 if(this.locked) return;
21622 var ds = this.grid.dataSource;
21623 var s = this.selections;
21624 s.each(function(r){
21625 this.deselectRow(ds.indexOfId(r.id));
21629 this.selections.clear();
21636 * Selects all rows.
21638 selectAll : function(){
21639 if(this.locked) return;
21640 this.selections.clear();
21641 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21642 this.selectRow(i, true);
21647 * Returns True if there is a selection.
21648 * @return {Boolean}
21650 hasSelection : function(){
21651 return this.selections.length > 0;
21655 * Returns True if the specified row is selected.
21656 * @param {Number/Record} record The record or index of the record to check
21657 * @return {Boolean}
21659 isSelected : function(index){
21660 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21661 return (r && this.selections.key(r.id) ? true : false);
21665 * Returns True if the specified record id is selected.
21666 * @param {String} id The id of record to check
21667 * @return {Boolean}
21669 isIdSelected : function(id){
21670 return (this.selections.key(id) ? true : false);
21674 handleMouseDown : function(e, t){
21675 var view = this.grid.getView(), rowIndex;
21676 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21679 if(e.shiftKey && this.last !== false){
21680 var last = this.last;
21681 this.selectRange(last, rowIndex, e.ctrlKey);
21682 this.last = last; // reset the last
21683 view.focusRow(rowIndex);
21685 var isSelected = this.isSelected(rowIndex);
21686 if(e.button !== 0 && isSelected){
21687 view.focusRow(rowIndex);
21688 }else if(e.ctrlKey && isSelected){
21689 this.deselectRow(rowIndex);
21690 }else if(!isSelected){
21691 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21692 view.focusRow(rowIndex);
21695 this.fireEvent("afterselectionchange", this);
21698 handleDragableRowClick : function(grid, rowIndex, e)
21700 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21701 this.selectRow(rowIndex, false);
21702 grid.view.focusRow(rowIndex);
21703 this.fireEvent("afterselectionchange", this);
21708 * Selects multiple rows.
21709 * @param {Array} rows Array of the indexes of the row to select
21710 * @param {Boolean} keepExisting (optional) True to keep existing selections
21712 selectRows : function(rows, keepExisting){
21714 this.clearSelections();
21716 for(var i = 0, len = rows.length; i < len; i++){
21717 this.selectRow(rows[i], true);
21722 * Selects a range of rows. All rows in between startRow and endRow are also selected.
21723 * @param {Number} startRow The index of the first row in the range
21724 * @param {Number} endRow The index of the last row in the range
21725 * @param {Boolean} keepExisting (optional) True to retain existing selections
21727 selectRange : function(startRow, endRow, keepExisting){
21728 if(this.locked) return;
21730 this.clearSelections();
21732 if(startRow <= endRow){
21733 for(var i = startRow; i <= endRow; i++){
21734 this.selectRow(i, true);
21737 for(var i = startRow; i >= endRow; i--){
21738 this.selectRow(i, true);
21744 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21745 * @param {Number} startRow The index of the first row in the range
21746 * @param {Number} endRow The index of the last row in the range
21748 deselectRange : function(startRow, endRow, preventViewNotify){
21749 if(this.locked) return;
21750 for(var i = startRow; i <= endRow; i++){
21751 this.deselectRow(i, preventViewNotify);
21757 * @param {Number} row The index of the row to select
21758 * @param {Boolean} keepExisting (optional) True to keep existing selections
21760 selectRow : function(index, keepExisting, preventViewNotify){
21761 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21762 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21763 if(!keepExisting || this.singleSelect){
21764 this.clearSelections();
21766 var r = this.grid.dataSource.getAt(index);
21767 this.selections.add(r);
21768 this.last = this.lastActive = index;
21769 if(!preventViewNotify){
21770 this.grid.getView().onRowSelect(index);
21772 this.fireEvent("rowselect", this, index, r);
21773 this.fireEvent("selectionchange", this);
21779 * @param {Number} row The index of the row to deselect
21781 deselectRow : function(index, preventViewNotify){
21782 if(this.locked) return;
21783 if(this.last == index){
21786 if(this.lastActive == index){
21787 this.lastActive = false;
21789 var r = this.grid.dataSource.getAt(index);
21790 this.selections.remove(r);
21791 if(!preventViewNotify){
21792 this.grid.getView().onRowDeselect(index);
21794 this.fireEvent("rowdeselect", this, index);
21795 this.fireEvent("selectionchange", this);
21799 restoreLast : function(){
21801 this.last = this._last;
21806 acceptsNav : function(row, col, cm){
21807 return !cm.isHidden(col) && cm.isCellEditable(col, row);
21811 onEditorKey : function(field, e){
21812 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21817 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21819 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21821 }else if(k == e.ENTER && !e.ctrlKey){
21825 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21827 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21829 }else if(k == e.ESC){
21833 g.startEditing(newCell[0], newCell[1]);
21838 * Ext JS Library 1.1.1
21839 * Copyright(c) 2006-2007, Ext JS, LLC.
21841 * Originally Released Under LGPL - original licence link has changed is not relivant.
21844 * <script type="text/javascript">
21848 * @class Roo.bootstrap.PagingToolbar
21849 * @extends Roo.bootstrap.NavSimplebar
21850 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21852 * Create a new PagingToolbar
21853 * @param {Object} config The config object
21854 * @param {Roo.data.Store} store
21856 Roo.bootstrap.PagingToolbar = function(config)
21858 // old args format still supported... - xtype is prefered..
21859 // created from xtype...
21861 this.ds = config.dataSource;
21863 if (config.store && !this.ds) {
21864 this.store= Roo.factory(config.store, Roo.data);
21865 this.ds = this.store;
21866 this.ds.xmodule = this.xmodule || false;
21869 this.toolbarItems = [];
21870 if (config.items) {
21871 this.toolbarItems = config.items;
21874 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21879 this.bind(this.ds);
21882 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21886 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21888 * @cfg {Roo.data.Store} dataSource
21889 * The underlying data store providing the paged data
21892 * @cfg {String/HTMLElement/Element} container
21893 * container The id or element that will contain the toolbar
21896 * @cfg {Boolean} displayInfo
21897 * True to display the displayMsg (defaults to false)
21900 * @cfg {Number} pageSize
21901 * The number of records to display per page (defaults to 20)
21905 * @cfg {String} displayMsg
21906 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21908 displayMsg : 'Displaying {0} - {1} of {2}',
21910 * @cfg {String} emptyMsg
21911 * The message to display when no records are found (defaults to "No data to display")
21913 emptyMsg : 'No data to display',
21915 * Customizable piece of the default paging text (defaults to "Page")
21918 beforePageText : "Page",
21920 * Customizable piece of the default paging text (defaults to "of %0")
21923 afterPageText : "of {0}",
21925 * Customizable piece of the default paging text (defaults to "First Page")
21928 firstText : "First Page",
21930 * Customizable piece of the default paging text (defaults to "Previous Page")
21933 prevText : "Previous Page",
21935 * Customizable piece of the default paging text (defaults to "Next Page")
21938 nextText : "Next Page",
21940 * Customizable piece of the default paging text (defaults to "Last Page")
21943 lastText : "Last Page",
21945 * Customizable piece of the default paging text (defaults to "Refresh")
21948 refreshText : "Refresh",
21952 onRender : function(ct, position)
21954 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21955 this.navgroup.parentId = this.id;
21956 this.navgroup.onRender(this.el, null);
21957 // add the buttons to the navgroup
21959 if(this.displayInfo){
21960 Roo.log(this.el.select('ul.navbar-nav',true).first());
21961 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21962 this.displayEl = this.el.select('.x-paging-info', true).first();
21963 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21964 // this.displayEl = navel.el.select('span',true).first();
21970 Roo.each(_this.buttons, function(e){ // this might need to use render????
21971 Roo.factory(e).onRender(_this.el, null);
21975 Roo.each(_this.toolbarItems, function(e) {
21976 _this.navgroup.addItem(e);
21980 this.first = this.navgroup.addItem({
21981 tooltip: this.firstText,
21983 icon : 'fa fa-backward',
21985 preventDefault: true,
21986 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21989 this.prev = this.navgroup.addItem({
21990 tooltip: this.prevText,
21992 icon : 'fa fa-step-backward',
21994 preventDefault: true,
21995 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
21997 //this.addSeparator();
22000 var field = this.navgroup.addItem( {
22002 cls : 'x-paging-position',
22004 html : this.beforePageText +
22005 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22006 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
22009 this.field = field.el.select('input', true).first();
22010 this.field.on("keydown", this.onPagingKeydown, this);
22011 this.field.on("focus", function(){this.dom.select();});
22014 this.afterTextEl = field.el.select('.x-paging-after',true).first();
22015 //this.field.setHeight(18);
22016 //this.addSeparator();
22017 this.next = this.navgroup.addItem({
22018 tooltip: this.nextText,
22020 html : ' <i class="fa fa-step-forward">',
22022 preventDefault: true,
22023 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
22025 this.last = this.navgroup.addItem({
22026 tooltip: this.lastText,
22027 icon : 'fa fa-forward',
22030 preventDefault: true,
22031 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
22033 //this.addSeparator();
22034 this.loading = this.navgroup.addItem({
22035 tooltip: this.refreshText,
22036 icon: 'fa fa-refresh',
22037 preventDefault: true,
22038 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22044 updateInfo : function(){
22045 if(this.displayEl){
22046 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22047 var msg = count == 0 ?
22051 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
22053 this.displayEl.update(msg);
22058 onLoad : function(ds, r, o){
22059 this.cursor = o.params ? o.params.start : 0;
22060 var d = this.getPageData(),
22064 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22065 this.field.dom.value = ap;
22066 this.first.setDisabled(ap == 1);
22067 this.prev.setDisabled(ap == 1);
22068 this.next.setDisabled(ap == ps);
22069 this.last.setDisabled(ap == ps);
22070 this.loading.enable();
22075 getPageData : function(){
22076 var total = this.ds.getTotalCount();
22079 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22080 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22085 onLoadError : function(){
22086 this.loading.enable();
22090 onPagingKeydown : function(e){
22091 var k = e.getKey();
22092 var d = this.getPageData();
22094 var v = this.field.dom.value, pageNum;
22095 if(!v || isNaN(pageNum = parseInt(v, 10))){
22096 this.field.dom.value = d.activePage;
22099 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22100 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22103 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))
22105 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22106 this.field.dom.value = pageNum;
22107 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22110 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22112 var v = this.field.dom.value, pageNum;
22113 var increment = (e.shiftKey) ? 10 : 1;
22114 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22116 if(!v || isNaN(pageNum = parseInt(v, 10))) {
22117 this.field.dom.value = d.activePage;
22120 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22122 this.field.dom.value = parseInt(v, 10) + increment;
22123 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22124 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22131 beforeLoad : function(){
22133 this.loading.disable();
22138 onClick : function(which){
22147 ds.load({params:{start: 0, limit: this.pageSize}});
22150 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22153 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22156 var total = ds.getTotalCount();
22157 var extra = total % this.pageSize;
22158 var lastStart = extra ? (total - extra) : total-this.pageSize;
22159 ds.load({params:{start: lastStart, limit: this.pageSize}});
22162 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22168 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22169 * @param {Roo.data.Store} store The data store to unbind
22171 unbind : function(ds){
22172 ds.un("beforeload", this.beforeLoad, this);
22173 ds.un("load", this.onLoad, this);
22174 ds.un("loadexception", this.onLoadError, this);
22175 ds.un("remove", this.updateInfo, this);
22176 ds.un("add", this.updateInfo, this);
22177 this.ds = undefined;
22181 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22182 * @param {Roo.data.Store} store The data store to bind
22184 bind : function(ds){
22185 ds.on("beforeload", this.beforeLoad, this);
22186 ds.on("load", this.onLoad, this);
22187 ds.on("loadexception", this.onLoadError, this);
22188 ds.on("remove", this.updateInfo, this);
22189 ds.on("add", this.updateInfo, this);
22200 * @class Roo.bootstrap.MessageBar
22201 * @extends Roo.bootstrap.Component
22202 * Bootstrap MessageBar class
22203 * @cfg {String} html contents of the MessageBar
22204 * @cfg {String} weight (info | success | warning | danger) default info
22205 * @cfg {String} beforeClass insert the bar before the given class
22206 * @cfg {Boolean} closable (true | false) default false
22207 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22210 * Create a new Element
22211 * @param {Object} config The config object
22214 Roo.bootstrap.MessageBar = function(config){
22215 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22218 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22224 beforeClass: 'bootstrap-sticky-wrap',
22226 getAutoCreate : function(){
22230 cls: 'alert alert-dismissable alert-' + this.weight,
22235 html: this.html || ''
22241 cfg.cls += ' alert-messages-fixed';
22255 onRender : function(ct, position)
22257 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22260 var cfg = Roo.apply({}, this.getAutoCreate());
22264 cfg.cls += ' ' + this.cls;
22267 cfg.style = this.style;
22269 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22271 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22274 this.el.select('>button.close').on('click', this.hide, this);
22280 if (!this.rendered) {
22286 this.fireEvent('show', this);
22292 if (!this.rendered) {
22298 this.fireEvent('hide', this);
22301 update : function()
22303 // var e = this.el.dom.firstChild;
22305 // if(this.closable){
22306 // e = e.nextSibling;
22309 // e.data = this.html || '';
22311 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22327 * @class Roo.bootstrap.Graph
22328 * @extends Roo.bootstrap.Component
22329 * Bootstrap Graph class
22333 @cfg {String} graphtype bar | vbar | pie
22334 @cfg {number} g_x coodinator | centre x (pie)
22335 @cfg {number} g_y coodinator | centre y (pie)
22336 @cfg {number} g_r radius (pie)
22337 @cfg {number} g_height height of the chart (respected by all elements in the set)
22338 @cfg {number} g_width width of the chart (respected by all elements in the set)
22339 @cfg {Object} title The title of the chart
22342 -opts (object) options for the chart
22344 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22345 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22347 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.
22348 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22350 o stretch (boolean)
22352 -opts (object) options for the pie
22355 o startAngle (number)
22356 o endAngle (number)
22360 * Create a new Input
22361 * @param {Object} config The config object
22364 Roo.bootstrap.Graph = function(config){
22365 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22371 * The img click event for the img.
22372 * @param {Roo.EventObject} e
22378 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22389 //g_colors: this.colors,
22396 getAutoCreate : function(){
22407 onRender : function(ct,position){
22408 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22409 this.raphael = Raphael(this.el.dom);
22411 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22412 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22413 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22414 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22416 r.text(160, 10, "Single Series Chart").attr(txtattr);
22417 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22418 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22419 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22421 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22422 r.barchart(330, 10, 300, 220, data1);
22423 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22424 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22427 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22428 // r.barchart(30, 30, 560, 250, xdata, {
22429 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22430 // axis : "0 0 1 1",
22431 // axisxlabels : xdata
22432 // //yvalues : cols,
22435 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22437 // this.load(null,xdata,{
22438 // axis : "0 0 1 1",
22439 // axisxlabels : xdata
22444 load : function(graphtype,xdata,opts){
22445 this.raphael.clear();
22447 graphtype = this.graphtype;
22452 var r = this.raphael,
22453 fin = function () {
22454 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22456 fout = function () {
22457 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22459 pfin = function() {
22460 this.sector.stop();
22461 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22464 this.label[0].stop();
22465 this.label[0].attr({ r: 7.5 });
22466 this.label[1].attr({ "font-weight": 800 });
22469 pfout = function() {
22470 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22473 this.label[0].animate({ r: 5 }, 500, "bounce");
22474 this.label[1].attr({ "font-weight": 400 });
22480 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22483 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22486 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
22487 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22489 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22496 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22501 setTitle: function(o)
22506 initEvents: function() {
22509 this.el.on('click', this.onClick, this);
22513 onClick : function(e)
22515 Roo.log('img onclick');
22516 this.fireEvent('click', this, e);
22528 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22531 * @class Roo.bootstrap.dash.NumberBox
22532 * @extends Roo.bootstrap.Component
22533 * Bootstrap NumberBox class
22534 * @cfg {String} headline Box headline
22535 * @cfg {String} content Box content
22536 * @cfg {String} icon Box icon
22537 * @cfg {String} footer Footer text
22538 * @cfg {String} fhref Footer href
22541 * Create a new NumberBox
22542 * @param {Object} config The config object
22546 Roo.bootstrap.dash.NumberBox = function(config){
22547 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22551 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
22560 getAutoCreate : function(){
22564 cls : 'small-box ',
22572 cls : 'roo-headline',
22573 html : this.headline
22577 cls : 'roo-content',
22578 html : this.content
22592 cls : 'ion ' + this.icon
22601 cls : 'small-box-footer',
22602 href : this.fhref || '#',
22606 cfg.cn.push(footer);
22613 onRender : function(ct,position){
22614 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22621 setHeadline: function (value)
22623 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22626 setFooter: function (value, href)
22628 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22631 this.el.select('a.small-box-footer',true).first().attr('href', href);
22636 setContent: function (value)
22638 this.el.select('.roo-content',true).first().dom.innerHTML = value;
22641 initEvents: function()
22655 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22658 * @class Roo.bootstrap.dash.TabBox
22659 * @extends Roo.bootstrap.Component
22660 * Bootstrap TabBox class
22661 * @cfg {String} title Title of the TabBox
22662 * @cfg {String} icon Icon of the TabBox
22663 * @cfg {Boolean} showtabs (true|false) show the tabs default true
22664 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22667 * Create a new TabBox
22668 * @param {Object} config The config object
22672 Roo.bootstrap.dash.TabBox = function(config){
22673 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22678 * When a pane is added
22679 * @param {Roo.bootstrap.dash.TabPane} pane
22683 * @event activatepane
22684 * When a pane is activated
22685 * @param {Roo.bootstrap.dash.TabPane} pane
22687 "activatepane" : true
22695 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
22700 tabScrollable : false,
22702 getChildContainer : function()
22704 return this.el.select('.tab-content', true).first();
22707 getAutoCreate : function(){
22711 cls: 'pull-left header',
22719 cls: 'fa ' + this.icon
22725 cls: 'nav nav-tabs pull-right',
22731 if(this.tabScrollable){
22738 cls: 'nav nav-tabs pull-right',
22749 cls: 'nav-tabs-custom',
22754 cls: 'tab-content no-padding',
22762 initEvents : function()
22764 //Roo.log('add add pane handler');
22765 this.on('addpane', this.onAddPane, this);
22768 * Updates the box title
22769 * @param {String} html to set the title to.
22771 setTitle : function(value)
22773 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22775 onAddPane : function(pane)
22777 this.panes.push(pane);
22778 //Roo.log('addpane');
22780 // tabs are rendere left to right..
22781 if(!this.showtabs){
22785 var ctr = this.el.select('.nav-tabs', true).first();
22788 var existing = ctr.select('.nav-tab',true);
22789 var qty = existing.getCount();;
22792 var tab = ctr.createChild({
22794 cls : 'nav-tab' + (qty ? '' : ' active'),
22802 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22805 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22807 pane.el.addClass('active');
22812 onTabClick : function(ev,un,ob,pane)
22814 //Roo.log('tab - prev default');
22815 ev.preventDefault();
22818 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22819 pane.tab.addClass('active');
22820 //Roo.log(pane.title);
22821 this.getChildContainer().select('.tab-pane',true).removeClass('active');
22822 // technically we should have a deactivate event.. but maybe add later.
22823 // and it should not de-activate the selected tab...
22824 this.fireEvent('activatepane', pane);
22825 pane.el.addClass('active');
22826 pane.fireEvent('activate');
22831 getActivePane : function()
22834 Roo.each(this.panes, function(p) {
22835 if(p.el.hasClass('active')){
22856 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22858 * @class Roo.bootstrap.TabPane
22859 * @extends Roo.bootstrap.Component
22860 * Bootstrap TabPane class
22861 * @cfg {Boolean} active (false | true) Default false
22862 * @cfg {String} title title of panel
22866 * Create a new TabPane
22867 * @param {Object} config The config object
22870 Roo.bootstrap.dash.TabPane = function(config){
22871 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22877 * When a pane is activated
22878 * @param {Roo.bootstrap.dash.TabPane} pane
22885 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
22890 // the tabBox that this is attached to.
22893 getAutoCreate : function()
22901 cfg.cls += ' active';
22906 initEvents : function()
22908 //Roo.log('trigger add pane handler');
22909 this.parent().fireEvent('addpane', this)
22913 * Updates the tab title
22914 * @param {String} html to set the title to.
22916 setTitle: function(str)
22922 this.tab.select('a', true).first().dom.innerHTML = str;
22939 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22942 * @class Roo.bootstrap.menu.Menu
22943 * @extends Roo.bootstrap.Component
22944 * Bootstrap Menu class - container for Menu
22945 * @cfg {String} html Text of the menu
22946 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22947 * @cfg {String} icon Font awesome icon
22948 * @cfg {String} pos Menu align to (top | bottom) default bottom
22952 * Create a new Menu
22953 * @param {Object} config The config object
22957 Roo.bootstrap.menu.Menu = function(config){
22958 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22962 * @event beforeshow
22963 * Fires before this menu is displayed
22964 * @param {Roo.bootstrap.menu.Menu} this
22968 * @event beforehide
22969 * Fires before this menu is hidden
22970 * @param {Roo.bootstrap.menu.Menu} this
22975 * Fires after this menu is displayed
22976 * @param {Roo.bootstrap.menu.Menu} this
22981 * Fires after this menu is hidden
22982 * @param {Roo.bootstrap.menu.Menu} this
22987 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22988 * @param {Roo.bootstrap.menu.Menu} this
22989 * @param {Roo.EventObject} e
22996 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
23000 weight : 'default',
23005 getChildContainer : function() {
23006 if(this.isSubMenu){
23010 return this.el.select('ul.dropdown-menu', true).first();
23013 getAutoCreate : function()
23018 cls : 'roo-menu-text',
23026 cls : 'fa ' + this.icon
23037 cls : 'dropdown-button btn btn-' + this.weight,
23042 cls : 'dropdown-toggle btn btn-' + this.weight,
23052 cls : 'dropdown-menu'
23058 if(this.pos == 'top'){
23059 cfg.cls += ' dropup';
23062 if(this.isSubMenu){
23065 cls : 'dropdown-menu'
23072 onRender : function(ct, position)
23074 this.isSubMenu = ct.hasClass('dropdown-submenu');
23076 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23079 initEvents : function()
23081 if(this.isSubMenu){
23085 this.hidden = true;
23087 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23088 this.triggerEl.on('click', this.onTriggerPress, this);
23090 this.buttonEl = this.el.select('button.dropdown-button', true).first();
23091 this.buttonEl.on('click', this.onClick, this);
23097 if(this.isSubMenu){
23101 return this.el.select('ul.dropdown-menu', true).first();
23104 onClick : function(e)
23106 this.fireEvent("click", this, e);
23109 onTriggerPress : function(e)
23111 if (this.isVisible()) {
23118 isVisible : function(){
23119 return !this.hidden;
23124 this.fireEvent("beforeshow", this);
23126 this.hidden = false;
23127 this.el.addClass('open');
23129 Roo.get(document).on("mouseup", this.onMouseUp, this);
23131 this.fireEvent("show", this);
23138 this.fireEvent("beforehide", this);
23140 this.hidden = true;
23141 this.el.removeClass('open');
23143 Roo.get(document).un("mouseup", this.onMouseUp);
23145 this.fireEvent("hide", this);
23148 onMouseUp : function()
23162 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23165 * @class Roo.bootstrap.menu.Item
23166 * @extends Roo.bootstrap.Component
23167 * Bootstrap MenuItem class
23168 * @cfg {Boolean} submenu (true | false) default false
23169 * @cfg {String} html text of the item
23170 * @cfg {String} href the link
23171 * @cfg {Boolean} disable (true | false) default false
23172 * @cfg {Boolean} preventDefault (true | false) default true
23173 * @cfg {String} icon Font awesome icon
23174 * @cfg {String} pos Submenu align to (left | right) default right
23178 * Create a new Item
23179 * @param {Object} config The config object
23183 Roo.bootstrap.menu.Item = function(config){
23184 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23188 * Fires when the mouse is hovering over this menu
23189 * @param {Roo.bootstrap.menu.Item} this
23190 * @param {Roo.EventObject} e
23195 * Fires when the mouse exits this menu
23196 * @param {Roo.bootstrap.menu.Item} this
23197 * @param {Roo.EventObject} e
23203 * The raw click event for the entire grid.
23204 * @param {Roo.EventObject} e
23210 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23215 preventDefault: true,
23220 getAutoCreate : function()
23225 cls : 'roo-menu-item-text',
23233 cls : 'fa ' + this.icon
23242 href : this.href || '#',
23249 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23253 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23255 if(this.pos == 'left'){
23256 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23263 initEvents : function()
23265 this.el.on('mouseover', this.onMouseOver, this);
23266 this.el.on('mouseout', this.onMouseOut, this);
23268 this.el.select('a', true).first().on('click', this.onClick, this);
23272 onClick : function(e)
23274 if(this.preventDefault){
23275 e.preventDefault();
23278 this.fireEvent("click", this, e);
23281 onMouseOver : function(e)
23283 if(this.submenu && this.pos == 'left'){
23284 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23287 this.fireEvent("mouseover", this, e);
23290 onMouseOut : function(e)
23292 this.fireEvent("mouseout", this, e);
23304 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23307 * @class Roo.bootstrap.menu.Separator
23308 * @extends Roo.bootstrap.Component
23309 * Bootstrap Separator class
23312 * Create a new Separator
23313 * @param {Object} config The config object
23317 Roo.bootstrap.menu.Separator = function(config){
23318 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23321 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23323 getAutoCreate : function(){
23344 * @class Roo.bootstrap.Tooltip
23345 * Bootstrap Tooltip class
23346 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23347 * to determine which dom element triggers the tooltip.
23349 * It needs to add support for additional attributes like tooltip-position
23352 * Create a new Toolti
23353 * @param {Object} config The config object
23356 Roo.bootstrap.Tooltip = function(config){
23357 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23360 Roo.apply(Roo.bootstrap.Tooltip, {
23362 * @function init initialize tooltip monitoring.
23366 currentTip : false,
23367 currentRegion : false,
23373 Roo.get(document).on('mouseover', this.enter ,this);
23374 Roo.get(document).on('mouseout', this.leave, this);
23377 this.currentTip = new Roo.bootstrap.Tooltip();
23380 enter : function(ev)
23382 var dom = ev.getTarget();
23384 //Roo.log(['enter',dom]);
23385 var el = Roo.fly(dom);
23386 if (this.currentEl) {
23388 //Roo.log(this.currentEl);
23389 //Roo.log(this.currentEl.contains(dom));
23390 if (this.currentEl == el) {
23393 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23399 if (this.currentTip.el) {
23400 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23405 // you can not look for children, as if el is the body.. then everythign is the child..
23406 if (!el.attr('tooltip')) { //
23407 if (!el.select("[tooltip]").elements.length) {
23410 // is the mouse over this child...?
23411 bindEl = el.select("[tooltip]").first();
23412 var xy = ev.getXY();
23413 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23414 //Roo.log("not in region.");
23417 //Roo.log("child element over..");
23420 this.currentEl = bindEl;
23421 this.currentTip.bind(bindEl);
23422 this.currentRegion = Roo.lib.Region.getRegion(dom);
23423 this.currentTip.enter();
23426 leave : function(ev)
23428 var dom = ev.getTarget();
23429 //Roo.log(['leave',dom]);
23430 if (!this.currentEl) {
23435 if (dom != this.currentEl.dom) {
23438 var xy = ev.getXY();
23439 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23442 // only activate leave if mouse cursor is outside... bounding box..
23447 if (this.currentTip) {
23448 this.currentTip.leave();
23450 //Roo.log('clear currentEl');
23451 this.currentEl = false;
23456 'left' : ['r-l', [-2,0], 'right'],
23457 'right' : ['l-r', [2,0], 'left'],
23458 'bottom' : ['t-b', [0,2], 'top'],
23459 'top' : [ 'b-t', [0,-2], 'bottom']
23465 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
23470 delay : null, // can be { show : 300 , hide: 500}
23474 hoverState : null, //???
23476 placement : 'bottom',
23478 getAutoCreate : function(){
23485 cls : 'tooltip-arrow'
23488 cls : 'tooltip-inner'
23495 bind : function(el)
23501 enter : function () {
23503 if (this.timeout != null) {
23504 clearTimeout(this.timeout);
23507 this.hoverState = 'in';
23508 //Roo.log("enter - show");
23509 if (!this.delay || !this.delay.show) {
23514 this.timeout = setTimeout(function () {
23515 if (_t.hoverState == 'in') {
23518 }, this.delay.show);
23522 clearTimeout(this.timeout);
23524 this.hoverState = 'out';
23525 if (!this.delay || !this.delay.hide) {
23531 this.timeout = setTimeout(function () {
23532 //Roo.log("leave - timeout");
23534 if (_t.hoverState == 'out') {
23536 Roo.bootstrap.Tooltip.currentEl = false;
23544 this.render(document.body);
23547 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23549 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23551 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23553 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23555 var placement = typeof this.placement == 'function' ?
23556 this.placement.call(this, this.el, on_el) :
23559 var autoToken = /\s?auto?\s?/i;
23560 var autoPlace = autoToken.test(placement);
23562 placement = placement.replace(autoToken, '') || 'top';
23566 //this.el.setXY([0,0]);
23568 //this.el.dom.style.display='block';
23570 //this.el.appendTo(on_el);
23572 var p = this.getPosition();
23573 var box = this.el.getBox();
23579 var align = Roo.bootstrap.Tooltip.alignment[placement];
23581 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23583 if(placement == 'top' || placement == 'bottom'){
23585 placement = 'right';
23588 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23589 placement = 'left';
23593 align = Roo.bootstrap.Tooltip.alignment[placement];
23595 this.el.alignTo(this.bindEl, align[0],align[1]);
23596 //var arrow = this.el.select('.arrow',true).first();
23597 //arrow.set(align[2],
23599 this.el.addClass(placement);
23601 this.el.addClass('in fade');
23603 this.hoverState = null;
23605 if (this.el.hasClass('fade')) {
23616 //this.el.setXY([0,0]);
23617 this.el.removeClass('in');
23633 * @class Roo.bootstrap.LocationPicker
23634 * @extends Roo.bootstrap.Component
23635 * Bootstrap LocationPicker class
23636 * @cfg {Number} latitude Position when init default 0
23637 * @cfg {Number} longitude Position when init default 0
23638 * @cfg {Number} zoom default 15
23639 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23640 * @cfg {Boolean} mapTypeControl default false
23641 * @cfg {Boolean} disableDoubleClickZoom default false
23642 * @cfg {Boolean} scrollwheel default true
23643 * @cfg {Boolean} streetViewControl default false
23644 * @cfg {Number} radius default 0
23645 * @cfg {String} locationName
23646 * @cfg {Boolean} draggable default true
23647 * @cfg {Boolean} enableAutocomplete default false
23648 * @cfg {Boolean} enableReverseGeocode default true
23649 * @cfg {String} markerTitle
23652 * Create a new LocationPicker
23653 * @param {Object} config The config object
23657 Roo.bootstrap.LocationPicker = function(config){
23659 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23664 * Fires when the picker initialized.
23665 * @param {Roo.bootstrap.LocationPicker} this
23666 * @param {Google Location} location
23670 * @event positionchanged
23671 * Fires when the picker position changed.
23672 * @param {Roo.bootstrap.LocationPicker} this
23673 * @param {Google Location} location
23675 positionchanged : true,
23678 * Fires when the map resize.
23679 * @param {Roo.bootstrap.LocationPicker} this
23684 * Fires when the map show.
23685 * @param {Roo.bootstrap.LocationPicker} this
23690 * Fires when the map hide.
23691 * @param {Roo.bootstrap.LocationPicker} this
23696 * Fires when click the map.
23697 * @param {Roo.bootstrap.LocationPicker} this
23698 * @param {Map event} e
23702 * @event mapRightClick
23703 * Fires when right click the map.
23704 * @param {Roo.bootstrap.LocationPicker} this
23705 * @param {Map event} e
23707 mapRightClick : true,
23709 * @event markerClick
23710 * Fires when click the marker.
23711 * @param {Roo.bootstrap.LocationPicker} this
23712 * @param {Map event} e
23714 markerClick : true,
23716 * @event markerRightClick
23717 * Fires when right click the marker.
23718 * @param {Roo.bootstrap.LocationPicker} this
23719 * @param {Map event} e
23721 markerRightClick : true,
23723 * @event OverlayViewDraw
23724 * Fires when OverlayView Draw
23725 * @param {Roo.bootstrap.LocationPicker} this
23727 OverlayViewDraw : true,
23729 * @event OverlayViewOnAdd
23730 * Fires when OverlayView Draw
23731 * @param {Roo.bootstrap.LocationPicker} this
23733 OverlayViewOnAdd : true,
23735 * @event OverlayViewOnRemove
23736 * Fires when OverlayView Draw
23737 * @param {Roo.bootstrap.LocationPicker} this
23739 OverlayViewOnRemove : true,
23741 * @event OverlayViewShow
23742 * Fires when OverlayView Draw
23743 * @param {Roo.bootstrap.LocationPicker} this
23744 * @param {Pixel} cpx
23746 OverlayViewShow : true,
23748 * @event OverlayViewHide
23749 * Fires when OverlayView Draw
23750 * @param {Roo.bootstrap.LocationPicker} this
23752 OverlayViewHide : true,
23754 * @event loadexception
23755 * Fires when load google lib failed.
23756 * @param {Roo.bootstrap.LocationPicker} this
23758 loadexception : true
23763 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
23765 gMapContext: false,
23771 mapTypeControl: false,
23772 disableDoubleClickZoom: false,
23774 streetViewControl: false,
23778 enableAutocomplete: false,
23779 enableReverseGeocode: true,
23782 getAutoCreate: function()
23787 cls: 'roo-location-picker'
23793 initEvents: function(ct, position)
23795 if(!this.el.getWidth() || this.isApplied()){
23799 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23804 initial: function()
23806 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
23807 this.fireEvent('loadexception', this);
23811 if(!this.mapTypeId){
23812 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23815 this.gMapContext = this.GMapContext();
23817 this.initOverlayView();
23819 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23823 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23824 _this.setPosition(_this.gMapContext.marker.position);
23827 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23828 _this.fireEvent('mapClick', this, event);
23832 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23833 _this.fireEvent('mapRightClick', this, event);
23837 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23838 _this.fireEvent('markerClick', this, event);
23842 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23843 _this.fireEvent('markerRightClick', this, event);
23847 this.setPosition(this.gMapContext.location);
23849 this.fireEvent('initial', this, this.gMapContext.location);
23852 initOverlayView: function()
23856 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23860 _this.fireEvent('OverlayViewDraw', _this);
23865 _this.fireEvent('OverlayViewOnAdd', _this);
23868 onRemove: function()
23870 _this.fireEvent('OverlayViewOnRemove', _this);
23873 show: function(cpx)
23875 _this.fireEvent('OverlayViewShow', _this, cpx);
23880 _this.fireEvent('OverlayViewHide', _this);
23886 fromLatLngToContainerPixel: function(event)
23888 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23891 isApplied: function()
23893 return this.getGmapContext() == false ? false : true;
23896 getGmapContext: function()
23898 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
23901 GMapContext: function()
23903 var position = new google.maps.LatLng(this.latitude, this.longitude);
23905 var _map = new google.maps.Map(this.el.dom, {
23908 mapTypeId: this.mapTypeId,
23909 mapTypeControl: this.mapTypeControl,
23910 disableDoubleClickZoom: this.disableDoubleClickZoom,
23911 scrollwheel: this.scrollwheel,
23912 streetViewControl: this.streetViewControl,
23913 locationName: this.locationName,
23914 draggable: this.draggable,
23915 enableAutocomplete: this.enableAutocomplete,
23916 enableReverseGeocode: this.enableReverseGeocode
23919 var _marker = new google.maps.Marker({
23920 position: position,
23922 title: this.markerTitle,
23923 draggable: this.draggable
23930 location: position,
23931 radius: this.radius,
23932 locationName: this.locationName,
23933 addressComponents: {
23934 formatted_address: null,
23935 addressLine1: null,
23936 addressLine2: null,
23938 streetNumber: null,
23942 stateOrProvince: null
23945 domContainer: this.el.dom,
23946 geodecoder: new google.maps.Geocoder()
23950 drawCircle: function(center, radius, options)
23952 if (this.gMapContext.circle != null) {
23953 this.gMapContext.circle.setMap(null);
23957 options = Roo.apply({}, options, {
23958 strokeColor: "#0000FF",
23959 strokeOpacity: .35,
23961 fillColor: "#0000FF",
23965 options.map = this.gMapContext.map;
23966 options.radius = radius;
23967 options.center = center;
23968 this.gMapContext.circle = new google.maps.Circle(options);
23969 return this.gMapContext.circle;
23975 setPosition: function(location)
23977 this.gMapContext.location = location;
23978 this.gMapContext.marker.setPosition(location);
23979 this.gMapContext.map.panTo(location);
23980 this.drawCircle(location, this.gMapContext.radius, {});
23984 if (this.gMapContext.settings.enableReverseGeocode) {
23985 this.gMapContext.geodecoder.geocode({
23986 latLng: this.gMapContext.location
23987 }, function(results, status) {
23989 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23990 _this.gMapContext.locationName = results[0].formatted_address;
23991 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23993 _this.fireEvent('positionchanged', this, location);
24000 this.fireEvent('positionchanged', this, location);
24005 google.maps.event.trigger(this.gMapContext.map, "resize");
24007 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24009 this.fireEvent('resize', this);
24012 setPositionByLatLng: function(latitude, longitude)
24014 this.setPosition(new google.maps.LatLng(latitude, longitude));
24017 getCurrentPosition: function()
24020 latitude: this.gMapContext.location.lat(),
24021 longitude: this.gMapContext.location.lng()
24025 getAddressName: function()
24027 return this.gMapContext.locationName;
24030 getAddressComponents: function()
24032 return this.gMapContext.addressComponents;
24035 address_component_from_google_geocode: function(address_components)
24039 for (var i = 0; i < address_components.length; i++) {
24040 var component = address_components[i];
24041 if (component.types.indexOf("postal_code") >= 0) {
24042 result.postalCode = component.short_name;
24043 } else if (component.types.indexOf("street_number") >= 0) {
24044 result.streetNumber = component.short_name;
24045 } else if (component.types.indexOf("route") >= 0) {
24046 result.streetName = component.short_name;
24047 } else if (component.types.indexOf("neighborhood") >= 0) {
24048 result.city = component.short_name;
24049 } else if (component.types.indexOf("locality") >= 0) {
24050 result.city = component.short_name;
24051 } else if (component.types.indexOf("sublocality") >= 0) {
24052 result.district = component.short_name;
24053 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24054 result.stateOrProvince = component.short_name;
24055 } else if (component.types.indexOf("country") >= 0) {
24056 result.country = component.short_name;
24060 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24061 result.addressLine2 = "";
24065 setZoomLevel: function(zoom)
24067 this.gMapContext.map.setZoom(zoom);
24080 this.fireEvent('show', this);
24091 this.fireEvent('hide', this);
24096 Roo.apply(Roo.bootstrap.LocationPicker, {
24098 OverlayView : function(map, options)
24100 options = options || {};
24114 * @class Roo.bootstrap.Alert
24115 * @extends Roo.bootstrap.Component
24116 * Bootstrap Alert class
24117 * @cfg {String} title The title of alert
24118 * @cfg {String} html The content of alert
24119 * @cfg {String} weight ( success | info | warning | danger )
24120 * @cfg {String} faicon font-awesomeicon
24123 * Create a new alert
24124 * @param {Object} config The config object
24128 Roo.bootstrap.Alert = function(config){
24129 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24133 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
24140 getAutoCreate : function()
24149 cls : 'roo-alert-icon'
24154 cls : 'roo-alert-title',
24159 cls : 'roo-alert-text',
24166 cfg.cn[0].cls += ' fa ' + this.faicon;
24170 cfg.cls += ' alert-' + this.weight;
24176 initEvents: function()
24178 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24181 setTitle : function(str)
24183 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24186 setText : function(str)
24188 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24191 setWeight : function(weight)
24194 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24197 this.weight = weight;
24199 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24202 setIcon : function(icon)
24205 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24208 this.faicon = icon;
24210 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24231 * @class Roo.bootstrap.UploadCropbox
24232 * @extends Roo.bootstrap.Component
24233 * Bootstrap UploadCropbox class
24234 * @cfg {String} emptyText show when image has been loaded
24235 * @cfg {String} rotateNotify show when image too small to rotate
24236 * @cfg {Number} errorTimeout default 3000
24237 * @cfg {Number} minWidth default 300
24238 * @cfg {Number} minHeight default 300
24239 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24240 * @cfg {Boolean} isDocument (true|false) default false
24241 * @cfg {String} url action url
24242 * @cfg {String} paramName default 'imageUpload'
24243 * @cfg {String} method default POST
24244 * @cfg {Boolean} loadMask (true|false) default true
24245 * @cfg {Boolean} loadingText default 'Loading...'
24248 * Create a new UploadCropbox
24249 * @param {Object} config The config object
24252 Roo.bootstrap.UploadCropbox = function(config){
24253 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24257 * @event beforeselectfile
24258 * Fire before select file
24259 * @param {Roo.bootstrap.UploadCropbox} this
24261 "beforeselectfile" : true,
24264 * Fire after initEvent
24265 * @param {Roo.bootstrap.UploadCropbox} this
24270 * Fire after initEvent
24271 * @param {Roo.bootstrap.UploadCropbox} this
24272 * @param {String} data
24277 * Fire when preparing the file data
24278 * @param {Roo.bootstrap.UploadCropbox} this
24279 * @param {Object} file
24284 * Fire when get exception
24285 * @param {Roo.bootstrap.UploadCropbox} this
24286 * @param {XMLHttpRequest} xhr
24288 "exception" : true,
24290 * @event beforeloadcanvas
24291 * Fire before load the canvas
24292 * @param {Roo.bootstrap.UploadCropbox} this
24293 * @param {String} src
24295 "beforeloadcanvas" : true,
24298 * Fire when trash image
24299 * @param {Roo.bootstrap.UploadCropbox} this
24304 * Fire when download the image
24305 * @param {Roo.bootstrap.UploadCropbox} this
24309 * @event footerbuttonclick
24310 * Fire when footerbuttonclick
24311 * @param {Roo.bootstrap.UploadCropbox} this
24312 * @param {String} type
24314 "footerbuttonclick" : true,
24318 * @param {Roo.bootstrap.UploadCropbox} this
24323 * Fire when rotate the image
24324 * @param {Roo.bootstrap.UploadCropbox} this
24325 * @param {String} pos
24330 * Fire when inspect the file
24331 * @param {Roo.bootstrap.UploadCropbox} this
24332 * @param {Object} file
24337 * Fire when xhr upload the file
24338 * @param {Roo.bootstrap.UploadCropbox} this
24339 * @param {Object} data
24344 * Fire when arrange the file data
24345 * @param {Roo.bootstrap.UploadCropbox} this
24346 * @param {Object} formData
24351 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24354 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24356 emptyText : 'Click to upload image',
24357 rotateNotify : 'Image is too small to rotate',
24358 errorTimeout : 3000,
24372 cropType : 'image/jpeg',
24374 canvasLoaded : false,
24375 isDocument : false,
24377 paramName : 'imageUpload',
24379 loadingText : 'Loading...',
24382 getAutoCreate : function()
24386 cls : 'roo-upload-cropbox',
24390 cls : 'roo-upload-cropbox-selector',
24395 cls : 'roo-upload-cropbox-body',
24396 style : 'cursor:pointer',
24400 cls : 'roo-upload-cropbox-preview'
24404 cls : 'roo-upload-cropbox-thumb'
24408 cls : 'roo-upload-cropbox-empty-notify',
24409 html : this.emptyText
24413 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24414 html : this.rotateNotify
24420 cls : 'roo-upload-cropbox-footer',
24423 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24433 onRender : function(ct, position)
24435 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24437 if (this.buttons.length) {
24439 Roo.each(this.buttons, function(bb) {
24441 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24443 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24449 this.maskEl = this.el;
24453 initEvents : function()
24455 this.urlAPI = (window.createObjectURL && window) ||
24456 (window.URL && URL.revokeObjectURL && URL) ||
24457 (window.webkitURL && webkitURL);
24459 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24460 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24462 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24463 this.selectorEl.hide();
24465 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24466 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24468 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24469 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24470 this.thumbEl.hide();
24472 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24473 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24475 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24476 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24477 this.errorEl.hide();
24479 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24480 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24481 this.footerEl.hide();
24483 this.setThumbBoxSize();
24489 this.fireEvent('initial', this);
24496 window.addEventListener("resize", function() { _this.resize(); } );
24498 this.bodyEl.on('click', this.beforeSelectFile, this);
24501 this.bodyEl.on('touchstart', this.onTouchStart, this);
24502 this.bodyEl.on('touchmove', this.onTouchMove, this);
24503 this.bodyEl.on('touchend', this.onTouchEnd, this);
24507 this.bodyEl.on('mousedown', this.onMouseDown, this);
24508 this.bodyEl.on('mousemove', this.onMouseMove, this);
24509 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24510 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24511 Roo.get(document).on('mouseup', this.onMouseUp, this);
24514 this.selectorEl.on('change', this.onFileSelected, this);
24520 this.baseScale = 1;
24522 this.baseRotate = 1;
24523 this.dragable = false;
24524 this.pinching = false;
24527 this.cropData = false;
24528 this.notifyEl.dom.innerHTML = this.emptyText;
24530 this.selectorEl.dom.value = '';
24534 resize : function()
24536 if(this.fireEvent('resize', this) != false){
24537 this.setThumbBoxPosition();
24538 this.setCanvasPosition();
24542 onFooterButtonClick : function(e, el, o, type)
24545 case 'rotate-left' :
24546 this.onRotateLeft(e);
24548 case 'rotate-right' :
24549 this.onRotateRight(e);
24552 this.beforeSelectFile(e);
24567 this.fireEvent('footerbuttonclick', this, type);
24570 beforeSelectFile : function(e)
24572 e.preventDefault();
24574 if(this.fireEvent('beforeselectfile', this) != false){
24575 this.selectorEl.dom.click();
24579 onFileSelected : function(e)
24581 e.preventDefault();
24583 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24587 var file = this.selectorEl.dom.files[0];
24589 if(this.fireEvent('inspect', this, file) != false){
24590 this.prepare(file);
24595 trash : function(e)
24597 this.fireEvent('trash', this);
24600 download : function(e)
24602 this.fireEvent('download', this);
24605 loadCanvas : function(src)
24607 if(this.fireEvent('beforeloadcanvas', this, src) != false){
24611 this.imageEl = document.createElement('img');
24615 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24617 this.imageEl.src = src;
24621 onLoadCanvas : function()
24623 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24624 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24626 this.bodyEl.un('click', this.beforeSelectFile, this);
24628 this.notifyEl.hide();
24629 this.thumbEl.show();
24630 this.footerEl.show();
24632 this.baseRotateLevel();
24634 if(this.isDocument){
24635 this.setThumbBoxSize();
24638 this.setThumbBoxPosition();
24640 this.baseScaleLevel();
24646 this.canvasLoaded = true;
24649 this.maskEl.unmask();
24654 setCanvasPosition : function()
24656 if(!this.canvasEl){
24660 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24661 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24663 this.previewEl.setLeft(pw);
24664 this.previewEl.setTop(ph);
24668 onMouseDown : function(e)
24672 this.dragable = true;
24673 this.pinching = false;
24675 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24676 this.dragable = false;
24680 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24681 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24685 onMouseMove : function(e)
24689 if(!this.canvasLoaded){
24693 if (!this.dragable){
24697 var minX = Math.ceil(this.thumbEl.getLeft(true));
24698 var minY = Math.ceil(this.thumbEl.getTop(true));
24700 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24701 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24703 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24704 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24706 x = x - this.mouseX;
24707 y = y - this.mouseY;
24709 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24710 var bgY = Math.ceil(y + this.previewEl.getTop(true));
24712 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24713 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24715 this.previewEl.setLeft(bgX);
24716 this.previewEl.setTop(bgY);
24718 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24719 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24722 onMouseUp : function(e)
24726 this.dragable = false;
24729 onMouseWheel : function(e)
24733 this.startScale = this.scale;
24735 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24737 if(!this.zoomable()){
24738 this.scale = this.startScale;
24747 zoomable : function()
24749 var minScale = this.thumbEl.getWidth() / this.minWidth;
24751 if(this.minWidth < this.minHeight){
24752 minScale = this.thumbEl.getHeight() / this.minHeight;
24755 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
24756 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
24760 (this.rotate == 0 || this.rotate == 180) &&
24762 width > this.imageEl.OriginWidth ||
24763 height > this.imageEl.OriginHeight ||
24764 (width < this.minWidth && height < this.minHeight)
24772 (this.rotate == 90 || this.rotate == 270) &&
24774 width > this.imageEl.OriginWidth ||
24775 height > this.imageEl.OriginHeight ||
24776 (width < this.minHeight && height < this.minWidth)
24783 !this.isDocument &&
24784 (this.rotate == 0 || this.rotate == 180) &&
24786 width < this.minWidth ||
24787 width > this.imageEl.OriginWidth ||
24788 height < this.minHeight ||
24789 height > this.imageEl.OriginHeight
24796 !this.isDocument &&
24797 (this.rotate == 90 || this.rotate == 270) &&
24799 width < this.minHeight ||
24800 width > this.imageEl.OriginWidth ||
24801 height < this.minWidth ||
24802 height > this.imageEl.OriginHeight
24812 onRotateLeft : function(e)
24814 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24816 var minScale = this.thumbEl.getWidth() / this.minWidth;
24818 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24819 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24821 this.startScale = this.scale;
24823 while (this.getScaleLevel() < minScale){
24825 this.scale = this.scale + 1;
24827 if(!this.zoomable()){
24832 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24833 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24838 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24845 this.scale = this.startScale;
24847 this.onRotateFail();
24852 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24854 if(this.isDocument){
24855 this.setThumbBoxSize();
24856 this.setThumbBoxPosition();
24857 this.setCanvasPosition();
24862 this.fireEvent('rotate', this, 'left');
24866 onRotateRight : function(e)
24868 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24870 var minScale = this.thumbEl.getWidth() / this.minWidth;
24872 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24873 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24875 this.startScale = this.scale;
24877 while (this.getScaleLevel() < minScale){
24879 this.scale = this.scale + 1;
24881 if(!this.zoomable()){
24886 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24887 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24892 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24899 this.scale = this.startScale;
24901 this.onRotateFail();
24906 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24908 if(this.isDocument){
24909 this.setThumbBoxSize();
24910 this.setThumbBoxPosition();
24911 this.setCanvasPosition();
24916 this.fireEvent('rotate', this, 'right');
24919 onRotateFail : function()
24921 this.errorEl.show(true);
24925 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
24930 this.previewEl.dom.innerHTML = '';
24932 var canvasEl = document.createElement("canvas");
24934 var contextEl = canvasEl.getContext("2d");
24936 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24937 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24938 var center = this.imageEl.OriginWidth / 2;
24940 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
24941 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24942 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24943 center = this.imageEl.OriginHeight / 2;
24946 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
24948 contextEl.translate(center, center);
24949 contextEl.rotate(this.rotate * Math.PI / 180);
24951 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24953 this.canvasEl = document.createElement("canvas");
24955 this.contextEl = this.canvasEl.getContext("2d");
24957 switch (this.rotate) {
24960 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24961 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24963 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24968 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24969 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24971 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24972 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);
24976 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24981 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24982 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24984 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24985 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);
24989 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);
24994 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24995 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24997 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24998 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25002 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);
25009 this.previewEl.appendChild(this.canvasEl);
25011 this.setCanvasPosition();
25016 if(!this.canvasLoaded){
25020 var imageCanvas = document.createElement("canvas");
25022 var imageContext = imageCanvas.getContext("2d");
25024 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25025 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25027 var center = imageCanvas.width / 2;
25029 imageContext.translate(center, center);
25031 imageContext.rotate(this.rotate * Math.PI / 180);
25033 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25035 var canvas = document.createElement("canvas");
25037 var context = canvas.getContext("2d");
25039 canvas.width = this.minWidth;
25040 canvas.height = this.minHeight;
25042 switch (this.rotate) {
25045 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25046 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25048 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25049 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25051 var targetWidth = this.minWidth - 2 * x;
25052 var targetHeight = this.minHeight - 2 * y;
25056 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25057 scale = targetWidth / width;
25060 if(x > 0 && y == 0){
25061 scale = targetHeight / height;
25064 if(x > 0 && y > 0){
25065 scale = targetWidth / width;
25067 if(width < height){
25068 scale = targetHeight / height;
25072 context.scale(scale, scale);
25074 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25075 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25077 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25078 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25080 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25085 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25086 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25088 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25089 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25091 var targetWidth = this.minWidth - 2 * x;
25092 var targetHeight = this.minHeight - 2 * y;
25096 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25097 scale = targetWidth / width;
25100 if(x > 0 && y == 0){
25101 scale = targetHeight / height;
25104 if(x > 0 && y > 0){
25105 scale = targetWidth / width;
25107 if(width < height){
25108 scale = targetHeight / height;
25112 context.scale(scale, scale);
25114 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25115 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25117 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25118 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25120 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25122 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25127 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25128 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25130 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25131 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25133 var targetWidth = this.minWidth - 2 * x;
25134 var targetHeight = this.minHeight - 2 * y;
25138 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25139 scale = targetWidth / width;
25142 if(x > 0 && y == 0){
25143 scale = targetHeight / height;
25146 if(x > 0 && y > 0){
25147 scale = targetWidth / width;
25149 if(width < height){
25150 scale = targetHeight / height;
25154 context.scale(scale, scale);
25156 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25157 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25159 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25160 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25162 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25163 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25165 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25170 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25171 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25173 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25174 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25176 var targetWidth = this.minWidth - 2 * x;
25177 var targetHeight = this.minHeight - 2 * y;
25181 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25182 scale = targetWidth / width;
25185 if(x > 0 && y == 0){
25186 scale = targetHeight / height;
25189 if(x > 0 && y > 0){
25190 scale = targetWidth / width;
25192 if(width < height){
25193 scale = targetHeight / height;
25197 context.scale(scale, scale);
25199 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25200 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25202 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25203 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25205 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25207 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25214 this.cropData = canvas.toDataURL(this.cropType);
25216 if(this.fireEvent('crop', this, this.cropData) !== false){
25217 this.process(this.file, this.cropData);
25224 setThumbBoxSize : function()
25228 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25229 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25230 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25232 this.minWidth = width;
25233 this.minHeight = height;
25235 if(this.rotate == 90 || this.rotate == 270){
25236 this.minWidth = height;
25237 this.minHeight = width;
25242 width = Math.ceil(this.minWidth * height / this.minHeight);
25244 if(this.minWidth > this.minHeight){
25246 height = Math.ceil(this.minHeight * width / this.minWidth);
25249 this.thumbEl.setStyle({
25250 width : width + 'px',
25251 height : height + 'px'
25258 setThumbBoxPosition : function()
25260 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25261 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25263 this.thumbEl.setLeft(x);
25264 this.thumbEl.setTop(y);
25268 baseRotateLevel : function()
25270 this.baseRotate = 1;
25273 typeof(this.exif) != 'undefined' &&
25274 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25275 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25277 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25280 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25284 baseScaleLevel : function()
25288 if(this.isDocument){
25290 if(this.baseRotate == 6 || this.baseRotate == 8){
25292 height = this.thumbEl.getHeight();
25293 this.baseScale = height / this.imageEl.OriginWidth;
25295 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25296 width = this.thumbEl.getWidth();
25297 this.baseScale = width / this.imageEl.OriginHeight;
25303 height = this.thumbEl.getHeight();
25304 this.baseScale = height / this.imageEl.OriginHeight;
25306 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25307 width = this.thumbEl.getWidth();
25308 this.baseScale = width / this.imageEl.OriginWidth;
25314 if(this.baseRotate == 6 || this.baseRotate == 8){
25316 width = this.thumbEl.getHeight();
25317 this.baseScale = width / this.imageEl.OriginHeight;
25319 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25320 height = this.thumbEl.getWidth();
25321 this.baseScale = height / this.imageEl.OriginHeight;
25324 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25325 height = this.thumbEl.getWidth();
25326 this.baseScale = height / this.imageEl.OriginHeight;
25328 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25329 width = this.thumbEl.getHeight();
25330 this.baseScale = width / this.imageEl.OriginWidth;
25337 width = this.thumbEl.getWidth();
25338 this.baseScale = width / this.imageEl.OriginWidth;
25340 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25341 height = this.thumbEl.getHeight();
25342 this.baseScale = height / this.imageEl.OriginHeight;
25345 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25347 height = this.thumbEl.getHeight();
25348 this.baseScale = height / this.imageEl.OriginHeight;
25350 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25351 width = this.thumbEl.getWidth();
25352 this.baseScale = width / this.imageEl.OriginWidth;
25360 getScaleLevel : function()
25362 return this.baseScale * Math.pow(1.1, this.scale);
25365 onTouchStart : function(e)
25367 if(!this.canvasLoaded){
25368 this.beforeSelectFile(e);
25372 var touches = e.browserEvent.touches;
25378 if(touches.length == 1){
25379 this.onMouseDown(e);
25383 if(touches.length != 2){
25389 for(var i = 0, finger; finger = touches[i]; i++){
25390 coords.push(finger.pageX, finger.pageY);
25393 var x = Math.pow(coords[0] - coords[2], 2);
25394 var y = Math.pow(coords[1] - coords[3], 2);
25396 this.startDistance = Math.sqrt(x + y);
25398 this.startScale = this.scale;
25400 this.pinching = true;
25401 this.dragable = false;
25405 onTouchMove : function(e)
25407 if(!this.pinching && !this.dragable){
25411 var touches = e.browserEvent.touches;
25418 this.onMouseMove(e);
25424 for(var i = 0, finger; finger = touches[i]; i++){
25425 coords.push(finger.pageX, finger.pageY);
25428 var x = Math.pow(coords[0] - coords[2], 2);
25429 var y = Math.pow(coords[1] - coords[3], 2);
25431 this.endDistance = Math.sqrt(x + y);
25433 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25435 if(!this.zoomable()){
25436 this.scale = this.startScale;
25444 onTouchEnd : function(e)
25446 this.pinching = false;
25447 this.dragable = false;
25451 process : function(file, crop)
25454 this.maskEl.mask(this.loadingText);
25457 this.xhr = new XMLHttpRequest();
25459 file.xhr = this.xhr;
25461 this.xhr.open(this.method, this.url, true);
25464 "Accept": "application/json",
25465 "Cache-Control": "no-cache",
25466 "X-Requested-With": "XMLHttpRequest"
25469 for (var headerName in headers) {
25470 var headerValue = headers[headerName];
25472 this.xhr.setRequestHeader(headerName, headerValue);
25478 this.xhr.onload = function()
25480 _this.xhrOnLoad(_this.xhr);
25483 this.xhr.onerror = function()
25485 _this.xhrOnError(_this.xhr);
25488 var formData = new FormData();
25490 formData.append('returnHTML', 'NO');
25493 formData.append('crop', crop);
25496 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25497 formData.append(this.paramName, file, file.name);
25500 if(typeof(file.filename) != 'undefined'){
25501 formData.append('filename', file.filename);
25504 if(typeof(file.mimetype) != 'undefined'){
25505 formData.append('mimetype', file.mimetype);
25508 if(this.fireEvent('arrange', this, formData) != false){
25509 this.xhr.send(formData);
25513 xhrOnLoad : function(xhr)
25516 this.maskEl.unmask();
25519 if (xhr.readyState !== 4) {
25520 this.fireEvent('exception', this, xhr);
25524 var response = Roo.decode(xhr.responseText);
25526 if(!response.success){
25527 this.fireEvent('exception', this, xhr);
25531 var response = Roo.decode(xhr.responseText);
25533 this.fireEvent('upload', this, response);
25537 xhrOnError : function()
25540 this.maskEl.unmask();
25543 Roo.log('xhr on error');
25545 var response = Roo.decode(xhr.responseText);
25551 prepare : function(file)
25554 this.maskEl.mask(this.loadingText);
25560 if(typeof(file) === 'string'){
25561 this.loadCanvas(file);
25565 if(!file || !this.urlAPI){
25570 this.cropType = file.type;
25574 if(this.fireEvent('prepare', this, this.file) != false){
25576 var reader = new FileReader();
25578 reader.onload = function (e) {
25579 if (e.target.error) {
25580 Roo.log(e.target.error);
25584 var buffer = e.target.result,
25585 dataView = new DataView(buffer),
25587 maxOffset = dataView.byteLength - 4,
25591 if (dataView.getUint16(0) === 0xffd8) {
25592 while (offset < maxOffset) {
25593 markerBytes = dataView.getUint16(offset);
25595 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25596 markerLength = dataView.getUint16(offset + 2) + 2;
25597 if (offset + markerLength > dataView.byteLength) {
25598 Roo.log('Invalid meta data: Invalid segment size.');
25602 if(markerBytes == 0xffe1){
25603 _this.parseExifData(
25610 offset += markerLength;
25620 var url = _this.urlAPI.createObjectURL(_this.file);
25622 _this.loadCanvas(url);
25627 reader.readAsArrayBuffer(this.file);
25633 parseExifData : function(dataView, offset, length)
25635 var tiffOffset = offset + 10,
25639 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25640 // No Exif data, might be XMP data instead
25644 // Check for the ASCII code for "Exif" (0x45786966):
25645 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25646 // No Exif data, might be XMP data instead
25649 if (tiffOffset + 8 > dataView.byteLength) {
25650 Roo.log('Invalid Exif data: Invalid segment size.');
25653 // Check for the two null bytes:
25654 if (dataView.getUint16(offset + 8) !== 0x0000) {
25655 Roo.log('Invalid Exif data: Missing byte alignment offset.');
25658 // Check the byte alignment:
25659 switch (dataView.getUint16(tiffOffset)) {
25661 littleEndian = true;
25664 littleEndian = false;
25667 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25670 // Check for the TIFF tag marker (0x002A):
25671 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25672 Roo.log('Invalid Exif data: Missing TIFF marker.');
25675 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25676 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25678 this.parseExifTags(
25681 tiffOffset + dirOffset,
25686 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25691 if (dirOffset + 6 > dataView.byteLength) {
25692 Roo.log('Invalid Exif data: Invalid directory offset.');
25695 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25696 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25697 if (dirEndOffset + 4 > dataView.byteLength) {
25698 Roo.log('Invalid Exif data: Invalid directory size.');
25701 for (i = 0; i < tagsNumber; i += 1) {
25705 dirOffset + 2 + 12 * i, // tag offset
25709 // Return the offset to the next directory:
25710 return dataView.getUint32(dirEndOffset, littleEndian);
25713 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
25715 var tag = dataView.getUint16(offset, littleEndian);
25717 this.exif[tag] = this.getExifValue(
25721 dataView.getUint16(offset + 2, littleEndian), // tag type
25722 dataView.getUint32(offset + 4, littleEndian), // tag length
25727 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25729 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25738 Roo.log('Invalid Exif data: Invalid tag type.');
25742 tagSize = tagType.size * length;
25743 // Determine if the value is contained in the dataOffset bytes,
25744 // or if the value at the dataOffset is a pointer to the actual data:
25745 dataOffset = tagSize > 4 ?
25746 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25747 if (dataOffset + tagSize > dataView.byteLength) {
25748 Roo.log('Invalid Exif data: Invalid data offset.');
25751 if (length === 1) {
25752 return tagType.getValue(dataView, dataOffset, littleEndian);
25755 for (i = 0; i < length; i += 1) {
25756 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
25759 if (tagType.ascii) {
25761 // Concatenate the chars:
25762 for (i = 0; i < values.length; i += 1) {
25764 // Ignore the terminating NULL byte(s):
25765 if (c === '\u0000') {
25777 Roo.apply(Roo.bootstrap.UploadCropbox, {
25779 'Orientation': 0x0112
25783 1: 0, //'top-left',
25785 3: 180, //'bottom-right',
25786 // 4: 'bottom-left',
25788 6: 90, //'right-top',
25789 // 7: 'right-bottom',
25790 8: 270 //'left-bottom'
25794 // byte, 8-bit unsigned int:
25796 getValue: function (dataView, dataOffset) {
25797 return dataView.getUint8(dataOffset);
25801 // ascii, 8-bit byte:
25803 getValue: function (dataView, dataOffset) {
25804 return String.fromCharCode(dataView.getUint8(dataOffset));
25809 // short, 16 bit int:
25811 getValue: function (dataView, dataOffset, littleEndian) {
25812 return dataView.getUint16(dataOffset, littleEndian);
25816 // long, 32 bit int:
25818 getValue: function (dataView, dataOffset, littleEndian) {
25819 return dataView.getUint32(dataOffset, littleEndian);
25823 // rational = two long values, first is numerator, second is denominator:
25825 getValue: function (dataView, dataOffset, littleEndian) {
25826 return dataView.getUint32(dataOffset, littleEndian) /
25827 dataView.getUint32(dataOffset + 4, littleEndian);
25831 // slong, 32 bit signed int:
25833 getValue: function (dataView, dataOffset, littleEndian) {
25834 return dataView.getInt32(dataOffset, littleEndian);
25838 // srational, two slongs, first is numerator, second is denominator:
25840 getValue: function (dataView, dataOffset, littleEndian) {
25841 return dataView.getInt32(dataOffset, littleEndian) /
25842 dataView.getInt32(dataOffset + 4, littleEndian);
25852 cls : 'btn-group roo-upload-cropbox-rotate-left',
25853 action : 'rotate-left',
25857 cls : 'btn btn-default',
25858 html : '<i class="fa fa-undo"></i>'
25864 cls : 'btn-group roo-upload-cropbox-picture',
25865 action : 'picture',
25869 cls : 'btn btn-default',
25870 html : '<i class="fa fa-picture-o"></i>'
25876 cls : 'btn-group roo-upload-cropbox-rotate-right',
25877 action : 'rotate-right',
25881 cls : 'btn btn-default',
25882 html : '<i class="fa fa-repeat"></i>'
25890 cls : 'btn-group roo-upload-cropbox-rotate-left',
25891 action : 'rotate-left',
25895 cls : 'btn btn-default',
25896 html : '<i class="fa fa-undo"></i>'
25902 cls : 'btn-group roo-upload-cropbox-download',
25903 action : 'download',
25907 cls : 'btn btn-default',
25908 html : '<i class="fa fa-download"></i>'
25914 cls : 'btn-group roo-upload-cropbox-crop',
25919 cls : 'btn btn-default',
25920 html : '<i class="fa fa-crop"></i>'
25926 cls : 'btn-group roo-upload-cropbox-trash',
25931 cls : 'btn btn-default',
25932 html : '<i class="fa fa-trash"></i>'
25938 cls : 'btn-group roo-upload-cropbox-rotate-right',
25939 action : 'rotate-right',
25943 cls : 'btn btn-default',
25944 html : '<i class="fa fa-repeat"></i>'
25952 cls : 'btn-group roo-upload-cropbox-rotate-left',
25953 action : 'rotate-left',
25957 cls : 'btn btn-default',
25958 html : '<i class="fa fa-undo"></i>'
25964 cls : 'btn-group roo-upload-cropbox-rotate-right',
25965 action : 'rotate-right',
25969 cls : 'btn btn-default',
25970 html : '<i class="fa fa-repeat"></i>'
25983 * @class Roo.bootstrap.DocumentManager
25984 * @extends Roo.bootstrap.Component
25985 * Bootstrap DocumentManager class
25986 * @cfg {String} paramName default 'imageUpload'
25987 * @cfg {String} method default POST
25988 * @cfg {String} url action url
25989 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
25990 * @cfg {Boolean} multiple multiple upload default true
25991 * @cfg {Number} thumbSize default 300
25992 * @cfg {String} fieldLabel
25993 * @cfg {Number} labelWidth default 4
25994 * @cfg {String} labelAlign (left|top) default left
25995 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
25998 * Create a new DocumentManager
25999 * @param {Object} config The config object
26002 Roo.bootstrap.DocumentManager = function(config){
26003 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26008 * Fire when initial the DocumentManager
26009 * @param {Roo.bootstrap.DocumentManager} this
26014 * inspect selected file
26015 * @param {Roo.bootstrap.DocumentManager} this
26016 * @param {File} file
26021 * Fire when xhr load exception
26022 * @param {Roo.bootstrap.DocumentManager} this
26023 * @param {XMLHttpRequest} xhr
26025 "exception" : true,
26028 * prepare the form data
26029 * @param {Roo.bootstrap.DocumentManager} this
26030 * @param {Object} formData
26035 * Fire when remove the file
26036 * @param {Roo.bootstrap.DocumentManager} this
26037 * @param {Object} file
26042 * Fire after refresh the file
26043 * @param {Roo.bootstrap.DocumentManager} this
26048 * Fire after click the image
26049 * @param {Roo.bootstrap.DocumentManager} this
26050 * @param {Object} file
26055 * Fire when upload a image and editable set to true
26056 * @param {Roo.bootstrap.DocumentManager} this
26057 * @param {Object} file
26061 * @event beforeselectfile
26062 * Fire before select file
26063 * @param {Roo.bootstrap.DocumentManager} this
26065 "beforeselectfile" : true,
26068 * Fire before process file
26069 * @param {Roo.bootstrap.DocumentManager} this
26070 * @param {Object} file
26077 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
26086 paramName : 'imageUpload',
26089 labelAlign : 'left',
26093 getAutoCreate : function()
26095 var managerWidget = {
26097 cls : 'roo-document-manager',
26101 cls : 'roo-document-manager-selector',
26106 cls : 'roo-document-manager-uploader',
26110 cls : 'roo-document-manager-upload-btn',
26111 html : '<i class="fa fa-plus"></i>'
26122 cls : 'column col-md-12',
26127 if(this.fieldLabel.length){
26132 cls : 'column col-md-12',
26133 html : this.fieldLabel
26137 cls : 'column col-md-12',
26142 if(this.labelAlign == 'left'){
26146 cls : 'column col-md-' + this.labelWidth,
26147 html : this.fieldLabel
26151 cls : 'column col-md-' + (12 - this.labelWidth),
26161 cls : 'row clearfix',
26169 initEvents : function()
26171 this.managerEl = this.el.select('.roo-document-manager', true).first();
26172 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26174 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26175 this.selectorEl.hide();
26178 this.selectorEl.attr('multiple', 'multiple');
26181 this.selectorEl.on('change', this.onFileSelected, this);
26183 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26184 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26186 this.uploader.on('click', this.onUploaderClick, this);
26188 this.renderProgressDialog();
26192 window.addEventListener("resize", function() { _this.refresh(); } );
26194 this.fireEvent('initial', this);
26197 renderProgressDialog : function()
26201 this.progressDialog = new Roo.bootstrap.Modal({
26202 cls : 'roo-document-manager-progress-dialog',
26203 allow_close : false,
26213 btnclick : function() {
26214 _this.uploadCancel();
26220 this.progressDialog.render(Roo.get(document.body));
26222 this.progress = new Roo.bootstrap.Progress({
26223 cls : 'roo-document-manager-progress',
26228 this.progress.render(this.progressDialog.getChildContainer());
26230 this.progressBar = new Roo.bootstrap.ProgressBar({
26231 cls : 'roo-document-manager-progress-bar',
26234 aria_valuemax : 12,
26238 this.progressBar.render(this.progress.getChildContainer());
26241 onUploaderClick : function(e)
26243 e.preventDefault();
26245 if(this.fireEvent('beforeselectfile', this) != false){
26246 this.selectorEl.dom.click();
26251 onFileSelected : function(e)
26253 e.preventDefault();
26255 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26259 Roo.each(this.selectorEl.dom.files, function(file){
26260 if(this.fireEvent('inspect', this, file) != false){
26261 this.files.push(file);
26271 this.selectorEl.dom.value = '';
26273 if(!this.files.length){
26277 if(this.boxes > 0 && this.files.length > this.boxes){
26278 this.files = this.files.slice(0, this.boxes);
26281 this.uploader.show();
26283 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26284 this.uploader.hide();
26293 Roo.each(this.files, function(file){
26295 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26296 var f = this.renderPreview(file);
26301 if(file.type.indexOf('image') != -1){
26302 this.delegates.push(
26304 _this.process(file);
26305 }).createDelegate(this)
26313 _this.process(file);
26314 }).createDelegate(this)
26319 this.files = files;
26321 this.delegates = this.delegates.concat(docs);
26323 if(!this.delegates.length){
26328 this.progressBar.aria_valuemax = this.delegates.length;
26335 arrange : function()
26337 if(!this.delegates.length){
26338 this.progressDialog.hide();
26343 var delegate = this.delegates.shift();
26345 this.progressDialog.show();
26347 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26349 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26354 refresh : function()
26356 this.uploader.show();
26358 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26359 this.uploader.hide();
26362 Roo.isTouch ? this.closable(false) : this.closable(true);
26364 this.fireEvent('refresh', this);
26367 onRemove : function(e, el, o)
26369 e.preventDefault();
26371 this.fireEvent('remove', this, o);
26375 remove : function(o)
26379 Roo.each(this.files, function(file){
26380 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26389 this.files = files;
26396 Roo.each(this.files, function(file){
26401 file.target.remove();
26410 onClick : function(e, el, o)
26412 e.preventDefault();
26414 this.fireEvent('click', this, o);
26418 closable : function(closable)
26420 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26422 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26434 xhrOnLoad : function(xhr)
26436 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26440 if (xhr.readyState !== 4) {
26442 this.fireEvent('exception', this, xhr);
26446 var response = Roo.decode(xhr.responseText);
26448 if(!response.success){
26450 this.fireEvent('exception', this, xhr);
26454 var file = this.renderPreview(response.data);
26456 this.files.push(file);
26462 xhrOnError : function()
26464 Roo.log('xhr on error');
26466 var response = Roo.decode(xhr.responseText);
26473 process : function(file)
26475 if(this.fireEvent('process', this, file) !== false){
26476 if(this.editable && file.type.indexOf('image') != -1){
26477 this.fireEvent('edit', this, file);
26481 this.uploadStart(file, false);
26488 uploadStart : function(file, crop)
26490 this.xhr = new XMLHttpRequest();
26492 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26497 file.xhr = this.xhr;
26499 this.managerEl.createChild({
26501 cls : 'roo-document-manager-loading',
26505 tooltip : file.name,
26506 cls : 'roo-document-manager-thumb',
26507 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26513 this.xhr.open(this.method, this.url, true);
26516 "Accept": "application/json",
26517 "Cache-Control": "no-cache",
26518 "X-Requested-With": "XMLHttpRequest"
26521 for (var headerName in headers) {
26522 var headerValue = headers[headerName];
26524 this.xhr.setRequestHeader(headerName, headerValue);
26530 this.xhr.onload = function()
26532 _this.xhrOnLoad(_this.xhr);
26535 this.xhr.onerror = function()
26537 _this.xhrOnError(_this.xhr);
26540 var formData = new FormData();
26542 formData.append('returnHTML', 'NO');
26545 formData.append('crop', crop);
26548 formData.append(this.paramName, file, file.name);
26550 if(this.fireEvent('prepare', this, formData) != false){
26551 this.xhr.send(formData);
26555 uploadCancel : function()
26559 this.delegates = [];
26561 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26568 renderPreview : function(file)
26570 if(typeof(file.target) != 'undefined' && file.target){
26574 var previewEl = this.managerEl.createChild({
26576 cls : 'roo-document-manager-preview',
26580 tooltip : file.filename,
26581 cls : 'roo-document-manager-thumb',
26582 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26587 html : '<i class="fa fa-times-circle"></i>'
26592 var close = previewEl.select('button.close', true).first();
26594 close.on('click', this.onRemove, this, file);
26596 file.target = previewEl;
26598 var image = previewEl.select('img', true).first();
26602 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26604 image.on('click', this.onClick, this, file);
26610 onPreviewLoad : function(file, image)
26612 if(typeof(file.target) == 'undefined' || !file.target){
26616 var width = image.dom.naturalWidth || image.dom.width;
26617 var height = image.dom.naturalHeight || image.dom.height;
26619 if(width > height){
26620 file.target.addClass('wide');
26624 file.target.addClass('tall');
26629 uploadFromSource : function(file, crop)
26631 this.xhr = new XMLHttpRequest();
26633 this.managerEl.createChild({
26635 cls : 'roo-document-manager-loading',
26639 tooltip : file.name,
26640 cls : 'roo-document-manager-thumb',
26641 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26647 this.xhr.open(this.method, this.url, true);
26650 "Accept": "application/json",
26651 "Cache-Control": "no-cache",
26652 "X-Requested-With": "XMLHttpRequest"
26655 for (var headerName in headers) {
26656 var headerValue = headers[headerName];
26658 this.xhr.setRequestHeader(headerName, headerValue);
26664 this.xhr.onload = function()
26666 _this.xhrOnLoad(_this.xhr);
26669 this.xhr.onerror = function()
26671 _this.xhrOnError(_this.xhr);
26674 var formData = new FormData();
26676 formData.append('returnHTML', 'NO');
26678 formData.append('crop', crop);
26680 if(typeof(file.filename) != 'undefined'){
26681 formData.append('filename', file.filename);
26684 if(typeof(file.mimetype) != 'undefined'){
26685 formData.append('mimetype', file.mimetype);
26688 if(this.fireEvent('prepare', this, formData) != false){
26689 this.xhr.send(formData);
26699 * @class Roo.bootstrap.DocumentViewer
26700 * @extends Roo.bootstrap.Component
26701 * Bootstrap DocumentViewer class
26704 * Create a new DocumentViewer
26705 * @param {Object} config The config object
26708 Roo.bootstrap.DocumentViewer = function(config){
26709 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26714 * Fire after initEvent
26715 * @param {Roo.bootstrap.DocumentViewer} this
26721 * @param {Roo.bootstrap.DocumentViewer} this
26726 * Fire after trash button
26727 * @param {Roo.bootstrap.DocumentViewer} this
26734 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
26736 getAutoCreate : function()
26740 cls : 'roo-document-viewer',
26744 cls : 'roo-document-viewer-body',
26748 cls : 'roo-document-viewer-thumb',
26752 cls : 'roo-document-viewer-image'
26760 cls : 'roo-document-viewer-footer',
26763 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
26771 cls : 'btn btn-default roo-document-viewer-trash',
26772 html : '<i class="fa fa-trash"></i>'
26785 initEvents : function()
26788 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
26789 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26791 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
26792 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26794 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
26795 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26797 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
26798 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26800 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
26801 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26803 this.bodyEl.on('click', this.onClick, this);
26805 this.trashBtn.on('click', this.onTrash, this);
26809 initial : function()
26811 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
26814 this.fireEvent('initial', this);
26818 onClick : function(e)
26820 e.preventDefault();
26822 this.fireEvent('click', this);
26825 onTrash : function(e)
26827 e.preventDefault();
26829 this.fireEvent('trash', this);
26841 * @class Roo.bootstrap.NavProgressBar
26842 * @extends Roo.bootstrap.Component
26843 * Bootstrap NavProgressBar class
26846 * Create a new nav progress bar
26847 * @param {Object} config The config object
26850 Roo.bootstrap.NavProgressBar = function(config){
26851 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
26853 this.bullets = this.bullets || [];
26855 // Roo.bootstrap.NavProgressBar.register(this);
26859 * Fires when the active item changes
26860 * @param {Roo.bootstrap.NavProgressBar} this
26861 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
26862 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
26869 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
26874 getAutoCreate : function()
26876 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
26880 cls : 'roo-navigation-bar-group',
26884 cls : 'roo-navigation-top-bar'
26888 cls : 'roo-navigation-bullets-bar',
26892 cls : 'roo-navigation-bar'
26899 cls : 'roo-navigation-bottom-bar'
26909 initEvents: function()
26914 onRender : function(ct, position)
26916 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
26918 if(this.bullets.length){
26919 Roo.each(this.bullets, function(b){
26928 addItem : function(cfg)
26930 var item = new Roo.bootstrap.NavProgressItem(cfg);
26932 item.parentId = this.id;
26933 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
26936 var top = new Roo.bootstrap.Element({
26938 cls : 'roo-navigation-bar-text'
26941 var bottom = new Roo.bootstrap.Element({
26943 cls : 'roo-navigation-bar-text'
26946 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
26947 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
26949 var topText = new Roo.bootstrap.Element({
26951 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
26954 var bottomText = new Roo.bootstrap.Element({
26956 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
26959 topText.onRender(top.el, null);
26960 bottomText.onRender(bottom.el, null);
26963 item.bottomEl = bottom;
26966 this.barItems.push(item);
26971 getActive : function()
26973 var active = false;
26975 Roo.each(this.barItems, function(v){
26977 if (!v.isActive()) {
26989 setActiveItem : function(item)
26993 Roo.each(this.barItems, function(v){
26994 if (v.rid == item.rid) {
26998 if (v.isActive()) {
26999 v.setActive(false);
27004 item.setActive(true);
27006 this.fireEvent('changed', this, item, prev);
27009 getBarItem: function(rid)
27013 Roo.each(this.barItems, function(e) {
27014 if (e.rid != rid) {
27025 indexOfItem : function(item)
27029 Roo.each(this.barItems, function(v, i){
27031 if (v.rid != item.rid) {
27042 setActiveNext : function()
27044 var i = this.indexOfItem(this.getActive());
27046 if (i > this.barItems.length) {
27050 this.setActiveItem(this.barItems[i+1]);
27053 setActivePrev : function()
27055 var i = this.indexOfItem(this.getActive());
27061 this.setActiveItem(this.barItems[i-1]);
27064 format : function()
27066 if(!this.barItems.length){
27070 var width = 100 / this.barItems.length;
27072 Roo.each(this.barItems, function(i){
27073 i.el.setStyle('width', width + '%');
27074 i.topEl.el.setStyle('width', width + '%');
27075 i.bottomEl.el.setStyle('width', width + '%');
27084 * Nav Progress Item
27089 * @class Roo.bootstrap.NavProgressItem
27090 * @extends Roo.bootstrap.Component
27091 * Bootstrap NavProgressItem class
27092 * @cfg {String} rid the reference id
27093 * @cfg {Boolean} active (true|false) Is item active default false
27094 * @cfg {Boolean} disabled (true|false) Is item active default false
27095 * @cfg {String} html
27096 * @cfg {String} position (top|bottom) text position default bottom
27097 * @cfg {String} icon show icon instead of number
27100 * Create a new NavProgressItem
27101 * @param {Object} config The config object
27103 Roo.bootstrap.NavProgressItem = function(config){
27104 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27109 * The raw click event for the entire grid.
27110 * @param {Roo.bootstrap.NavProgressItem} this
27111 * @param {Roo.EventObject} e
27118 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
27124 position : 'bottom',
27127 getAutoCreate : function()
27129 var iconCls = 'roo-navigation-bar-item-icon';
27131 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27135 cls: 'roo-navigation-bar-item',
27145 cfg.cls += ' active';
27148 cfg.cls += ' disabled';
27154 disable : function()
27156 this.setDisabled(true);
27159 enable : function()
27161 this.setDisabled(false);
27164 initEvents: function()
27166 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27168 this.iconEl.on('click', this.onClick, this);
27171 onClick : function(e)
27173 e.preventDefault();
27179 if(this.fireEvent('click', this, e) === false){
27183 this.parent().setActiveItem(this);
27186 isActive: function ()
27188 return this.active;
27191 setActive : function(state)
27193 if(this.active == state){
27197 this.active = state;
27200 this.el.addClass('active');
27204 this.el.removeClass('active');
27209 setDisabled : function(state)
27211 if(this.disabled == state){
27215 this.disabled = state;
27218 this.el.addClass('disabled');
27222 this.el.removeClass('disabled');
27225 tooltipEl : function()
27227 return this.el.select('.roo-navigation-bar-item-icon', true).first();;