4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
23 * Do not use directly - it does not do anything..
24 * @param {Object} config The config object
29 Roo.bootstrap.Component = function(config){
30 Roo.bootstrap.Component.superclass.constructor.call(this, config);
34 * @event childrenrendered
35 * Fires when the children have been rendered..
36 * @param {Roo.bootstrap.Component} this
38 "childrenrendered" : true
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
50 allowDomMove : false, // to stop relocations in parent onRender...
60 * Initialize Events for the element
62 initEvents : function() { },
68 can_build_overlaid : true,
70 container_method : false,
77 // returns the parent component..
78 return Roo.ComponentMgr.get(this.parentId)
84 onRender : function(ct, position)
86 // Roo.log("Call onRender: " + this.xtype);
88 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
91 if (this.el.attr('xtype')) {
92 this.el.attr('xtypex', this.el.attr('xtype'));
93 this.el.dom.removeAttribute('xtype');
103 var cfg = Roo.apply({}, this.getAutoCreate());
104 cfg.id = this.id || Roo.id();
106 // fill in the extra attributes
107 if (this.xattr && typeof(this.xattr) =='object') {
108 for (var i in this.xattr) {
109 cfg[i] = this.xattr[i];
114 cfg.dataId = this.dataId;
118 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121 if (this.style) { // fixme needs to support more complex style data.
122 cfg.style = this.style;
126 cfg.name = this.name;
129 this.el = ct.createChild(cfg, position);
132 this.tooltipEl().attr('tooltip', this.tooltip);
135 if(this.tabIndex !== undefined){
136 this.el.dom.setAttribute('tabIndex', this.tabIndex);
143 * Fetch the element to add children to
144 * @return {Roo.Element} defaults to this.el
146 getChildContainer : function()
151 * Fetch the element to display the tooltip on.
152 * @return {Roo.Element} defaults to this.el
154 tooltipEl : function()
159 addxtype : function(tree,cntr)
163 cn = Roo.factory(tree);
165 cn.parentType = this.xtype; //??
166 cn.parentId = this.id;
168 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
169 if (typeof(cn.container_method) == 'string') {
170 cntr = cn.container_method;
174 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
176 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
178 var build_from_html = Roo.XComponent.build_from_html;
180 var is_body = (tree.xtype == 'Body') ;
182 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
184 var self_cntr_el = Roo.get(this[cntr](false));
186 // do not try and build conditional elements
187 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
191 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
192 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
193 return this.addxtypeChild(tree,cntr);
196 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202 Roo.log('skipping render');
208 if (!build_from_html) {
212 // this i think handles overlaying multiple children of the same type
213 // with the sam eelement.. - which might be buggy..
215 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
221 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
225 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
230 addxtypeChild : function (tree, cntr)
232 Roo.debug && Roo.log('addxtypeChild:' + cntr);
234 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
237 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
238 (typeof(tree['flexy:foreach']) != 'undefined');
242 skip_children = false;
243 // render the element if it's not BODY.
244 if (tree.xtype != 'Body') {
246 cn = Roo.factory(tree);
248 cn.parentType = this.xtype; //??
249 cn.parentId = this.id;
251 var build_from_html = Roo.XComponent.build_from_html;
254 // does the container contain child eleemnts with 'xtype' attributes.
255 // that match this xtype..
256 // note - when we render we create these as well..
257 // so we should check to see if body has xtype set.
258 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
260 var self_cntr_el = Roo.get(this[cntr](false));
261 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
263 //Roo.log(Roo.XComponent.build_from_html);
264 //Roo.log("got echild:");
267 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
268 // and are not displayed -this causes this to use up the wrong element when matching.
269 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
272 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
273 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
279 //echild.dom.removeAttribute('xtype');
281 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
282 Roo.debug && Roo.log(self_cntr_el);
283 Roo.debug && Roo.log(echild);
284 Roo.debug && Roo.log(cn);
290 // if object has flexy:if - then it may or may not be rendered.
291 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
292 // skip a flexy if element.
293 Roo.debug && Roo.log('skipping render');
294 Roo.debug && Roo.log(tree);
296 Roo.debug && Roo.log('skipping all children');
297 skip_children = true;
302 // actually if flexy:foreach is found, we really want to create
303 // multiple copies here...
305 //Roo.log(this[cntr]());
306 cn.render(this[cntr](true));
308 // then add the element..
316 if (typeof (tree.menu) != 'undefined') {
317 tree.menu.parentType = cn.xtype;
318 tree.menu.triggerEl = cn.el;
319 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
323 if (!tree.items || !tree.items.length) {
327 var items = tree.items;
330 //Roo.log(items.length);
332 if (!skip_children) {
333 for(var i =0;i < items.length;i++) {
334 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
340 this.fireEvent('childrenrendered', this);
345 * Show a component - removes 'hidden' class
350 this.el.removeClass('hidden');
354 * Hide a component - adds 'hidden' class
358 if (this.el && !this.el.hasClass('hidden')) {
359 this.el.addClass('hidden');
373 * @class Roo.bootstrap.Body
374 * @extends Roo.bootstrap.Component
375 * Bootstrap Body class
379 * @param {Object} config The config object
382 Roo.bootstrap.Body = function(config){
383 Roo.bootstrap.Body.superclass.constructor.call(this, config);
384 this.el = Roo.get(document.body);
385 if (this.cls && this.cls.length) {
386 Roo.get(document.body).addClass(this.cls);
390 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
395 onRender : function(ct, position)
397 /* Roo.log("Roo.bootstrap.Body - onRender");
398 if (this.cls && this.cls.length) {
399 Roo.get(document.body).addClass(this.cls);
419 * @class Roo.bootstrap.ButtonGroup
420 * @extends Roo.bootstrap.Component
421 * Bootstrap ButtonGroup class
422 * @cfg {String} size lg | sm | xs (default empty normal)
423 * @cfg {String} align vertical | justified (default none)
424 * @cfg {String} direction up | down (default down)
425 * @cfg {Boolean} toolbar false | true
426 * @cfg {Boolean} btn true | false
431 * @param {Object} config The config object
434 Roo.bootstrap.ButtonGroup = function(config){
435 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
438 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
446 getAutoCreate : function(){
452 cfg.html = this.html || cfg.html;
463 if (['vertical','justified'].indexOf(this.align)!==-1) {
464 cfg.cls = 'btn-group-' + this.align;
466 if (this.align == 'justified') {
467 console.log(this.items);
471 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
472 cfg.cls += ' btn-group-' + this.size;
475 if (this.direction == 'up') {
476 cfg.cls += ' dropup' ;
492 * @class Roo.bootstrap.Button
493 * @extends Roo.bootstrap.Component
494 * Bootstrap Button class
495 * @cfg {String} html The button content
496 * @cfg {String} weight ( primary | success | info | warning | danger | link ) default
497 * @cfg {String} size ( lg | sm | xs)
498 * @cfg {String} tag ( a | input | submit)
499 * @cfg {String} href empty or href
500 * @cfg {Boolean} disabled default false;
501 * @cfg {Boolean} isClose default false;
502 * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
503 * @cfg {String} badge text for badge
504 * @cfg {String} theme default
505 * @cfg {Boolean} inverse
506 * @cfg {Boolean} toggle
507 * @cfg {String} ontext text for on toggle state
508 * @cfg {String} offtext text for off toggle state
509 * @cfg {Boolean} defaulton
510 * @cfg {Boolean} preventDefault default true
511 * @cfg {Boolean} removeClass remove the standard class..
512 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
515 * Create a new button
516 * @param {Object} config The config object
520 Roo.bootstrap.Button = function(config){
521 Roo.bootstrap.Button.superclass.constructor.call(this, config);
526 * When a butotn is pressed
527 * @param {Roo.bootstrap.Button} this
528 * @param {Roo.EventObject} e
533 * After the button has been toggles
534 * @param {Roo.EventObject} e
535 * @param {boolean} pressed (also available as button.pressed)
541 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
559 preventDefault: true,
568 getAutoCreate : function(){
576 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
577 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
582 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
584 if (this.toggle == true) {
587 cls: 'slider-frame roo-button',
592 'data-off-text':'OFF',
593 cls: 'slider-button',
599 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
600 cfg.cls += ' '+this.weight;
609 cfg["aria-hidden"] = true;
611 cfg.html = "×";
617 if (this.theme==='default') {
618 cfg.cls = 'btn roo-button';
620 //if (this.parentType != 'Navbar') {
621 this.weight = this.weight.length ? this.weight : 'default';
623 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
625 cfg.cls += ' btn-' + this.weight;
627 } else if (this.theme==='glow') {
630 cfg.cls = 'btn-glow roo-button';
632 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
634 cfg.cls += ' ' + this.weight;
640 this.cls += ' inverse';
645 cfg.cls += ' active';
649 cfg.disabled = 'disabled';
653 Roo.log('changing to ul' );
655 this.glyphicon = 'caret';
658 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
660 //gsRoo.log(this.parentType);
661 if (this.parentType === 'Navbar' && !this.parent().bar) {
662 Roo.log('changing to li?');
671 href : this.href || '#'
674 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
675 cfg.cls += ' dropdown';
682 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
684 if (this.glyphicon) {
685 cfg.html = ' ' + cfg.html;
690 cls: 'glyphicon glyphicon-' + this.glyphicon
700 // cfg.cls='btn roo-button';
704 var value = cfg.html;
709 cls: 'glyphicon glyphicon-' + this.glyphicon,
728 cfg.cls += ' dropdown';
729 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
732 if (cfg.tag !== 'a' && this.href !== '') {
733 throw "Tag must be a to set href.";
734 } else if (this.href.length > 0) {
735 cfg.href = this.href;
738 if(this.removeClass){
743 cfg.target = this.target;
748 initEvents: function() {
749 // Roo.log('init events?');
750 // Roo.log(this.el.dom);
753 if (typeof (this.menu) != 'undefined') {
754 this.menu.parentType = this.xtype;
755 this.menu.triggerEl = this.el;
756 this.addxtype(Roo.apply({}, this.menu));
760 if (this.el.hasClass('roo-button')) {
761 this.el.on('click', this.onClick, this);
763 this.el.select('.roo-button').on('click', this.onClick, this);
766 if(this.removeClass){
767 this.el.on('click', this.onClick, this);
770 this.el.enableDisplayMode();
773 onClick : function(e)
780 Roo.log('button on click ');
781 if(this.preventDefault){
784 if (this.pressed === true || this.pressed === false) {
785 this.pressed = !this.pressed;
786 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
787 this.fireEvent('toggle', this, e, this.pressed);
791 this.fireEvent('click', this, e);
795 * Enables this button
799 this.disabled = false;
800 this.el.removeClass('disabled');
804 * Disable this button
808 this.disabled = true;
809 this.el.addClass('disabled');
812 * sets the active state on/off,
813 * @param {Boolean} state (optional) Force a particular state
815 setActive : function(v) {
817 this.el[v ? 'addClass' : 'removeClass']('active');
820 * toggles the current active state
822 toggleActive : function()
824 var active = this.el.hasClass('active');
825 this.setActive(!active);
829 setText : function(str)
831 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
835 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
858 * @class Roo.bootstrap.Column
859 * @extends Roo.bootstrap.Component
860 * Bootstrap Column class
861 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
862 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
863 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
864 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
865 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
866 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
867 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
868 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
871 * @cfg {Boolean} hidden (true|false) hide the element
872 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
873 * @cfg {String} fa (ban|check|...) font awesome icon
874 * @cfg {Number} fasize (1|2|....) font awsome size
876 * @cfg {String} icon (info-sign|check|...) glyphicon name
878 * @cfg {String} html content of column.
881 * Create a new Column
882 * @param {Object} config The config object
885 Roo.bootstrap.Column = function(config){
886 Roo.bootstrap.Column.superclass.constructor.call(this, config);
889 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
907 getAutoCreate : function(){
908 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
916 ['xs','sm','md','lg'].map(function(size){
917 //Roo.log( size + ':' + settings[size]);
919 if (settings[size+'off'] !== false) {
920 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
923 if (settings[size] === false) {
926 Roo.log(settings[size]);
927 if (!settings[size]) { // 0 = hidden
928 cfg.cls += ' hidden-' + size;
931 cfg.cls += ' col-' + size + '-' + settings[size];
936 cfg.cls += ' hidden';
939 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
940 cfg.cls +=' alert alert-' + this.alert;
944 if (this.html.length) {
945 cfg.html = this.html;
949 if (this.fasize > 1) {
950 fasize = ' fa-' + this.fasize + 'x';
952 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
957 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
976 * @class Roo.bootstrap.Container
977 * @extends Roo.bootstrap.Component
978 * Bootstrap Container class
979 * @cfg {Boolean} jumbotron is it a jumbotron element
980 * @cfg {String} html content of element
981 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
982 * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
983 * @cfg {String} header content of header (for panel)
984 * @cfg {String} footer content of footer (for panel)
985 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
986 * @cfg {String} tag (header|aside|section) type of HTML tag.
987 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
988 * @cfg {String} fa (ban|check|...) font awesome icon
989 * @cfg {String} icon (info-sign|check|...) glyphicon name
990 * @cfg {Boolean} hidden (true|false) hide the element
991 * @cfg {Boolean} expandable (true|false) default false
992 * @cfg {Boolean} expanded (true|false) default true
993 * @cfg {String} rheader contet on the right of header
997 * Create a new Container
998 * @param {Object} config The config object
1001 Roo.bootstrap.Container = function(config){
1002 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1008 * After the panel has been expand
1010 * @param {Roo.bootstrap.Container} this
1015 * After the panel has been collapsed
1017 * @param {Roo.bootstrap.Container} this
1023 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1040 getChildContainer : function() {
1046 if (this.panel.length) {
1047 return this.el.select('.panel-body',true).first();
1054 getAutoCreate : function(){
1057 tag : this.tag || 'div',
1061 if (this.jumbotron) {
1062 cfg.cls = 'jumbotron';
1067 // - this is applied by the parent..
1069 // cfg.cls = this.cls + '';
1072 if (this.sticky.length) {
1074 var bd = Roo.get(document.body);
1075 if (!bd.hasClass('bootstrap-sticky')) {
1076 bd.addClass('bootstrap-sticky');
1077 Roo.select('html',true).setStyle('height', '100%');
1080 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1084 if (this.well.length) {
1085 switch (this.well) {
1088 cfg.cls +=' well well-' +this.well;
1097 cfg.cls += ' hidden';
1101 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1102 cfg.cls +=' alert alert-' + this.alert;
1107 if (this.panel.length) {
1108 cfg.cls += ' panel panel-' + this.panel;
1110 if (this.header.length) {
1114 if(this.expandable){
1116 cfg.cls = cfg.cls + ' expandable';
1120 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1128 cls : 'panel-title',
1129 html : (this.expandable ? ' ' : '') + this.header
1133 cls: 'panel-header-right',
1139 cls : 'panel-heading',
1140 style : this.expandable ? 'cursor: pointer' : '',
1148 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1153 if (this.footer.length) {
1155 cls : 'panel-footer',
1164 body.html = this.html || cfg.html;
1165 // prefix with the icons..
1167 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1170 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1175 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1176 cfg.cls = 'container';
1182 initEvents: function()
1184 if(!this.expandable){
1188 var headerEl = this.headerEl();
1194 headerEl.on('click', this.onToggleClick, this);
1198 onToggleClick : function()
1200 var headerEl = this.headerEl();
1216 if(this.fireEvent('expand', this)) {
1218 this.expanded = true;
1220 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1222 this.el.select('.panel-body',true).first().removeClass('hide');
1224 var toggleEl = this.toggleEl();
1230 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1235 collapse : function()
1237 if(this.fireEvent('collapse', this)) {
1239 this.expanded = false;
1241 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1242 this.el.select('.panel-body',true).first().addClass('hide');
1244 var toggleEl = this.toggleEl();
1250 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1254 toggleEl : function()
1256 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1260 return this.el.select('.panel-heading .fa',true).first();
1263 headerEl : function()
1265 if(!this.el || !this.panel.length || !this.header.length){
1269 return this.el.select('.panel-heading',true).first()
1272 titleEl : function()
1274 if(!this.el || !this.panel.length || !this.header.length){
1278 return this.el.select('.panel-title',true).first();
1281 setTitle : function(v)
1283 var titleEl = this.titleEl();
1289 titleEl.dom.innerHTML = v;
1292 getTitle : function()
1295 var titleEl = this.titleEl();
1301 return titleEl.dom.innerHTML;
1304 setRightTitle : function(v)
1306 var t = this.el.select('.panel-header-right',true).first();
1312 t.dom.innerHTML = v;
1326 * @class Roo.bootstrap.Img
1327 * @extends Roo.bootstrap.Component
1328 * Bootstrap Img class
1329 * @cfg {Boolean} imgResponsive false | true
1330 * @cfg {String} border rounded | circle | thumbnail
1331 * @cfg {String} src image source
1332 * @cfg {String} alt image alternative text
1333 * @cfg {String} href a tag href
1334 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1335 * @cfg {String} xsUrl xs image source
1336 * @cfg {String} smUrl sm image source
1337 * @cfg {String} mdUrl md image source
1338 * @cfg {String} lgUrl lg image source
1341 * Create a new Input
1342 * @param {Object} config The config object
1345 Roo.bootstrap.Img = function(config){
1346 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1352 * The img click event for the img.
1353 * @param {Roo.EventObject} e
1359 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1361 imgResponsive: true,
1371 getAutoCreate : function()
1373 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1374 return this.createSingleImg();
1379 cls: 'roo-image-responsive-group',
1384 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1386 if(!_this[size + 'Url']){
1392 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1393 html: _this.html || cfg.html,
1394 src: _this[size + 'Url']
1397 img.cls += ' roo-image-responsive-' + size;
1399 var s = ['xs', 'sm', 'md', 'lg'];
1401 s.splice(s.indexOf(size), 1);
1403 Roo.each(s, function(ss){
1404 img.cls += ' hidden-' + ss;
1407 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1408 cfg.cls += ' img-' + _this.border;
1412 cfg.alt = _this.alt;
1425 a.target = _this.target;
1429 cfg.cn.push((_this.href) ? a : img);
1436 createSingleImg : function()
1440 cls: (this.imgResponsive) ? 'img-responsive' : '',
1444 cfg.html = this.html || cfg.html;
1446 cfg.src = this.src || cfg.src;
1448 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1449 cfg.cls += ' img-' + this.border;
1466 a.target = this.target;
1471 return (this.href) ? a : cfg;
1474 initEvents: function()
1477 this.el.on('click', this.onClick, this);
1482 onClick : function(e)
1484 Roo.log('img onclick');
1485 this.fireEvent('click', this, e);
1499 * @class Roo.bootstrap.Link
1500 * @extends Roo.bootstrap.Component
1501 * Bootstrap Link Class
1502 * @cfg {String} alt image alternative text
1503 * @cfg {String} href a tag href
1504 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1505 * @cfg {String} html the content of the link.
1506 * @cfg {String} anchor name for the anchor link
1508 * @cfg {Boolean} preventDefault (true | false) default false
1512 * Create a new Input
1513 * @param {Object} config The config object
1516 Roo.bootstrap.Link = function(config){
1517 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1523 * The img click event for the img.
1524 * @param {Roo.EventObject} e
1530 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1534 preventDefault: false,
1538 getAutoCreate : function()
1544 // anchor's do not require html/href...
1545 if (this.anchor === false) {
1546 cfg.html = this.html || '';
1547 cfg.href = this.href || '#';
1549 cfg.name = this.anchor;
1550 if (this.html !== false) {
1551 cfg.html = this.html;
1553 if (this.href !== false) {
1554 cfg.href = this.href;
1558 if(this.alt !== false){
1563 if(this.target !== false) {
1564 cfg.target = this.target;
1570 initEvents: function() {
1572 if(!this.href || this.preventDefault){
1573 this.el.on('click', this.onClick, this);
1577 onClick : function(e)
1579 if(this.preventDefault){
1582 //Roo.log('img onclick');
1583 this.fireEvent('click', this, e);
1596 * @class Roo.bootstrap.Header
1597 * @extends Roo.bootstrap.Component
1598 * Bootstrap Header class
1599 * @cfg {String} html content of header
1600 * @cfg {Number} level (1|2|3|4|5|6) default 1
1603 * Create a new Header
1604 * @param {Object} config The config object
1608 Roo.bootstrap.Header = function(config){
1609 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1612 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1620 getAutoCreate : function(){
1625 tag: 'h' + (1 *this.level),
1626 html: this.html || ''
1638 * Ext JS Library 1.1.1
1639 * Copyright(c) 2006-2007, Ext JS, LLC.
1641 * Originally Released Under LGPL - original licence link has changed is not relivant.
1644 * <script type="text/javascript">
1648 * @class Roo.bootstrap.MenuMgr
1649 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1652 Roo.bootstrap.MenuMgr = function(){
1653 var menus, active, groups = {}, attached = false, lastShow = new Date();
1655 // private - called when first menu is created
1658 active = new Roo.util.MixedCollection();
1659 Roo.get(document).addKeyListener(27, function(){
1660 if(active.length > 0){
1668 if(active && active.length > 0){
1669 var c = active.clone();
1679 if(active.length < 1){
1680 Roo.get(document).un("mouseup", onMouseDown);
1688 var last = active.last();
1689 lastShow = new Date();
1692 Roo.get(document).on("mouseup", onMouseDown);
1697 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1698 m.parentMenu.activeChild = m;
1699 }else if(last && last.isVisible()){
1700 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1705 function onBeforeHide(m){
1707 m.activeChild.hide();
1709 if(m.autoHideTimer){
1710 clearTimeout(m.autoHideTimer);
1711 delete m.autoHideTimer;
1716 function onBeforeShow(m){
1717 var pm = m.parentMenu;
1718 if(!pm && !m.allowOtherMenus){
1720 }else if(pm && pm.activeChild && active != m){
1721 pm.activeChild.hide();
1725 // private this should really trigger on mouseup..
1726 function onMouseDown(e){
1727 Roo.log("on Mouse Up");
1728 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1738 function onBeforeCheck(mi, state){
1740 var g = groups[mi.group];
1741 for(var i = 0, l = g.length; i < l; i++){
1743 g[i].setChecked(false);
1752 * Hides all menus that are currently visible
1754 hideAll : function(){
1759 register : function(menu){
1763 menus[menu.id] = menu;
1764 menu.on("beforehide", onBeforeHide);
1765 menu.on("hide", onHide);
1766 menu.on("beforeshow", onBeforeShow);
1767 menu.on("show", onShow);
1769 if(g && menu.events["checkchange"]){
1773 groups[g].push(menu);
1774 menu.on("checkchange", onCheck);
1779 * Returns a {@link Roo.menu.Menu} object
1780 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1781 * be used to generate and return a new Menu instance.
1783 get : function(menu){
1784 if(typeof menu == "string"){ // menu id
1786 }else if(menu.events){ // menu instance
1789 /*else if(typeof menu.length == 'number'){ // array of menu items?
1790 return new Roo.bootstrap.Menu({items:menu});
1791 }else{ // otherwise, must be a config
1792 return new Roo.bootstrap.Menu(menu);
1799 unregister : function(menu){
1800 delete menus[menu.id];
1801 menu.un("beforehide", onBeforeHide);
1802 menu.un("hide", onHide);
1803 menu.un("beforeshow", onBeforeShow);
1804 menu.un("show", onShow);
1806 if(g && menu.events["checkchange"]){
1807 groups[g].remove(menu);
1808 menu.un("checkchange", onCheck);
1813 registerCheckable : function(menuItem){
1814 var g = menuItem.group;
1819 groups[g].push(menuItem);
1820 menuItem.on("beforecheckchange", onBeforeCheck);
1825 unregisterCheckable : function(menuItem){
1826 var g = menuItem.group;
1828 groups[g].remove(menuItem);
1829 menuItem.un("beforecheckchange", onBeforeCheck);
1841 * @class Roo.bootstrap.Menu
1842 * @extends Roo.bootstrap.Component
1843 * Bootstrap Menu class - container for MenuItems
1844 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1848 * @param {Object} config The config object
1852 Roo.bootstrap.Menu = function(config){
1853 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1854 if (this.registerMenu) {
1855 Roo.bootstrap.MenuMgr.register(this);
1860 * Fires before this menu is displayed
1861 * @param {Roo.menu.Menu} this
1866 * Fires before this menu is hidden
1867 * @param {Roo.menu.Menu} this
1872 * Fires after this menu is displayed
1873 * @param {Roo.menu.Menu} this
1878 * Fires after this menu is hidden
1879 * @param {Roo.menu.Menu} this
1884 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1885 * @param {Roo.menu.Menu} this
1886 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1887 * @param {Roo.EventObject} e
1892 * Fires when the mouse is hovering over this menu
1893 * @param {Roo.menu.Menu} this
1894 * @param {Roo.EventObject} e
1895 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1900 * Fires when the mouse exits this menu
1901 * @param {Roo.menu.Menu} this
1902 * @param {Roo.EventObject} e
1903 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1908 * Fires when a menu item contained in this menu is clicked
1909 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1910 * @param {Roo.EventObject} e
1914 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1917 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1921 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1924 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1926 registerMenu : true,
1928 menuItems :false, // stores the menu items..
1934 getChildContainer : function() {
1938 getAutoCreate : function(){
1940 //if (['right'].indexOf(this.align)!==-1) {
1941 // cfg.cn[1].cls += ' pull-right'
1947 cls : 'dropdown-menu' ,
1948 style : 'z-index:1000'
1952 if (this.type === 'submenu') {
1953 cfg.cls = 'submenu active';
1955 if (this.type === 'treeview') {
1956 cfg.cls = 'treeview-menu';
1961 initEvents : function() {
1963 // Roo.log("ADD event");
1964 // Roo.log(this.triggerEl.dom);
1965 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1967 this.triggerEl.addClass('dropdown-toggle');
1968 this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1970 this.el.on("mouseover", this.onMouseOver, this);
1971 this.el.on("mouseout", this.onMouseOut, this);
1975 findTargetItem : function(e){
1976 var t = e.getTarget(".dropdown-menu-item", this.el, true);
1980 //Roo.log(t); Roo.log(t.id);
1982 //Roo.log(this.menuitems);
1983 return this.menuitems.get(t.id);
1985 //return this.items.get(t.menuItemId);
1990 onClick : function(e){
1991 Roo.log("menu.onClick");
1992 var t = this.findTargetItem(e);
1993 if(!t || t.isContainer){
1998 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
1999 if(t == this.activeItem && t.shouldDeactivate(e)){
2000 this.activeItem.deactivate();
2001 delete this.activeItem;
2005 this.setActiveItem(t, true);
2013 Roo.log('pass click event');
2017 this.fireEvent("click", this, t, e);
2021 onMouseOver : function(e){
2022 var t = this.findTargetItem(e);
2025 // if(t.canActivate && !t.disabled){
2026 // this.setActiveItem(t, true);
2030 this.fireEvent("mouseover", this, e, t);
2032 isVisible : function(){
2033 return !this.hidden;
2035 onMouseOut : function(e){
2036 var t = this.findTargetItem(e);
2039 // if(t == this.activeItem && t.shouldDeactivate(e)){
2040 // this.activeItem.deactivate();
2041 // delete this.activeItem;
2044 this.fireEvent("mouseout", this, e, t);
2049 * Displays this menu relative to another element
2050 * @param {String/HTMLElement/Roo.Element} element The element to align to
2051 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2052 * the element (defaults to this.defaultAlign)
2053 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2055 show : function(el, pos, parentMenu){
2056 this.parentMenu = parentMenu;
2060 this.fireEvent("beforeshow", this);
2061 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2064 * Displays this menu at a specific xy position
2065 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2066 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2068 showAt : function(xy, parentMenu, /* private: */_e){
2069 this.parentMenu = parentMenu;
2074 this.fireEvent("beforeshow", this);
2075 //xy = this.el.adjustForConstraints(xy);
2079 this.hideMenuItems();
2080 this.hidden = false;
2081 this.triggerEl.addClass('open');
2083 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2084 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2089 this.fireEvent("show", this);
2095 this.doFocus.defer(50, this);
2099 doFocus : function(){
2101 this.focusEl.focus();
2106 * Hides this menu and optionally all parent menus
2107 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2109 hide : function(deep){
2111 this.hideMenuItems();
2112 if(this.el && this.isVisible()){
2113 this.fireEvent("beforehide", this);
2114 if(this.activeItem){
2115 this.activeItem.deactivate();
2116 this.activeItem = null;
2118 this.triggerEl.removeClass('open');;
2120 this.fireEvent("hide", this);
2122 if(deep === true && this.parentMenu){
2123 this.parentMenu.hide(true);
2127 onTriggerPress : function(e)
2130 Roo.log('trigger press');
2131 //Roo.log(e.getTarget());
2132 // Roo.log(this.triggerEl.dom);
2133 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2137 if (this.isVisible()) {
2142 this.show(this.triggerEl, false, false);
2151 hideMenuItems : function()
2153 //$(backdrop).remove()
2154 Roo.select('.open',true).each(function(aa) {
2156 aa.removeClass('open');
2157 //var parent = getParent($(this))
2158 //var relatedTarget = { relatedTarget: this }
2160 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2161 //if (e.isDefaultPrevented()) return
2162 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2165 addxtypeChild : function (tree, cntr) {
2166 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2168 this.menuitems.add(comp);
2189 * @class Roo.bootstrap.MenuItem
2190 * @extends Roo.bootstrap.Component
2191 * Bootstrap MenuItem class
2192 * @cfg {String} html the menu label
2193 * @cfg {String} href the link
2194 * @cfg {Boolean} preventDefault (true | false) default true
2195 * @cfg {Boolean} isContainer (true | false) default false
2199 * Create a new MenuItem
2200 * @param {Object} config The config object
2204 Roo.bootstrap.MenuItem = function(config){
2205 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2210 * The raw click event for the entire grid.
2211 * @param {Roo.bootstrap.MenuItem} this
2212 * @param {Roo.EventObject} e
2218 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2222 preventDefault: true,
2223 isContainer : false,
2225 getAutoCreate : function(){
2227 if(this.isContainer){
2230 cls: 'dropdown-menu-item'
2236 cls: 'dropdown-menu-item',
2245 if (this.parent().type == 'treeview') {
2246 cfg.cls = 'treeview-menu';
2249 cfg.cn[0].href = this.href || cfg.cn[0].href ;
2250 cfg.cn[0].html = this.html || cfg.cn[0].html ;
2254 initEvents: function() {
2256 //this.el.select('a').on('click', this.onClick, this);
2259 onClick : function(e)
2261 Roo.log('item on click ');
2262 //if(this.preventDefault){
2263 // e.preventDefault();
2265 //this.parent().hideMenuItems();
2267 this.fireEvent('click', this, e);
2286 * @class Roo.bootstrap.MenuSeparator
2287 * @extends Roo.bootstrap.Component
2288 * Bootstrap MenuSeparator class
2291 * Create a new MenuItem
2292 * @param {Object} config The config object
2296 Roo.bootstrap.MenuSeparator = function(config){
2297 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2300 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2302 getAutoCreate : function(){
2321 * @class Roo.bootstrap.Modal
2322 * @extends Roo.bootstrap.Component
2323 * Bootstrap Modal class
2324 * @cfg {String} title Title of dialog
2325 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2326 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2327 * @cfg {Boolean} specificTitle default false
2328 * @cfg {Array} buttons Array of buttons or standard button set..
2329 * @cfg {String} buttonPosition (left|right|center) default right
2330 * @cfg {Boolean} animate default true
2331 * @cfg {Boolean} allow_close default true
2334 * Create a new Modal Dialog
2335 * @param {Object} config The config object
2338 Roo.bootstrap.Modal = function(config){
2339 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2344 * The raw btnclick event for the button
2345 * @param {Roo.EventObject} e
2349 this.buttons = this.buttons || [];
2352 this.tmpl = Roo.factory(this.tmpl);
2357 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2359 title : 'test dialog',
2369 specificTitle: false,
2371 buttonPosition: 'right',
2385 onRender : function(ct, position)
2387 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2390 var cfg = Roo.apply({}, this.getAutoCreate());
2393 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2395 //if (!cfg.name.length) {
2399 cfg.cls += ' ' + this.cls;
2402 cfg.style = this.style;
2404 this.el = Roo.get(document.body).createChild(cfg, position);
2406 //var type = this.el.dom.type;
2411 if(this.tabIndex !== undefined){
2412 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2416 this.bodyEl = this.el.select('.modal-body',true).first();
2417 this.closeEl = this.el.select('.modal-header .close', true).first();
2418 this.footerEl = this.el.select('.modal-footer',true).first();
2419 this.titleEl = this.el.select('.modal-title',true).first();
2423 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2424 this.maskEl.enableDisplayMode("block");
2426 //this.el.addClass("x-dlg-modal");
2428 if (this.buttons.length) {
2429 Roo.each(this.buttons, function(bb) {
2430 var b = Roo.apply({}, bb);
2431 b.xns = b.xns || Roo.bootstrap;
2432 b.xtype = b.xtype || 'Button';
2433 if (typeof(b.listeners) == 'undefined') {
2434 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2437 var btn = Roo.factory(b);
2439 btn.onRender(this.el.select('.modal-footer div').first());
2443 // render the children.
2446 if(typeof(this.items) != 'undefined'){
2447 var items = this.items;
2450 for(var i =0;i < items.length;i++) {
2451 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2455 this.items = nitems;
2457 // where are these used - they used to be body/close/footer
2461 //this.el.addClass([this.fieldClass, this.cls]);
2465 getAutoCreate : function(){
2470 html : this.html || ''
2475 cls : 'modal-title',
2479 if(this.specificTitle){
2485 if (this.allow_close) {
2496 style : 'display: none',
2499 cls: "modal-dialog",
2502 cls : "modal-content",
2505 cls : 'modal-header',
2510 cls : 'modal-footer',
2514 cls: 'btn-' + this.buttonPosition
2531 modal.cls += ' fade';
2537 getChildContainer : function() {
2542 getButtonContainer : function() {
2543 return this.el.select('.modal-footer div',true).first();
2546 initEvents : function()
2548 if (this.allow_close) {
2549 this.closeEl.on('click', this.hide, this);
2554 window.addEventListener("resize", function() { _this.resize(); } );
2560 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2565 if (!this.rendered) {
2569 this.el.setStyle('display', 'block');
2573 (function(){ _this.el.addClass('in'); }).defer(50);
2575 this.el.addClass('in');
2578 // not sure how we can show data in here..
2580 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2583 Roo.get(document.body).addClass("x-body-masked");
2584 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2586 this.el.setStyle('zIndex', '10001');
2588 this.fireEvent('show', this);
2595 Roo.get(document.body).removeClass("x-body-masked");
2596 this.el.removeClass('in');
2600 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2602 this.el.setStyle('display', 'none');
2605 this.fireEvent('hide', this);
2608 addButton : function(str, cb)
2612 var b = Roo.apply({}, { html : str } );
2613 b.xns = b.xns || Roo.bootstrap;
2614 b.xtype = b.xtype || 'Button';
2615 if (typeof(b.listeners) == 'undefined') {
2616 b.listeners = { click : cb.createDelegate(this) };
2619 var btn = Roo.factory(b);
2621 btn.onRender(this.el.select('.modal-footer div').first());
2627 setDefaultButton : function(btn)
2629 //this.el.select('.modal-footer').()
2631 resizeTo: function(w,h)
2635 setContentSize : function(w, h)
2639 onButtonClick: function(btn,e)
2642 this.fireEvent('btnclick', btn.name, e);
2645 * Set the title of the Dialog
2646 * @param {String} str new Title
2648 setTitle: function(str) {
2649 this.titleEl.dom.innerHTML = str;
2652 * Set the body of the Dialog
2653 * @param {String} str new Title
2655 setBody: function(str) {
2656 this.bodyEl.dom.innerHTML = str;
2659 * Set the body of the Dialog using the template
2660 * @param {Obj} data - apply this data to the template and replace the body contents.
2662 applyBody: function(obj)
2665 Roo.log("Error - using apply Body without a template");
2668 this.tmpl.overwrite(this.bodyEl, obj);
2674 Roo.apply(Roo.bootstrap.Modal, {
2676 * Button config that displays a single OK button
2685 * Button config that displays Yes and No buttons
2701 * Button config that displays OK and Cancel buttons
2716 * Button config that displays Yes, No and Cancel buttons
2739 * messagebox - can be used as a replace
2743 * @class Roo.MessageBox
2744 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2748 Roo.Msg.alert('Status', 'Changes saved successfully.');
2750 // Prompt for user data:
2751 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2753 // process text value...
2757 // Show a dialog using config options:
2759 title:'Save Changes?',
2760 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2761 buttons: Roo.Msg.YESNOCANCEL,
2768 Roo.bootstrap.MessageBox = function(){
2769 var dlg, opt, mask, waitTimer;
2770 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2771 var buttons, activeTextEl, bwidth;
2775 var handleButton = function(button){
2777 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2781 var handleHide = function(){
2783 dlg.el.removeClass(opt.cls);
2786 // Roo.TaskMgr.stop(waitTimer);
2787 // waitTimer = null;
2792 var updateButtons = function(b){
2795 buttons["ok"].hide();
2796 buttons["cancel"].hide();
2797 buttons["yes"].hide();
2798 buttons["no"].hide();
2799 //dlg.footer.dom.style.display = 'none';
2802 dlg.footerEl.dom.style.display = '';
2803 for(var k in buttons){
2804 if(typeof buttons[k] != "function"){
2807 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2808 width += buttons[k].el.getWidth()+15;
2818 var handleEsc = function(d, k, e){
2819 if(opt && opt.closable !== false){
2829 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2830 * @return {Roo.BasicDialog} The BasicDialog element
2832 getDialog : function(){
2834 dlg = new Roo.bootstrap.Modal( {
2837 //constraintoviewport:false,
2839 //collapsible : false,
2844 //buttonAlign:"center",
2845 closeClick : function(){
2846 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2849 handleButton("cancel");
2854 dlg.on("hide", handleHide);
2856 //dlg.addKeyListener(27, handleEsc);
2858 this.buttons = buttons;
2859 var bt = this.buttonText;
2860 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2861 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2862 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2863 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2865 bodyEl = dlg.bodyEl.createChild({
2867 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2868 '<textarea class="roo-mb-textarea"></textarea>' +
2869 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2871 msgEl = bodyEl.dom.firstChild;
2872 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2873 textboxEl.enableDisplayMode();
2874 textboxEl.addKeyListener([10,13], function(){
2875 if(dlg.isVisible() && opt && opt.buttons){
2878 }else if(opt.buttons.yes){
2879 handleButton("yes");
2883 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2884 textareaEl.enableDisplayMode();
2885 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2886 progressEl.enableDisplayMode();
2887 var pf = progressEl.dom.firstChild;
2889 pp = Roo.get(pf.firstChild);
2890 pp.setHeight(pf.offsetHeight);
2898 * Updates the message box body text
2899 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2900 * the XHTML-compliant non-breaking space character '&#160;')
2901 * @return {Roo.MessageBox} This message box
2903 updateText : function(text){
2904 if(!dlg.isVisible() && !opt.width){
2905 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2907 msgEl.innerHTML = text || ' ';
2909 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2910 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2912 Math.min(opt.width || cw , this.maxWidth),
2913 Math.max(opt.minWidth || this.minWidth, bwidth)
2916 activeTextEl.setWidth(w);
2918 if(dlg.isVisible()){
2919 dlg.fixedcenter = false;
2921 // to big, make it scroll. = But as usual stupid IE does not support
2924 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2925 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2926 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2928 bodyEl.dom.style.height = '';
2929 bodyEl.dom.style.overflowY = '';
2932 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2934 bodyEl.dom.style.overflowX = '';
2937 dlg.setContentSize(w, bodyEl.getHeight());
2938 if(dlg.isVisible()){
2939 dlg.fixedcenter = true;
2945 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
2946 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2947 * @param {Number} value Any number between 0 and 1 (e.g., .5)
2948 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2949 * @return {Roo.MessageBox} This message box
2951 updateProgress : function(value, text){
2953 this.updateText(text);
2955 if (pp) { // weird bug on my firefox - for some reason this is not defined
2956 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2962 * Returns true if the message box is currently displayed
2963 * @return {Boolean} True if the message box is visible, else false
2965 isVisible : function(){
2966 return dlg && dlg.isVisible();
2970 * Hides the message box if it is displayed
2973 if(this.isVisible()){
2979 * Displays a new message box, or reinitializes an existing message box, based on the config options
2980 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2981 * The following config object properties are supported:
2983 Property Type Description
2984 ---------- --------------- ------------------------------------------------------------------------------------
2985 animEl String/Element An id or Element from which the message box should animate as it opens and
2986 closes (defaults to undefined)
2987 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2988 cancel:'Bar'}), or false to not show any buttons (defaults to false)
2989 closable Boolean False to hide the top-right close button (defaults to true). Note that
2990 progress and wait dialogs will ignore this property and always hide the
2991 close button as they can only be closed programmatically.
2992 cls String A custom CSS class to apply to the message box element
2993 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
2994 displayed (defaults to 75)
2995 fn Function A callback function to execute after closing the dialog. The arguments to the
2996 function will be btn (the name of the button that was clicked, if applicable,
2997 e.g. "ok"), and text (the value of the active text field, if applicable).
2998 Progress and wait dialogs will ignore this option since they do not respond to
2999 user actions and can only be closed programmatically, so any required function
3000 should be called by the same code after it closes the dialog.
3001 icon String A CSS class that provides a background image to be used as an icon for
3002 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3003 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3004 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3005 modal Boolean False to allow user interaction with the page while the message box is
3006 displayed (defaults to true)
3007 msg String A string that will replace the existing message box body text (defaults
3008 to the XHTML-compliant non-breaking space character ' ')
3009 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3010 progress Boolean True to display a progress bar (defaults to false)
3011 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3012 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3013 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3014 title String The title text
3015 value String The string value to set into the active textbox element if displayed
3016 wait Boolean True to display a progress bar (defaults to false)
3017 width Number The width of the dialog in pixels
3024 msg: 'Please enter your address:',
3026 buttons: Roo.MessageBox.OKCANCEL,
3029 animEl: 'addAddressBtn'
3032 * @param {Object} config Configuration options
3033 * @return {Roo.MessageBox} This message box
3035 show : function(options)
3038 // this causes nightmares if you show one dialog after another
3039 // especially on callbacks..
3041 if(this.isVisible()){
3044 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3045 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3046 Roo.log("New Dialog Message:" + options.msg )
3047 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3048 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3051 var d = this.getDialog();
3053 d.setTitle(opt.title || " ");
3054 d.closeEl.setDisplayed(opt.closable !== false);
3055 activeTextEl = textboxEl;
3056 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3061 textareaEl.setHeight(typeof opt.multiline == "number" ?
3062 opt.multiline : this.defaultTextHeight);
3063 activeTextEl = textareaEl;
3072 progressEl.setDisplayed(opt.progress === true);
3073 this.updateProgress(0);
3074 activeTextEl.dom.value = opt.value || "";
3076 dlg.setDefaultButton(activeTextEl);
3078 var bs = opt.buttons;
3082 }else if(bs && bs.yes){
3083 db = buttons["yes"];
3085 dlg.setDefaultButton(db);
3087 bwidth = updateButtons(opt.buttons);
3088 this.updateText(opt.msg);
3090 d.el.addClass(opt.cls);
3092 d.proxyDrag = opt.proxyDrag === true;
3093 d.modal = opt.modal !== false;
3094 d.mask = opt.modal !== false ? mask : false;
3096 // force it to the end of the z-index stack so it gets a cursor in FF
3097 document.body.appendChild(dlg.el.dom);
3098 d.animateTarget = null;
3099 d.show(options.animEl);
3105 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3106 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3107 * and closing the message box when the process is complete.
3108 * @param {String} title The title bar text
3109 * @param {String} msg The message box body text
3110 * @return {Roo.MessageBox} This message box
3112 progress : function(title, msg){
3119 minWidth: this.minProgressWidth,
3126 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3127 * If a callback function is passed it will be called after the user clicks the button, and the
3128 * id of the button that was clicked will be passed as the only parameter to the callback
3129 * (could also be the top-right close button).
3130 * @param {String} title The title bar text
3131 * @param {String} msg The message box body text
3132 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3133 * @param {Object} scope (optional) The scope of the callback function
3134 * @return {Roo.MessageBox} This message box
3136 alert : function(title, msg, fn, scope){
3149 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3150 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3151 * You are responsible for closing the message box when the process is complete.
3152 * @param {String} msg The message box body text
3153 * @param {String} title (optional) The title bar text
3154 * @return {Roo.MessageBox} This message box
3156 wait : function(msg, title){
3167 waitTimer = Roo.TaskMgr.start({
3169 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3177 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3178 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3179 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3180 * @param {String} title The title bar text
3181 * @param {String} msg The message box body text
3182 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3183 * @param {Object} scope (optional) The scope of the callback function
3184 * @return {Roo.MessageBox} This message box
3186 confirm : function(title, msg, fn, scope){
3190 buttons: this.YESNO,
3199 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3200 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3201 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3202 * (could also be the top-right close button) and the text that was entered will be passed as the two
3203 * parameters to the callback.
3204 * @param {String} title The title bar text
3205 * @param {String} msg The message box body text
3206 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3207 * @param {Object} scope (optional) The scope of the callback function
3208 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3209 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3210 * @return {Roo.MessageBox} This message box
3212 prompt : function(title, msg, fn, scope, multiline){
3216 buttons: this.OKCANCEL,
3221 multiline: multiline,
3228 * Button config that displays a single OK button
3233 * Button config that displays Yes and No buttons
3236 YESNO : {yes:true, no:true},
3238 * Button config that displays OK and Cancel buttons
3241 OKCANCEL : {ok:true, cancel:true},
3243 * Button config that displays Yes, No and Cancel buttons
3246 YESNOCANCEL : {yes:true, no:true, cancel:true},
3249 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3252 defaultTextHeight : 75,
3254 * The maximum width in pixels of the message box (defaults to 600)
3259 * The minimum width in pixels of the message box (defaults to 100)
3264 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3265 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3268 minProgressWidth : 250,
3270 * An object containing the default button text strings that can be overriden for localized language support.
3271 * Supported properties are: ok, cancel, yes and no.
3272 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3285 * Shorthand for {@link Roo.MessageBox}
3287 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3288 Roo.Msg = Roo.Msg || Roo.MessageBox;
3297 * @class Roo.bootstrap.Navbar
3298 * @extends Roo.bootstrap.Component
3299 * Bootstrap Navbar class
3302 * Create a new Navbar
3303 * @param {Object} config The config object
3307 Roo.bootstrap.Navbar = function(config){
3308 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3312 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3321 getAutoCreate : function(){
3324 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3328 initEvents :function ()
3330 //Roo.log(this.el.select('.navbar-toggle',true));
3331 this.el.select('.navbar-toggle',true).on('click', function() {
3332 // Roo.log('click');
3333 this.el.select('.navbar-collapse',true).toggleClass('in');
3341 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3343 var size = this.el.getSize();
3344 this.maskEl.setSize(size.width, size.height);
3345 this.maskEl.enableDisplayMode("block");
3354 getChildContainer : function()
3356 if (this.el.select('.collapse').getCount()) {
3357 return this.el.select('.collapse',true).first();
3390 * @class Roo.bootstrap.NavSimplebar
3391 * @extends Roo.bootstrap.Navbar
3392 * Bootstrap Sidebar class
3394 * @cfg {Boolean} inverse is inverted color
3396 * @cfg {String} type (nav | pills | tabs)
3397 * @cfg {Boolean} arrangement stacked | justified
3398 * @cfg {String} align (left | right) alignment
3400 * @cfg {Boolean} main (true|false) main nav bar? default false
3401 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3403 * @cfg {String} tag (header|footer|nav|div) default is nav
3409 * Create a new Sidebar
3410 * @param {Object} config The config object
3414 Roo.bootstrap.NavSimplebar = function(config){
3415 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3418 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3434 getAutoCreate : function(){
3438 tag : this.tag || 'div',
3451 this.type = this.type || 'nav';
3452 if (['tabs','pills'].indexOf(this.type)!==-1) {
3453 cfg.cn[0].cls += ' nav-' + this.type
3457 if (this.type!=='nav') {
3458 Roo.log('nav type must be nav/tabs/pills')
3460 cfg.cn[0].cls += ' navbar-nav'
3466 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3467 cfg.cn[0].cls += ' nav-' + this.arrangement;
3471 if (this.align === 'right') {
3472 cfg.cn[0].cls += ' navbar-right';
3476 cfg.cls += ' navbar-inverse';
3503 * @class Roo.bootstrap.NavHeaderbar
3504 * @extends Roo.bootstrap.NavSimplebar
3505 * Bootstrap Sidebar class
3507 * @cfg {String} brand what is brand
3508 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3509 * @cfg {String} brand_href href of the brand
3510 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3511 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3512 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3513 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3516 * Create a new Sidebar
3517 * @param {Object} config The config object
3521 Roo.bootstrap.NavHeaderbar = function(config){
3522 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3526 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3533 desktopCenter : false,
3536 getAutoCreate : function(){
3539 tag: this.nav || 'nav',
3546 if (this.desktopCenter) {
3547 cn.push({cls : 'container', cn : []});
3554 cls: 'navbar-header',
3559 cls: 'navbar-toggle',
3560 'data-toggle': 'collapse',
3565 html: 'Toggle navigation'
3587 cls: 'collapse navbar-collapse',
3591 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3593 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3594 cfg.cls += ' navbar-' + this.position;
3596 // tag can override this..
3598 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3601 if (this.brand !== '') {
3604 href: this.brand_href ? this.brand_href : '#',
3605 cls: 'navbar-brand',
3613 cfg.cls += ' main-nav';
3621 getHeaderChildContainer : function()
3623 if (this.el.select('.navbar-header').getCount()) {
3624 return this.el.select('.navbar-header',true).first();
3627 return this.getChildContainer();
3631 initEvents : function()
3633 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3635 if (this.autohide) {
3640 Roo.get(document).on('scroll',function(e) {
3641 var ns = Roo.get(document).getScroll().top;
3642 var os = prevScroll;
3646 ft.removeClass('slideDown');
3647 ft.addClass('slideUp');
3650 ft.removeClass('slideUp');
3651 ft.addClass('slideDown');
3672 * @class Roo.bootstrap.NavSidebar
3673 * @extends Roo.bootstrap.Navbar
3674 * Bootstrap Sidebar class
3677 * Create a new Sidebar
3678 * @param {Object} config The config object
3682 Roo.bootstrap.NavSidebar = function(config){
3683 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3686 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3688 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3690 getAutoCreate : function(){
3695 cls: 'sidebar sidebar-nav'
3717 * @class Roo.bootstrap.NavGroup
3718 * @extends Roo.bootstrap.Component
3719 * Bootstrap NavGroup class
3720 * @cfg {String} align (left|right)
3721 * @cfg {Boolean} inverse
3722 * @cfg {String} type (nav|pills|tab) default nav
3723 * @cfg {String} navId - reference Id for navbar.
3727 * Create a new nav group
3728 * @param {Object} config The config object
3731 Roo.bootstrap.NavGroup = function(config){
3732 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3735 Roo.bootstrap.NavGroup.register(this);
3739 * Fires when the active item changes
3740 * @param {Roo.bootstrap.NavGroup} this
3741 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3742 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3749 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3760 getAutoCreate : function()
3762 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3769 if (['tabs','pills'].indexOf(this.type)!==-1) {
3770 cfg.cls += ' nav-' + this.type
3772 if (this.type!=='nav') {
3773 Roo.log('nav type must be nav/tabs/pills')
3775 cfg.cls += ' navbar-nav'
3778 if (this.parent().sidebar) {
3781 cls: 'dashboard-menu sidebar-menu'
3787 if (this.form === true) {
3793 if (this.align === 'right') {
3794 cfg.cls += ' navbar-right';
3796 cfg.cls += ' navbar-left';
3800 if (this.align === 'right') {
3801 cfg.cls += ' navbar-right';
3805 cfg.cls += ' navbar-inverse';
3813 * sets the active Navigation item
3814 * @param {Roo.bootstrap.NavItem} the new current navitem
3816 setActiveItem : function(item)
3819 Roo.each(this.navItems, function(v){
3824 v.setActive(false, true);
3831 item.setActive(true, true);
3832 this.fireEvent('changed', this, item, prev);
3837 * gets the active Navigation item
3838 * @return {Roo.bootstrap.NavItem} the current navitem
3840 getActive : function()
3844 Roo.each(this.navItems, function(v){
3855 indexOfNav : function()
3859 Roo.each(this.navItems, function(v,i){
3870 * adds a Navigation item
3871 * @param {Roo.bootstrap.NavItem} the navitem to add
3873 addItem : function(cfg)
3875 var cn = new Roo.bootstrap.NavItem(cfg);
3877 cn.parentId = this.id;
3878 cn.onRender(this.el, null);
3882 * register a Navigation item
3883 * @param {Roo.bootstrap.NavItem} the navitem to add
3885 register : function(item)
3887 this.navItems.push( item);
3888 item.navId = this.navId;
3893 * clear all the Navigation item
3896 clearAll : function()
3899 this.el.dom.innerHTML = '';
3902 getNavItem: function(tabId)
3905 Roo.each(this.navItems, function(e) {
3906 if (e.tabId == tabId) {
3916 setActiveNext : function()
3918 var i = this.indexOfNav(this.getActive());
3919 if (i > this.navItems.length) {
3922 this.setActiveItem(this.navItems[i+1]);
3924 setActivePrev : function()
3926 var i = this.indexOfNav(this.getActive());
3930 this.setActiveItem(this.navItems[i-1]);
3932 clearWasActive : function(except) {
3933 Roo.each(this.navItems, function(e) {
3934 if (e.tabId != except.tabId && e.was_active) {
3935 e.was_active = false;
3942 getWasActive : function ()
3945 Roo.each(this.navItems, function(e) {
3960 Roo.apply(Roo.bootstrap.NavGroup, {
3964 * register a Navigation Group
3965 * @param {Roo.bootstrap.NavGroup} the navgroup to add
3967 register : function(navgrp)
3969 this.groups[navgrp.navId] = navgrp;
3973 * fetch a Navigation Group based on the navigation ID
3974 * @param {string} the navgroup to add
3975 * @returns {Roo.bootstrap.NavGroup} the navgroup
3977 get: function(navId) {
3978 if (typeof(this.groups[navId]) == 'undefined') {
3980 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3982 return this.groups[navId] ;
3997 * @class Roo.bootstrap.NavItem
3998 * @extends Roo.bootstrap.Component
3999 * Bootstrap Navbar.NavItem class
4000 * @cfg {String} href link to
4001 * @cfg {String} html content of button
4002 * @cfg {String} badge text inside badge
4003 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4004 * @cfg {String} glyphicon name of glyphicon
4005 * @cfg {String} icon name of font awesome icon
4006 * @cfg {Boolean} active Is item active
4007 * @cfg {Boolean} disabled Is item disabled
4009 * @cfg {Boolean} preventDefault (true | false) default false
4010 * @cfg {String} tabId the tab that this item activates.
4011 * @cfg {String} tagtype (a|span) render as a href or span?
4012 * @cfg {Boolean} animateRef (true|false) link to element default false
4015 * Create a new Navbar Item
4016 * @param {Object} config The config object
4018 Roo.bootstrap.NavItem = function(config){
4019 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4024 * The raw click event for the entire grid.
4025 * @param {Roo.EventObject} e
4030 * Fires when the active item active state changes
4031 * @param {Roo.bootstrap.NavItem} this
4032 * @param {boolean} state the new state
4038 * Fires when scroll to element
4039 * @param {Roo.bootstrap.NavItem} this
4040 * @param {Object} options
4041 * @param {Roo.EventObject} e
4049 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4057 preventDefault : false,
4064 getAutoCreate : function(){
4072 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4074 if (this.disabled) {
4075 cfg.cls += ' disabled';
4078 if (this.href || this.html || this.glyphicon || this.icon) {
4082 href : this.href || "#",
4083 html: this.html || ''
4088 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4091 if(this.glyphicon) {
4092 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4097 cfg.cn[0].html += " <span class='caret'></span>";
4101 if (this.badge !== '') {
4103 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4111 initEvents: function()
4113 if (typeof (this.menu) != 'undefined') {
4114 this.menu.parentType = this.xtype;
4115 this.menu.triggerEl = this.el;
4116 this.menu = this.addxtype(Roo.apply({}, this.menu));
4119 this.el.select('a',true).on('click', this.onClick, this);
4121 if(this.tagtype == 'span'){
4122 this.el.select('span',true).on('click', this.onClick, this);
4125 // at this point parent should be available..
4126 this.parent().register(this);
4129 onClick : function(e)
4132 this.preventDefault ||
4139 if (this.disabled) {
4143 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4144 if (tg && tg.transition) {
4145 Roo.log("waiting for the transitionend");
4151 //Roo.log("fire event clicked");
4152 if(this.fireEvent('click', this, e) === false){
4156 if(this.tagtype == 'span'){
4160 //Roo.log(this.href);
4161 var ael = this.el.select('a',true).first();
4164 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4165 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4166 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4167 return; // ignore... - it's a 'hash' to another page.
4171 this.scrollToElement(e);
4175 var p = this.parent();
4177 if (['tabs','pills'].indexOf(p.type)!==-1) {
4178 if (typeof(p.setActiveItem) !== 'undefined') {
4179 p.setActiveItem(this);
4183 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4184 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4185 // remove the collapsed menu expand...
4186 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4190 isActive: function () {
4193 setActive : function(state, fire, is_was_active)
4195 if (this.active && !state && this.navId) {
4196 this.was_active = true;
4197 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4199 nv.clearWasActive(this);
4203 this.active = state;
4206 this.el.removeClass('active');
4207 } else if (!this.el.hasClass('active')) {
4208 this.el.addClass('active');
4211 this.fireEvent('changed', this, state);
4214 // show a panel if it's registered and related..
4216 if (!this.navId || !this.tabId || !state || is_was_active) {
4220 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4224 var pan = tg.getPanelByName(this.tabId);
4228 // if we can not flip to new panel - go back to old nav highlight..
4229 if (false == tg.showPanel(pan)) {
4230 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4232 var onav = nv.getWasActive();
4234 onav.setActive(true, false, true);
4243 // this should not be here...
4244 setDisabled : function(state)
4246 this.disabled = state;
4248 this.el.removeClass('disabled');
4249 } else if (!this.el.hasClass('disabled')) {
4250 this.el.addClass('disabled');
4256 * Fetch the element to display the tooltip on.
4257 * @return {Roo.Element} defaults to this.el
4259 tooltipEl : function()
4261 return this.el.select('' + this.tagtype + '', true).first();
4264 scrollToElement : function(e)
4266 var c = document.body;
4269 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4271 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4272 c = document.documentElement;
4275 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4281 var o = target.calcOffsetsTo(c);
4288 this.fireEvent('scrollto', this, options, e);
4290 Roo.get(c).scrollTo('top', options.value, true);
4303 * <span> icon </span>
4304 * <span> text </span>
4305 * <span>badge </span>
4309 * @class Roo.bootstrap.NavSidebarItem
4310 * @extends Roo.bootstrap.NavItem
4311 * Bootstrap Navbar.NavSidebarItem class
4313 * Create a new Navbar Button
4314 * @param {Object} config The config object
4316 Roo.bootstrap.NavSidebarItem = function(config){
4317 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4322 * The raw click event for the entire grid.
4323 * @param {Roo.EventObject} e
4328 * Fires when the active item active state changes
4329 * @param {Roo.bootstrap.NavSidebarItem} this
4330 * @param {boolean} state the new state
4338 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4341 getAutoCreate : function(){
4346 href : this.href || '#',
4358 html : this.html || ''
4363 cfg.cls += ' active';
4367 if (this.glyphicon || this.icon) {
4368 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4369 a.cn.push({ tag : 'i', cls : c }) ;
4374 if (this.badge !== '') {
4375 a.cn.push({ tag: 'span', cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge });
4379 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4380 a.cls += 'dropdown-toggle treeview' ;
4404 * @class Roo.bootstrap.Row
4405 * @extends Roo.bootstrap.Component
4406 * Bootstrap Row class (contains columns...)
4410 * @param {Object} config The config object
4413 Roo.bootstrap.Row = function(config){
4414 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4417 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4419 getAutoCreate : function(){
4438 * @class Roo.bootstrap.Element
4439 * @extends Roo.bootstrap.Component
4440 * Bootstrap Element class
4441 * @cfg {String} html contents of the element
4442 * @cfg {String} tag tag of the element
4443 * @cfg {String} cls class of the element
4444 * @cfg {Boolean} preventDefault (true|false) default false
4445 * @cfg {Boolean} clickable (true|false) default false
4448 * Create a new Element
4449 * @param {Object} config The config object
4452 Roo.bootstrap.Element = function(config){
4453 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4459 * When a element is chick
4460 * @param {Roo.bootstrap.Element} this
4461 * @param {Roo.EventObject} e
4467 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4472 preventDefault: false,
4475 getAutoCreate : function(){
4486 initEvents: function()
4488 Roo.bootstrap.Element.superclass.initEvents.call(this);
4491 this.el.on('click', this.onClick, this);
4496 onClick : function(e)
4498 if(this.preventDefault){
4502 this.fireEvent('click', this, e);
4505 getValue : function()
4507 return this.el.dom.innerHTML;
4510 setValue : function(value)
4512 this.el.dom.innerHTML = value;
4527 * @class Roo.bootstrap.Pagination
4528 * @extends Roo.bootstrap.Component
4529 * Bootstrap Pagination class
4530 * @cfg {String} size xs | sm | md | lg
4531 * @cfg {Boolean} inverse false | true
4534 * Create a new Pagination
4535 * @param {Object} config The config object
4538 Roo.bootstrap.Pagination = function(config){
4539 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4542 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4548 getAutoCreate : function(){
4554 cfg.cls += ' inverse';
4560 cfg.cls += " " + this.cls;
4578 * @class Roo.bootstrap.PaginationItem
4579 * @extends Roo.bootstrap.Component
4580 * Bootstrap PaginationItem class
4581 * @cfg {String} html text
4582 * @cfg {String} href the link
4583 * @cfg {Boolean} preventDefault (true | false) default true
4584 * @cfg {Boolean} active (true | false) default false
4585 * @cfg {Boolean} disabled default false
4589 * Create a new PaginationItem
4590 * @param {Object} config The config object
4594 Roo.bootstrap.PaginationItem = function(config){
4595 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4600 * The raw click event for the entire grid.
4601 * @param {Roo.EventObject} e
4607 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4611 preventDefault: true,
4616 getAutoCreate : function(){
4622 href : this.href ? this.href : '#',
4623 html : this.html ? this.html : ''
4633 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4637 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4643 initEvents: function() {
4645 this.el.on('click', this.onClick, this);
4648 onClick : function(e)
4650 Roo.log('PaginationItem on click ');
4651 if(this.preventDefault){
4659 this.fireEvent('click', this, e);
4675 * @class Roo.bootstrap.Slider
4676 * @extends Roo.bootstrap.Component
4677 * Bootstrap Slider class
4680 * Create a new Slider
4681 * @param {Object} config The config object
4684 Roo.bootstrap.Slider = function(config){
4685 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4688 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4690 getAutoCreate : function(){
4694 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4698 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4710 * Ext JS Library 1.1.1
4711 * Copyright(c) 2006-2007, Ext JS, LLC.
4713 * Originally Released Under LGPL - original licence link has changed is not relivant.
4716 * <script type="text/javascript">
4721 * @class Roo.grid.ColumnModel
4722 * @extends Roo.util.Observable
4723 * This is the default implementation of a ColumnModel used by the Grid. It defines
4724 * the columns in the grid.
4727 var colModel = new Roo.grid.ColumnModel([
4728 {header: "Ticker", width: 60, sortable: true, locked: true},
4729 {header: "Company Name", width: 150, sortable: true},
4730 {header: "Market Cap.", width: 100, sortable: true},
4731 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4732 {header: "Employees", width: 100, sortable: true, resizable: false}
4737 * The config options listed for this class are options which may appear in each
4738 * individual column definition.
4739 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4741 * @param {Object} config An Array of column config objects. See this class's
4742 * config objects for details.
4744 Roo.grid.ColumnModel = function(config){
4746 * The config passed into the constructor
4748 this.config = config;
4751 // if no id, create one
4752 // if the column does not have a dataIndex mapping,
4753 // map it to the order it is in the config
4754 for(var i = 0, len = config.length; i < len; i++){
4756 if(typeof c.dataIndex == "undefined"){
4759 if(typeof c.renderer == "string"){
4760 c.renderer = Roo.util.Format[c.renderer];
4762 if(typeof c.id == "undefined"){
4765 if(c.editor && c.editor.xtype){
4766 c.editor = Roo.factory(c.editor, Roo.grid);
4768 if(c.editor && c.editor.isFormField){
4769 c.editor = new Roo.grid.GridEditor(c.editor);
4771 this.lookup[c.id] = c;
4775 * The width of columns which have no width specified (defaults to 100)
4778 this.defaultWidth = 100;
4781 * Default sortable of columns which have no sortable specified (defaults to false)
4784 this.defaultSortable = false;
4788 * @event widthchange
4789 * Fires when the width of a column changes.
4790 * @param {ColumnModel} this
4791 * @param {Number} columnIndex The column index
4792 * @param {Number} newWidth The new width
4794 "widthchange": true,
4796 * @event headerchange
4797 * Fires when the text of a header changes.
4798 * @param {ColumnModel} this
4799 * @param {Number} columnIndex The column index
4800 * @param {Number} newText The new header text
4802 "headerchange": true,
4804 * @event hiddenchange
4805 * Fires when a column is hidden or "unhidden".
4806 * @param {ColumnModel} this
4807 * @param {Number} columnIndex The column index
4808 * @param {Boolean} hidden true if hidden, false otherwise
4810 "hiddenchange": true,
4812 * @event columnmoved
4813 * Fires when a column is moved.
4814 * @param {ColumnModel} this
4815 * @param {Number} oldIndex
4816 * @param {Number} newIndex
4818 "columnmoved" : true,
4820 * @event columlockchange
4821 * Fires when a column's locked state is changed
4822 * @param {ColumnModel} this
4823 * @param {Number} colIndex
4824 * @param {Boolean} locked true if locked
4826 "columnlockchange" : true
4828 Roo.grid.ColumnModel.superclass.constructor.call(this);
4830 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4832 * @cfg {String} header The header text to display in the Grid view.
4835 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4836 * {@link Roo.data.Record} definition from which to draw the column's value. If not
4837 * specified, the column's index is used as an index into the Record's data Array.
4840 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4841 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4844 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4845 * Defaults to the value of the {@link #defaultSortable} property.
4846 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4849 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
4852 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
4855 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4858 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4861 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4862 * given the cell's data value. See {@link #setRenderer}. If not specified, the
4863 * default renderer uses the raw data value. If an object is returned (bootstrap only)
4864 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4867 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
4870 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
4873 * @cfg {String} cursor (Optional)
4876 * @cfg {String} tooltip (Optional)
4879 * Returns the id of the column at the specified index.
4880 * @param {Number} index The column index
4881 * @return {String} the id
4883 getColumnId : function(index){
4884 return this.config[index].id;
4888 * Returns the column for a specified id.
4889 * @param {String} id The column id
4890 * @return {Object} the column
4892 getColumnById : function(id){
4893 return this.lookup[id];
4898 * Returns the column for a specified dataIndex.
4899 * @param {String} dataIndex The column dataIndex
4900 * @return {Object|Boolean} the column or false if not found
4902 getColumnByDataIndex: function(dataIndex){
4903 var index = this.findColumnIndex(dataIndex);
4904 return index > -1 ? this.config[index] : false;
4908 * Returns the index for a specified column id.
4909 * @param {String} id The column id
4910 * @return {Number} the index, or -1 if not found
4912 getIndexById : function(id){
4913 for(var i = 0, len = this.config.length; i < len; i++){
4914 if(this.config[i].id == id){
4922 * Returns the index for a specified column dataIndex.
4923 * @param {String} dataIndex The column dataIndex
4924 * @return {Number} the index, or -1 if not found
4927 findColumnIndex : function(dataIndex){
4928 for(var i = 0, len = this.config.length; i < len; i++){
4929 if(this.config[i].dataIndex == dataIndex){
4937 moveColumn : function(oldIndex, newIndex){
4938 var c = this.config[oldIndex];
4939 this.config.splice(oldIndex, 1);
4940 this.config.splice(newIndex, 0, c);
4941 this.dataMap = null;
4942 this.fireEvent("columnmoved", this, oldIndex, newIndex);
4945 isLocked : function(colIndex){
4946 return this.config[colIndex].locked === true;
4949 setLocked : function(colIndex, value, suppressEvent){
4950 if(this.isLocked(colIndex) == value){
4953 this.config[colIndex].locked = value;
4955 this.fireEvent("columnlockchange", this, colIndex, value);
4959 getTotalLockedWidth : function(){
4961 for(var i = 0; i < this.config.length; i++){
4962 if(this.isLocked(i) && !this.isHidden(i)){
4963 this.totalWidth += this.getColumnWidth(i);
4969 getLockedCount : function(){
4970 for(var i = 0, len = this.config.length; i < len; i++){
4971 if(!this.isLocked(i)){
4978 * Returns the number of columns.
4981 getColumnCount : function(visibleOnly){
4982 if(visibleOnly === true){
4984 for(var i = 0, len = this.config.length; i < len; i++){
4985 if(!this.isHidden(i)){
4991 return this.config.length;
4995 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4996 * @param {Function} fn
4997 * @param {Object} scope (optional)
4998 * @return {Array} result
5000 getColumnsBy : function(fn, scope){
5002 for(var i = 0, len = this.config.length; i < len; i++){
5003 var c = this.config[i];
5004 if(fn.call(scope||this, c, i) === true){
5012 * Returns true if the specified column is sortable.
5013 * @param {Number} col The column index
5016 isSortable : function(col){
5017 if(typeof this.config[col].sortable == "undefined"){
5018 return this.defaultSortable;
5020 return this.config[col].sortable;
5024 * Returns the rendering (formatting) function defined for the column.
5025 * @param {Number} col The column index.
5026 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5028 getRenderer : function(col){
5029 if(!this.config[col].renderer){
5030 return Roo.grid.ColumnModel.defaultRenderer;
5032 return this.config[col].renderer;
5036 * Sets the rendering (formatting) function for a column.
5037 * @param {Number} col The column index
5038 * @param {Function} fn The function to use to process the cell's raw data
5039 * to return HTML markup for the grid view. The render function is called with
5040 * the following parameters:<ul>
5041 * <li>Data value.</li>
5042 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5043 * <li>css A CSS style string to apply to the table cell.</li>
5044 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5045 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5046 * <li>Row index</li>
5047 * <li>Column index</li>
5048 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5050 setRenderer : function(col, fn){
5051 this.config[col].renderer = fn;
5055 * Returns the width for the specified column.
5056 * @param {Number} col The column index
5059 getColumnWidth : function(col){
5060 return this.config[col].width * 1 || this.defaultWidth;
5064 * Sets the width for a column.
5065 * @param {Number} col The column index
5066 * @param {Number} width The new width
5068 setColumnWidth : function(col, width, suppressEvent){
5069 this.config[col].width = width;
5070 this.totalWidth = null;
5072 this.fireEvent("widthchange", this, col, width);
5077 * Returns the total width of all columns.
5078 * @param {Boolean} includeHidden True to include hidden column widths
5081 getTotalWidth : function(includeHidden){
5082 if(!this.totalWidth){
5083 this.totalWidth = 0;
5084 for(var i = 0, len = this.config.length; i < len; i++){
5085 if(includeHidden || !this.isHidden(i)){
5086 this.totalWidth += this.getColumnWidth(i);
5090 return this.totalWidth;
5094 * Returns the header for the specified column.
5095 * @param {Number} col The column index
5098 getColumnHeader : function(col){
5099 return this.config[col].header;
5103 * Sets the header for a column.
5104 * @param {Number} col The column index
5105 * @param {String} header The new header
5107 setColumnHeader : function(col, header){
5108 this.config[col].header = header;
5109 this.fireEvent("headerchange", this, col, header);
5113 * Returns the tooltip for the specified column.
5114 * @param {Number} col The column index
5117 getColumnTooltip : function(col){
5118 return this.config[col].tooltip;
5121 * Sets the tooltip for a column.
5122 * @param {Number} col The column index
5123 * @param {String} tooltip The new tooltip
5125 setColumnTooltip : function(col, tooltip){
5126 this.config[col].tooltip = tooltip;
5130 * Returns the dataIndex for the specified column.
5131 * @param {Number} col The column index
5134 getDataIndex : function(col){
5135 return this.config[col].dataIndex;
5139 * Sets the dataIndex for a column.
5140 * @param {Number} col The column index
5141 * @param {Number} dataIndex The new dataIndex
5143 setDataIndex : function(col, dataIndex){
5144 this.config[col].dataIndex = dataIndex;
5150 * Returns true if the cell is editable.
5151 * @param {Number} colIndex The column index
5152 * @param {Number} rowIndex The row index
5155 isCellEditable : function(colIndex, rowIndex){
5156 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5160 * Returns the editor defined for the cell/column.
5161 * return false or null to disable editing.
5162 * @param {Number} colIndex The column index
5163 * @param {Number} rowIndex The row index
5166 getCellEditor : function(colIndex, rowIndex){
5167 return this.config[colIndex].editor;
5171 * Sets if a column is editable.
5172 * @param {Number} col The column index
5173 * @param {Boolean} editable True if the column is editable
5175 setEditable : function(col, editable){
5176 this.config[col].editable = editable;
5181 * Returns true if the column is hidden.
5182 * @param {Number} colIndex The column index
5185 isHidden : function(colIndex){
5186 return this.config[colIndex].hidden;
5191 * Returns true if the column width cannot be changed
5193 isFixed : function(colIndex){
5194 return this.config[colIndex].fixed;
5198 * Returns true if the column can be resized
5201 isResizable : function(colIndex){
5202 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5205 * Sets if a column is hidden.
5206 * @param {Number} colIndex The column index
5207 * @param {Boolean} hidden True if the column is hidden
5209 setHidden : function(colIndex, hidden){
5210 this.config[colIndex].hidden = hidden;
5211 this.totalWidth = null;
5212 this.fireEvent("hiddenchange", this, colIndex, hidden);
5216 * Sets the editor for a column.
5217 * @param {Number} col The column index
5218 * @param {Object} editor The editor object
5220 setEditor : function(col, editor){
5221 this.config[col].editor = editor;
5225 Roo.grid.ColumnModel.defaultRenderer = function(value){
5226 if(typeof value == "string" && value.length < 1){
5232 // Alias for backwards compatibility
5233 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5236 * Ext JS Library 1.1.1
5237 * Copyright(c) 2006-2007, Ext JS, LLC.
5239 * Originally Released Under LGPL - original licence link has changed is not relivant.
5242 * <script type="text/javascript">
5246 * @class Roo.LoadMask
5247 * A simple utility class for generically masking elements while loading data. If the element being masked has
5248 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5249 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5250 * element's UpdateManager load indicator and will be destroyed after the initial load.
5252 * Create a new LoadMask
5253 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5254 * @param {Object} config The config object
5256 Roo.LoadMask = function(el, config){
5257 this.el = Roo.get(el);
5258 Roo.apply(this, config);
5260 this.store.on('beforeload', this.onBeforeLoad, this);
5261 this.store.on('load', this.onLoad, this);
5262 this.store.on('loadexception', this.onLoadException, this);
5263 this.removeMask = false;
5265 var um = this.el.getUpdateManager();
5266 um.showLoadIndicator = false; // disable the default indicator
5267 um.on('beforeupdate', this.onBeforeLoad, this);
5268 um.on('update', this.onLoad, this);
5269 um.on('failure', this.onLoad, this);
5270 this.removeMask = true;
5274 Roo.LoadMask.prototype = {
5276 * @cfg {Boolean} removeMask
5277 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5278 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5282 * The text to display in a centered loading message box (defaults to 'Loading...')
5286 * @cfg {String} msgCls
5287 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5289 msgCls : 'x-mask-loading',
5292 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5298 * Disables the mask to prevent it from being displayed
5300 disable : function(){
5301 this.disabled = true;
5305 * Enables the mask so that it can be displayed
5307 enable : function(){
5308 this.disabled = false;
5311 onLoadException : function()
5315 if (typeof(arguments[3]) != 'undefined') {
5316 Roo.MessageBox.alert("Error loading",arguments[3]);
5320 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5321 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5330 this.el.unmask(this.removeMask);
5335 this.el.unmask(this.removeMask);
5339 onBeforeLoad : function(){
5341 this.el.mask(this.msg, this.msgCls);
5346 destroy : function(){
5348 this.store.un('beforeload', this.onBeforeLoad, this);
5349 this.store.un('load', this.onLoad, this);
5350 this.store.un('loadexception', this.onLoadException, this);
5352 var um = this.el.getUpdateManager();
5353 um.un('beforeupdate', this.onBeforeLoad, this);
5354 um.un('update', this.onLoad, this);
5355 um.un('failure', this.onLoad, this);
5366 * @class Roo.bootstrap.Table
5367 * @extends Roo.bootstrap.Component
5368 * Bootstrap Table class
5369 * @cfg {String} cls table class
5370 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5371 * @cfg {String} bgcolor Specifies the background color for a table
5372 * @cfg {Number} border Specifies whether the table cells should have borders or not
5373 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5374 * @cfg {Number} cellspacing Specifies the space between cells
5375 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5376 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5377 * @cfg {String} sortable Specifies that the table should be sortable
5378 * @cfg {String} summary Specifies a summary of the content of a table
5379 * @cfg {Number} width Specifies the width of a table
5380 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5382 * @cfg {boolean} striped Should the rows be alternative striped
5383 * @cfg {boolean} bordered Add borders to the table
5384 * @cfg {boolean} hover Add hover highlighting
5385 * @cfg {boolean} condensed Format condensed
5386 * @cfg {boolean} responsive Format condensed
5387 * @cfg {Boolean} loadMask (true|false) default false
5388 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5389 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5390 * @cfg {Boolean} rowSelection (true|false) default false
5391 * @cfg {Boolean} cellSelection (true|false) default false
5392 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5396 * Create a new Table
5397 * @param {Object} config The config object
5400 Roo.bootstrap.Table = function(config){
5401 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5404 this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5405 this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5406 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5407 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5411 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5412 this.sm = this.selModel;
5413 this.sm.xmodule = this.xmodule || false;
5415 if (this.cm && typeof(this.cm.config) == 'undefined') {
5416 this.colModel = new Roo.grid.ColumnModel(this.cm);
5417 this.cm = this.colModel;
5418 this.cm.xmodule = this.xmodule || false;
5421 this.store= Roo.factory(this.store, Roo.data);
5422 this.ds = this.store;
5423 this.ds.xmodule = this.xmodule || false;
5426 if (this.footer && this.store) {
5427 this.footer.dataSource = this.ds;
5428 this.footer = Roo.factory(this.footer);
5435 * Fires when a cell is clicked
5436 * @param {Roo.bootstrap.Table} this
5437 * @param {Roo.Element} el
5438 * @param {Number} rowIndex
5439 * @param {Number} columnIndex
5440 * @param {Roo.EventObject} e
5444 * @event celldblclick
5445 * Fires when a cell is double clicked
5446 * @param {Roo.bootstrap.Table} this
5447 * @param {Roo.Element} el
5448 * @param {Number} rowIndex
5449 * @param {Number} columnIndex
5450 * @param {Roo.EventObject} e
5452 "celldblclick" : true,
5455 * Fires when a row is clicked
5456 * @param {Roo.bootstrap.Table} this
5457 * @param {Roo.Element} el
5458 * @param {Number} rowIndex
5459 * @param {Roo.EventObject} e
5463 * @event rowdblclick
5464 * Fires when a row is double clicked
5465 * @param {Roo.bootstrap.Table} this
5466 * @param {Roo.Element} el
5467 * @param {Number} rowIndex
5468 * @param {Roo.EventObject} e
5470 "rowdblclick" : true,
5473 * Fires when a mouseover occur
5474 * @param {Roo.bootstrap.Table} this
5475 * @param {Roo.Element} el
5476 * @param {Number} rowIndex
5477 * @param {Number} columnIndex
5478 * @param {Roo.EventObject} e
5483 * Fires when a mouseout occur
5484 * @param {Roo.bootstrap.Table} this
5485 * @param {Roo.Element} el
5486 * @param {Number} rowIndex
5487 * @param {Number} columnIndex
5488 * @param {Roo.EventObject} e
5493 * Fires when a row is rendered, so you can change add a style to it.
5494 * @param {Roo.bootstrap.Table} this
5495 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5499 * @event rowsrendered
5500 * Fires when all the rows have been rendered
5501 * @param {Roo.bootstrap.Table} this
5503 'rowsrendered' : true
5508 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5533 rowSelection : false,
5534 cellSelection : false,
5537 // Roo.Element - the tbody
5540 getAutoCreate : function(){
5541 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5550 cfg.cls += ' table-striped';
5554 cfg.cls += ' table-hover';
5556 if (this.bordered) {
5557 cfg.cls += ' table-bordered';
5559 if (this.condensed) {
5560 cfg.cls += ' table-condensed';
5562 if (this.responsive) {
5563 cfg.cls += ' table-responsive';
5567 cfg.cls+= ' ' +this.cls;
5570 // this lot should be simplifed...
5573 cfg.align=this.align;
5576 cfg.bgcolor=this.bgcolor;
5579 cfg.border=this.border;
5581 if (this.cellpadding) {
5582 cfg.cellpadding=this.cellpadding;
5584 if (this.cellspacing) {
5585 cfg.cellspacing=this.cellspacing;
5588 cfg.frame=this.frame;
5591 cfg.rules=this.rules;
5593 if (this.sortable) {
5594 cfg.sortable=this.sortable;
5597 cfg.summary=this.summary;
5600 cfg.width=this.width;
5603 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5606 if(this.store || this.cm){
5607 if(this.headerShow){
5608 cfg.cn.push(this.renderHeader());
5611 cfg.cn.push(this.renderBody());
5613 if(this.footerShow){
5614 cfg.cn.push(this.renderFooter());
5617 cfg.cls+= ' TableGrid';
5620 return { cn : [ cfg ] };
5623 initEvents : function()
5625 if(!this.store || !this.cm){
5629 //Roo.log('initEvents with ds!!!!');
5631 this.mainBody = this.el.select('tbody', true).first();
5636 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5637 e.on('click', _this.sort, _this);
5640 this.el.on("click", this.onClick, this);
5641 this.el.on("dblclick", this.onDblClick, this);
5643 // why is this done????? = it breaks dialogs??
5644 //this.parent().el.setStyle('position', 'relative');
5648 this.footer.parentId = this.id;
5649 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5652 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5654 this.store.on('load', this.onLoad, this);
5655 this.store.on('beforeload', this.onBeforeLoad, this);
5656 this.store.on('update', this.onUpdate, this);
5657 this.store.on('add', this.onAdd, this);
5661 onMouseover : function(e, el)
5663 var cell = Roo.get(el);
5669 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5670 cell = cell.findParent('td', false, true);
5673 var row = cell.findParent('tr', false, true);
5674 var cellIndex = cell.dom.cellIndex;
5675 var rowIndex = row.dom.rowIndex - 1; // start from 0
5677 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5681 onMouseout : function(e, el)
5683 var cell = Roo.get(el);
5689 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5690 cell = cell.findParent('td', false, true);
5693 var row = cell.findParent('tr', false, true);
5694 var cellIndex = cell.dom.cellIndex;
5695 var rowIndex = row.dom.rowIndex - 1; // start from 0
5697 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5701 onClick : function(e, el)
5703 var cell = Roo.get(el);
5705 if(!cell || (!this.cellSelection && !this.rowSelection)){
5709 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5710 cell = cell.findParent('td', false, true);
5713 if(!cell || typeof(cell) == 'undefined'){
5717 var row = cell.findParent('tr', false, true);
5719 if(!row || typeof(row) == 'undefined'){
5723 var cellIndex = cell.dom.cellIndex;
5724 var rowIndex = this.getRowIndex(row);
5726 // why??? - should these not be based on SelectionModel?
5727 if(this.cellSelection){
5728 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5731 if(this.rowSelection){
5732 this.fireEvent('rowclick', this, row, rowIndex, e);
5738 onDblClick : function(e,el)
5740 var cell = Roo.get(el);
5742 if(!cell || (!this.CellSelection && !this.RowSelection)){
5746 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5747 cell = cell.findParent('td', false, true);
5750 if(!cell || typeof(cell) == 'undefined'){
5754 var row = cell.findParent('tr', false, true);
5756 if(!row || typeof(row) == 'undefined'){
5760 var cellIndex = cell.dom.cellIndex;
5761 var rowIndex = this.getRowIndex(row);
5763 if(this.CellSelection){
5764 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5767 if(this.RowSelection){
5768 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5772 sort : function(e,el)
5774 var col = Roo.get(el);
5776 if(!col.hasClass('sortable')){
5780 var sort = col.attr('sort');
5783 if(col.hasClass('glyphicon-arrow-up')){
5787 this.store.sortInfo = {field : sort, direction : dir};
5790 Roo.log("calling footer first");
5791 this.footer.onClick('first');
5794 this.store.load({ params : { start : 0 } });
5798 renderHeader : function()
5807 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5809 var config = cm.config[i];
5814 html: cm.getColumnHeader(i)
5819 if(typeof(config.lgHeader) != 'undefined'){
5820 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5823 if(typeof(config.mdHeader) != 'undefined'){
5824 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5827 if(typeof(config.smHeader) != 'undefined'){
5828 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5831 if(typeof(config.xsHeader) != 'undefined'){
5832 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5839 if(typeof(config.tooltip) != 'undefined'){
5840 c.tooltip = config.tooltip;
5843 if(typeof(config.colspan) != 'undefined'){
5844 c.colspan = config.colspan;
5847 if(typeof(config.hidden) != 'undefined' && config.hidden){
5848 c.style += ' display:none;';
5851 if(typeof(config.dataIndex) != 'undefined'){
5852 c.sort = config.dataIndex;
5855 if(typeof(config.sortable) != 'undefined' && config.sortable){
5859 if(typeof(config.align) != 'undefined' && config.align.length){
5860 c.style += ' text-align:' + config.align + ';';
5863 if(typeof(config.width) != 'undefined'){
5864 c.style += ' width:' + config.width + 'px;';
5867 if(typeof(config.cls) != 'undefined'){
5868 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5877 renderBody : function()
5887 colspan : this.cm.getColumnCount()
5897 renderFooter : function()
5907 colspan : this.cm.getColumnCount()
5921 Roo.log('ds onload');
5926 var ds = this.store;
5928 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5929 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5931 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5932 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5935 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5936 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5940 var tbody = this.mainBody;
5942 if(ds.getCount() > 0){
5943 ds.data.each(function(d,rowIndex){
5944 var row = this.renderRow(cm, ds, rowIndex);
5946 tbody.createChild(row);
5950 if(row.cellObjects.length){
5951 Roo.each(row.cellObjects, function(r){
5952 _this.renderCellObject(r);
5959 Roo.each(this.el.select('tbody td', true).elements, function(e){
5960 e.on('mouseover', _this.onMouseover, _this);
5963 Roo.each(this.el.select('tbody td', true).elements, function(e){
5964 e.on('mouseout', _this.onMouseout, _this);
5966 this.fireEvent('rowsrendered', this);
5967 //if(this.loadMask){
5968 // this.maskEl.hide();
5973 onUpdate : function(ds,record)
5975 this.refreshRow(record);
5978 onRemove : function(ds, record, index, isUpdate){
5979 if(isUpdate !== true){
5980 this.fireEvent("beforerowremoved", this, index, record);
5982 var bt = this.mainBody.dom;
5984 var rows = this.el.select('tbody > tr', true).elements;
5986 if(typeof(rows[index]) != 'undefined'){
5987 bt.removeChild(rows[index].dom);
5990 // if(bt.rows[index]){
5991 // bt.removeChild(bt.rows[index]);
5994 if(isUpdate !== true){
5995 //this.stripeRows(index);
5996 //this.syncRowHeights(index, index);
5998 this.fireEvent("rowremoved", this, index, record);
6002 onAdd : function(ds, records, rowIndex)
6004 //Roo.log('on Add called');
6005 // - note this does not handle multiple adding very well..
6006 var bt = this.mainBody.dom;
6007 for (var i =0 ; i < records.length;i++) {
6008 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6009 //Roo.log(records[i]);
6010 //Roo.log(this.store.getAt(rowIndex+i));
6011 this.insertRow(this.store, rowIndex + i, false);
6018 refreshRow : function(record){
6019 var ds = this.store, index;
6020 if(typeof record == 'number'){
6022 record = ds.getAt(index);
6024 index = ds.indexOf(record);
6026 this.insertRow(ds, index, true);
6027 this.onRemove(ds, record, index+1, true);
6028 //this.syncRowHeights(index, index);
6030 this.fireEvent("rowupdated", this, index, record);
6033 insertRow : function(dm, rowIndex, isUpdate){
6036 this.fireEvent("beforerowsinserted", this, rowIndex);
6038 //var s = this.getScrollState();
6039 var row = this.renderRow(this.cm, this.store, rowIndex);
6040 // insert before rowIndex..
6041 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6045 if(row.cellObjects.length){
6046 Roo.each(row.cellObjects, function(r){
6047 _this.renderCellObject(r);
6052 this.fireEvent("rowsinserted", this, rowIndex);
6053 //this.syncRowHeights(firstRow, lastRow);
6054 //this.stripeRows(firstRow);
6061 getRowDom : function(rowIndex)
6063 var rows = this.el.select('tbody > tr', true).elements;
6065 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6068 // returns the object tree for a tr..
6071 renderRow : function(cm, ds, rowIndex)
6074 var d = ds.getAt(rowIndex);
6081 var cellObjects = [];
6083 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6084 var config = cm.config[i];
6086 var renderer = cm.getRenderer(i);
6090 if(typeof(renderer) !== 'undefined'){
6091 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6093 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6094 // and are rendered into the cells after the row is rendered - using the id for the element.
6096 if(typeof(value) === 'object'){
6106 rowIndex : rowIndex,
6111 this.fireEvent('rowclass', this, rowcfg);
6115 cls : rowcfg.rowClass,
6117 html: (typeof(value) === 'object') ? '' : value
6124 if(typeof(config.colspan) != 'undefined'){
6125 td.colspan = config.colspan;
6128 if(typeof(config.hidden) != 'undefined' && config.hidden){
6129 td.style += ' display:none;';
6132 if(typeof(config.align) != 'undefined' && config.align.length){
6133 td.style += ' text-align:' + config.align + ';';
6136 if(typeof(config.width) != 'undefined'){
6137 td.style += ' width:' + config.width + 'px;';
6140 if(typeof(config.cursor) != 'undefined'){
6141 td.style += ' cursor:' + config.cursor + ';';
6144 if(typeof(config.cls) != 'undefined'){
6145 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6152 row.cellObjects = cellObjects;
6160 onBeforeLoad : function()
6162 //Roo.log('ds onBeforeLoad');
6166 //if(this.loadMask){
6167 // this.maskEl.show();
6175 this.el.select('tbody', true).first().dom.innerHTML = '';
6178 * Show or hide a row.
6179 * @param {Number} rowIndex to show or hide
6180 * @param {Boolean} state hide
6182 setRowVisibility : function(rowIndex, state)
6184 var bt = this.mainBody.dom;
6186 var rows = this.el.select('tbody > tr', true).elements;
6188 if(typeof(rows[rowIndex]) == 'undefined'){
6191 rows[rowIndex].dom.style.display = state ? '' : 'none';
6195 getSelectionModel : function(){
6197 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6199 return this.selModel;
6202 * Render the Roo.bootstrap object from renderder
6204 renderCellObject : function(r)
6208 var t = r.cfg.render(r.container);
6211 Roo.each(r.cfg.cn, function(c){
6213 container: t.getChildContainer(),
6216 _this.renderCellObject(child);
6221 getRowIndex : function(row)
6225 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6248 * @class Roo.bootstrap.TableCell
6249 * @extends Roo.bootstrap.Component
6250 * Bootstrap TableCell class
6251 * @cfg {String} html cell contain text
6252 * @cfg {String} cls cell class
6253 * @cfg {String} tag cell tag (td|th) default td
6254 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6255 * @cfg {String} align Aligns the content in a cell
6256 * @cfg {String} axis Categorizes cells
6257 * @cfg {String} bgcolor Specifies the background color of a cell
6258 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6259 * @cfg {Number} colspan Specifies the number of columns a cell should span
6260 * @cfg {String} headers Specifies one or more header cells a cell is related to
6261 * @cfg {Number} height Sets the height of a cell
6262 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6263 * @cfg {Number} rowspan Sets the number of rows a cell should span
6264 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6265 * @cfg {String} valign Vertical aligns the content in a cell
6266 * @cfg {Number} width Specifies the width of a cell
6269 * Create a new TableCell
6270 * @param {Object} config The config object
6273 Roo.bootstrap.TableCell = function(config){
6274 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6277 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6297 getAutoCreate : function(){
6298 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6318 cfg.align=this.align
6324 cfg.bgcolor=this.bgcolor
6327 cfg.charoff=this.charoff
6330 cfg.colspan=this.colspan
6333 cfg.headers=this.headers
6336 cfg.height=this.height
6339 cfg.nowrap=this.nowrap
6342 cfg.rowspan=this.rowspan
6345 cfg.scope=this.scope
6348 cfg.valign=this.valign
6351 cfg.width=this.width
6370 * @class Roo.bootstrap.TableRow
6371 * @extends Roo.bootstrap.Component
6372 * Bootstrap TableRow class
6373 * @cfg {String} cls row class
6374 * @cfg {String} align Aligns the content in a table row
6375 * @cfg {String} bgcolor Specifies a background color for a table row
6376 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6377 * @cfg {String} valign Vertical aligns the content in a table row
6380 * Create a new TableRow
6381 * @param {Object} config The config object
6384 Roo.bootstrap.TableRow = function(config){
6385 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6388 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6396 getAutoCreate : function(){
6397 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6407 cfg.align = this.align;
6410 cfg.bgcolor = this.bgcolor;
6413 cfg.charoff = this.charoff;
6416 cfg.valign = this.valign;
6434 * @class Roo.bootstrap.TableBody
6435 * @extends Roo.bootstrap.Component
6436 * Bootstrap TableBody class
6437 * @cfg {String} cls element class
6438 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6439 * @cfg {String} align Aligns the content inside the element
6440 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6441 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6444 * Create a new TableBody
6445 * @param {Object} config The config object
6448 Roo.bootstrap.TableBody = function(config){
6449 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6452 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6460 getAutoCreate : function(){
6461 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6475 cfg.align = this.align;
6478 cfg.charoff = this.charoff;
6481 cfg.valign = this.valign;
6488 // initEvents : function()
6495 // this.store = Roo.factory(this.store, Roo.data);
6496 // this.store.on('load', this.onLoad, this);
6498 // this.store.load();
6502 // onLoad: function ()
6504 // this.fireEvent('load', this);
6514 * Ext JS Library 1.1.1
6515 * Copyright(c) 2006-2007, Ext JS, LLC.
6517 * Originally Released Under LGPL - original licence link has changed is not relivant.
6520 * <script type="text/javascript">
6523 // as we use this in bootstrap.
6524 Roo.namespace('Roo.form');
6526 * @class Roo.form.Action
6527 * Internal Class used to handle form actions
6529 * @param {Roo.form.BasicForm} el The form element or its id
6530 * @param {Object} config Configuration options
6535 // define the action interface
6536 Roo.form.Action = function(form, options){
6538 this.options = options || {};
6541 * Client Validation Failed
6544 Roo.form.Action.CLIENT_INVALID = 'client';
6546 * Server Validation Failed
6549 Roo.form.Action.SERVER_INVALID = 'server';
6551 * Connect to Server Failed
6554 Roo.form.Action.CONNECT_FAILURE = 'connect';
6556 * Reading Data from Server Failed
6559 Roo.form.Action.LOAD_FAILURE = 'load';
6561 Roo.form.Action.prototype = {
6563 failureType : undefined,
6564 response : undefined,
6568 run : function(options){
6573 success : function(response){
6578 handleResponse : function(response){
6582 // default connection failure
6583 failure : function(response){
6585 this.response = response;
6586 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6587 this.form.afterAction(this, false);
6590 processResponse : function(response){
6591 this.response = response;
6592 if(!response.responseText){
6595 this.result = this.handleResponse(response);
6599 // utility functions used internally
6600 getUrl : function(appendParams){
6601 var url = this.options.url || this.form.url || this.form.el.dom.action;
6603 var p = this.getParams();
6605 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6611 getMethod : function(){
6612 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6615 getParams : function(){
6616 var bp = this.form.baseParams;
6617 var p = this.options.params;
6619 if(typeof p == "object"){
6620 p = Roo.urlEncode(Roo.applyIf(p, bp));
6621 }else if(typeof p == 'string' && bp){
6622 p += '&' + Roo.urlEncode(bp);
6625 p = Roo.urlEncode(bp);
6630 createCallback : function(){
6632 success: this.success,
6633 failure: this.failure,
6635 timeout: (this.form.timeout*1000),
6636 upload: this.form.fileUpload ? this.success : undefined
6641 Roo.form.Action.Submit = function(form, options){
6642 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6645 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6648 haveProgress : false,
6649 uploadComplete : false,
6651 // uploadProgress indicator.
6652 uploadProgress : function()
6654 if (!this.form.progressUrl) {
6658 if (!this.haveProgress) {
6659 Roo.MessageBox.progress("Uploading", "Uploading");
6661 if (this.uploadComplete) {
6662 Roo.MessageBox.hide();
6666 this.haveProgress = true;
6668 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6670 var c = new Roo.data.Connection();
6672 url : this.form.progressUrl,
6677 success : function(req){
6678 //console.log(data);
6682 rdata = Roo.decode(req.responseText)
6684 Roo.log("Invalid data from server..");
6688 if (!rdata || !rdata.success) {
6690 Roo.MessageBox.alert(Roo.encode(rdata));
6693 var data = rdata.data;
6695 if (this.uploadComplete) {
6696 Roo.MessageBox.hide();
6701 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6702 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6705 this.uploadProgress.defer(2000,this);
6708 failure: function(data) {
6709 Roo.log('progress url failed ');
6720 // run get Values on the form, so it syncs any secondary forms.
6721 this.form.getValues();
6723 var o = this.options;
6724 var method = this.getMethod();
6725 var isPost = method == 'POST';
6726 if(o.clientValidation === false || this.form.isValid()){
6728 if (this.form.progressUrl) {
6729 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6730 (new Date() * 1) + '' + Math.random());
6735 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6736 form:this.form.el.dom,
6737 url:this.getUrl(!isPost),
6739 params:isPost ? this.getParams() : null,
6740 isUpload: this.form.fileUpload
6743 this.uploadProgress();
6745 }else if (o.clientValidation !== false){ // client validation failed
6746 this.failureType = Roo.form.Action.CLIENT_INVALID;
6747 this.form.afterAction(this, false);
6751 success : function(response)
6753 this.uploadComplete= true;
6754 if (this.haveProgress) {
6755 Roo.MessageBox.hide();
6759 var result = this.processResponse(response);
6760 if(result === true || result.success){
6761 this.form.afterAction(this, true);
6765 this.form.markInvalid(result.errors);
6766 this.failureType = Roo.form.Action.SERVER_INVALID;
6768 this.form.afterAction(this, false);
6770 failure : function(response)
6772 this.uploadComplete= true;
6773 if (this.haveProgress) {
6774 Roo.MessageBox.hide();
6777 this.response = response;
6778 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6779 this.form.afterAction(this, false);
6782 handleResponse : function(response){
6783 if(this.form.errorReader){
6784 var rs = this.form.errorReader.read(response);
6787 for(var i = 0, len = rs.records.length; i < len; i++) {
6788 var r = rs.records[i];
6792 if(errors.length < 1){
6796 success : rs.success,
6802 ret = Roo.decode(response.responseText);
6806 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6816 Roo.form.Action.Load = function(form, options){
6817 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6818 this.reader = this.form.reader;
6821 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6826 Roo.Ajax.request(Roo.apply(
6827 this.createCallback(), {
6828 method:this.getMethod(),
6829 url:this.getUrl(false),
6830 params:this.getParams()
6834 success : function(response){
6836 var result = this.processResponse(response);
6837 if(result === true || !result.success || !result.data){
6838 this.failureType = Roo.form.Action.LOAD_FAILURE;
6839 this.form.afterAction(this, false);
6842 this.form.clearInvalid();
6843 this.form.setValues(result.data);
6844 this.form.afterAction(this, true);
6847 handleResponse : function(response){
6848 if(this.form.reader){
6849 var rs = this.form.reader.read(response);
6850 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6852 success : rs.success,
6856 return Roo.decode(response.responseText);
6860 Roo.form.Action.ACTION_TYPES = {
6861 'load' : Roo.form.Action.Load,
6862 'submit' : Roo.form.Action.Submit
6871 * @class Roo.bootstrap.Form
6872 * @extends Roo.bootstrap.Component
6873 * Bootstrap Form class
6874 * @cfg {String} method GET | POST (default POST)
6875 * @cfg {String} labelAlign top | left (default top)
6876 * @cfg {String} align left | right - for navbars
6877 * @cfg {Boolean} loadMask load mask when submit (default true)
6882 * @param {Object} config The config object
6886 Roo.bootstrap.Form = function(config){
6887 Roo.bootstrap.Form.superclass.constructor.call(this, config);
6890 * @event clientvalidation
6891 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6892 * @param {Form} this
6893 * @param {Boolean} valid true if the form has passed client-side validation
6895 clientvalidation: true,
6897 * @event beforeaction
6898 * Fires before any action is performed. Return false to cancel the action.
6899 * @param {Form} this
6900 * @param {Action} action The action to be performed
6904 * @event actionfailed
6905 * Fires when an action fails.
6906 * @param {Form} this
6907 * @param {Action} action The action that failed
6909 actionfailed : true,
6911 * @event actioncomplete
6912 * Fires when an action is completed.
6913 * @param {Form} this
6914 * @param {Action} action The action that completed
6916 actioncomplete : true
6921 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
6924 * @cfg {String} method
6925 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6930 * The URL to use for form actions if one isn't supplied in the action options.
6933 * @cfg {Boolean} fileUpload
6934 * Set to true if this form is a file upload.
6938 * @cfg {Object} baseParams
6939 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6943 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6947 * @cfg {Sting} align (left|right) for navbar forms
6952 activeAction : null,
6955 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6956 * element by passing it or its id or mask the form itself by passing in true.
6959 waitMsgTarget : false,
6963 getAutoCreate : function(){
6967 method : this.method || 'POST',
6968 id : this.id || Roo.id(),
6971 if (this.parent().xtype.match(/^Nav/)) {
6972 cfg.cls = 'navbar-form navbar-' + this.align;
6976 if (this.labelAlign == 'left' ) {
6977 cfg.cls += ' form-horizontal';
6983 initEvents : function()
6985 this.el.on('submit', this.onSubmit, this);
6986 // this was added as random key presses on the form where triggering form submit.
6987 this.el.on('keypress', function(e) {
6988 if (e.getCharCode() != 13) {
6991 // we might need to allow it for textareas.. and some other items.
6992 // check e.getTarget().
6994 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6998 Roo.log("keypress blocked");
7006 onSubmit : function(e){
7011 * Returns true if client-side validation on the form is successful.
7014 isValid : function(){
7015 var items = this.getItems();
7017 items.each(function(f){
7026 * Returns true if any fields in this form have changed since their original load.
7029 isDirty : function(){
7031 var items = this.getItems();
7032 items.each(function(f){
7042 * Performs a predefined action (submit or load) or custom actions you define on this form.
7043 * @param {String} actionName The name of the action type
7044 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7045 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7046 * accept other config options):
7048 Property Type Description
7049 ---------------- --------------- ----------------------------------------------------------------------------------
7050 url String The url for the action (defaults to the form's url)
7051 method String The form method to use (defaults to the form's method, or POST if not defined)
7052 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7053 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7054 validate the form on the client (defaults to false)
7056 * @return {BasicForm} this
7058 doAction : function(action, options){
7059 if(typeof action == 'string'){
7060 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7062 if(this.fireEvent('beforeaction', this, action) !== false){
7063 this.beforeAction(action);
7064 action.run.defer(100, action);
7070 beforeAction : function(action){
7071 var o = action.options;
7074 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7076 // not really supported yet.. ??
7078 //if(this.waitMsgTarget === true){
7079 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7080 //}else if(this.waitMsgTarget){
7081 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7082 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7084 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7090 afterAction : function(action, success){
7091 this.activeAction = null;
7092 var o = action.options;
7094 //if(this.waitMsgTarget === true){
7096 //}else if(this.waitMsgTarget){
7097 // this.waitMsgTarget.unmask();
7099 // Roo.MessageBox.updateProgress(1);
7100 // Roo.MessageBox.hide();
7107 Roo.callback(o.success, o.scope, [this, action]);
7108 this.fireEvent('actioncomplete', this, action);
7112 // failure condition..
7113 // we have a scenario where updates need confirming.
7114 // eg. if a locking scenario exists..
7115 // we look for { errors : { needs_confirm : true }} in the response.
7117 (typeof(action.result) != 'undefined') &&
7118 (typeof(action.result.errors) != 'undefined') &&
7119 (typeof(action.result.errors.needs_confirm) != 'undefined')
7122 Roo.log("not supported yet");
7125 Roo.MessageBox.confirm(
7126 "Change requires confirmation",
7127 action.result.errorMsg,
7132 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7142 Roo.callback(o.failure, o.scope, [this, action]);
7143 // show an error message if no failed handler is set..
7144 if (!this.hasListener('actionfailed')) {
7145 Roo.log("need to add dialog support");
7147 Roo.MessageBox.alert("Error",
7148 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7149 action.result.errorMsg :
7150 "Saving Failed, please check your entries or try again"
7155 this.fireEvent('actionfailed', this, action);
7160 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7161 * @param {String} id The value to search for
7164 findField : function(id){
7165 var items = this.getItems();
7166 var field = items.get(id);
7168 items.each(function(f){
7169 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7176 return field || null;
7179 * Mark fields in this form invalid in bulk.
7180 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7181 * @return {BasicForm} this
7183 markInvalid : function(errors){
7184 if(errors instanceof Array){
7185 for(var i = 0, len = errors.length; i < len; i++){
7186 var fieldError = errors[i];
7187 var f = this.findField(fieldError.id);
7189 f.markInvalid(fieldError.msg);
7195 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7196 field.markInvalid(errors[id]);
7200 //Roo.each(this.childForms || [], function (f) {
7201 // f.markInvalid(errors);
7208 * Set values for fields in this form in bulk.
7209 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7210 * @return {BasicForm} this
7212 setValues : function(values){
7213 if(values instanceof Array){ // array of objects
7214 for(var i = 0, len = values.length; i < len; i++){
7216 var f = this.findField(v.id);
7218 f.setValue(v.value);
7219 if(this.trackResetOnLoad){
7220 f.originalValue = f.getValue();
7224 }else{ // object hash
7227 if(typeof values[id] != 'function' && (field = this.findField(id))){
7229 if (field.setFromData &&
7231 field.displayField &&
7232 // combos' with local stores can
7233 // be queried via setValue()
7234 // to set their value..
7235 (field.store && !field.store.isLocal)
7239 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7240 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7241 field.setFromData(sd);
7244 field.setValue(values[id]);
7248 if(this.trackResetOnLoad){
7249 field.originalValue = field.getValue();
7255 //Roo.each(this.childForms || [], function (f) {
7256 // f.setValues(values);
7263 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7264 * they are returned as an array.
7265 * @param {Boolean} asString
7268 getValues : function(asString){
7269 //if (this.childForms) {
7270 // copy values from the child forms
7271 // Roo.each(this.childForms, function (f) {
7272 // this.setValues(f.getValues());
7278 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7279 if(asString === true){
7282 return Roo.urlDecode(fs);
7286 * Returns the fields in this form as an object with key/value pairs.
7287 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7290 getFieldValues : function(with_hidden)
7292 var items = this.getItems();
7294 items.each(function(f){
7298 var v = f.getValue();
7299 if (f.inputType =='radio') {
7300 if (typeof(ret[f.getName()]) == 'undefined') {
7301 ret[f.getName()] = ''; // empty..
7304 if (!f.el.dom.checked) {
7312 // not sure if this supported any more..
7313 if ((typeof(v) == 'object') && f.getRawValue) {
7314 v = f.getRawValue() ; // dates..
7316 // combo boxes where name != hiddenName...
7317 if (f.name != f.getName()) {
7318 ret[f.name] = f.getRawValue();
7320 ret[f.getName()] = v;
7327 * Clears all invalid messages in this form.
7328 * @return {BasicForm} this
7330 clearInvalid : function(){
7331 var items = this.getItems();
7333 items.each(function(f){
7344 * @return {BasicForm} this
7347 var items = this.getItems();
7348 items.each(function(f){
7352 Roo.each(this.childForms || [], function (f) {
7359 getItems : function()
7361 var r=new Roo.util.MixedCollection(false, function(o){
7362 return o.id || (o.id = Roo.id());
7364 var iter = function(el) {
7371 Roo.each(el.items,function(e) {
7391 * Ext JS Library 1.1.1
7392 * Copyright(c) 2006-2007, Ext JS, LLC.
7394 * Originally Released Under LGPL - original licence link has changed is not relivant.
7397 * <script type="text/javascript">
7400 * @class Roo.form.VTypes
7401 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7404 Roo.form.VTypes = function(){
7405 // closure these in so they are only created once.
7406 var alpha = /^[a-zA-Z_]+$/;
7407 var alphanum = /^[a-zA-Z0-9_]+$/;
7408 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7409 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7411 // All these messages and functions are configurable
7414 * The function used to validate email addresses
7415 * @param {String} value The email address
7417 'email' : function(v){
7418 return email.test(v);
7421 * The error text to display when the email validation function returns false
7424 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7426 * The keystroke filter mask to be applied on email input
7429 'emailMask' : /[a-z0-9_\.\-@]/i,
7432 * The function used to validate URLs
7433 * @param {String} value The URL
7435 'url' : function(v){
7439 * The error text to display when the url validation function returns false
7442 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7445 * The function used to validate alpha values
7446 * @param {String} value The value
7448 'alpha' : function(v){
7449 return alpha.test(v);
7452 * The error text to display when the alpha validation function returns false
7455 'alphaText' : 'This field should only contain letters and _',
7457 * The keystroke filter mask to be applied on alpha input
7460 'alphaMask' : /[a-z_]/i,
7463 * The function used to validate alphanumeric values
7464 * @param {String} value The value
7466 'alphanum' : function(v){
7467 return alphanum.test(v);
7470 * The error text to display when the alphanumeric validation function returns false
7473 'alphanumText' : 'This field should only contain letters, numbers and _',
7475 * The keystroke filter mask to be applied on alphanumeric input
7478 'alphanumMask' : /[a-z0-9_]/i
7488 * @class Roo.bootstrap.Input
7489 * @extends Roo.bootstrap.Component
7490 * Bootstrap Input class
7491 * @cfg {Boolean} disabled is it disabled
7492 * @cfg {String} fieldLabel - the label associated
7493 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7494 * @cfg {String} name name of the input
7495 * @cfg {string} fieldLabel - the label associated
7496 * @cfg {string} inputType - input / file submit ...
7497 * @cfg {string} placeholder - placeholder to put in text.
7498 * @cfg {string} before - input group add on before
7499 * @cfg {string} after - input group add on after
7500 * @cfg {string} size - (lg|sm) or leave empty..
7501 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7502 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7503 * @cfg {Number} md colspan out of 12 for computer-sized screens
7504 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7505 * @cfg {string} value default value of the input
7506 * @cfg {Number} labelWidth set the width of label (0-12)
7507 * @cfg {String} labelAlign (top|left)
7508 * @cfg {Boolean} readOnly Specifies that the field should be read-only
7509 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7511 * @cfg {String} align (left|center|right) Default left
7512 * @cfg {Boolean} forceFeedback (true|false) Default false
7518 * Create a new Input
7519 * @param {Object} config The config object
7522 Roo.bootstrap.Input = function(config){
7523 Roo.bootstrap.Input.superclass.constructor.call(this, config);
7528 * Fires when this field receives input focus.
7529 * @param {Roo.form.Field} this
7534 * Fires when this field loses input focus.
7535 * @param {Roo.form.Field} this
7540 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
7541 * {@link Roo.EventObject#getKey} to determine which key was pressed.
7542 * @param {Roo.form.Field} this
7543 * @param {Roo.EventObject} e The event object
7548 * Fires just before the field blurs if the field value has changed.
7549 * @param {Roo.form.Field} this
7550 * @param {Mixed} newValue The new value
7551 * @param {Mixed} oldValue The original value
7556 * Fires after the field has been marked as invalid.
7557 * @param {Roo.form.Field} this
7558 * @param {String} msg The validation message
7563 * Fires after the field has been validated with no errors.
7564 * @param {Roo.form.Field} this
7569 * Fires after the key up
7570 * @param {Roo.form.Field} this
7571 * @param {Roo.EventObject} e The event Object
7577 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
7579 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7580 automatic validation (defaults to "keyup").
7582 validationEvent : "keyup",
7584 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7586 validateOnBlur : true,
7588 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7590 validationDelay : 250,
7592 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7594 focusClass : "x-form-focus", // not needed???
7598 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7600 invalidClass : "has-warning",
7603 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7605 validClass : "has-success",
7608 * @cfg {Boolean} hasFeedback (true|false) default true
7613 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7615 invalidFeedbackClass : "glyphicon-warning-sign",
7618 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7620 validFeedbackClass : "glyphicon-ok",
7623 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7625 selectOnFocus : false,
7628 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7632 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7637 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7639 disableKeyFilter : false,
7642 * @cfg {Boolean} disabled True to disable the field (defaults to false).
7646 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7650 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7652 blankText : "This field is required",
7655 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7659 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7661 maxLength : Number.MAX_VALUE,
7663 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7665 minLengthText : "The minimum length for this field is {0}",
7667 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7669 maxLengthText : "The maximum length for this field is {0}",
7673 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7674 * If available, this function will be called only after the basic validators all return true, and will be passed the
7675 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7679 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7680 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7681 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
7685 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7689 autocomplete: false,
7708 formatedValue : false,
7709 forceFeedback : false,
7711 parentLabelAlign : function()
7714 while (parent.parent()) {
7715 parent = parent.parent();
7716 if (typeof(parent.labelAlign) !='undefined') {
7717 return parent.labelAlign;
7724 getAutoCreate : function(){
7726 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7732 if(this.inputType != 'hidden'){
7733 cfg.cls = 'form-group' //input-group
7739 type : this.inputType,
7741 cls : 'form-control',
7742 placeholder : this.placeholder || '',
7743 autocomplete : this.autocomplete || 'new-password'
7748 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7751 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7752 input.maxLength = this.maxLength;
7755 if (this.disabled) {
7756 input.disabled=true;
7759 if (this.readOnly) {
7760 input.readonly=true;
7764 input.name = this.name;
7767 input.cls += ' input-' + this.size;
7770 ['xs','sm','md','lg'].map(function(size){
7771 if (settings[size]) {
7772 cfg.cls += ' col-' + size + '-' + settings[size];
7776 var inputblock = input;
7780 cls: 'glyphicon form-control-feedback'
7783 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7786 cls : 'has-feedback',
7794 if (this.before || this.after) {
7797 cls : 'input-group',
7801 if (this.before && typeof(this.before) == 'string') {
7803 inputblock.cn.push({
7805 cls : 'roo-input-before input-group-addon',
7809 if (this.before && typeof(this.before) == 'object') {
7810 this.before = Roo.factory(this.before);
7811 Roo.log(this.before);
7812 inputblock.cn.push({
7814 cls : 'roo-input-before input-group-' +
7815 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7819 inputblock.cn.push(input);
7821 if (this.after && typeof(this.after) == 'string') {
7822 inputblock.cn.push({
7824 cls : 'roo-input-after input-group-addon',
7828 if (this.after && typeof(this.after) == 'object') {
7829 this.after = Roo.factory(this.after);
7830 Roo.log(this.after);
7831 inputblock.cn.push({
7833 cls : 'roo-input-after input-group-' +
7834 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7838 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7839 inputblock.cls += ' has-feedback';
7840 inputblock.cn.push(feedback);
7844 if (align ==='left' && this.fieldLabel.length) {
7845 Roo.log("left and has label");
7851 cls : 'control-label col-sm-' + this.labelWidth,
7852 html : this.fieldLabel
7856 cls : "col-sm-" + (12 - this.labelWidth),
7863 } else if ( this.fieldLabel.length) {
7869 //cls : 'input-group-addon',
7870 html : this.fieldLabel
7880 Roo.log(" no label && no align");
7889 Roo.log('input-parentType: ' + this.parentType);
7891 if (this.parentType === 'Navbar' && this.parent().bar) {
7892 cfg.cls += ' navbar-form';
7900 * return the real input element.
7902 inputEl: function ()
7904 return this.el.select('input.form-control',true).first();
7907 tooltipEl : function()
7909 return this.inputEl();
7912 setDisabled : function(v)
7914 var i = this.inputEl().dom;
7916 i.removeAttribute('disabled');
7920 i.setAttribute('disabled','true');
7922 initEvents : function()
7925 this.inputEl().on("keydown" , this.fireKey, this);
7926 this.inputEl().on("focus", this.onFocus, this);
7927 this.inputEl().on("blur", this.onBlur, this);
7929 this.inputEl().relayEvent('keyup', this);
7931 // reference to original value for reset
7932 this.originalValue = this.getValue();
7933 //Roo.form.TextField.superclass.initEvents.call(this);
7934 if(this.validationEvent == 'keyup'){
7935 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7936 this.inputEl().on('keyup', this.filterValidation, this);
7938 else if(this.validationEvent !== false){
7939 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7942 if(this.selectOnFocus){
7943 this.on("focus", this.preFocus, this);
7946 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7947 this.inputEl().on("keypress", this.filterKeys, this);
7950 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
7951 this.el.on("click", this.autoSize, this);
7954 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7955 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7958 if (typeof(this.before) == 'object') {
7959 this.before.render(this.el.select('.roo-input-before',true).first());
7961 if (typeof(this.after) == 'object') {
7962 this.after.render(this.el.select('.roo-input-after',true).first());
7967 filterValidation : function(e){
7968 if(!e.isNavKeyPress()){
7969 this.validationTask.delay(this.validationDelay);
7973 * Validates the field value
7974 * @return {Boolean} True if the value is valid, else false
7976 validate : function(){
7977 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7978 if(this.disabled || this.validateValue(this.getRawValue())){
7989 * Validates a value according to the field's validation rules and marks the field as invalid
7990 * if the validation fails
7991 * @param {Mixed} value The value to validate
7992 * @return {Boolean} True if the value is valid, else false
7994 validateValue : function(value){
7995 if(value.length < 1) { // if it's blank
7996 if(this.allowBlank){
8002 if(value.length < this.minLength){
8005 if(value.length > this.maxLength){
8009 var vt = Roo.form.VTypes;
8010 if(!vt[this.vtype](value, this)){
8014 if(typeof this.validator == "function"){
8015 var msg = this.validator(value);
8021 if(this.regex && !this.regex.test(value)){
8031 fireKey : function(e){
8032 //Roo.log('field ' + e.getKey());
8033 if(e.isNavKeyPress()){
8034 this.fireEvent("specialkey", this, e);
8037 focus : function (selectText){
8039 this.inputEl().focus();
8040 if(selectText === true){
8041 this.inputEl().dom.select();
8047 onFocus : function(){
8048 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8049 // this.el.addClass(this.focusClass);
8052 this.hasFocus = true;
8053 this.startValue = this.getValue();
8054 this.fireEvent("focus", this);
8058 beforeBlur : Roo.emptyFn,
8062 onBlur : function(){
8064 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8065 //this.el.removeClass(this.focusClass);
8067 this.hasFocus = false;
8068 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8071 var v = this.getValue();
8072 if(String(v) !== String(this.startValue)){
8073 this.fireEvent('change', this, v, this.startValue);
8075 this.fireEvent("blur", this);
8079 * Resets the current field value to the originally loaded value and clears any validation messages
8082 this.setValue(this.originalValue);
8086 * Returns the name of the field
8087 * @return {Mixed} name The name field
8089 getName: function(){
8093 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8094 * @return {Mixed} value The field value
8096 getValue : function(){
8098 var v = this.inputEl().getValue();
8103 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8104 * @return {Mixed} value The field value
8106 getRawValue : function(){
8107 var v = this.inputEl().getValue();
8113 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8114 * @param {Mixed} value The value to set
8116 setRawValue : function(v){
8117 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8120 selectText : function(start, end){
8121 var v = this.getRawValue();
8123 start = start === undefined ? 0 : start;
8124 end = end === undefined ? v.length : end;
8125 var d = this.inputEl().dom;
8126 if(d.setSelectionRange){
8127 d.setSelectionRange(start, end);
8128 }else if(d.createTextRange){
8129 var range = d.createTextRange();
8130 range.moveStart("character", start);
8131 range.moveEnd("character", v.length-end);
8138 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8139 * @param {Mixed} value The value to set
8141 setValue : function(v){
8144 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8150 processValue : function(value){
8151 if(this.stripCharsRe){
8152 var newValue = value.replace(this.stripCharsRe, '');
8153 if(newValue !== value){
8154 this.setRawValue(newValue);
8161 preFocus : function(){
8163 if(this.selectOnFocus){
8164 this.inputEl().dom.select();
8167 filterKeys : function(e){
8169 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8172 var c = e.getCharCode(), cc = String.fromCharCode(c);
8173 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8176 if(!this.maskRe.test(cc)){
8181 * Clear any invalid styles/messages for this field
8183 clearInvalid : function(){
8185 if(!this.el || this.preventMark){ // not rendered
8188 this.el.removeClass(this.invalidClass);
8190 this.fireEvent('valid', this);
8194 * Mark this field as valid
8196 markValid : function(){
8197 if(!this.el || this.preventMark){ // not rendered
8201 this.el.removeClass([this.invalidClass, this.validClass]);
8203 var feedback = this.el.select('.form-control-feedback', true).first();
8206 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8209 if(this.disabled || this.allowBlank){
8213 this.el.addClass(this.validClass);
8215 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8217 var feedback = this.el.select('.form-control-feedback', true).first();
8220 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8221 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8226 this.fireEvent('valid', this);
8230 * Mark this field as invalid
8231 * @param {String} msg The validation message
8233 markInvalid : function(msg){
8234 if(!this.el || this.preventMark){ // not rendered
8238 this.el.removeClass([this.invalidClass, this.validClass]);
8240 var feedback = this.el.select('.form-control-feedback', true).first();
8243 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8246 if(this.disabled || this.allowBlank){
8250 this.el.addClass(this.invalidClass);
8252 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8254 var feedback = this.el.select('.form-control-feedback', true).first();
8257 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8259 if(this.getValue().length || this.forceFeedback){
8260 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8267 this.fireEvent('invalid', this, msg);
8270 SafariOnKeyDown : function(event)
8272 // this is a workaround for a password hang bug on chrome/ webkit.
8274 var isSelectAll = false;
8276 if(this.inputEl().dom.selectionEnd > 0){
8277 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8279 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8280 event.preventDefault();
8285 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8287 event.preventDefault();
8288 // this is very hacky as keydown always get's upper case.
8290 var cc = String.fromCharCode(event.getCharCode());
8291 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8295 adjustWidth : function(tag, w){
8296 tag = tag.toLowerCase();
8297 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8298 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8302 if(tag == 'textarea'){
8305 }else if(Roo.isOpera){
8309 if(tag == 'textarea'){
8328 * @class Roo.bootstrap.TextArea
8329 * @extends Roo.bootstrap.Input
8330 * Bootstrap TextArea class
8331 * @cfg {Number} cols Specifies the visible width of a text area
8332 * @cfg {Number} rows Specifies the visible number of lines in a text area
8333 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8334 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8335 * @cfg {string} html text
8338 * Create a new TextArea
8339 * @param {Object} config The config object
8342 Roo.bootstrap.TextArea = function(config){
8343 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8347 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8357 getAutoCreate : function(){
8359 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8370 value : this.value || '',
8371 html: this.html || '',
8372 cls : 'form-control',
8373 placeholder : this.placeholder || ''
8377 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8378 input.maxLength = this.maxLength;
8382 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8386 input.cols = this.cols;
8389 if (this.readOnly) {
8390 input.readonly = true;
8394 input.name = this.name;
8398 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8402 ['xs','sm','md','lg'].map(function(size){
8403 if (settings[size]) {
8404 cfg.cls += ' col-' + size + '-' + settings[size];
8408 var inputblock = input;
8410 if(this.hasFeedback && !this.allowBlank){
8414 cls: 'glyphicon form-control-feedback'
8418 cls : 'has-feedback',
8427 if (this.before || this.after) {
8430 cls : 'input-group',
8434 inputblock.cn.push({
8436 cls : 'input-group-addon',
8441 inputblock.cn.push(input);
8443 if(this.hasFeedback && !this.allowBlank){
8444 inputblock.cls += ' has-feedback';
8445 inputblock.cn.push(feedback);
8449 inputblock.cn.push({
8451 cls : 'input-group-addon',
8458 if (align ==='left' && this.fieldLabel.length) {
8459 Roo.log("left and has label");
8465 cls : 'control-label col-sm-' + this.labelWidth,
8466 html : this.fieldLabel
8470 cls : "col-sm-" + (12 - this.labelWidth),
8477 } else if ( this.fieldLabel.length) {
8483 //cls : 'input-group-addon',
8484 html : this.fieldLabel
8494 Roo.log(" no label && no align");
8504 if (this.disabled) {
8505 input.disabled=true;
8512 * return the real textarea element.
8514 inputEl: function ()
8516 return this.el.select('textarea.form-control',true).first();
8524 * trigger field - base class for combo..
8529 * @class Roo.bootstrap.TriggerField
8530 * @extends Roo.bootstrap.Input
8531 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8532 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8533 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8534 * for which you can provide a custom implementation. For example:
8536 var trigger = new Roo.bootstrap.TriggerField();
8537 trigger.onTriggerClick = myTriggerFn;
8538 trigger.applyTo('my-field');
8541 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8542 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8543 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
8544 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8545 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8548 * Create a new TriggerField.
8549 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8550 * to the base TextField)
8552 Roo.bootstrap.TriggerField = function(config){
8553 this.mimicing = false;
8554 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8557 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
8559 * @cfg {String} triggerClass A CSS class to apply to the trigger
8562 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8567 * @cfg {Boolean} removable (true|false) special filter default false
8571 /** @cfg {Boolean} grow @hide */
8572 /** @cfg {Number} growMin @hide */
8573 /** @cfg {Number} growMax @hide */
8579 autoSize: Roo.emptyFn,
8586 actionMode : 'wrap',
8591 getAutoCreate : function(){
8593 var align = this.labelAlign || this.parentLabelAlign();
8598 cls: 'form-group' //input-group
8605 type : this.inputType,
8606 cls : 'form-control',
8607 autocomplete: 'new-password',
8608 placeholder : this.placeholder || ''
8612 input.name = this.name;
8615 input.cls += ' input-' + this.size;
8618 if (this.disabled) {
8619 input.disabled=true;
8622 var inputblock = input;
8624 if(this.hasFeedback && !this.allowBlank){
8628 cls: 'glyphicon form-control-feedback'
8631 if(this.removable && !this.editable && !this.tickable){
8633 cls : 'has-feedback',
8639 cls : 'roo-combo-removable-btn close'
8646 cls : 'has-feedback',
8655 if(this.removable && !this.editable && !this.tickable){
8657 cls : 'roo-removable',
8663 cls : 'roo-combo-removable-btn close'
8670 if (this.before || this.after) {
8673 cls : 'input-group',
8677 inputblock.cn.push({
8679 cls : 'input-group-addon',
8684 inputblock.cn.push(input);
8686 if(this.hasFeedback && !this.allowBlank){
8687 inputblock.cls += ' has-feedback';
8688 inputblock.cn.push(feedback);
8692 inputblock.cn.push({
8694 cls : 'input-group-addon',
8707 cls: 'form-hidden-field'
8715 Roo.log('multiple');
8723 cls: 'form-hidden-field'
8727 cls: 'select2-choices',
8731 cls: 'select2-search-field',
8744 cls: 'select2-container input-group',
8749 // cls: 'typeahead typeahead-long dropdown-menu',
8750 // style: 'display:none'
8755 if(!this.multiple && this.showToggleBtn){
8761 if (this.caret != false) {
8764 cls: 'fa fa-' + this.caret
8771 cls : 'input-group-addon btn dropdown-toggle',
8776 cls: 'combobox-clear',
8790 combobox.cls += ' select2-container-multi';
8793 if (align ==='left' && this.fieldLabel.length) {
8795 Roo.log("left and has label");
8801 cls : 'control-label col-sm-' + this.labelWidth,
8802 html : this.fieldLabel
8806 cls : "col-sm-" + (12 - this.labelWidth),
8813 } else if ( this.fieldLabel.length) {
8819 //cls : 'input-group-addon',
8820 html : this.fieldLabel
8830 Roo.log(" no label && no align");
8837 ['xs','sm','md','lg'].map(function(size){
8838 if (settings[size]) {
8839 cfg.cls += ' col-' + size + '-' + settings[size];
8850 onResize : function(w, h){
8851 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8852 // if(typeof w == 'number'){
8853 // var x = w - this.trigger.getWidth();
8854 // this.inputEl().setWidth(this.adjustWidth('input', x));
8855 // this.trigger.setStyle('left', x+'px');
8860 adjustSize : Roo.BoxComponent.prototype.adjustSize,
8863 getResizeEl : function(){
8864 return this.inputEl();
8868 getPositionEl : function(){
8869 return this.inputEl();
8873 alignErrorIcon : function(){
8874 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8878 initEvents : function(){
8882 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8883 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8884 if(!this.multiple && this.showToggleBtn){
8885 this.trigger = this.el.select('span.dropdown-toggle',true).first();
8886 if(this.hideTrigger){
8887 this.trigger.setDisplayed(false);
8889 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8893 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8896 if(this.removable && !this.editable && !this.tickable){
8897 var close = this.closeTriggerEl();
8900 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
8901 close.on('click', this.removeBtnClick, this, close);
8905 //this.trigger.addClassOnOver('x-form-trigger-over');
8906 //this.trigger.addClassOnClick('x-form-trigger-click');
8909 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8913 closeTriggerEl : function()
8915 var close = this.el.select('.roo-combo-removable-btn', true).first();
8916 return close ? close : false;
8919 removeBtnClick : function(e, h, el)
8923 if(this.fireEvent("remove", this) !== false){
8928 createList : function()
8930 this.list = Roo.get(document.body).createChild({
8932 cls: 'typeahead typeahead-long dropdown-menu',
8933 style: 'display:none'
8936 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8941 initTrigger : function(){
8946 onDestroy : function(){
8948 this.trigger.removeAllListeners();
8949 // this.trigger.remove();
8952 // this.wrap.remove();
8954 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8958 onFocus : function(){
8959 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8962 this.wrap.addClass('x-trigger-wrap-focus');
8963 this.mimicing = true;
8964 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8965 if(this.monitorTab){
8966 this.el.on("keydown", this.checkTab, this);
8973 checkTab : function(e){
8974 if(e.getKey() == e.TAB){
8980 onBlur : function(){
8985 mimicBlur : function(e, t){
8987 if(!this.wrap.contains(t) && this.validateBlur()){
8994 triggerBlur : function(){
8995 this.mimicing = false;
8996 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8997 if(this.monitorTab){
8998 this.el.un("keydown", this.checkTab, this);
9000 //this.wrap.removeClass('x-trigger-wrap-focus');
9001 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9005 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9006 validateBlur : function(e, t){
9011 onDisable : function(){
9012 this.inputEl().dom.disabled = true;
9013 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9015 // this.wrap.addClass('x-item-disabled');
9020 onEnable : function(){
9021 this.inputEl().dom.disabled = false;
9022 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9024 // this.el.removeClass('x-item-disabled');
9029 onShow : function(){
9030 var ae = this.getActionEl();
9033 ae.dom.style.display = '';
9034 ae.dom.style.visibility = 'visible';
9040 onHide : function(){
9041 var ae = this.getActionEl();
9042 ae.dom.style.display = 'none';
9046 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9047 * by an implementing function.
9049 * @param {EventObject} e
9051 onTriggerClick : Roo.emptyFn
9055 * Ext JS Library 1.1.1
9056 * Copyright(c) 2006-2007, Ext JS, LLC.
9058 * Originally Released Under LGPL - original licence link has changed is not relivant.
9061 * <script type="text/javascript">
9066 * @class Roo.data.SortTypes
9068 * Defines the default sorting (casting?) comparison functions used when sorting data.
9070 Roo.data.SortTypes = {
9072 * Default sort that does nothing
9073 * @param {Mixed} s The value being converted
9074 * @return {Mixed} The comparison value
9081 * The regular expression used to strip tags
9085 stripTagsRE : /<\/?[^>]+>/gi,
9088 * Strips all HTML tags to sort on text only
9089 * @param {Mixed} s The value being converted
9090 * @return {String} The comparison value
9092 asText : function(s){
9093 return String(s).replace(this.stripTagsRE, "");
9097 * Strips all HTML tags to sort on text only - Case insensitive
9098 * @param {Mixed} s The value being converted
9099 * @return {String} The comparison value
9101 asUCText : function(s){
9102 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9106 * Case insensitive string
9107 * @param {Mixed} s The value being converted
9108 * @return {String} The comparison value
9110 asUCString : function(s) {
9111 return String(s).toUpperCase();
9116 * @param {Mixed} s The value being converted
9117 * @return {Number} The comparison value
9119 asDate : function(s) {
9123 if(s instanceof Date){
9126 return Date.parse(String(s));
9131 * @param {Mixed} s The value being converted
9132 * @return {Float} The comparison value
9134 asFloat : function(s) {
9135 var val = parseFloat(String(s).replace(/,/g, ""));
9136 if(isNaN(val)) val = 0;
9142 * @param {Mixed} s The value being converted
9143 * @return {Number} The comparison value
9145 asInt : function(s) {
9146 var val = parseInt(String(s).replace(/,/g, ""));
9147 if(isNaN(val)) val = 0;
9152 * Ext JS Library 1.1.1
9153 * Copyright(c) 2006-2007, Ext JS, LLC.
9155 * Originally Released Under LGPL - original licence link has changed is not relivant.
9158 * <script type="text/javascript">
9162 * @class Roo.data.Record
9163 * Instances of this class encapsulate both record <em>definition</em> information, and record
9164 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9165 * to access Records cached in an {@link Roo.data.Store} object.<br>
9167 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9168 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9171 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9173 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9174 * {@link #create}. The parameters are the same.
9175 * @param {Array} data An associative Array of data values keyed by the field name.
9176 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9177 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9178 * not specified an integer id is generated.
9180 Roo.data.Record = function(data, id){
9181 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9186 * Generate a constructor for a specific record layout.
9187 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9188 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9189 * Each field definition object may contain the following properties: <ul>
9190 * <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,
9191 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9192 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9193 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9194 * is being used, then this is a string containing the javascript expression to reference the data relative to
9195 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9196 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9197 * this may be omitted.</p></li>
9198 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9199 * <ul><li>auto (Default, implies no conversion)</li>
9204 * <li>date</li></ul></p></li>
9205 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9206 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9207 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9208 * by the Reader into an object that will be stored in the Record. It is passed the
9209 * following parameters:<ul>
9210 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9212 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9214 * <br>usage:<br><pre><code>
9215 var TopicRecord = Roo.data.Record.create(
9216 {name: 'title', mapping: 'topic_title'},
9217 {name: 'author', mapping: 'username'},
9218 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9219 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9220 {name: 'lastPoster', mapping: 'user2'},
9221 {name: 'excerpt', mapping: 'post_text'}
9224 var myNewRecord = new TopicRecord({
9225 title: 'Do my job please',
9228 lastPost: new Date(),
9229 lastPoster: 'Animal',
9230 excerpt: 'No way dude!'
9232 myStore.add(myNewRecord);
9237 Roo.data.Record.create = function(o){
9239 f.superclass.constructor.apply(this, arguments);
9241 Roo.extend(f, Roo.data.Record);
9242 var p = f.prototype;
9243 p.fields = new Roo.util.MixedCollection(false, function(field){
9246 for(var i = 0, len = o.length; i < len; i++){
9247 p.fields.add(new Roo.data.Field(o[i]));
9249 f.getField = function(name){
9250 return p.fields.get(name);
9255 Roo.data.Record.AUTO_ID = 1000;
9256 Roo.data.Record.EDIT = 'edit';
9257 Roo.data.Record.REJECT = 'reject';
9258 Roo.data.Record.COMMIT = 'commit';
9260 Roo.data.Record.prototype = {
9262 * Readonly flag - true if this record has been modified.
9271 join : function(store){
9276 * Set the named field to the specified value.
9277 * @param {String} name The name of the field to set.
9278 * @param {Object} value The value to set the field to.
9280 set : function(name, value){
9281 if(this.data[name] == value){
9288 if(typeof this.modified[name] == 'undefined'){
9289 this.modified[name] = this.data[name];
9291 this.data[name] = value;
9292 if(!this.editing && this.store){
9293 this.store.afterEdit(this);
9298 * Get the value of the named field.
9299 * @param {String} name The name of the field to get the value of.
9300 * @return {Object} The value of the field.
9302 get : function(name){
9303 return this.data[name];
9307 beginEdit : function(){
9308 this.editing = true;
9313 cancelEdit : function(){
9314 this.editing = false;
9315 delete this.modified;
9319 endEdit : function(){
9320 this.editing = false;
9321 if(this.dirty && this.store){
9322 this.store.afterEdit(this);
9327 * Usually called by the {@link Roo.data.Store} which owns the Record.
9328 * Rejects all changes made to the Record since either creation, or the last commit operation.
9329 * Modified fields are reverted to their original values.
9331 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9332 * of reject operations.
9334 reject : function(){
9335 var m = this.modified;
9337 if(typeof m[n] != "function"){
9338 this.data[n] = m[n];
9342 delete this.modified;
9343 this.editing = false;
9345 this.store.afterReject(this);
9350 * Usually called by the {@link Roo.data.Store} which owns the Record.
9351 * Commits all changes made to the Record since either creation, or the last commit operation.
9353 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9354 * of commit operations.
9356 commit : function(){
9358 delete this.modified;
9359 this.editing = false;
9361 this.store.afterCommit(this);
9366 hasError : function(){
9367 return this.error != null;
9371 clearError : function(){
9376 * Creates a copy of this record.
9377 * @param {String} id (optional) A new record id if you don't want to use this record's id
9380 copy : function(newId) {
9381 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9385 * Ext JS Library 1.1.1
9386 * Copyright(c) 2006-2007, Ext JS, LLC.
9388 * Originally Released Under LGPL - original licence link has changed is not relivant.
9391 * <script type="text/javascript">
9397 * @class Roo.data.Store
9398 * @extends Roo.util.Observable
9399 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9400 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9402 * 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
9403 * has no knowledge of the format of the data returned by the Proxy.<br>
9405 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9406 * instances from the data object. These records are cached and made available through accessor functions.
9408 * Creates a new Store.
9409 * @param {Object} config A config object containing the objects needed for the Store to access data,
9410 * and read the data into Records.
9412 Roo.data.Store = function(config){
9413 this.data = new Roo.util.MixedCollection(false);
9414 this.data.getKey = function(o){
9417 this.baseParams = {};
9424 "multisort" : "_multisort"
9427 if(config && config.data){
9428 this.inlineData = config.data;
9432 Roo.apply(this, config);
9434 if(this.reader){ // reader passed
9435 this.reader = Roo.factory(this.reader, Roo.data);
9436 this.reader.xmodule = this.xmodule || false;
9437 if(!this.recordType){
9438 this.recordType = this.reader.recordType;
9440 if(this.reader.onMetaChange){
9441 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9445 if(this.recordType){
9446 this.fields = this.recordType.prototype.fields;
9452 * @event datachanged
9453 * Fires when the data cache has changed, and a widget which is using this Store
9454 * as a Record cache should refresh its view.
9455 * @param {Store} this
9460 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9461 * @param {Store} this
9462 * @param {Object} meta The JSON metadata
9467 * Fires when Records have been added to the Store
9468 * @param {Store} this
9469 * @param {Roo.data.Record[]} records The array of Records added
9470 * @param {Number} index The index at which the record(s) were added
9475 * Fires when a Record has been removed from the Store
9476 * @param {Store} this
9477 * @param {Roo.data.Record} record The Record that was removed
9478 * @param {Number} index The index at which the record was removed
9483 * Fires when a Record has been updated
9484 * @param {Store} this
9485 * @param {Roo.data.Record} record The Record that was updated
9486 * @param {String} operation The update operation being performed. Value may be one of:
9488 Roo.data.Record.EDIT
9489 Roo.data.Record.REJECT
9490 Roo.data.Record.COMMIT
9496 * Fires when the data cache has been cleared.
9497 * @param {Store} this
9502 * Fires before a request is made for a new data object. If the beforeload handler returns false
9503 * the load action will be canceled.
9504 * @param {Store} this
9505 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9509 * @event beforeloadadd
9510 * Fires after a new set of Records has been loaded.
9511 * @param {Store} this
9512 * @param {Roo.data.Record[]} records The Records that were loaded
9513 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9515 beforeloadadd : true,
9518 * Fires after a new set of Records has been loaded, before they are added to the store.
9519 * @param {Store} this
9520 * @param {Roo.data.Record[]} records The Records that were loaded
9521 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9522 * @params {Object} return from reader
9526 * @event loadexception
9527 * Fires if an exception occurs in the Proxy during loading.
9528 * Called with the signature of the Proxy's "loadexception" event.
9529 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9532 * @param {Object} return from JsonData.reader() - success, totalRecords, records
9533 * @param {Object} load options
9534 * @param {Object} jsonData from your request (normally this contains the Exception)
9536 loadexception : true
9540 this.proxy = Roo.factory(this.proxy, Roo.data);
9541 this.proxy.xmodule = this.xmodule || false;
9542 this.relayEvents(this.proxy, ["loadexception"]);
9544 this.sortToggle = {};
9545 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9547 Roo.data.Store.superclass.constructor.call(this);
9549 if(this.inlineData){
9550 this.loadData(this.inlineData);
9551 delete this.inlineData;
9555 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9557 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
9558 * without a remote query - used by combo/forms at present.
9562 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9565 * @cfg {Array} data Inline data to be loaded when the store is initialized.
9568 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9569 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9572 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9573 * on any HTTP request
9576 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9579 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9583 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9584 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9589 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9590 * loaded or when a record is removed. (defaults to false).
9592 pruneModifiedRecords : false,
9598 * Add Records to the Store and fires the add event.
9599 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9601 add : function(records){
9602 records = [].concat(records);
9603 for(var i = 0, len = records.length; i < len; i++){
9604 records[i].join(this);
9606 var index = this.data.length;
9607 this.data.addAll(records);
9608 this.fireEvent("add", this, records, index);
9612 * Remove a Record from the Store and fires the remove event.
9613 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9615 remove : function(record){
9616 var index = this.data.indexOf(record);
9617 this.data.removeAt(index);
9618 if(this.pruneModifiedRecords){
9619 this.modified.remove(record);
9621 this.fireEvent("remove", this, record, index);
9625 * Remove all Records from the Store and fires the clear event.
9627 removeAll : function(){
9629 if(this.pruneModifiedRecords){
9632 this.fireEvent("clear", this);
9636 * Inserts Records to the Store at the given index and fires the add event.
9637 * @param {Number} index The start index at which to insert the passed Records.
9638 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9640 insert : function(index, records){
9641 records = [].concat(records);
9642 for(var i = 0, len = records.length; i < len; i++){
9643 this.data.insert(index, records[i]);
9644 records[i].join(this);
9646 this.fireEvent("add", this, records, index);
9650 * Get the index within the cache of the passed Record.
9651 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9652 * @return {Number} The index of the passed Record. Returns -1 if not found.
9654 indexOf : function(record){
9655 return this.data.indexOf(record);
9659 * Get the index within the cache of the Record with the passed id.
9660 * @param {String} id The id of the Record to find.
9661 * @return {Number} The index of the Record. Returns -1 if not found.
9663 indexOfId : function(id){
9664 return this.data.indexOfKey(id);
9668 * Get the Record with the specified id.
9669 * @param {String} id The id of the Record to find.
9670 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9672 getById : function(id){
9673 return this.data.key(id);
9677 * Get the Record at the specified index.
9678 * @param {Number} index The index of the Record to find.
9679 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9681 getAt : function(index){
9682 return this.data.itemAt(index);
9686 * Returns a range of Records between specified indices.
9687 * @param {Number} startIndex (optional) The starting index (defaults to 0)
9688 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9689 * @return {Roo.data.Record[]} An array of Records
9691 getRange : function(start, end){
9692 return this.data.getRange(start, end);
9696 storeOptions : function(o){
9697 o = Roo.apply({}, o);
9700 this.lastOptions = o;
9704 * Loads the Record cache from the configured Proxy using the configured Reader.
9706 * If using remote paging, then the first load call must specify the <em>start</em>
9707 * and <em>limit</em> properties in the options.params property to establish the initial
9708 * position within the dataset, and the number of Records to cache on each read from the Proxy.
9710 * <strong>It is important to note that for remote data sources, loading is asynchronous,
9711 * and this call will return before the new data has been loaded. Perform any post-processing
9712 * in a callback function, or in a "load" event handler.</strong>
9714 * @param {Object} options An object containing properties which control loading options:<ul>
9715 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9716 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9717 * passed the following arguments:<ul>
9718 * <li>r : Roo.data.Record[]</li>
9719 * <li>options: Options object from the load call</li>
9720 * <li>success: Boolean success indicator</li></ul></li>
9721 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9722 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9725 load : function(options){
9726 options = options || {};
9727 if(this.fireEvent("beforeload", this, options) !== false){
9728 this.storeOptions(options);
9729 var p = Roo.apply(options.params || {}, this.baseParams);
9730 // if meta was not loaded from remote source.. try requesting it.
9731 if (!this.reader.metaFromRemote) {
9734 if(this.sortInfo && this.remoteSort){
9735 var pn = this.paramNames;
9736 p[pn["sort"]] = this.sortInfo.field;
9737 p[pn["dir"]] = this.sortInfo.direction;
9739 if (this.multiSort) {
9740 var pn = this.paramNames;
9741 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9744 this.proxy.load(p, this.reader, this.loadRecords, this, options);
9749 * Reloads the Record cache from the configured Proxy using the configured Reader and
9750 * the options from the last load operation performed.
9751 * @param {Object} options (optional) An object containing properties which may override the options
9752 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9753 * the most recently used options are reused).
9755 reload : function(options){
9756 this.load(Roo.applyIf(options||{}, this.lastOptions));
9760 // Called as a callback by the Reader during a load operation.
9761 loadRecords : function(o, options, success){
9762 if(!o || success === false){
9763 if(success !== false){
9764 this.fireEvent("load", this, [], options, o);
9766 if(options.callback){
9767 options.callback.call(options.scope || this, [], options, false);
9771 // if data returned failure - throw an exception.
9772 if (o.success === false) {
9773 // show a message if no listener is registered.
9774 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9775 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9777 // loadmask wil be hooked into this..
9778 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9781 var r = o.records, t = o.totalRecords || r.length;
9783 this.fireEvent("beforeloadadd", this, r, options, o);
9785 if(!options || options.add !== true){
9786 if(this.pruneModifiedRecords){
9789 for(var i = 0, len = r.length; i < len; i++){
9793 this.data = this.snapshot;
9794 delete this.snapshot;
9797 this.data.addAll(r);
9798 this.totalLength = t;
9800 this.fireEvent("datachanged", this);
9802 this.totalLength = Math.max(t, this.data.length+r.length);
9805 this.fireEvent("load", this, r, options, o);
9806 if(options.callback){
9807 options.callback.call(options.scope || this, r, options, true);
9813 * Loads data from a passed data block. A Reader which understands the format of the data
9814 * must have been configured in the constructor.
9815 * @param {Object} data The data block from which to read the Records. The format of the data expected
9816 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9817 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9819 loadData : function(o, append){
9820 var r = this.reader.readRecords(o);
9821 this.loadRecords(r, {add: append}, true);
9825 * Gets the number of cached records.
9827 * <em>If using paging, this may not be the total size of the dataset. If the data object
9828 * used by the Reader contains the dataset size, then the getTotalCount() function returns
9829 * the data set size</em>
9831 getCount : function(){
9832 return this.data.length || 0;
9836 * Gets the total number of records in the dataset as returned by the server.
9838 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9839 * the dataset size</em>
9841 getTotalCount : function(){
9842 return this.totalLength || 0;
9846 * Returns the sort state of the Store as an object with two properties:
9848 field {String} The name of the field by which the Records are sorted
9849 direction {String} The sort order, "ASC" or "DESC"
9852 getSortState : function(){
9853 return this.sortInfo;
9857 applySort : function(){
9858 if(this.sortInfo && !this.remoteSort){
9859 var s = this.sortInfo, f = s.field;
9860 var st = this.fields.get(f).sortType;
9861 var fn = function(r1, r2){
9862 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9863 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9865 this.data.sort(s.direction, fn);
9866 if(this.snapshot && this.snapshot != this.data){
9867 this.snapshot.sort(s.direction, fn);
9873 * Sets the default sort column and order to be used by the next load operation.
9874 * @param {String} fieldName The name of the field to sort by.
9875 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9877 setDefaultSort : function(field, dir){
9878 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9883 * If remote sorting is used, the sort is performed on the server, and the cache is
9884 * reloaded. If local sorting is used, the cache is sorted internally.
9885 * @param {String} fieldName The name of the field to sort by.
9886 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9888 sort : function(fieldName, dir){
9889 var f = this.fields.get(fieldName);
9891 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9893 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9894 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9899 this.sortToggle[f.name] = dir;
9900 this.sortInfo = {field: f.name, direction: dir};
9901 if(!this.remoteSort){
9903 this.fireEvent("datachanged", this);
9905 this.load(this.lastOptions);
9910 * Calls the specified function for each of the Records in the cache.
9911 * @param {Function} fn The function to call. The Record is passed as the first parameter.
9912 * Returning <em>false</em> aborts and exits the iteration.
9913 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9915 each : function(fn, scope){
9916 this.data.each(fn, scope);
9920 * Gets all records modified since the last commit. Modified records are persisted across load operations
9921 * (e.g., during paging).
9922 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9924 getModifiedRecords : function(){
9925 return this.modified;
9929 createFilterFn : function(property, value, anyMatch){
9930 if(!value.exec){ // not a regex
9931 value = String(value);
9932 if(value.length == 0){
9935 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9938 return value.test(r.data[property]);
9943 * Sums the value of <i>property</i> for each record between start and end and returns the result.
9944 * @param {String} property A field on your records
9945 * @param {Number} start The record index to start at (defaults to 0)
9946 * @param {Number} end The last record index to include (defaults to length - 1)
9947 * @return {Number} The sum
9949 sum : function(property, start, end){
9950 var rs = this.data.items, v = 0;
9952 end = (end || end === 0) ? end : rs.length-1;
9954 for(var i = start; i <= end; i++){
9955 v += (rs[i].data[property] || 0);
9961 * Filter the records by a specified property.
9962 * @param {String} field A field on your records
9963 * @param {String/RegExp} value Either a string that the field
9964 * should start with or a RegExp to test against the field
9965 * @param {Boolean} anyMatch True to match any part not just the beginning
9967 filter : function(property, value, anyMatch){
9968 var fn = this.createFilterFn(property, value, anyMatch);
9969 return fn ? this.filterBy(fn) : this.clearFilter();
9973 * Filter by a function. The specified function will be called with each
9974 * record in this data source. If the function returns true the record is included,
9975 * otherwise it is filtered.
9976 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9977 * @param {Object} scope (optional) The scope of the function (defaults to this)
9979 filterBy : function(fn, scope){
9980 this.snapshot = this.snapshot || this.data;
9981 this.data = this.queryBy(fn, scope||this);
9982 this.fireEvent("datachanged", this);
9986 * Query the records by a specified property.
9987 * @param {String} field A field on your records
9988 * @param {String/RegExp} value Either a string that the field
9989 * should start with or a RegExp to test against the field
9990 * @param {Boolean} anyMatch True to match any part not just the beginning
9991 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9993 query : function(property, value, anyMatch){
9994 var fn = this.createFilterFn(property, value, anyMatch);
9995 return fn ? this.queryBy(fn) : this.data.clone();
9999 * Query by a function. The specified function will be called with each
10000 * record in this data source. If the function returns true the record is included
10002 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10003 * @param {Object} scope (optional) The scope of the function (defaults to this)
10004 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10006 queryBy : function(fn, scope){
10007 var data = this.snapshot || this.data;
10008 return data.filterBy(fn, scope||this);
10012 * Collects unique values for a particular dataIndex from this store.
10013 * @param {String} dataIndex The property to collect
10014 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10015 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10016 * @return {Array} An array of the unique values
10018 collect : function(dataIndex, allowNull, bypassFilter){
10019 var d = (bypassFilter === true && this.snapshot) ?
10020 this.snapshot.items : this.data.items;
10021 var v, sv, r = [], l = {};
10022 for(var i = 0, len = d.length; i < len; i++){
10023 v = d[i].data[dataIndex];
10025 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10034 * Revert to a view of the Record cache with no filtering applied.
10035 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10037 clearFilter : function(suppressEvent){
10038 if(this.snapshot && this.snapshot != this.data){
10039 this.data = this.snapshot;
10040 delete this.snapshot;
10041 if(suppressEvent !== true){
10042 this.fireEvent("datachanged", this);
10048 afterEdit : function(record){
10049 if(this.modified.indexOf(record) == -1){
10050 this.modified.push(record);
10052 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10056 afterReject : function(record){
10057 this.modified.remove(record);
10058 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10062 afterCommit : function(record){
10063 this.modified.remove(record);
10064 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10068 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10069 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10071 commitChanges : function(){
10072 var m = this.modified.slice(0);
10073 this.modified = [];
10074 for(var i = 0, len = m.length; i < len; i++){
10080 * Cancel outstanding changes on all changed records.
10082 rejectChanges : function(){
10083 var m = this.modified.slice(0);
10084 this.modified = [];
10085 for(var i = 0, len = m.length; i < len; i++){
10090 onMetaChange : function(meta, rtype, o){
10091 this.recordType = rtype;
10092 this.fields = rtype.prototype.fields;
10093 delete this.snapshot;
10094 this.sortInfo = meta.sortInfo || this.sortInfo;
10095 this.modified = [];
10096 this.fireEvent('metachange', this, this.reader.meta);
10099 moveIndex : function(data, type)
10101 var index = this.indexOf(data);
10103 var newIndex = index + type;
10107 this.insert(newIndex, data);
10112 * Ext JS Library 1.1.1
10113 * Copyright(c) 2006-2007, Ext JS, LLC.
10115 * Originally Released Under LGPL - original licence link has changed is not relivant.
10118 * <script type="text/javascript">
10122 * @class Roo.data.SimpleStore
10123 * @extends Roo.data.Store
10124 * Small helper class to make creating Stores from Array data easier.
10125 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10126 * @cfg {Array} fields An array of field definition objects, or field name strings.
10127 * @cfg {Array} data The multi-dimensional array of data
10129 * @param {Object} config
10131 Roo.data.SimpleStore = function(config){
10132 Roo.data.SimpleStore.superclass.constructor.call(this, {
10134 reader: new Roo.data.ArrayReader({
10137 Roo.data.Record.create(config.fields)
10139 proxy : new Roo.data.MemoryProxy(config.data)
10143 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10145 * Ext JS Library 1.1.1
10146 * Copyright(c) 2006-2007, Ext JS, LLC.
10148 * Originally Released Under LGPL - original licence link has changed is not relivant.
10151 * <script type="text/javascript">
10156 * @extends Roo.data.Store
10157 * @class Roo.data.JsonStore
10158 * Small helper class to make creating Stores for JSON data easier. <br/>
10160 var store = new Roo.data.JsonStore({
10161 url: 'get-images.php',
10163 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10166 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10167 * JsonReader and HttpProxy (unless inline data is provided).</b>
10168 * @cfg {Array} fields An array of field definition objects, or field name strings.
10170 * @param {Object} config
10172 Roo.data.JsonStore = function(c){
10173 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10174 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10175 reader: new Roo.data.JsonReader(c, c.fields)
10178 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10180 * Ext JS Library 1.1.1
10181 * Copyright(c) 2006-2007, Ext JS, LLC.
10183 * Originally Released Under LGPL - original licence link has changed is not relivant.
10186 * <script type="text/javascript">
10190 Roo.data.Field = function(config){
10191 if(typeof config == "string"){
10192 config = {name: config};
10194 Roo.apply(this, config);
10197 this.type = "auto";
10200 var st = Roo.data.SortTypes;
10201 // named sortTypes are supported, here we look them up
10202 if(typeof this.sortType == "string"){
10203 this.sortType = st[this.sortType];
10206 // set default sortType for strings and dates
10207 if(!this.sortType){
10210 this.sortType = st.asUCString;
10213 this.sortType = st.asDate;
10216 this.sortType = st.none;
10221 var stripRe = /[\$,%]/g;
10223 // prebuilt conversion function for this field, instead of
10224 // switching every time we're reading a value
10226 var cv, dateFormat = this.dateFormat;
10231 cv = function(v){ return v; };
10234 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10238 return v !== undefined && v !== null && v !== '' ?
10239 parseInt(String(v).replace(stripRe, ""), 10) : '';
10244 return v !== undefined && v !== null && v !== '' ?
10245 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10250 cv = function(v){ return v === true || v === "true" || v == 1; };
10257 if(v instanceof Date){
10261 if(dateFormat == "timestamp"){
10262 return new Date(v*1000);
10264 return Date.parseDate(v, dateFormat);
10266 var parsed = Date.parse(v);
10267 return parsed ? new Date(parsed) : null;
10276 Roo.data.Field.prototype = {
10284 * Ext JS Library 1.1.1
10285 * Copyright(c) 2006-2007, Ext JS, LLC.
10287 * Originally Released Under LGPL - original licence link has changed is not relivant.
10290 * <script type="text/javascript">
10293 // Base class for reading structured data from a data source. This class is intended to be
10294 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10297 * @class Roo.data.DataReader
10298 * Base class for reading structured data from a data source. This class is intended to be
10299 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10302 Roo.data.DataReader = function(meta, recordType){
10306 this.recordType = recordType instanceof Array ?
10307 Roo.data.Record.create(recordType) : recordType;
10310 Roo.data.DataReader.prototype = {
10312 * Create an empty record
10313 * @param {Object} data (optional) - overlay some values
10314 * @return {Roo.data.Record} record created.
10316 newRow : function(d) {
10318 this.recordType.prototype.fields.each(function(c) {
10320 case 'int' : da[c.name] = 0; break;
10321 case 'date' : da[c.name] = new Date(); break;
10322 case 'float' : da[c.name] = 0.0; break;
10323 case 'boolean' : da[c.name] = false; break;
10324 default : da[c.name] = ""; break;
10328 return new this.recordType(Roo.apply(da, d));
10333 * Ext JS Library 1.1.1
10334 * Copyright(c) 2006-2007, Ext JS, LLC.
10336 * Originally Released Under LGPL - original licence link has changed is not relivant.
10339 * <script type="text/javascript">
10343 * @class Roo.data.DataProxy
10344 * @extends Roo.data.Observable
10345 * This class is an abstract base class for implementations which provide retrieval of
10346 * unformatted data objects.<br>
10348 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10349 * (of the appropriate type which knows how to parse the data object) to provide a block of
10350 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10352 * Custom implementations must implement the load method as described in
10353 * {@link Roo.data.HttpProxy#load}.
10355 Roo.data.DataProxy = function(){
10358 * @event beforeload
10359 * Fires before a network request is made to retrieve a data object.
10360 * @param {Object} This DataProxy object.
10361 * @param {Object} params The params parameter to the load function.
10366 * Fires before the load method's callback is called.
10367 * @param {Object} This DataProxy object.
10368 * @param {Object} o The data object.
10369 * @param {Object} arg The callback argument object passed to the load function.
10373 * @event loadexception
10374 * Fires if an Exception occurs during data retrieval.
10375 * @param {Object} This DataProxy object.
10376 * @param {Object} o The data object.
10377 * @param {Object} arg The callback argument object passed to the load function.
10378 * @param {Object} e The Exception.
10380 loadexception : true
10382 Roo.data.DataProxy.superclass.constructor.call(this);
10385 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10388 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10392 * Ext JS Library 1.1.1
10393 * Copyright(c) 2006-2007, Ext JS, LLC.
10395 * Originally Released Under LGPL - original licence link has changed is not relivant.
10398 * <script type="text/javascript">
10401 * @class Roo.data.MemoryProxy
10402 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10403 * to the Reader when its load method is called.
10405 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10407 Roo.data.MemoryProxy = function(data){
10411 Roo.data.MemoryProxy.superclass.constructor.call(this);
10415 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10417 * Load data from the requested source (in this case an in-memory
10418 * data object passed to the constructor), read the data object into
10419 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10420 * process that block using the passed callback.
10421 * @param {Object} params This parameter is not used by the MemoryProxy class.
10422 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10423 * object into a block of Roo.data.Records.
10424 * @param {Function} callback The function into which to pass the block of Roo.data.records.
10425 * The function must be passed <ul>
10426 * <li>The Record block object</li>
10427 * <li>The "arg" argument from the load function</li>
10428 * <li>A boolean success indicator</li>
10430 * @param {Object} scope The scope in which to call the callback
10431 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10433 load : function(params, reader, callback, scope, arg){
10434 params = params || {};
10437 result = reader.readRecords(this.data);
10439 this.fireEvent("loadexception", this, arg, null, e);
10440 callback.call(scope, null, arg, false);
10443 callback.call(scope, result, arg, true);
10447 update : function(params, records){
10452 * Ext JS Library 1.1.1
10453 * Copyright(c) 2006-2007, Ext JS, LLC.
10455 * Originally Released Under LGPL - original licence link has changed is not relivant.
10458 * <script type="text/javascript">
10461 * @class Roo.data.HttpProxy
10462 * @extends Roo.data.DataProxy
10463 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10464 * configured to reference a certain URL.<br><br>
10466 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10467 * from which the running page was served.<br><br>
10469 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10471 * Be aware that to enable the browser to parse an XML document, the server must set
10472 * the Content-Type header in the HTTP response to "text/xml".
10474 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10475 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
10476 * will be used to make the request.
10478 Roo.data.HttpProxy = function(conn){
10479 Roo.data.HttpProxy.superclass.constructor.call(this);
10480 // is conn a conn config or a real conn?
10482 this.useAjax = !conn || !conn.events;
10486 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10487 // thse are take from connection...
10490 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10493 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10494 * extra parameters to each request made by this object. (defaults to undefined)
10497 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10498 * to each request made by this object. (defaults to undefined)
10501 * @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)
10504 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10507 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10513 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10517 * Return the {@link Roo.data.Connection} object being used by this Proxy.
10518 * @return {Connection} The Connection object. This object may be used to subscribe to events on
10519 * a finer-grained basis than the DataProxy events.
10521 getConnection : function(){
10522 return this.useAjax ? Roo.Ajax : this.conn;
10526 * Load data from the configured {@link Roo.data.Connection}, read the data object into
10527 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10528 * process that block using the passed callback.
10529 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10530 * for the request to the remote server.
10531 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10532 * object into a block of Roo.data.Records.
10533 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10534 * The function must be passed <ul>
10535 * <li>The Record block object</li>
10536 * <li>The "arg" argument from the load function</li>
10537 * <li>A boolean success indicator</li>
10539 * @param {Object} scope The scope in which to call the callback
10540 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10542 load : function(params, reader, callback, scope, arg){
10543 if(this.fireEvent("beforeload", this, params) !== false){
10545 params : params || {},
10547 callback : callback,
10552 callback : this.loadResponse,
10556 Roo.applyIf(o, this.conn);
10557 if(this.activeRequest){
10558 Roo.Ajax.abort(this.activeRequest);
10560 this.activeRequest = Roo.Ajax.request(o);
10562 this.conn.request(o);
10565 callback.call(scope||this, null, arg, false);
10570 loadResponse : function(o, success, response){
10571 delete this.activeRequest;
10573 this.fireEvent("loadexception", this, o, response);
10574 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10579 result = o.reader.read(response);
10581 this.fireEvent("loadexception", this, o, response, e);
10582 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10586 this.fireEvent("load", this, o, o.request.arg);
10587 o.request.callback.call(o.request.scope, result, o.request.arg, true);
10591 update : function(dataSet){
10596 updateResponse : function(dataSet){
10601 * Ext JS Library 1.1.1
10602 * Copyright(c) 2006-2007, Ext JS, LLC.
10604 * Originally Released Under LGPL - original licence link has changed is not relivant.
10607 * <script type="text/javascript">
10611 * @class Roo.data.ScriptTagProxy
10612 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10613 * other than the originating domain of the running page.<br><br>
10615 * <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
10616 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10618 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10619 * source code that is used as the source inside a <script> tag.<br><br>
10621 * In order for the browser to process the returned data, the server must wrap the data object
10622 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10623 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10624 * depending on whether the callback name was passed:
10627 boolean scriptTag = false;
10628 String cb = request.getParameter("callback");
10631 response.setContentType("text/javascript");
10633 response.setContentType("application/x-json");
10635 Writer out = response.getWriter();
10637 out.write(cb + "(");
10639 out.print(dataBlock.toJsonString());
10646 * @param {Object} config A configuration object.
10648 Roo.data.ScriptTagProxy = function(config){
10649 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10650 Roo.apply(this, config);
10651 this.head = document.getElementsByTagName("head")[0];
10654 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10656 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10658 * @cfg {String} url The URL from which to request the data object.
10661 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10665 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10666 * the server the name of the callback function set up by the load call to process the returned data object.
10667 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10668 * javascript output which calls this named function passing the data object as its only parameter.
10670 callbackParam : "callback",
10672 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10673 * name to the request.
10678 * Load data from the configured URL, read the data object into
10679 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10680 * process that block using the passed callback.
10681 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10682 * for the request to the remote server.
10683 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10684 * object into a block of Roo.data.Records.
10685 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10686 * The function must be passed <ul>
10687 * <li>The Record block object</li>
10688 * <li>The "arg" argument from the load function</li>
10689 * <li>A boolean success indicator</li>
10691 * @param {Object} scope The scope in which to call the callback
10692 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10694 load : function(params, reader, callback, scope, arg){
10695 if(this.fireEvent("beforeload", this, params) !== false){
10697 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10699 var url = this.url;
10700 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10702 url += "&_dc=" + (new Date().getTime());
10704 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10707 cb : "stcCallback"+transId,
10708 scriptId : "stcScript"+transId,
10712 callback : callback,
10718 window[trans.cb] = function(o){
10719 conn.handleResponse(o, trans);
10722 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10724 if(this.autoAbort !== false){
10728 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10730 var script = document.createElement("script");
10731 script.setAttribute("src", url);
10732 script.setAttribute("type", "text/javascript");
10733 script.setAttribute("id", trans.scriptId);
10734 this.head.appendChild(script);
10736 this.trans = trans;
10738 callback.call(scope||this, null, arg, false);
10743 isLoading : function(){
10744 return this.trans ? true : false;
10748 * Abort the current server request.
10750 abort : function(){
10751 if(this.isLoading()){
10752 this.destroyTrans(this.trans);
10757 destroyTrans : function(trans, isLoaded){
10758 this.head.removeChild(document.getElementById(trans.scriptId));
10759 clearTimeout(trans.timeoutId);
10761 window[trans.cb] = undefined;
10763 delete window[trans.cb];
10766 // if hasn't been loaded, wait for load to remove it to prevent script error
10767 window[trans.cb] = function(){
10768 window[trans.cb] = undefined;
10770 delete window[trans.cb];
10777 handleResponse : function(o, trans){
10778 this.trans = false;
10779 this.destroyTrans(trans, true);
10782 result = trans.reader.readRecords(o);
10784 this.fireEvent("loadexception", this, o, trans.arg, e);
10785 trans.callback.call(trans.scope||window, null, trans.arg, false);
10788 this.fireEvent("load", this, o, trans.arg);
10789 trans.callback.call(trans.scope||window, result, trans.arg, true);
10793 handleFailure : function(trans){
10794 this.trans = false;
10795 this.destroyTrans(trans, false);
10796 this.fireEvent("loadexception", this, null, trans.arg);
10797 trans.callback.call(trans.scope||window, null, trans.arg, false);
10801 * Ext JS Library 1.1.1
10802 * Copyright(c) 2006-2007, Ext JS, LLC.
10804 * Originally Released Under LGPL - original licence link has changed is not relivant.
10807 * <script type="text/javascript">
10811 * @class Roo.data.JsonReader
10812 * @extends Roo.data.DataReader
10813 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10814 * based on mappings in a provided Roo.data.Record constructor.
10816 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10817 * in the reply previously.
10822 var RecordDef = Roo.data.Record.create([
10823 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
10824 {name: 'occupation'} // This field will use "occupation" as the mapping.
10826 var myReader = new Roo.data.JsonReader({
10827 totalProperty: "results", // The property which contains the total dataset size (optional)
10828 root: "rows", // The property which contains an Array of row objects
10829 id: "id" // The property within each row object that provides an ID for the record (optional)
10833 * This would consume a JSON file like this:
10835 { 'results': 2, 'rows': [
10836 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10837 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10840 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10841 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10842 * paged from the remote server.
10843 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10844 * @cfg {String} root name of the property which contains the Array of row objects.
10845 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10846 * @cfg {Array} fields Array of field definition objects
10848 * Create a new JsonReader
10849 * @param {Object} meta Metadata configuration options
10850 * @param {Object} recordType Either an Array of field definition objects,
10851 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10853 Roo.data.JsonReader = function(meta, recordType){
10856 // set some defaults:
10857 Roo.applyIf(meta, {
10858 totalProperty: 'total',
10859 successProperty : 'success',
10864 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10866 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10869 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
10870 * Used by Store query builder to append _requestMeta to params.
10873 metaFromRemote : false,
10875 * This method is only used by a DataProxy which has retrieved data from a remote server.
10876 * @param {Object} response The XHR object which contains the JSON data in its responseText.
10877 * @return {Object} data A data block which is used by an Roo.data.Store object as
10878 * a cache of Roo.data.Records.
10880 read : function(response){
10881 var json = response.responseText;
10883 var o = /* eval:var:o */ eval("("+json+")");
10885 throw {message: "JsonReader.read: Json object not found"};
10891 this.metaFromRemote = true;
10892 this.meta = o.metaData;
10893 this.recordType = Roo.data.Record.create(o.metaData.fields);
10894 this.onMetaChange(this.meta, this.recordType, o);
10896 return this.readRecords(o);
10899 // private function a store will implement
10900 onMetaChange : function(meta, recordType, o){
10907 simpleAccess: function(obj, subsc) {
10914 getJsonAccessor: function(){
10916 return function(expr) {
10918 return(re.test(expr))
10919 ? new Function("obj", "return obj." + expr)
10924 return Roo.emptyFn;
10929 * Create a data block containing Roo.data.Records from an XML document.
10930 * @param {Object} o An object which contains an Array of row objects in the property specified
10931 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10932 * which contains the total size of the dataset.
10933 * @return {Object} data A data block which is used by an Roo.data.Store object as
10934 * a cache of Roo.data.Records.
10936 readRecords : function(o){
10938 * After any data loads, the raw JSON data is available for further custom processing.
10942 var s = this.meta, Record = this.recordType,
10943 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10945 // Generate extraction functions for the totalProperty, the root, the id, and for each field
10947 if(s.totalProperty) {
10948 this.getTotal = this.getJsonAccessor(s.totalProperty);
10950 if(s.successProperty) {
10951 this.getSuccess = this.getJsonAccessor(s.successProperty);
10953 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10955 var g = this.getJsonAccessor(s.id);
10956 this.getId = function(rec) {
10958 return (r === undefined || r === "") ? null : r;
10961 this.getId = function(){return null;};
10964 for(var jj = 0; jj < fl; jj++){
10966 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10967 this.ef[jj] = this.getJsonAccessor(map);
10971 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10972 if(s.totalProperty){
10973 var vt = parseInt(this.getTotal(o), 10);
10978 if(s.successProperty){
10979 var vs = this.getSuccess(o);
10980 if(vs === false || vs === 'false'){
10985 for(var i = 0; i < c; i++){
10988 var id = this.getId(n);
10989 for(var j = 0; j < fl; j++){
10991 var v = this.ef[j](n);
10993 Roo.log('missing convert for ' + f.name);
10997 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10999 var record = new Record(values, id);
11001 records[i] = record;
11007 totalRecords : totalRecords
11012 * Ext JS Library 1.1.1
11013 * Copyright(c) 2006-2007, Ext JS, LLC.
11015 * Originally Released Under LGPL - original licence link has changed is not relivant.
11018 * <script type="text/javascript">
11022 * @class Roo.data.ArrayReader
11023 * @extends Roo.data.DataReader
11024 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11025 * Each element of that Array represents a row of data fields. The
11026 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11027 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11031 var RecordDef = Roo.data.Record.create([
11032 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11033 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11035 var myReader = new Roo.data.ArrayReader({
11036 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11040 * This would consume an Array like this:
11042 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11044 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11046 * Create a new JsonReader
11047 * @param {Object} meta Metadata configuration options.
11048 * @param {Object} recordType Either an Array of field definition objects
11049 * as specified to {@link Roo.data.Record#create},
11050 * or an {@link Roo.data.Record} object
11051 * created using {@link Roo.data.Record#create}.
11053 Roo.data.ArrayReader = function(meta, recordType){
11054 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11057 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11059 * Create a data block containing Roo.data.Records from an XML document.
11060 * @param {Object} o An Array of row objects which represents the dataset.
11061 * @return {Object} data A data block which is used by an Roo.data.Store object as
11062 * a cache of Roo.data.Records.
11064 readRecords : function(o){
11065 var sid = this.meta ? this.meta.id : null;
11066 var recordType = this.recordType, fields = recordType.prototype.fields;
11069 for(var i = 0; i < root.length; i++){
11072 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11073 for(var j = 0, jlen = fields.length; j < jlen; j++){
11074 var f = fields.items[j];
11075 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11076 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11078 values[f.name] = v;
11080 var record = new recordType(values, id);
11082 records[records.length] = record;
11086 totalRecords : records.length
11095 * @class Roo.bootstrap.ComboBox
11096 * @extends Roo.bootstrap.TriggerField
11097 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11098 * @cfg {Boolean} append (true|false) default false
11099 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11100 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11101 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11102 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11103 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11104 * @cfg {Boolean} animate default true
11105 * @cfg {Boolean} emptyResultText only for touch device
11107 * Create a new ComboBox.
11108 * @param {Object} config Configuration options
11110 Roo.bootstrap.ComboBox = function(config){
11111 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11115 * Fires when the dropdown list is expanded
11116 * @param {Roo.bootstrap.ComboBox} combo This combo box
11121 * Fires when the dropdown list is collapsed
11122 * @param {Roo.bootstrap.ComboBox} combo This combo box
11126 * @event beforeselect
11127 * Fires before a list item is selected. Return false to cancel the selection.
11128 * @param {Roo.bootstrap.ComboBox} combo This combo box
11129 * @param {Roo.data.Record} record The data record returned from the underlying store
11130 * @param {Number} index The index of the selected item in the dropdown list
11132 'beforeselect' : true,
11135 * Fires when a list item is selected
11136 * @param {Roo.bootstrap.ComboBox} combo This combo box
11137 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11138 * @param {Number} index The index of the selected item in the dropdown list
11142 * @event beforequery
11143 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11144 * The event object passed has these properties:
11145 * @param {Roo.bootstrap.ComboBox} combo This combo box
11146 * @param {String} query The query
11147 * @param {Boolean} forceAll true to force "all" query
11148 * @param {Boolean} cancel true to cancel the query
11149 * @param {Object} e The query event object
11151 'beforequery': true,
11154 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11155 * @param {Roo.bootstrap.ComboBox} combo This combo box
11160 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11161 * @param {Roo.bootstrap.ComboBox} combo This combo box
11162 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11167 * Fires when the remove value from the combobox array
11168 * @param {Roo.bootstrap.ComboBox} combo This combo box
11172 * @event specialfilter
11173 * Fires when specialfilter
11174 * @param {Roo.bootstrap.ComboBox} combo This combo box
11176 'specialfilter' : true
11181 this.tickItems = [];
11183 this.selectedIndex = -1;
11184 if(this.mode == 'local'){
11185 if(config.queryDelay === undefined){
11186 this.queryDelay = 10;
11188 if(config.minChars === undefined){
11194 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11197 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11198 * rendering into an Roo.Editor, defaults to false)
11201 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11202 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11205 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11208 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11209 * the dropdown list (defaults to undefined, with no header element)
11213 * @cfg {String/Roo.Template} tpl The template to use to render the output
11217 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11219 listWidth: undefined,
11221 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11222 * mode = 'remote' or 'text' if mode = 'local')
11224 displayField: undefined,
11227 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11228 * mode = 'remote' or 'value' if mode = 'local').
11229 * Note: use of a valueField requires the user make a selection
11230 * in order for a value to be mapped.
11232 valueField: undefined,
11236 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11237 * field's data value (defaults to the underlying DOM element's name)
11239 hiddenName: undefined,
11241 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11245 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11247 selectedClass: 'active',
11250 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11254 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11255 * anchor positions (defaults to 'tl-bl')
11257 listAlign: 'tl-bl?',
11259 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11263 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11264 * query specified by the allQuery config option (defaults to 'query')
11266 triggerAction: 'query',
11268 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11269 * (defaults to 4, does not apply if editable = false)
11273 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11274 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11278 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11279 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11283 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11284 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11288 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11289 * when editable = true (defaults to false)
11291 selectOnFocus:false,
11293 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11295 queryParam: 'query',
11297 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
11298 * when mode = 'remote' (defaults to 'Loading...')
11300 loadingText: 'Loading...',
11302 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11306 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11310 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11311 * traditional select (defaults to true)
11315 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11319 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11323 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11324 * listWidth has a higher value)
11328 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11329 * allow the user to set arbitrary text into the field (defaults to false)
11331 forceSelection:false,
11333 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11334 * if typeAhead = true (defaults to 250)
11336 typeAheadDelay : 250,
11338 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11339 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11341 valueNotFoundText : undefined,
11343 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11345 blockFocus : false,
11348 * @cfg {Boolean} disableClear Disable showing of clear button.
11350 disableClear : false,
11352 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
11354 alwaysQuery : false,
11357 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
11362 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11364 invalidClass : "has-warning",
11367 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11369 validClass : "has-success",
11372 * @cfg {Boolean} specialFilter (true|false) special filter default false
11374 specialFilter : false,
11377 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11379 mobileTouchView : true,
11391 btnPosition : 'right',
11392 triggerList : true,
11393 showToggleBtn : true,
11395 emptyResultText: 'Empty',
11396 // element that contains real text value.. (when hidden is used..)
11398 getAutoCreate : function()
11406 if(Roo.isTouch && this.mobileTouchView){
11407 cfg = this.getAutoCreateTouchView();
11414 if(!this.tickable){
11415 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11420 * ComboBox with tickable selections
11423 var align = this.labelAlign || this.parentLabelAlign();
11426 cls : 'form-group roo-combobox-tickable' //input-group
11431 cls : 'tickable-buttons',
11436 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11443 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11450 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11457 buttons.cn.unshift({
11459 cls: 'select2-search-field-input'
11465 Roo.each(buttons.cn, function(c){
11467 c.cls += ' btn-' + _this.size;
11470 if (_this.disabled) {
11481 cls: 'form-hidden-field'
11485 cls: 'select2-choices',
11489 cls: 'select2-search-field',
11501 cls: 'select2-container input-group select2-container-multi',
11506 // cls: 'typeahead typeahead-long dropdown-menu',
11507 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
11512 if(this.hasFeedback && !this.allowBlank){
11516 cls: 'glyphicon form-control-feedback'
11519 combobox.cn.push(feedback);
11522 if (align ==='left' && this.fieldLabel.length) {
11524 Roo.log("left and has label");
11530 cls : 'control-label col-sm-' + this.labelWidth,
11531 html : this.fieldLabel
11535 cls : "col-sm-" + (12 - this.labelWidth),
11542 } else if ( this.fieldLabel.length) {
11548 //cls : 'input-group-addon',
11549 html : this.fieldLabel
11559 Roo.log(" no label && no align");
11566 ['xs','sm','md','lg'].map(function(size){
11567 if (settings[size]) {
11568 cfg.cls += ' col-' + size + '-' + settings[size];
11576 _initEventsCalled : false,
11579 initEvents: function()
11582 if (this._initEventsCalled) { // as we call render... prevent looping...
11585 this._initEventsCalled = true;
11588 throw "can not find store for combo";
11591 this.store = Roo.factory(this.store, Roo.data);
11593 // if we are building from html. then this element is so complex, that we can not really
11594 // use the rendered HTML.
11595 // so we have to trash and replace the previous code.
11596 if (Roo.XComponent.build_from_html) {
11598 // remove this element....
11599 var e = this.el.dom, k=0;
11600 while (e ) { e = e.previousSibling; ++k;}
11605 this.rendered = false;
11607 this.render(this.parent().getChildContainer(true), k);
11618 if(Roo.isTouch && this.mobileTouchView){
11619 this.initTouchView();
11624 this.initTickableEvents();
11628 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11630 if(this.hiddenName){
11632 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11634 this.hiddenField.dom.value =
11635 this.hiddenValue !== undefined ? this.hiddenValue :
11636 this.value !== undefined ? this.value : '';
11638 // prevent input submission
11639 this.el.dom.removeAttribute('name');
11640 this.hiddenField.dom.setAttribute('name', this.hiddenName);
11645 // this.el.dom.setAttribute('autocomplete', 'off');
11648 var cls = 'x-combo-list';
11650 //this.list = new Roo.Layer({
11651 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11657 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11658 _this.list.setWidth(lw);
11661 this.list.on('mouseover', this.onViewOver, this);
11662 this.list.on('mousemove', this.onViewMove, this);
11664 this.list.on('scroll', this.onViewScroll, this);
11667 this.list.swallowEvent('mousewheel');
11668 this.assetHeight = 0;
11671 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11672 this.assetHeight += this.header.getHeight();
11675 this.innerList = this.list.createChild({cls:cls+'-inner'});
11676 this.innerList.on('mouseover', this.onViewOver, this);
11677 this.innerList.on('mousemove', this.onViewMove, this);
11678 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11680 if(this.allowBlank && !this.pageSize && !this.disableClear){
11681 this.footer = this.list.createChild({cls:cls+'-ft'});
11682 this.pageTb = new Roo.Toolbar(this.footer);
11686 this.footer = this.list.createChild({cls:cls+'-ft'});
11687 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11688 {pageSize: this.pageSize});
11692 if (this.pageTb && this.allowBlank && !this.disableClear) {
11694 this.pageTb.add(new Roo.Toolbar.Fill(), {
11695 cls: 'x-btn-icon x-btn-clear',
11697 handler: function()
11700 _this.clearValue();
11701 _this.onSelect(false, -1);
11706 this.assetHeight += this.footer.getHeight();
11711 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11714 this.view = new Roo.View(this.list, this.tpl, {
11715 singleSelect:true, store: this.store, selectedClass: this.selectedClass
11717 //this.view.wrapEl.setDisplayed(false);
11718 this.view.on('click', this.onViewClick, this);
11722 this.store.on('beforeload', this.onBeforeLoad, this);
11723 this.store.on('load', this.onLoad, this);
11724 this.store.on('loadexception', this.onLoadException, this);
11726 if(this.resizable){
11727 this.resizer = new Roo.Resizable(this.list, {
11728 pinned:true, handles:'se'
11730 this.resizer.on('resize', function(r, w, h){
11731 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11732 this.listWidth = w;
11733 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11734 this.restrictHeight();
11736 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11739 if(!this.editable){
11740 this.editable = true;
11741 this.setEditable(false);
11746 if (typeof(this.events.add.listeners) != 'undefined') {
11748 this.addicon = this.wrap.createChild(
11749 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
11751 this.addicon.on('click', function(e) {
11752 this.fireEvent('add', this);
11755 if (typeof(this.events.edit.listeners) != 'undefined') {
11757 this.editicon = this.wrap.createChild(
11758 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
11759 if (this.addicon) {
11760 this.editicon.setStyle('margin-left', '40px');
11762 this.editicon.on('click', function(e) {
11764 // we fire even if inothing is selected..
11765 this.fireEvent('edit', this, this.lastData );
11771 this.keyNav = new Roo.KeyNav(this.inputEl(), {
11772 "up" : function(e){
11773 this.inKeyMode = true;
11777 "down" : function(e){
11778 if(!this.isExpanded()){
11779 this.onTriggerClick();
11781 this.inKeyMode = true;
11786 "enter" : function(e){
11787 // this.onViewClick();
11791 if(this.fireEvent("specialkey", this, e)){
11792 this.onViewClick(false);
11798 "esc" : function(e){
11802 "tab" : function(e){
11805 if(this.fireEvent("specialkey", this, e)){
11806 this.onViewClick(false);
11814 doRelay : function(foo, bar, hname){
11815 if(hname == 'down' || this.scope.isExpanded()){
11816 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11825 this.queryDelay = Math.max(this.queryDelay || 10,
11826 this.mode == 'local' ? 10 : 250);
11829 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11831 if(this.typeAhead){
11832 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11834 if(this.editable !== false){
11835 this.inputEl().on("keyup", this.onKeyUp, this);
11837 if(this.forceSelection){
11838 this.inputEl().on('blur', this.doForce, this);
11842 this.choices = this.el.select('ul.select2-choices', true).first();
11843 this.searchField = this.el.select('ul li.select2-search-field', true).first();
11847 initTickableEvents: function()
11851 if(this.hiddenName){
11853 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11855 this.hiddenField.dom.value =
11856 this.hiddenValue !== undefined ? this.hiddenValue :
11857 this.value !== undefined ? this.value : '';
11859 // prevent input submission
11860 this.el.dom.removeAttribute('name');
11861 this.hiddenField.dom.setAttribute('name', this.hiddenName);
11866 // this.list = this.el.select('ul.dropdown-menu',true).first();
11868 this.choices = this.el.select('ul.select2-choices', true).first();
11869 this.searchField = this.el.select('ul li.select2-search-field', true).first();
11870 if(this.triggerList){
11871 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11874 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11875 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11877 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11878 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11880 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11881 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11883 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11884 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11885 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11888 this.cancelBtn.hide();
11893 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11894 _this.list.setWidth(lw);
11897 this.list.on('mouseover', this.onViewOver, this);
11898 this.list.on('mousemove', this.onViewMove, this);
11900 this.list.on('scroll', this.onViewScroll, this);
11903 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>';
11906 this.view = new Roo.View(this.list, this.tpl, {
11907 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11910 //this.view.wrapEl.setDisplayed(false);
11911 this.view.on('click', this.onViewClick, this);
11915 this.store.on('beforeload', this.onBeforeLoad, this);
11916 this.store.on('load', this.onLoad, this);
11917 this.store.on('loadexception', this.onLoadException, this);
11920 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11921 "up" : function(e){
11922 this.inKeyMode = true;
11926 "down" : function(e){
11927 this.inKeyMode = true;
11931 "enter" : function(e){
11932 if(this.fireEvent("specialkey", this, e)){
11933 this.onViewClick(false);
11939 "esc" : function(e){
11940 this.onTickableFooterButtonClick(e, false, false);
11943 "tab" : function(e){
11944 this.fireEvent("specialkey", this, e);
11946 this.onTickableFooterButtonClick(e, false, false);
11953 doRelay : function(e, fn, key){
11954 if(this.scope.isExpanded()){
11955 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11964 this.queryDelay = Math.max(this.queryDelay || 10,
11965 this.mode == 'local' ? 10 : 250);
11968 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11970 if(this.typeAhead){
11971 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11974 if(this.editable !== false){
11975 this.tickableInputEl().on("keyup", this.onKeyUp, this);
11980 onDestroy : function(){
11982 this.view.setStore(null);
11983 this.view.el.removeAllListeners();
11984 this.view.el.remove();
11985 this.view.purgeListeners();
11988 this.list.dom.innerHTML = '';
11992 this.store.un('beforeload', this.onBeforeLoad, this);
11993 this.store.un('load', this.onLoad, this);
11994 this.store.un('loadexception', this.onLoadException, this);
11996 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12000 fireKey : function(e){
12001 if(e.isNavKeyPress() && !this.list.isVisible()){
12002 this.fireEvent("specialkey", this, e);
12007 onResize: function(w, h){
12008 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12010 // if(typeof w != 'number'){
12011 // // we do not handle it!?!?
12014 // var tw = this.trigger.getWidth();
12015 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12016 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12018 // this.inputEl().setWidth( this.adjustWidth('input', x));
12020 // //this.trigger.setStyle('left', x+'px');
12022 // if(this.list && this.listWidth === undefined){
12023 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12024 // this.list.setWidth(lw);
12025 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12033 * Allow or prevent the user from directly editing the field text. If false is passed,
12034 * the user will only be able to select from the items defined in the dropdown list. This method
12035 * is the runtime equivalent of setting the 'editable' config option at config time.
12036 * @param {Boolean} value True to allow the user to directly edit the field text
12038 setEditable : function(value){
12039 if(value == this.editable){
12042 this.editable = value;
12044 this.inputEl().dom.setAttribute('readOnly', true);
12045 this.inputEl().on('mousedown', this.onTriggerClick, this);
12046 this.inputEl().addClass('x-combo-noedit');
12048 this.inputEl().dom.setAttribute('readOnly', false);
12049 this.inputEl().un('mousedown', this.onTriggerClick, this);
12050 this.inputEl().removeClass('x-combo-noedit');
12056 onBeforeLoad : function(combo,opts){
12057 if(!this.hasFocus){
12061 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12063 this.restrictHeight();
12064 this.selectedIndex = -1;
12068 onLoad : function(){
12070 this.hasQuery = false;
12072 if(!this.hasFocus){
12076 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12077 this.loading.hide();
12080 if(this.store.getCount() > 0){
12082 this.restrictHeight();
12083 if(this.lastQuery == this.allQuery){
12084 if(this.editable && !this.tickable){
12085 this.inputEl().dom.select();
12089 !this.selectByValue(this.value, true) &&
12092 !this.store.lastOptions ||
12093 typeof(this.store.lastOptions.add) == 'undefined' ||
12094 this.store.lastOptions.add != true
12097 this.select(0, true);
12100 if(this.autoFocus){
12103 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12104 this.taTask.delay(this.typeAheadDelay);
12108 this.onEmptyResults();
12114 onLoadException : function()
12116 this.hasQuery = false;
12118 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12119 this.loading.hide();
12122 if(this.tickable && this.editable){
12128 Roo.log(this.store.reader.jsonData);
12129 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12131 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12137 onTypeAhead : function(){
12138 if(this.store.getCount() > 0){
12139 var r = this.store.getAt(0);
12140 var newValue = r.data[this.displayField];
12141 var len = newValue.length;
12142 var selStart = this.getRawValue().length;
12144 if(selStart != len){
12145 this.setRawValue(newValue);
12146 this.selectText(selStart, newValue.length);
12152 onSelect : function(record, index){
12154 if(this.fireEvent('beforeselect', this, record, index) !== false){
12156 this.setFromData(index > -1 ? record.data : false);
12159 this.fireEvent('select', this, record, index);
12164 * Returns the currently selected field value or empty string if no value is set.
12165 * @return {String} value The selected value
12167 getValue : function(){
12170 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12173 if(this.valueField){
12174 return typeof this.value != 'undefined' ? this.value : '';
12176 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12181 * Clears any text/value currently set in the field
12183 clearValue : function(){
12184 if(this.hiddenField){
12185 this.hiddenField.dom.value = '';
12188 this.setRawValue('');
12189 this.lastSelectionText = '';
12190 this.lastData = false;
12192 var close = this.closeTriggerEl();
12201 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12202 * will be displayed in the field. If the value does not match the data value of an existing item,
12203 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12204 * Otherwise the field will be blank (although the value will still be set).
12205 * @param {String} value The value to match
12207 setValue : function(v){
12214 if(this.valueField){
12215 var r = this.findRecord(this.valueField, v);
12217 text = r.data[this.displayField];
12218 }else if(this.valueNotFoundText !== undefined){
12219 text = this.valueNotFoundText;
12222 this.lastSelectionText = text;
12223 if(this.hiddenField){
12224 this.hiddenField.dom.value = v;
12226 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12229 var close = this.closeTriggerEl();
12232 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12236 * @property {Object} the last set data for the element
12241 * Sets the value of the field based on a object which is related to the record format for the store.
12242 * @param {Object} value the value to set as. or false on reset?
12244 setFromData : function(o){
12251 var dv = ''; // display value
12252 var vv = ''; // value value..
12254 if (this.displayField) {
12255 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12257 // this is an error condition!!!
12258 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12261 if(this.valueField){
12262 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12265 var close = this.closeTriggerEl();
12268 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12271 if(this.hiddenField){
12272 this.hiddenField.dom.value = vv;
12274 this.lastSelectionText = dv;
12275 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12279 // no hidden field.. - we store the value in 'value', but still display
12280 // display field!!!!
12281 this.lastSelectionText = dv;
12282 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12289 reset : function(){
12290 // overridden so that last data is reset..
12297 this.setValue(this.originalValue);
12298 this.clearInvalid();
12299 this.lastData = false;
12301 this.view.clearSelections();
12305 findRecord : function(prop, value){
12307 if(this.store.getCount() > 0){
12308 this.store.each(function(r){
12309 if(r.data[prop] == value){
12319 getName: function()
12321 // returns hidden if it's set..
12322 if (!this.rendered) {return ''};
12323 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
12327 onViewMove : function(e, t){
12328 this.inKeyMode = false;
12332 onViewOver : function(e, t){
12333 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12336 var item = this.view.findItemFromChild(t);
12339 var index = this.view.indexOf(item);
12340 this.select(index, false);
12345 onViewClick : function(view, doFocus, el, e)
12347 var index = this.view.getSelectedIndexes()[0];
12349 var r = this.store.getAt(index);
12353 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12360 Roo.each(this.tickItems, function(v,k){
12362 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12363 _this.tickItems.splice(k, 1);
12365 if(typeof(e) == 'undefined' && view == false){
12366 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12378 this.tickItems.push(r.data);
12380 if(typeof(e) == 'undefined' && view == false){
12381 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12388 this.onSelect(r, index);
12390 if(doFocus !== false && !this.blockFocus){
12391 this.inputEl().focus();
12396 restrictHeight : function(){
12397 //this.innerList.dom.style.height = '';
12398 //var inner = this.innerList.dom;
12399 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12400 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12401 //this.list.beginUpdate();
12402 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12403 this.list.alignTo(this.inputEl(), this.listAlign);
12404 this.list.alignTo(this.inputEl(), this.listAlign);
12405 //this.list.endUpdate();
12409 onEmptyResults : function(){
12411 if(this.tickable && this.editable){
12412 this.restrictHeight();
12420 * Returns true if the dropdown list is expanded, else false.
12422 isExpanded : function(){
12423 return this.list.isVisible();
12427 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12428 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12429 * @param {String} value The data value of the item to select
12430 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12431 * selected item if it is not currently in view (defaults to true)
12432 * @return {Boolean} True if the value matched an item in the list, else false
12434 selectByValue : function(v, scrollIntoView){
12435 if(v !== undefined && v !== null){
12436 var r = this.findRecord(this.valueField || this.displayField, v);
12438 this.select(this.store.indexOf(r), scrollIntoView);
12446 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12447 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12448 * @param {Number} index The zero-based index of the list item to select
12449 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12450 * selected item if it is not currently in view (defaults to true)
12452 select : function(index, scrollIntoView){
12453 this.selectedIndex = index;
12454 this.view.select(index);
12455 if(scrollIntoView !== false){
12456 var el = this.view.getNode(index);
12458 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12461 this.list.scrollChildIntoView(el, false);
12467 selectNext : function(){
12468 var ct = this.store.getCount();
12470 if(this.selectedIndex == -1){
12472 }else if(this.selectedIndex < ct-1){
12473 this.select(this.selectedIndex+1);
12479 selectPrev : function(){
12480 var ct = this.store.getCount();
12482 if(this.selectedIndex == -1){
12484 }else if(this.selectedIndex != 0){
12485 this.select(this.selectedIndex-1);
12491 onKeyUp : function(e){
12492 if(this.editable !== false && !e.isSpecialKey()){
12493 this.lastKey = e.getKey();
12494 this.dqTask.delay(this.queryDelay);
12499 validateBlur : function(){
12500 return !this.list || !this.list.isVisible();
12504 initQuery : function(){
12506 var v = this.getRawValue();
12508 if(this.tickable && this.editable){
12509 v = this.tickableInputEl().getValue();
12516 doForce : function(){
12517 if(this.inputEl().dom.value.length > 0){
12518 this.inputEl().dom.value =
12519 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12525 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
12526 * query allowing the query action to be canceled if needed.
12527 * @param {String} query The SQL query to execute
12528 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12529 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
12530 * saved in the current store (defaults to false)
12532 doQuery : function(q, forceAll){
12534 if(q === undefined || q === null){
12539 forceAll: forceAll,
12543 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12548 forceAll = qe.forceAll;
12549 if(forceAll === true || (q.length >= this.minChars)){
12551 this.hasQuery = true;
12553 if(this.lastQuery != q || this.alwaysQuery){
12554 this.lastQuery = q;
12555 if(this.mode == 'local'){
12556 this.selectedIndex = -1;
12558 this.store.clearFilter();
12561 if(this.specialFilter){
12562 this.fireEvent('specialfilter', this);
12567 this.store.filter(this.displayField, q);
12570 this.store.fireEvent("datachanged", this.store);
12577 this.store.baseParams[this.queryParam] = q;
12579 var options = {params : this.getParams(q)};
12582 options.add = true;
12583 options.params.start = this.page * this.pageSize;
12586 this.store.load(options);
12589 * this code will make the page width larger, at the beginning, the list not align correctly,
12590 * we should expand the list on onLoad
12591 * so command out it
12596 this.selectedIndex = -1;
12601 this.loadNext = false;
12605 getParams : function(q){
12607 //p[this.queryParam] = q;
12611 p.limit = this.pageSize;
12617 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12619 collapse : function(){
12620 if(!this.isExpanded()){
12627 this.hasFocus = false;
12629 this.cancelBtn.hide();
12630 this.trigger.show();
12633 this.tickableInputEl().dom.value = '';
12634 this.tickableInputEl().blur();
12639 Roo.get(document).un('mousedown', this.collapseIf, this);
12640 Roo.get(document).un('mousewheel', this.collapseIf, this);
12641 if (!this.editable) {
12642 Roo.get(document).un('keydown', this.listKeyPress, this);
12644 this.fireEvent('collapse', this);
12648 collapseIf : function(e){
12649 var in_combo = e.within(this.el);
12650 var in_list = e.within(this.list);
12651 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12653 if (in_combo || in_list || is_list) {
12654 //e.stopPropagation();
12659 this.onTickableFooterButtonClick(e, false, false);
12667 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12669 expand : function(){
12671 if(this.isExpanded() || !this.hasFocus){
12675 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12676 this.list.setWidth(lw);
12683 this.restrictHeight();
12687 this.tickItems = Roo.apply([], this.item);
12690 this.cancelBtn.show();
12691 this.trigger.hide();
12694 this.tickableInputEl().focus();
12699 Roo.get(document).on('mousedown', this.collapseIf, this);
12700 Roo.get(document).on('mousewheel', this.collapseIf, this);
12701 if (!this.editable) {
12702 Roo.get(document).on('keydown', this.listKeyPress, this);
12705 this.fireEvent('expand', this);
12709 // Implements the default empty TriggerField.onTriggerClick function
12710 onTriggerClick : function(e)
12712 Roo.log('trigger click');
12714 if(this.disabled || !this.triggerList){
12719 this.loadNext = false;
12721 if(this.isExpanded()){
12723 if (!this.blockFocus) {
12724 this.inputEl().focus();
12728 this.hasFocus = true;
12729 if(this.triggerAction == 'all') {
12730 this.doQuery(this.allQuery, true);
12732 this.doQuery(this.getRawValue());
12734 if (!this.blockFocus) {
12735 this.inputEl().focus();
12740 onTickableTriggerClick : function(e)
12747 this.loadNext = false;
12748 this.hasFocus = true;
12750 if(this.triggerAction == 'all') {
12751 this.doQuery(this.allQuery, true);
12753 this.doQuery(this.getRawValue());
12757 onSearchFieldClick : function(e)
12759 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12760 this.onTickableFooterButtonClick(e, false, false);
12764 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12769 this.loadNext = false;
12770 this.hasFocus = true;
12772 if(this.triggerAction == 'all') {
12773 this.doQuery(this.allQuery, true);
12775 this.doQuery(this.getRawValue());
12779 listKeyPress : function(e)
12781 //Roo.log('listkeypress');
12782 // scroll to first matching element based on key pres..
12783 if (e.isSpecialKey()) {
12786 var k = String.fromCharCode(e.getKey()).toUpperCase();
12789 var csel = this.view.getSelectedNodes();
12790 var cselitem = false;
12792 var ix = this.view.indexOf(csel[0]);
12793 cselitem = this.store.getAt(ix);
12794 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12800 this.store.each(function(v) {
12802 // start at existing selection.
12803 if (cselitem.id == v.id) {
12809 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12810 match = this.store.indexOf(v);
12816 if (match === false) {
12817 return true; // no more action?
12820 this.view.select(match);
12821 var sn = Roo.get(this.view.getSelectedNodes()[0])
12822 sn.scrollIntoView(sn.dom.parentNode, false);
12825 onViewScroll : function(e, t){
12827 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){
12831 this.hasQuery = true;
12833 this.loading = this.list.select('.loading', true).first();
12835 if(this.loading === null){
12836 this.list.createChild({
12838 cls: 'loading select2-more-results select2-active',
12839 html: 'Loading more results...'
12842 this.loading = this.list.select('.loading', true).first();
12844 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12846 this.loading.hide();
12849 this.loading.show();
12854 this.loadNext = true;
12856 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12861 addItem : function(o)
12863 var dv = ''; // display value
12865 if (this.displayField) {
12866 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12868 // this is an error condition!!!
12869 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12876 var choice = this.choices.createChild({
12878 cls: 'select2-search-choice',
12887 cls: 'select2-search-choice-close',
12892 }, this.searchField);
12894 var close = choice.select('a.select2-search-choice-close', true).first()
12896 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12904 this.inputEl().dom.value = '';
12909 onRemoveItem : function(e, _self, o)
12911 e.preventDefault();
12913 this.lastItem = Roo.apply([], this.item);
12915 var index = this.item.indexOf(o.data) * 1;
12918 Roo.log('not this item?!');
12922 this.item.splice(index, 1);
12927 this.fireEvent('remove', this, e);
12933 syncValue : function()
12935 if(!this.item.length){
12942 Roo.each(this.item, function(i){
12943 if(_this.valueField){
12944 value.push(i[_this.valueField]);
12951 this.value = value.join(',');
12953 if(this.hiddenField){
12954 this.hiddenField.dom.value = this.value;
12957 this.store.fireEvent("datachanged", this.store);
12960 clearItem : function()
12962 if(!this.multiple){
12968 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12976 if(this.tickable && !Roo.isTouch){
12977 this.view.refresh();
12981 inputEl: function ()
12983 if(Roo.isTouch && this.mobileTouchView){
12984 return this.el.select('input.form-control',true).first();
12988 return this.searchField;
12991 return this.el.select('input.form-control',true).first();
12995 onTickableFooterButtonClick : function(e, btn, el)
12997 e.preventDefault();
12999 this.lastItem = Roo.apply([], this.item);
13001 if(btn && btn.name == 'cancel'){
13002 this.tickItems = Roo.apply([], this.item);
13011 Roo.each(this.tickItems, function(o){
13019 validate : function()
13021 var v = this.getRawValue();
13024 v = this.getValue();
13027 if(this.disabled || this.allowBlank || v.length){
13032 this.markInvalid();
13036 tickableInputEl : function()
13038 if(!this.tickable || !this.editable){
13039 return this.inputEl();
13042 return this.inputEl().select('.select2-search-field-input', true).first();
13046 getAutoCreateTouchView : function()
13051 cls: 'form-group' //input-group
13057 type : this.inputType,
13058 cls : 'form-control x-combo-noedit',
13059 autocomplete: 'new-password',
13060 placeholder : this.placeholder || '',
13065 input.name = this.name;
13069 input.cls += ' input-' + this.size;
13072 if (this.disabled) {
13073 input.disabled = true;
13084 inputblock.cls += ' input-group';
13086 inputblock.cn.unshift({
13088 cls : 'input-group-addon',
13093 if(this.removable && !this.multiple){
13094 inputblock.cls += ' roo-removable';
13096 inputblock.cn.push({
13099 cls : 'roo-combo-removable-btn close'
13103 if(this.hasFeedback && !this.allowBlank){
13105 inputblock.cls += ' has-feedback';
13107 inputblock.cn.push({
13109 cls: 'glyphicon form-control-feedback'
13116 inputblock.cls += (this.before) ? '' : ' input-group';
13118 inputblock.cn.push({
13120 cls : 'input-group-addon',
13131 cls: 'form-hidden-field'
13145 cls: 'form-hidden-field'
13149 cls: 'select2-choices',
13153 cls: 'select2-search-field',
13166 cls: 'select2-container input-group',
13173 combobox.cls += ' select2-container-multi';
13176 var align = this.labelAlign || this.parentLabelAlign();
13180 if(this.fieldLabel.length){
13182 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13183 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13188 cls : 'control-label ' + lw,
13189 html : this.fieldLabel
13201 var settings = this;
13203 ['xs','sm','md','lg'].map(function(size){
13204 if (settings[size]) {
13205 cfg.cls += ' col-' + size + '-' + settings[size];
13212 initTouchView : function()
13214 this.renderTouchView();
13216 this.touchViewEl.on('scroll', function(){
13217 this.el.dom.scrollTop = 0;
13220 this.inputEl().on("click", this.showTouchView, this);
13221 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13222 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13224 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13226 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13227 this.store.on('load', this.onTouchViewLoad, this);
13228 this.store.on('loadexception', this.onTouchViewLoadException, this);
13230 if(this.hiddenName){
13232 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13234 this.hiddenField.dom.value =
13235 this.hiddenValue !== undefined ? this.hiddenValue :
13236 this.value !== undefined ? this.value : '';
13238 this.el.dom.removeAttribute('name');
13239 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13243 this.choices = this.el.select('ul.select2-choices', true).first();
13244 this.searchField = this.el.select('ul li.select2-search-field', true).first();
13247 if(this.removable && !this.multiple){
13248 var close = this.closeTriggerEl();
13250 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13251 close.on('click', this.removeBtnClick, this, close);
13260 renderTouchView : function()
13262 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13263 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13265 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13266 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13268 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13269 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13270 this.touchViewBodyEl.setStyle('overflow', 'auto');
13272 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13273 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13275 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13276 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13280 showTouchView : function()
13282 this.touchViewHeaderEl.hide();
13284 if(this.fieldLabel.length){
13285 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13286 this.touchViewHeaderEl.show();
13289 this.touchViewEl.show();
13291 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13292 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13294 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13296 if(this.fieldLabel.length){
13297 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13300 this.touchViewBodyEl.setHeight(bodyHeight);
13304 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13306 this.touchViewEl.addClass('in');
13309 this.doTouchViewQuery();
13313 hideTouchView : function()
13315 this.touchViewEl.removeClass('in');
13319 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13321 this.touchViewEl.setStyle('display', 'none');
13326 setTouchViewValue : function()
13333 Roo.each(this.tickItems, function(o){
13338 this.hideTouchView();
13341 doTouchViewQuery : function()
13350 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13354 if(!this.alwaysQuery || this.mode == 'local'){
13355 this.onTouchViewLoad();
13362 onTouchViewBeforeLoad : function(combo,opts)
13368 onTouchViewLoad : function()
13370 if(this.store.getCount() < 1){
13371 this.onTouchViewEmptyResults();
13375 this.clearTouchView();
13377 var rawValue = this.getRawValue();
13379 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13381 this.tickItems = [];
13383 this.store.data.each(function(d, rowIndex){
13384 var row = this.touchViewListGroup.createChild(template);
13386 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13387 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13390 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13391 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13394 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13395 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13396 this.tickItems.push(d.data);
13399 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13403 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13405 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13407 if(this.fieldLabel.length){
13408 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13411 var listHeight = this.touchViewListGroup.getHeight();
13415 if(firstChecked && listHeight > bodyHeight){
13416 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13421 onTouchViewLoadException : function()
13423 this.hideTouchView();
13426 onTouchViewEmptyResults : function()
13428 this.clearTouchView();
13430 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13432 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13436 clearTouchView : function()
13438 this.touchViewListGroup.dom.innerHTML = '';
13441 onTouchViewClick : function(e, el, o)
13443 e.preventDefault();
13446 var rowIndex = o.rowIndex;
13448 var r = this.store.getAt(rowIndex);
13450 if(!this.multiple){
13451 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13452 c.dom.removeAttribute('checked');
13455 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13457 this.setFromData(r.data);
13459 var close = this.closeTriggerEl();
13465 this.hideTouchView();
13467 this.fireEvent('select', this, r, rowIndex);
13472 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13473 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13474 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13478 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13479 this.addItem(r.data);
13480 this.tickItems.push(r.data);
13486 * @cfg {Boolean} grow
13490 * @cfg {Number} growMin
13494 * @cfg {Number} growMax
13503 Roo.apply(Roo.bootstrap.ComboBox, {
13507 cls: 'modal-header',
13529 cls: 'list-group-item',
13533 cls: 'roo-combobox-list-group-item-value'
13537 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13551 listItemCheckbox : {
13553 cls: 'list-group-item',
13557 cls: 'roo-combobox-list-group-item-value'
13561 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13577 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13582 cls: 'modal-footer',
13590 cls: 'col-xs-6 text-left',
13593 cls: 'btn btn-danger roo-touch-view-cancel',
13599 cls: 'col-xs-6 text-right',
13602 cls: 'btn btn-success roo-touch-view-ok',
13613 Roo.apply(Roo.bootstrap.ComboBox, {
13615 touchViewTemplate : {
13617 cls: 'modal fade roo-combobox-touch-view',
13621 cls: 'modal-dialog',
13625 cls: 'modal-content',
13627 Roo.bootstrap.ComboBox.header,
13628 Roo.bootstrap.ComboBox.body,
13629 Roo.bootstrap.ComboBox.footer
13638 * Ext JS Library 1.1.1
13639 * Copyright(c) 2006-2007, Ext JS, LLC.
13641 * Originally Released Under LGPL - original licence link has changed is not relivant.
13644 * <script type="text/javascript">
13649 * @extends Roo.util.Observable
13650 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
13651 * This class also supports single and multi selection modes. <br>
13652 * Create a data model bound view:
13654 var store = new Roo.data.Store(...);
13656 var view = new Roo.View({
13658 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
13660 singleSelect: true,
13661 selectedClass: "ydataview-selected",
13665 // listen for node click?
13666 view.on("click", function(vw, index, node, e){
13667 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13671 dataModel.load("foobar.xml");
13673 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13675 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13676 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13678 * Note: old style constructor is still suported (container, template, config)
13681 * Create a new View
13682 * @param {Object} config The config object
13685 Roo.View = function(config, depreciated_tpl, depreciated_config){
13687 this.parent = false;
13689 if (typeof(depreciated_tpl) == 'undefined') {
13690 // new way.. - universal constructor.
13691 Roo.apply(this, config);
13692 this.el = Roo.get(this.el);
13695 this.el = Roo.get(config);
13696 this.tpl = depreciated_tpl;
13697 Roo.apply(this, depreciated_config);
13699 this.wrapEl = this.el.wrap().wrap();
13700 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13703 if(typeof(this.tpl) == "string"){
13704 this.tpl = new Roo.Template(this.tpl);
13706 // support xtype ctors..
13707 this.tpl = new Roo.factory(this.tpl, Roo);
13711 this.tpl.compile();
13716 * @event beforeclick
13717 * Fires before a click is processed. Returns false to cancel the default action.
13718 * @param {Roo.View} this
13719 * @param {Number} index The index of the target node
13720 * @param {HTMLElement} node The target node
13721 * @param {Roo.EventObject} e The raw event object
13723 "beforeclick" : true,
13726 * Fires when a template node is clicked.
13727 * @param {Roo.View} this
13728 * @param {Number} index The index of the target node
13729 * @param {HTMLElement} node The target node
13730 * @param {Roo.EventObject} e The raw event object
13735 * Fires when a template node is double clicked.
13736 * @param {Roo.View} this
13737 * @param {Number} index The index of the target node
13738 * @param {HTMLElement} node The target node
13739 * @param {Roo.EventObject} e The raw event object
13743 * @event contextmenu
13744 * Fires when a template node is right clicked.
13745 * @param {Roo.View} this
13746 * @param {Number} index The index of the target node
13747 * @param {HTMLElement} node The target node
13748 * @param {Roo.EventObject} e The raw event object
13750 "contextmenu" : true,
13752 * @event selectionchange
13753 * Fires when the selected nodes change.
13754 * @param {Roo.View} this
13755 * @param {Array} selections Array of the selected nodes
13757 "selectionchange" : true,
13760 * @event beforeselect
13761 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13762 * @param {Roo.View} this
13763 * @param {HTMLElement} node The node to be selected
13764 * @param {Array} selections Array of currently selected nodes
13766 "beforeselect" : true,
13768 * @event preparedata
13769 * Fires on every row to render, to allow you to change the data.
13770 * @param {Roo.View} this
13771 * @param {Object} data to be rendered (change this)
13773 "preparedata" : true
13781 "click": this.onClick,
13782 "dblclick": this.onDblClick,
13783 "contextmenu": this.onContextMenu,
13787 this.selections = [];
13789 this.cmp = new Roo.CompositeElementLite([]);
13791 this.store = Roo.factory(this.store, Roo.data);
13792 this.setStore(this.store, true);
13795 if ( this.footer && this.footer.xtype) {
13797 var fctr = this.wrapEl.appendChild(document.createElement("div"));
13799 this.footer.dataSource = this.store
13800 this.footer.container = fctr;
13801 this.footer = Roo.factory(this.footer, Roo);
13802 fctr.insertFirst(this.el);
13804 // this is a bit insane - as the paging toolbar seems to detach the el..
13805 // dom.parentNode.parentNode.parentNode
13806 // they get detached?
13810 Roo.View.superclass.constructor.call(this);
13815 Roo.extend(Roo.View, Roo.util.Observable, {
13818 * @cfg {Roo.data.Store} store Data store to load data from.
13823 * @cfg {String|Roo.Element} el The container element.
13828 * @cfg {String|Roo.Template} tpl The template used by this View
13832 * @cfg {String} dataName the named area of the template to use as the data area
13833 * Works with domtemplates roo-name="name"
13837 * @cfg {String} selectedClass The css class to add to selected nodes
13839 selectedClass : "x-view-selected",
13841 * @cfg {String} emptyText The empty text to show when nothing is loaded.
13846 * @cfg {String} text to display on mask (default Loading)
13850 * @cfg {Boolean} multiSelect Allow multiple selection
13852 multiSelect : false,
13854 * @cfg {Boolean} singleSelect Allow single selection
13856 singleSelect: false,
13859 * @cfg {Boolean} toggleSelect - selecting
13861 toggleSelect : false,
13864 * @cfg {Boolean} tickable - selecting
13869 * Returns the element this view is bound to.
13870 * @return {Roo.Element}
13872 getEl : function(){
13873 return this.wrapEl;
13879 * Refreshes the view. - called by datachanged on the store. - do not call directly.
13881 refresh : function(){
13882 //Roo.log('refresh');
13885 // if we are using something like 'domtemplate', then
13886 // the what gets used is:
13887 // t.applySubtemplate(NAME, data, wrapping data..)
13888 // the outer template then get' applied with
13889 // the store 'extra data'
13890 // and the body get's added to the
13891 // roo-name="data" node?
13892 // <span class='roo-tpl-{name}'></span> ?????
13896 this.clearSelections();
13897 this.el.update("");
13899 var records = this.store.getRange();
13900 if(records.length < 1) {
13902 // is this valid?? = should it render a template??
13904 this.el.update(this.emptyText);
13908 if (this.dataName) {
13909 this.el.update(t.apply(this.store.meta)); //????
13910 el = this.el.child('.roo-tpl-' + this.dataName);
13913 for(var i = 0, len = records.length; i < len; i++){
13914 var data = this.prepareData(records[i].data, i, records[i]);
13915 this.fireEvent("preparedata", this, data, i, records[i]);
13917 var d = Roo.apply({}, data);
13920 Roo.apply(d, {'roo-id' : Roo.id()});
13924 Roo.each(this.parent.item, function(item){
13925 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
13928 Roo.apply(d, {'roo-data-checked' : 'checked'});
13932 html[html.length] = Roo.util.Format.trim(
13934 t.applySubtemplate(this.dataName, d, this.store.meta) :
13941 el.update(html.join(""));
13942 this.nodes = el.dom.childNodes;
13943 this.updateIndexes(0);
13948 * Function to override to reformat the data that is sent to
13949 * the template for each node.
13950 * DEPRICATED - use the preparedata event handler.
13951 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
13952 * a JSON object for an UpdateManager bound view).
13954 prepareData : function(data, index, record)
13956 this.fireEvent("preparedata", this, data, index, record);
13960 onUpdate : function(ds, record){
13961 // Roo.log('on update');
13962 this.clearSelections();
13963 var index = this.store.indexOf(record);
13964 var n = this.nodes[index];
13965 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
13966 n.parentNode.removeChild(n);
13967 this.updateIndexes(index, index);
13973 onAdd : function(ds, records, index)
13975 //Roo.log(['on Add', ds, records, index] );
13976 this.clearSelections();
13977 if(this.nodes.length == 0){
13981 var n = this.nodes[index];
13982 for(var i = 0, len = records.length; i < len; i++){
13983 var d = this.prepareData(records[i].data, i, records[i]);
13985 this.tpl.insertBefore(n, d);
13988 this.tpl.append(this.el, d);
13991 this.updateIndexes(index);
13994 onRemove : function(ds, record, index){
13995 // Roo.log('onRemove');
13996 this.clearSelections();
13997 var el = this.dataName ?
13998 this.el.child('.roo-tpl-' + this.dataName) :
14001 el.dom.removeChild(this.nodes[index]);
14002 this.updateIndexes(index);
14006 * Refresh an individual node.
14007 * @param {Number} index
14009 refreshNode : function(index){
14010 this.onUpdate(this.store, this.store.getAt(index));
14013 updateIndexes : function(startIndex, endIndex){
14014 var ns = this.nodes;
14015 startIndex = startIndex || 0;
14016 endIndex = endIndex || ns.length - 1;
14017 for(var i = startIndex; i <= endIndex; i++){
14018 ns[i].nodeIndex = i;
14023 * Changes the data store this view uses and refresh the view.
14024 * @param {Store} store
14026 setStore : function(store, initial){
14027 if(!initial && this.store){
14028 this.store.un("datachanged", this.refresh);
14029 this.store.un("add", this.onAdd);
14030 this.store.un("remove", this.onRemove);
14031 this.store.un("update", this.onUpdate);
14032 this.store.un("clear", this.refresh);
14033 this.store.un("beforeload", this.onBeforeLoad);
14034 this.store.un("load", this.onLoad);
14035 this.store.un("loadexception", this.onLoad);
14039 store.on("datachanged", this.refresh, this);
14040 store.on("add", this.onAdd, this);
14041 store.on("remove", this.onRemove, this);
14042 store.on("update", this.onUpdate, this);
14043 store.on("clear", this.refresh, this);
14044 store.on("beforeload", this.onBeforeLoad, this);
14045 store.on("load", this.onLoad, this);
14046 store.on("loadexception", this.onLoad, this);
14054 * onbeforeLoad - masks the loading area.
14057 onBeforeLoad : function(store,opts)
14059 //Roo.log('onBeforeLoad');
14061 this.el.update("");
14063 this.el.mask(this.mask ? this.mask : "Loading" );
14065 onLoad : function ()
14072 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14073 * @param {HTMLElement} node
14074 * @return {HTMLElement} The template node
14076 findItemFromChild : function(node){
14077 var el = this.dataName ?
14078 this.el.child('.roo-tpl-' + this.dataName,true) :
14081 if(!node || node.parentNode == el){
14084 var p = node.parentNode;
14085 while(p && p != el){
14086 if(p.parentNode == el){
14095 onClick : function(e){
14096 var item = this.findItemFromChild(e.getTarget());
14098 var index = this.indexOf(item);
14099 if(this.onItemClick(item, index, e) !== false){
14100 this.fireEvent("click", this, index, item, e);
14103 this.clearSelections();
14108 onContextMenu : function(e){
14109 var item = this.findItemFromChild(e.getTarget());
14111 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14116 onDblClick : function(e){
14117 var item = this.findItemFromChild(e.getTarget());
14119 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14123 onItemClick : function(item, index, e)
14125 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14128 if (this.toggleSelect) {
14129 var m = this.isSelected(item) ? 'unselect' : 'select';
14132 _t[m](item, true, false);
14135 if(this.multiSelect || this.singleSelect){
14136 if(this.multiSelect && e.shiftKey && this.lastSelection){
14137 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14139 this.select(item, this.multiSelect && e.ctrlKey);
14140 this.lastSelection = item;
14143 if(!this.tickable){
14144 e.preventDefault();
14152 * Get the number of selected nodes.
14155 getSelectionCount : function(){
14156 return this.selections.length;
14160 * Get the currently selected nodes.
14161 * @return {Array} An array of HTMLElements
14163 getSelectedNodes : function(){
14164 return this.selections;
14168 * Get the indexes of the selected nodes.
14171 getSelectedIndexes : function(){
14172 var indexes = [], s = this.selections;
14173 for(var i = 0, len = s.length; i < len; i++){
14174 indexes.push(s[i].nodeIndex);
14180 * Clear all selections
14181 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14183 clearSelections : function(suppressEvent){
14184 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14185 this.cmp.elements = this.selections;
14186 this.cmp.removeClass(this.selectedClass);
14187 this.selections = [];
14188 if(!suppressEvent){
14189 this.fireEvent("selectionchange", this, this.selections);
14195 * Returns true if the passed node is selected
14196 * @param {HTMLElement/Number} node The node or node index
14197 * @return {Boolean}
14199 isSelected : function(node){
14200 var s = this.selections;
14204 node = this.getNode(node);
14205 return s.indexOf(node) !== -1;
14210 * @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
14211 * @param {Boolean} keepExisting (optional) true to keep existing selections
14212 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14214 select : function(nodeInfo, keepExisting, suppressEvent){
14215 if(nodeInfo instanceof Array){
14217 this.clearSelections(true);
14219 for(var i = 0, len = nodeInfo.length; i < len; i++){
14220 this.select(nodeInfo[i], true, true);
14224 var node = this.getNode(nodeInfo);
14225 if(!node || this.isSelected(node)){
14226 return; // already selected.
14229 this.clearSelections(true);
14232 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14233 Roo.fly(node).addClass(this.selectedClass);
14234 this.selections.push(node);
14235 if(!suppressEvent){
14236 this.fireEvent("selectionchange", this, this.selections);
14244 * @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
14245 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14246 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14248 unselect : function(nodeInfo, keepExisting, suppressEvent)
14250 if(nodeInfo instanceof Array){
14251 Roo.each(this.selections, function(s) {
14252 this.unselect(s, nodeInfo);
14256 var node = this.getNode(nodeInfo);
14257 if(!node || !this.isSelected(node)){
14258 //Roo.log("not selected");
14259 return; // not selected.
14263 Roo.each(this.selections, function(s) {
14265 Roo.fly(node).removeClass(this.selectedClass);
14272 this.selections= ns;
14273 this.fireEvent("selectionchange", this, this.selections);
14277 * Gets a template node.
14278 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14279 * @return {HTMLElement} The node or null if it wasn't found
14281 getNode : function(nodeInfo){
14282 if(typeof nodeInfo == "string"){
14283 return document.getElementById(nodeInfo);
14284 }else if(typeof nodeInfo == "number"){
14285 return this.nodes[nodeInfo];
14291 * Gets a range template nodes.
14292 * @param {Number} startIndex
14293 * @param {Number} endIndex
14294 * @return {Array} An array of nodes
14296 getNodes : function(start, end){
14297 var ns = this.nodes;
14298 start = start || 0;
14299 end = typeof end == "undefined" ? ns.length - 1 : end;
14302 for(var i = start; i <= end; i++){
14306 for(var i = start; i >= end; i--){
14314 * Finds the index of the passed node
14315 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14316 * @return {Number} The index of the node or -1
14318 indexOf : function(node){
14319 node = this.getNode(node);
14320 if(typeof node.nodeIndex == "number"){
14321 return node.nodeIndex;
14323 var ns = this.nodes;
14324 for(var i = 0, len = ns.length; i < len; i++){
14335 * based on jquery fullcalendar
14339 Roo.bootstrap = Roo.bootstrap || {};
14341 * @class Roo.bootstrap.Calendar
14342 * @extends Roo.bootstrap.Component
14343 * Bootstrap Calendar class
14344 * @cfg {Boolean} loadMask (true|false) default false
14345 * @cfg {Object} header generate the user specific header of the calendar, default false
14348 * Create a new Container
14349 * @param {Object} config The config object
14354 Roo.bootstrap.Calendar = function(config){
14355 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14359 * Fires when a date is selected
14360 * @param {DatePicker} this
14361 * @param {Date} date The selected date
14365 * @event monthchange
14366 * Fires when the displayed month changes
14367 * @param {DatePicker} this
14368 * @param {Date} date The selected month
14370 'monthchange': true,
14372 * @event evententer
14373 * Fires when mouse over an event
14374 * @param {Calendar} this
14375 * @param {event} Event
14377 'evententer': true,
14379 * @event eventleave
14380 * Fires when the mouse leaves an
14381 * @param {Calendar} this
14384 'eventleave': true,
14386 * @event eventclick
14387 * Fires when the mouse click an
14388 * @param {Calendar} this
14397 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
14400 * @cfg {Number} startDay
14401 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14409 getAutoCreate : function(){
14412 var fc_button = function(name, corner, style, content ) {
14413 return Roo.apply({},{
14415 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
14417 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14420 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14431 style : 'width:100%',
14438 cls : 'fc-header-left',
14440 fc_button('prev', 'left', 'arrow', '‹' ),
14441 fc_button('next', 'right', 'arrow', '›' ),
14442 { tag: 'span', cls: 'fc-header-space' },
14443 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
14451 cls : 'fc-header-center',
14455 cls: 'fc-header-title',
14458 html : 'month / year'
14466 cls : 'fc-header-right',
14468 /* fc_button('month', 'left', '', 'month' ),
14469 fc_button('week', '', '', 'week' ),
14470 fc_button('day', 'right', '', 'day' )
14482 header = this.header;
14485 var cal_heads = function() {
14487 // fixme - handle this.
14489 for (var i =0; i < Date.dayNames.length; i++) {
14490 var d = Date.dayNames[i];
14493 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14494 html : d.substring(0,3)
14498 ret[0].cls += ' fc-first';
14499 ret[6].cls += ' fc-last';
14502 var cal_cell = function(n) {
14505 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14510 cls: 'fc-day-number',
14514 cls: 'fc-day-content',
14518 style: 'position: relative;' // height: 17px;
14530 var cal_rows = function() {
14533 for (var r = 0; r < 6; r++) {
14540 for (var i =0; i < Date.dayNames.length; i++) {
14541 var d = Date.dayNames[i];
14542 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14545 row.cn[0].cls+=' fc-first';
14546 row.cn[0].cn[0].style = 'min-height:90px';
14547 row.cn[6].cls+=' fc-last';
14551 ret[0].cls += ' fc-first';
14552 ret[4].cls += ' fc-prev-last';
14553 ret[5].cls += ' fc-last';
14560 cls: 'fc-border-separate',
14561 style : 'width:100%',
14569 cls : 'fc-first fc-last',
14587 cls : 'fc-content',
14588 style : "position: relative;",
14591 cls : 'fc-view fc-view-month fc-grid',
14592 style : 'position: relative',
14593 unselectable : 'on',
14596 cls : 'fc-event-container',
14597 style : 'position:absolute;z-index:8;top:0;left:0;'
14615 initEvents : function()
14618 throw "can not find store for calendar";
14624 style: "text-align:center",
14628 style: "background-color:white;width:50%;margin:250 auto",
14632 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
14643 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14645 var size = this.el.select('.fc-content', true).first().getSize();
14646 this.maskEl.setSize(size.width, size.height);
14647 this.maskEl.enableDisplayMode("block");
14648 if(!this.loadMask){
14649 this.maskEl.hide();
14652 this.store = Roo.factory(this.store, Roo.data);
14653 this.store.on('load', this.onLoad, this);
14654 this.store.on('beforeload', this.onBeforeLoad, this);
14658 this.cells = this.el.select('.fc-day',true);
14659 //Roo.log(this.cells);
14660 this.textNodes = this.el.query('.fc-day-number');
14661 this.cells.addClassOnOver('fc-state-hover');
14663 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14664 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14665 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14666 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14668 this.on('monthchange', this.onMonthChange, this);
14670 this.update(new Date().clearTime());
14673 resize : function() {
14674 var sz = this.el.getSize();
14676 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14677 this.el.select('.fc-day-content div',true).setHeight(34);
14682 showPrevMonth : function(e){
14683 this.update(this.activeDate.add("mo", -1));
14685 showToday : function(e){
14686 this.update(new Date().clearTime());
14689 showNextMonth : function(e){
14690 this.update(this.activeDate.add("mo", 1));
14694 showPrevYear : function(){
14695 this.update(this.activeDate.add("y", -1));
14699 showNextYear : function(){
14700 this.update(this.activeDate.add("y", 1));
14705 update : function(date)
14707 var vd = this.activeDate;
14708 this.activeDate = date;
14709 // if(vd && this.el){
14710 // var t = date.getTime();
14711 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14712 // Roo.log('using add remove');
14714 // this.fireEvent('monthchange', this, date);
14716 // this.cells.removeClass("fc-state-highlight");
14717 // this.cells.each(function(c){
14718 // if(c.dateValue == t){
14719 // c.addClass("fc-state-highlight");
14720 // setTimeout(function(){
14721 // try{c.dom.firstChild.focus();}catch(e){}
14731 var days = date.getDaysInMonth();
14733 var firstOfMonth = date.getFirstDateOfMonth();
14734 var startingPos = firstOfMonth.getDay()-this.startDay;
14736 if(startingPos < this.startDay){
14740 var pm = date.add(Date.MONTH, -1);
14741 var prevStart = pm.getDaysInMonth()-startingPos;
14743 this.cells = this.el.select('.fc-day',true);
14744 this.textNodes = this.el.query('.fc-day-number');
14745 this.cells.addClassOnOver('fc-state-hover');
14747 var cells = this.cells.elements;
14748 var textEls = this.textNodes;
14750 Roo.each(cells, function(cell){
14751 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14754 days += startingPos;
14756 // convert everything to numbers so it's fast
14757 var day = 86400000;
14758 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14761 //Roo.log(prevStart);
14763 var today = new Date().clearTime().getTime();
14764 var sel = date.clearTime().getTime();
14765 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14766 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14767 var ddMatch = this.disabledDatesRE;
14768 var ddText = this.disabledDatesText;
14769 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14770 var ddaysText = this.disabledDaysText;
14771 var format = this.format;
14773 var setCellClass = function(cal, cell){
14777 //Roo.log('set Cell Class');
14779 var t = d.getTime();
14783 cell.dateValue = t;
14785 cell.className += " fc-today";
14786 cell.className += " fc-state-highlight";
14787 cell.title = cal.todayText;
14790 // disable highlight in other month..
14791 //cell.className += " fc-state-highlight";
14796 cell.className = " fc-state-disabled";
14797 cell.title = cal.minText;
14801 cell.className = " fc-state-disabled";
14802 cell.title = cal.maxText;
14806 if(ddays.indexOf(d.getDay()) != -1){
14807 cell.title = ddaysText;
14808 cell.className = " fc-state-disabled";
14811 if(ddMatch && format){
14812 var fvalue = d.dateFormat(format);
14813 if(ddMatch.test(fvalue)){
14814 cell.title = ddText.replace("%0", fvalue);
14815 cell.className = " fc-state-disabled";
14819 if (!cell.initialClassName) {
14820 cell.initialClassName = cell.dom.className;
14823 cell.dom.className = cell.initialClassName + ' ' + cell.className;
14828 for(; i < startingPos; i++) {
14829 textEls[i].innerHTML = (++prevStart);
14830 d.setDate(d.getDate()+1);
14832 cells[i].className = "fc-past fc-other-month";
14833 setCellClass(this, cells[i]);
14838 for(; i < days; i++){
14839 intDay = i - startingPos + 1;
14840 textEls[i].innerHTML = (intDay);
14841 d.setDate(d.getDate()+1);
14843 cells[i].className = ''; // "x-date-active";
14844 setCellClass(this, cells[i]);
14848 for(; i < 42; i++) {
14849 textEls[i].innerHTML = (++extraDays);
14850 d.setDate(d.getDate()+1);
14852 cells[i].className = "fc-future fc-other-month";
14853 setCellClass(this, cells[i]);
14856 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14858 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14860 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14861 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14863 if(totalRows != 6){
14864 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14865 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14868 this.fireEvent('monthchange', this, date);
14872 if(!this.internalRender){
14873 var main = this.el.dom.firstChild;
14874 var w = main.offsetWidth;
14875 this.el.setWidth(w + this.el.getBorderWidth("lr"));
14876 Roo.fly(main).setWidth(w);
14877 this.internalRender = true;
14878 // opera does not respect the auto grow header center column
14879 // then, after it gets a width opera refuses to recalculate
14880 // without a second pass
14881 if(Roo.isOpera && !this.secondPass){
14882 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14883 this.secondPass = true;
14884 this.update.defer(10, this, [date]);
14891 findCell : function(dt) {
14892 dt = dt.clearTime().getTime();
14894 this.cells.each(function(c){
14895 //Roo.log("check " +c.dateValue + '?=' + dt);
14896 if(c.dateValue == dt){
14906 findCells : function(ev) {
14907 var s = ev.start.clone().clearTime().getTime();
14909 var e= ev.end.clone().clearTime().getTime();
14912 this.cells.each(function(c){
14913 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
14915 if(c.dateValue > e){
14918 if(c.dateValue < s){
14927 // findBestRow: function(cells)
14931 // for (var i =0 ; i < cells.length;i++) {
14932 // ret = Math.max(cells[i].rows || 0,ret);
14939 addItem : function(ev)
14941 // look for vertical location slot in
14942 var cells = this.findCells(ev);
14944 // ev.row = this.findBestRow(cells);
14946 // work out the location.
14950 for(var i =0; i < cells.length; i++) {
14952 cells[i].row = cells[0].row;
14955 cells[i].row = cells[i].row + 1;
14965 if (crow.start.getY() == cells[i].getY()) {
14967 crow.end = cells[i];
14984 cells[0].events.push(ev);
14986 this.calevents.push(ev);
14989 clearEvents: function() {
14991 if(!this.calevents){
14995 Roo.each(this.cells.elements, function(c){
15001 Roo.each(this.calevents, function(e) {
15002 Roo.each(e.els, function(el) {
15003 el.un('mouseenter' ,this.onEventEnter, this);
15004 el.un('mouseleave' ,this.onEventLeave, this);
15009 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15015 renderEvents: function()
15019 this.cells.each(function(c) {
15028 if(c.row != c.events.length){
15029 r = 4 - (4 - (c.row - c.events.length));
15032 c.events = ev.slice(0, r);
15033 c.more = ev.slice(r);
15035 if(c.more.length && c.more.length == 1){
15036 c.events.push(c.more.pop());
15039 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15043 this.cells.each(function(c) {
15045 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15048 for (var e = 0; e < c.events.length; e++){
15049 var ev = c.events[e];
15050 var rows = ev.rows;
15052 for(var i = 0; i < rows.length; i++) {
15054 // how many rows should it span..
15057 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15058 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15060 unselectable : "on",
15063 cls: 'fc-event-inner',
15067 // cls: 'fc-event-time',
15068 // html : cells.length > 1 ? '' : ev.time
15072 cls: 'fc-event-title',
15073 html : String.format('{0}', ev.title)
15080 cls: 'ui-resizable-handle ui-resizable-e',
15081 html : '  '
15088 cfg.cls += ' fc-event-start';
15090 if ((i+1) == rows.length) {
15091 cfg.cls += ' fc-event-end';
15094 var ctr = _this.el.select('.fc-event-container',true).first();
15095 var cg = ctr.createChild(cfg);
15097 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15098 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15100 var r = (c.more.length) ? 1 : 0;
15101 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15102 cg.setWidth(ebox.right - sbox.x -2);
15104 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15105 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15106 cg.on('click', _this.onEventClick, _this, ev);
15117 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15118 style : 'position: absolute',
15119 unselectable : "on",
15122 cls: 'fc-event-inner',
15126 cls: 'fc-event-title',
15134 cls: 'ui-resizable-handle ui-resizable-e',
15135 html : '  '
15141 var ctr = _this.el.select('.fc-event-container',true).first();
15142 var cg = ctr.createChild(cfg);
15144 var sbox = c.select('.fc-day-content',true).first().getBox();
15145 var ebox = c.select('.fc-day-content',true).first().getBox();
15147 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15148 cg.setWidth(ebox.right - sbox.x -2);
15150 cg.on('click', _this.onMoreEventClick, _this, c.more);
15160 onEventEnter: function (e, el,event,d) {
15161 this.fireEvent('evententer', this, el, event);
15164 onEventLeave: function (e, el,event,d) {
15165 this.fireEvent('eventleave', this, el, event);
15168 onEventClick: function (e, el,event,d) {
15169 this.fireEvent('eventclick', this, el, event);
15172 onMonthChange: function () {
15176 onMoreEventClick: function(e, el, more)
15180 this.calpopover.placement = 'right';
15181 this.calpopover.setTitle('More');
15183 this.calpopover.setContent('');
15185 var ctr = this.calpopover.el.select('.popover-content', true).first();
15187 Roo.each(more, function(m){
15189 cls : 'fc-event-hori fc-event-draggable',
15192 var cg = ctr.createChild(cfg);
15194 cg.on('click', _this.onEventClick, _this, m);
15197 this.calpopover.show(el);
15202 onLoad: function ()
15204 this.calevents = [];
15207 if(this.store.getCount() > 0){
15208 this.store.data.each(function(d){
15211 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15212 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15213 time : d.data.start_time,
15214 title : d.data.title,
15215 description : d.data.description,
15216 venue : d.data.venue
15221 this.renderEvents();
15223 if(this.calevents.length && this.loadMask){
15224 this.maskEl.hide();
15228 onBeforeLoad: function()
15230 this.clearEvents();
15232 this.maskEl.show();
15246 * @class Roo.bootstrap.Popover
15247 * @extends Roo.bootstrap.Component
15248 * Bootstrap Popover class
15249 * @cfg {String} html contents of the popover (or false to use children..)
15250 * @cfg {String} title of popover (or false to hide)
15251 * @cfg {String} placement how it is placed
15252 * @cfg {String} trigger click || hover (or false to trigger manually)
15253 * @cfg {String} over what (parent or false to trigger manually.)
15254 * @cfg {Number} delay - delay before showing
15257 * Create a new Popover
15258 * @param {Object} config The config object
15261 Roo.bootstrap.Popover = function(config){
15262 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15265 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
15267 title: 'Fill in a title',
15270 placement : 'right',
15271 trigger : 'hover', // hover
15277 can_build_overlaid : false,
15279 getChildContainer : function()
15281 return this.el.select('.popover-content',true).first();
15284 getAutoCreate : function(){
15285 Roo.log('make popover?');
15287 cls : 'popover roo-dynamic',
15288 style: 'display:block',
15294 cls : 'popover-inner',
15298 cls: 'popover-title',
15302 cls : 'popover-content',
15313 setTitle: function(str)
15316 this.el.select('.popover-title',true).first().dom.innerHTML = str;
15318 setContent: function(str)
15321 this.el.select('.popover-content',true).first().dom.innerHTML = str;
15323 // as it get's added to the bottom of the page.
15324 onRender : function(ct, position)
15326 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15328 var cfg = Roo.apply({}, this.getAutoCreate());
15332 cfg.cls += ' ' + this.cls;
15335 cfg.style = this.style;
15337 Roo.log("adding to ")
15338 this.el = Roo.get(document.body).createChild(cfg, position);
15344 initEvents : function()
15346 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15347 this.el.enableDisplayMode('block');
15349 if (this.over === false) {
15352 if (this.triggers === false) {
15355 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15356 var triggers = this.trigger ? this.trigger.split(' ') : [];
15357 Roo.each(triggers, function(trigger) {
15359 if (trigger == 'click') {
15360 on_el.on('click', this.toggle, this);
15361 } else if (trigger != 'manual') {
15362 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
15363 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15365 on_el.on(eventIn ,this.enter, this);
15366 on_el.on(eventOut, this.leave, this);
15377 toggle : function () {
15378 this.hoverState == 'in' ? this.leave() : this.enter();
15381 enter : function () {
15384 clearTimeout(this.timeout);
15386 this.hoverState = 'in';
15388 if (!this.delay || !this.delay.show) {
15393 this.timeout = setTimeout(function () {
15394 if (_t.hoverState == 'in') {
15397 }, this.delay.show)
15399 leave : function() {
15400 clearTimeout(this.timeout);
15402 this.hoverState = 'out';
15404 if (!this.delay || !this.delay.hide) {
15409 this.timeout = setTimeout(function () {
15410 if (_t.hoverState == 'out') {
15413 }, this.delay.hide)
15416 show : function (on_el)
15419 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15422 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15423 if (this.html !== false) {
15424 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15426 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15427 if (!this.title.length) {
15428 this.el.select('.popover-title',true).hide();
15431 var placement = typeof this.placement == 'function' ?
15432 this.placement.call(this, this.el, on_el) :
15435 var autoToken = /\s?auto?\s?/i;
15436 var autoPlace = autoToken.test(placement);
15438 placement = placement.replace(autoToken, '') || 'top';
15442 //this.el.setXY([0,0]);
15444 this.el.dom.style.display='block';
15445 this.el.addClass(placement);
15447 //this.el.appendTo(on_el);
15449 var p = this.getPosition();
15450 var box = this.el.getBox();
15455 var align = Roo.bootstrap.Popover.alignment[placement];
15456 this.el.alignTo(on_el, align[0],align[1]);
15457 //var arrow = this.el.select('.arrow',true).first();
15458 //arrow.set(align[2],
15460 this.el.addClass('in');
15463 if (this.el.hasClass('fade')) {
15470 this.el.setXY([0,0]);
15471 this.el.removeClass('in');
15473 this.hoverState = null;
15479 Roo.bootstrap.Popover.alignment = {
15480 'left' : ['r-l', [-10,0], 'right'],
15481 'right' : ['l-r', [10,0], 'left'],
15482 'bottom' : ['t-b', [0,10], 'top'],
15483 'top' : [ 'b-t', [0,-10], 'bottom']
15494 * @class Roo.bootstrap.Progress
15495 * @extends Roo.bootstrap.Component
15496 * Bootstrap Progress class
15497 * @cfg {Boolean} striped striped of the progress bar
15498 * @cfg {Boolean} active animated of the progress bar
15502 * Create a new Progress
15503 * @param {Object} config The config object
15506 Roo.bootstrap.Progress = function(config){
15507 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15510 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
15515 getAutoCreate : function(){
15523 cfg.cls += ' progress-striped';
15527 cfg.cls += ' active';
15546 * @class Roo.bootstrap.ProgressBar
15547 * @extends Roo.bootstrap.Component
15548 * Bootstrap ProgressBar class
15549 * @cfg {Number} aria_valuenow aria-value now
15550 * @cfg {Number} aria_valuemin aria-value min
15551 * @cfg {Number} aria_valuemax aria-value max
15552 * @cfg {String} label label for the progress bar
15553 * @cfg {String} panel (success | info | warning | danger )
15554 * @cfg {String} role role of the progress bar
15555 * @cfg {String} sr_only text
15559 * Create a new ProgressBar
15560 * @param {Object} config The config object
15563 Roo.bootstrap.ProgressBar = function(config){
15564 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15567 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
15571 aria_valuemax : 100,
15577 getAutoCreate : function()
15582 cls: 'progress-bar',
15583 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15595 cfg.role = this.role;
15598 if(this.aria_valuenow){
15599 cfg['aria-valuenow'] = this.aria_valuenow;
15602 if(this.aria_valuemin){
15603 cfg['aria-valuemin'] = this.aria_valuemin;
15606 if(this.aria_valuemax){
15607 cfg['aria-valuemax'] = this.aria_valuemax;
15610 if(this.label && !this.sr_only){
15611 cfg.html = this.label;
15615 cfg.cls += ' progress-bar-' + this.panel;
15621 update : function(aria_valuenow)
15623 this.aria_valuenow = aria_valuenow;
15625 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15640 * @class Roo.bootstrap.TabGroup
15641 * @extends Roo.bootstrap.Column
15642 * Bootstrap Column class
15643 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15644 * @cfg {Boolean} carousel true to make the group behave like a carousel
15645 * @cfg {Number} bullets show the panel pointer.. default 0
15646 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15647 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15648 * @cfg {Number} timer auto slide timer .. default 0 millisecond
15651 * Create a new TabGroup
15652 * @param {Object} config The config object
15655 Roo.bootstrap.TabGroup = function(config){
15656 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15658 this.navId = Roo.id();
15661 Roo.bootstrap.TabGroup.register(this);
15665 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
15668 transition : false,
15673 slideOnTouch : false,
15675 getAutoCreate : function()
15677 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15679 cfg.cls += ' tab-content';
15681 Roo.log('get auto create...............');
15683 if (this.carousel) {
15684 cfg.cls += ' carousel slide';
15687 cls : 'carousel-inner'
15690 if(this.bullets > 0 && !Roo.isTouch){
15693 cls : 'carousel-bullets',
15697 if(this.bullets_cls){
15698 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15701 for (var i = 0; i < this.bullets; i++){
15703 cls : 'bullet bullet-' + i
15711 cfg.cn[0].cn = bullets;
15718 initEvents: function()
15720 Roo.log('-------- init events on tab group ---------');
15722 if(this.bullets > 0 && !Roo.isTouch){
15728 if(Roo.isTouch && this.slideOnTouch){
15729 this.el.on("touchstart", this.onTouchStart, this);
15732 if(this.autoslide){
15735 this.slideFn = window.setInterval(function() {
15736 _this.showPanelNext();
15742 onTouchStart : function(e, el, o)
15744 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15748 this.showPanelNext();
15751 getChildContainer : function()
15753 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15757 * register a Navigation item
15758 * @param {Roo.bootstrap.NavItem} the navitem to add
15760 register : function(item)
15762 this.tabs.push( item);
15763 item.navId = this.navId; // not really needed..
15767 getActivePanel : function()
15770 Roo.each(this.tabs, function(t) {
15780 getPanelByName : function(n)
15783 Roo.each(this.tabs, function(t) {
15784 if (t.tabId == n) {
15792 indexOfPanel : function(p)
15795 Roo.each(this.tabs, function(t,i) {
15796 if (t.tabId == p.tabId) {
15805 * show a specific panel
15806 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15807 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15809 showPanel : function (pan)
15811 if(this.transition){
15812 Roo.log("waiting for the transitionend");
15816 if (typeof(pan) == 'number') {
15817 pan = this.tabs[pan];
15819 if (typeof(pan) == 'string') {
15820 pan = this.getPanelByName(pan);
15822 if (pan.tabId == this.getActivePanel().tabId) {
15825 var cur = this.getActivePanel();
15827 if (false === cur.fireEvent('beforedeactivate')) {
15831 if(this.bullets > 0 && !Roo.isTouch){
15832 this.setActiveBullet(this.indexOfPanel(pan));
15835 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15837 this.transition = true;
15838 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
15839 var lr = dir == 'next' ? 'left' : 'right';
15840 pan.el.addClass(dir); // or prev
15841 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
15842 cur.el.addClass(lr); // or right
15843 pan.el.addClass(lr);
15846 cur.el.on('transitionend', function() {
15847 Roo.log("trans end?");
15849 pan.el.removeClass([lr,dir]);
15850 pan.setActive(true);
15852 cur.el.removeClass([lr]);
15853 cur.setActive(false);
15855 _this.transition = false;
15857 }, this, { single: true } );
15862 cur.setActive(false);
15863 pan.setActive(true);
15868 showPanelNext : function()
15870 var i = this.indexOfPanel(this.getActivePanel());
15872 if (i >= this.tabs.length - 1 && !this.autoslide) {
15876 if (i >= this.tabs.length - 1 && this.autoslide) {
15880 this.showPanel(this.tabs[i+1]);
15883 showPanelPrev : function()
15885 var i = this.indexOfPanel(this.getActivePanel());
15887 if (i < 1 && !this.autoslide) {
15891 if (i < 1 && this.autoslide) {
15892 i = this.tabs.length;
15895 this.showPanel(this.tabs[i-1]);
15898 initBullet : function()
15906 for (var i = 0; i < this.bullets; i++){
15907 var bullet = this.el.select('.bullet-' + i, true).first();
15913 bullet.on('click', (function(e, el, o, ii, t){
15915 e.preventDefault();
15917 _this.showPanel(ii);
15919 if(_this.autoslide && _this.slideFn){
15920 clearInterval(_this.slideFn);
15921 _this.slideFn = window.setInterval(function() {
15922 _this.showPanelNext();
15926 }).createDelegate(this, [i, bullet], true));
15930 setActiveBullet : function(i)
15936 Roo.each(this.el.select('.bullet', true).elements, function(el){
15937 el.removeClass('selected');
15940 var bullet = this.el.select('.bullet-' + i, true).first();
15946 bullet.addClass('selected');
15957 Roo.apply(Roo.bootstrap.TabGroup, {
15961 * register a Navigation Group
15962 * @param {Roo.bootstrap.NavGroup} the navgroup to add
15964 register : function(navgrp)
15966 this.groups[navgrp.navId] = navgrp;
15970 * fetch a Navigation Group based on the navigation ID
15971 * if one does not exist , it will get created.
15972 * @param {string} the navgroup to add
15973 * @returns {Roo.bootstrap.NavGroup} the navgroup
15975 get: function(navId) {
15976 if (typeof(this.groups[navId]) == 'undefined') {
15977 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
15979 return this.groups[navId] ;
15994 * @class Roo.bootstrap.TabPanel
15995 * @extends Roo.bootstrap.Component
15996 * Bootstrap TabPanel class
15997 * @cfg {Boolean} active panel active
15998 * @cfg {String} html panel content
15999 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16000 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16004 * Create a new TabPanel
16005 * @param {Object} config The config object
16008 Roo.bootstrap.TabPanel = function(config){
16009 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16013 * Fires when the active status changes
16014 * @param {Roo.bootstrap.TabPanel} this
16015 * @param {Boolean} state the new state
16020 * @event beforedeactivate
16021 * Fires before a tab is de-activated - can be used to do validation on a form.
16022 * @param {Roo.bootstrap.TabPanel} this
16023 * @return {Boolean} false if there is an error
16026 'beforedeactivate': true
16029 this.tabId = this.tabId || Roo.id();
16033 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16040 getAutoCreate : function(){
16043 // item is needed for carousel - not sure if it has any effect otherwise
16044 cls: 'tab-pane item',
16045 html: this.html || ''
16049 cfg.cls += ' active';
16053 cfg.tabId = this.tabId;
16060 initEvents: function()
16062 Roo.log('-------- init events on tab panel ---------');
16064 var p = this.parent();
16065 this.navId = this.navId || p.navId;
16067 if (typeof(this.navId) != 'undefined') {
16068 // not really needed.. but just in case.. parent should be a NavGroup.
16069 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16070 Roo.log(['register', tg, this]);
16073 var i = tg.tabs.length - 1;
16075 if(this.active && tg.bullets > 0 && i < tg.bullets){
16076 tg.setActiveBullet(i);
16083 onRender : function(ct, position)
16085 // Roo.log("Call onRender: " + this.xtype);
16087 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16095 setActive: function(state)
16097 Roo.log("panel - set active " + this.tabId + "=" + state);
16099 this.active = state;
16101 this.el.removeClass('active');
16103 } else if (!this.el.hasClass('active')) {
16104 this.el.addClass('active');
16107 this.fireEvent('changed', this, state);
16124 * @class Roo.bootstrap.DateField
16125 * @extends Roo.bootstrap.Input
16126 * Bootstrap DateField class
16127 * @cfg {Number} weekStart default 0
16128 * @cfg {String} viewMode default empty, (months|years)
16129 * @cfg {String} minViewMode default empty, (months|years)
16130 * @cfg {Number} startDate default -Infinity
16131 * @cfg {Number} endDate default Infinity
16132 * @cfg {Boolean} todayHighlight default false
16133 * @cfg {Boolean} todayBtn default false
16134 * @cfg {Boolean} calendarWeeks default false
16135 * @cfg {Object} daysOfWeekDisabled default empty
16136 * @cfg {Boolean} singleMode default false (true | false)
16138 * @cfg {Boolean} keyboardNavigation default true
16139 * @cfg {String} language default en
16142 * Create a new DateField
16143 * @param {Object} config The config object
16146 Roo.bootstrap.DateField = function(config){
16147 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16151 * Fires when this field show.
16152 * @param {Roo.bootstrap.DateField} this
16153 * @param {Mixed} date The date value
16158 * Fires when this field hide.
16159 * @param {Roo.bootstrap.DateField} this
16160 * @param {Mixed} date The date value
16165 * Fires when select a date.
16166 * @param {Roo.bootstrap.DateField} this
16167 * @param {Mixed} date The date value
16173 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16176 * @cfg {String} format
16177 * The default date format string which can be overriden for localization support. The format must be
16178 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16182 * @cfg {String} altFormats
16183 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16184 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16186 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16194 todayHighlight : false,
16200 keyboardNavigation: true,
16202 calendarWeeks: false,
16204 startDate: -Infinity,
16208 daysOfWeekDisabled: [],
16212 singleMode : false,
16214 UTCDate: function()
16216 return new Date(Date.UTC.apply(Date, arguments));
16219 UTCToday: function()
16221 var today = new Date();
16222 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16225 getDate: function() {
16226 var d = this.getUTCDate();
16227 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16230 getUTCDate: function() {
16234 setDate: function(d) {
16235 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16238 setUTCDate: function(d) {
16240 this.setValue(this.formatDate(this.date));
16243 onRender: function(ct, position)
16246 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16248 this.language = this.language || 'en';
16249 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16250 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16252 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16253 this.format = this.format || 'm/d/y';
16254 this.isInline = false;
16255 this.isInput = true;
16256 this.component = this.el.select('.add-on', true).first() || false;
16257 this.component = (this.component && this.component.length === 0) ? false : this.component;
16258 this.hasInput = this.component && this.inputEL().length;
16260 if (typeof(this.minViewMode === 'string')) {
16261 switch (this.minViewMode) {
16263 this.minViewMode = 1;
16266 this.minViewMode = 2;
16269 this.minViewMode = 0;
16274 if (typeof(this.viewMode === 'string')) {
16275 switch (this.viewMode) {
16288 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16290 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16292 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16294 this.picker().on('mousedown', this.onMousedown, this);
16295 this.picker().on('click', this.onClick, this);
16297 this.picker().addClass('datepicker-dropdown');
16299 this.startViewMode = this.viewMode;
16301 if(this.singleMode){
16302 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16303 v.setVisibilityMode(Roo.Element.DISPLAY)
16307 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16308 v.setStyle('width', '189px');
16312 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16313 if(!this.calendarWeeks){
16318 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16319 v.attr('colspan', function(i, val){
16320 return parseInt(val) + 1;
16325 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16327 this.setStartDate(this.startDate);
16328 this.setEndDate(this.endDate);
16330 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16337 if(this.isInline) {
16342 picker : function()
16344 return this.pickerEl;
16345 // return this.el.select('.datepicker', true).first();
16348 fillDow: function()
16350 var dowCnt = this.weekStart;
16359 if(this.calendarWeeks){
16367 while (dowCnt < this.weekStart + 7) {
16371 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16375 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16378 fillMonths: function()
16381 var months = this.picker().select('>.datepicker-months td', true).first();
16383 months.dom.innerHTML = '';
16389 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16392 months.createChild(month);
16399 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;
16401 if (this.date < this.startDate) {
16402 this.viewDate = new Date(this.startDate);
16403 } else if (this.date > this.endDate) {
16404 this.viewDate = new Date(this.endDate);
16406 this.viewDate = new Date(this.date);
16414 var d = new Date(this.viewDate),
16415 year = d.getUTCFullYear(),
16416 month = d.getUTCMonth(),
16417 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16418 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16419 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16420 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16421 currentDate = this.date && this.date.valueOf(),
16422 today = this.UTCToday();
16424 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16426 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16428 // this.picker.select('>tfoot th.today').
16429 // .text(dates[this.language].today)
16430 // .toggle(this.todayBtn !== false);
16432 this.updateNavArrows();
16435 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16437 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16439 prevMonth.setUTCDate(day);
16441 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16443 var nextMonth = new Date(prevMonth);
16445 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16447 nextMonth = nextMonth.valueOf();
16449 var fillMonths = false;
16451 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16453 while(prevMonth.valueOf() < nextMonth) {
16456 if (prevMonth.getUTCDay() === this.weekStart) {
16458 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16466 if(this.calendarWeeks){
16467 // ISO 8601: First week contains first thursday.
16468 // ISO also states week starts on Monday, but we can be more abstract here.
16470 // Start of current week: based on weekstart/current date
16471 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16472 // Thursday of this week
16473 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16474 // First Thursday of year, year from thursday
16475 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16476 // Calendar week: ms between thursdays, div ms per day, div 7 days
16477 calWeek = (th - yth) / 864e5 / 7 + 1;
16479 fillMonths.cn.push({
16487 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16489 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16492 if (this.todayHighlight &&
16493 prevMonth.getUTCFullYear() == today.getFullYear() &&
16494 prevMonth.getUTCMonth() == today.getMonth() &&
16495 prevMonth.getUTCDate() == today.getDate()) {
16496 clsName += ' today';
16499 if (currentDate && prevMonth.valueOf() === currentDate) {
16500 clsName += ' active';
16503 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16504 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16505 clsName += ' disabled';
16508 fillMonths.cn.push({
16510 cls: 'day ' + clsName,
16511 html: prevMonth.getDate()
16514 prevMonth.setDate(prevMonth.getDate()+1);
16517 var currentYear = this.date && this.date.getUTCFullYear();
16518 var currentMonth = this.date && this.date.getUTCMonth();
16520 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16522 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16523 v.removeClass('active');
16525 if(currentYear === year && k === currentMonth){
16526 v.addClass('active');
16529 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16530 v.addClass('disabled');
16536 year = parseInt(year/10, 10) * 10;
16538 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16540 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16543 for (var i = -1; i < 11; i++) {
16544 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16546 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16554 showMode: function(dir)
16557 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16560 Roo.each(this.picker().select('>div',true).elements, function(v){
16561 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16564 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16569 if(this.isInline) return;
16571 this.picker().removeClass(['bottom', 'top']);
16573 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16575 * place to the top of element!
16579 this.picker().addClass('top');
16580 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16585 this.picker().addClass('bottom');
16587 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16590 parseDate : function(value)
16592 if(!value || value instanceof Date){
16595 var v = Date.parseDate(value, this.format);
16596 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16597 v = Date.parseDate(value, 'Y-m-d');
16599 if(!v && this.altFormats){
16600 if(!this.altFormatsArray){
16601 this.altFormatsArray = this.altFormats.split("|");
16603 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16604 v = Date.parseDate(value, this.altFormatsArray[i]);
16610 formatDate : function(date, fmt)
16612 return (!date || !(date instanceof Date)) ?
16613 date : date.dateFormat(fmt || this.format);
16616 onFocus : function()
16618 Roo.bootstrap.DateField.superclass.onFocus.call(this);
16622 onBlur : function()
16624 Roo.bootstrap.DateField.superclass.onBlur.call(this);
16626 var d = this.inputEl().getValue();
16635 this.picker().show();
16639 this.fireEvent('show', this, this.date);
16644 if(this.isInline) return;
16645 this.picker().hide();
16646 this.viewMode = this.startViewMode;
16649 this.fireEvent('hide', this, this.date);
16653 onMousedown: function(e)
16655 e.stopPropagation();
16656 e.preventDefault();
16661 Roo.bootstrap.DateField.superclass.keyup.call(this);
16665 setValue: function(v)
16668 // v can be a string or a date..
16671 var d = new Date(this.parseDate(v) ).clearTime();
16673 if(isNaN(d.getTime())){
16674 this.date = this.viewDate = '';
16675 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16679 v = this.formatDate(d);
16681 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16683 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16687 this.fireEvent('select', this, this.date);
16691 getValue: function()
16693 return this.formatDate(this.date);
16696 fireKey: function(e)
16698 if (!this.picker().isVisible()){
16699 if (e.keyCode == 27) // allow escape to hide and re-show picker
16704 var dateChanged = false,
16706 newDate, newViewDate;
16711 e.preventDefault();
16715 if (!this.keyboardNavigation) break;
16716 dir = e.keyCode == 37 ? -1 : 1;
16719 newDate = this.moveYear(this.date, dir);
16720 newViewDate = this.moveYear(this.viewDate, dir);
16721 } else if (e.shiftKey){
16722 newDate = this.moveMonth(this.date, dir);
16723 newViewDate = this.moveMonth(this.viewDate, dir);
16725 newDate = new Date(this.date);
16726 newDate.setUTCDate(this.date.getUTCDate() + dir);
16727 newViewDate = new Date(this.viewDate);
16728 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16730 if (this.dateWithinRange(newDate)){
16731 this.date = newDate;
16732 this.viewDate = newViewDate;
16733 this.setValue(this.formatDate(this.date));
16735 e.preventDefault();
16736 dateChanged = true;
16741 if (!this.keyboardNavigation) break;
16742 dir = e.keyCode == 38 ? -1 : 1;
16744 newDate = this.moveYear(this.date, dir);
16745 newViewDate = this.moveYear(this.viewDate, dir);
16746 } else if (e.shiftKey){
16747 newDate = this.moveMonth(this.date, dir);
16748 newViewDate = this.moveMonth(this.viewDate, dir);
16750 newDate = new Date(this.date);
16751 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16752 newViewDate = new Date(this.viewDate);
16753 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16755 if (this.dateWithinRange(newDate)){
16756 this.date = newDate;
16757 this.viewDate = newViewDate;
16758 this.setValue(this.formatDate(this.date));
16760 e.preventDefault();
16761 dateChanged = true;
16765 this.setValue(this.formatDate(this.date));
16767 e.preventDefault();
16770 this.setValue(this.formatDate(this.date));
16784 onClick: function(e)
16786 e.stopPropagation();
16787 e.preventDefault();
16789 var target = e.getTarget();
16791 if(target.nodeName.toLowerCase() === 'i'){
16792 target = Roo.get(target).dom.parentNode;
16795 var nodeName = target.nodeName;
16796 var className = target.className;
16797 var html = target.innerHTML;
16798 //Roo.log(nodeName);
16800 switch(nodeName.toLowerCase()) {
16802 switch(className) {
16808 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16809 switch(this.viewMode){
16811 this.viewDate = this.moveMonth(this.viewDate, dir);
16815 this.viewDate = this.moveYear(this.viewDate, dir);
16821 var date = new Date();
16822 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16824 this.setValue(this.formatDate(this.date));
16831 if (className.indexOf('disabled') < 0) {
16832 this.viewDate.setUTCDate(1);
16833 if (className.indexOf('month') > -1) {
16834 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16836 var year = parseInt(html, 10) || 0;
16837 this.viewDate.setUTCFullYear(year);
16841 if(this.singleMode){
16842 this.setValue(this.formatDate(this.viewDate));
16853 //Roo.log(className);
16854 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
16855 var day = parseInt(html, 10) || 1;
16856 var year = this.viewDate.getUTCFullYear(),
16857 month = this.viewDate.getUTCMonth();
16859 if (className.indexOf('old') > -1) {
16866 } else if (className.indexOf('new') > -1) {
16874 //Roo.log([year,month,day]);
16875 this.date = this.UTCDate(year, month, day,0,0,0,0);
16876 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16878 //Roo.log(this.formatDate(this.date));
16879 this.setValue(this.formatDate(this.date));
16886 setStartDate: function(startDate)
16888 this.startDate = startDate || -Infinity;
16889 if (this.startDate !== -Infinity) {
16890 this.startDate = this.parseDate(this.startDate);
16893 this.updateNavArrows();
16896 setEndDate: function(endDate)
16898 this.endDate = endDate || Infinity;
16899 if (this.endDate !== Infinity) {
16900 this.endDate = this.parseDate(this.endDate);
16903 this.updateNavArrows();
16906 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
16908 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
16909 if (typeof(this.daysOfWeekDisabled) !== 'object') {
16910 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
16912 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
16913 return parseInt(d, 10);
16916 this.updateNavArrows();
16919 updateNavArrows: function()
16921 if(this.singleMode){
16925 var d = new Date(this.viewDate),
16926 year = d.getUTCFullYear(),
16927 month = d.getUTCMonth();
16929 Roo.each(this.picker().select('.prev', true).elements, function(v){
16931 switch (this.viewMode) {
16934 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
16940 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
16947 Roo.each(this.picker().select('.next', true).elements, function(v){
16949 switch (this.viewMode) {
16952 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
16958 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
16966 moveMonth: function(date, dir)
16968 if (!dir) return date;
16969 var new_date = new Date(date.valueOf()),
16970 day = new_date.getUTCDate(),
16971 month = new_date.getUTCMonth(),
16972 mag = Math.abs(dir),
16974 dir = dir > 0 ? 1 : -1;
16977 // If going back one month, make sure month is not current month
16978 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
16980 return new_date.getUTCMonth() == month;
16982 // If going forward one month, make sure month is as expected
16983 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
16985 return new_date.getUTCMonth() != new_month;
16987 new_month = month + dir;
16988 new_date.setUTCMonth(new_month);
16989 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
16990 if (new_month < 0 || new_month > 11)
16991 new_month = (new_month + 12) % 12;
16993 // For magnitudes >1, move one month at a time...
16994 for (var i=0; i<mag; i++)
16995 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
16996 new_date = this.moveMonth(new_date, dir);
16997 // ...then reset the day, keeping it in the new month
16998 new_month = new_date.getUTCMonth();
16999 new_date.setUTCDate(day);
17001 return new_month != new_date.getUTCMonth();
17004 // Common date-resetting loop -- if date is beyond end of month, make it
17007 new_date.setUTCDate(--day);
17008 new_date.setUTCMonth(new_month);
17013 moveYear: function(date, dir)
17015 return this.moveMonth(date, dir*12);
17018 dateWithinRange: function(date)
17020 return date >= this.startDate && date <= this.endDate;
17026 this.picker().remove();
17031 Roo.apply(Roo.bootstrap.DateField, {
17042 html: '<i class="fa fa-arrow-left"/>'
17052 html: '<i class="fa fa-arrow-right"/>'
17094 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17095 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17096 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17097 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17098 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17111 navFnc: 'FullYear',
17116 navFnc: 'FullYear',
17121 Roo.apply(Roo.bootstrap.DateField, {
17125 cls: 'datepicker dropdown-menu roo-dynamic',
17129 cls: 'datepicker-days',
17133 cls: 'table-condensed',
17135 Roo.bootstrap.DateField.head,
17139 Roo.bootstrap.DateField.footer
17146 cls: 'datepicker-months',
17150 cls: 'table-condensed',
17152 Roo.bootstrap.DateField.head,
17153 Roo.bootstrap.DateField.content,
17154 Roo.bootstrap.DateField.footer
17161 cls: 'datepicker-years',
17165 cls: 'table-condensed',
17167 Roo.bootstrap.DateField.head,
17168 Roo.bootstrap.DateField.content,
17169 Roo.bootstrap.DateField.footer
17188 * @class Roo.bootstrap.TimeField
17189 * @extends Roo.bootstrap.Input
17190 * Bootstrap DateField class
17194 * Create a new TimeField
17195 * @param {Object} config The config object
17198 Roo.bootstrap.TimeField = function(config){
17199 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17203 * Fires when this field show.
17204 * @param {Roo.bootstrap.DateField} thisthis
17205 * @param {Mixed} date The date value
17210 * Fires when this field hide.
17211 * @param {Roo.bootstrap.DateField} this
17212 * @param {Mixed} date The date value
17217 * Fires when select a date.
17218 * @param {Roo.bootstrap.DateField} this
17219 * @param {Mixed} date The date value
17225 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17228 * @cfg {String} format
17229 * The default time format string which can be overriden for localization support. The format must be
17230 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17234 onRender: function(ct, position)
17237 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17239 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17241 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17243 this.pop = this.picker().select('>.datepicker-time',true).first();
17244 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17246 this.picker().on('mousedown', this.onMousedown, this);
17247 this.picker().on('click', this.onClick, this);
17249 this.picker().addClass('datepicker-dropdown');
17254 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17255 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17256 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17257 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17258 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17259 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17263 fireKey: function(e){
17264 if (!this.picker().isVisible()){
17265 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17271 e.preventDefault();
17279 this.onTogglePeriod();
17282 this.onIncrementMinutes();
17285 this.onDecrementMinutes();
17294 onClick: function(e) {
17295 e.stopPropagation();
17296 e.preventDefault();
17299 picker : function()
17301 return this.el.select('.datepicker', true).first();
17304 fillTime: function()
17306 var time = this.pop.select('tbody', true).first();
17308 time.dom.innerHTML = '';
17323 cls: 'hours-up glyphicon glyphicon-chevron-up'
17343 cls: 'minutes-up glyphicon glyphicon-chevron-up'
17364 cls: 'timepicker-hour',
17379 cls: 'timepicker-minute',
17394 cls: 'btn btn-primary period',
17416 cls: 'hours-down glyphicon glyphicon-chevron-down'
17436 cls: 'minutes-down glyphicon glyphicon-chevron-down'
17454 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17461 var hours = this.time.getHours();
17462 var minutes = this.time.getMinutes();
17475 hours = hours - 12;
17479 hours = '0' + hours;
17483 minutes = '0' + minutes;
17486 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17487 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17488 this.pop.select('button', true).first().dom.innerHTML = period;
17494 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17496 var cls = ['bottom'];
17498 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17505 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17510 this.picker().addClass(cls.join('-'));
17514 Roo.each(cls, function(c){
17516 _this.picker().setTop(_this.inputEl().getHeight());
17520 _this.picker().setTop(0 - _this.picker().getHeight());
17525 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17529 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17536 onFocus : function()
17538 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17542 onBlur : function()
17544 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17550 this.picker().show();
17555 this.fireEvent('show', this, this.date);
17560 this.picker().hide();
17563 this.fireEvent('hide', this, this.date);
17566 setTime : function()
17569 this.setValue(this.time.format(this.format));
17571 this.fireEvent('select', this, this.date);
17576 onMousedown: function(e){
17577 e.stopPropagation();
17578 e.preventDefault();
17581 onIncrementHours: function()
17583 Roo.log('onIncrementHours');
17584 this.time = this.time.add(Date.HOUR, 1);
17589 onDecrementHours: function()
17591 Roo.log('onDecrementHours');
17592 this.time = this.time.add(Date.HOUR, -1);
17596 onIncrementMinutes: function()
17598 Roo.log('onIncrementMinutes');
17599 this.time = this.time.add(Date.MINUTE, 1);
17603 onDecrementMinutes: function()
17605 Roo.log('onDecrementMinutes');
17606 this.time = this.time.add(Date.MINUTE, -1);
17610 onTogglePeriod: function()
17612 Roo.log('onTogglePeriod');
17613 this.time = this.time.add(Date.HOUR, 12);
17620 Roo.apply(Roo.bootstrap.TimeField, {
17650 cls: 'btn btn-info ok',
17662 Roo.apply(Roo.bootstrap.TimeField, {
17666 cls: 'datepicker dropdown-menu',
17670 cls: 'datepicker-time',
17674 cls: 'table-condensed',
17676 Roo.bootstrap.TimeField.content,
17677 Roo.bootstrap.TimeField.footer
17696 * @class Roo.bootstrap.MonthField
17697 * @extends Roo.bootstrap.Input
17698 * Bootstrap MonthField class
17700 * @cfg {String} language default en
17703 * Create a new MonthField
17704 * @param {Object} config The config object
17707 Roo.bootstrap.MonthField = function(config){
17708 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17713 * Fires when this field show.
17714 * @param {Roo.bootstrap.MonthField} this
17715 * @param {Mixed} date The date value
17720 * Fires when this field hide.
17721 * @param {Roo.bootstrap.MonthField} this
17722 * @param {Mixed} date The date value
17727 * Fires when select a date.
17728 * @param {Roo.bootstrap.MonthField} this
17729 * @param {String} oldvalue The old value
17730 * @param {String} newvalue The new value
17736 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
17738 onRender: function(ct, position)
17741 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17743 this.language = this.language || 'en';
17744 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17745 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17747 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17748 this.isInline = false;
17749 this.isInput = true;
17750 this.component = this.el.select('.add-on', true).first() || false;
17751 this.component = (this.component && this.component.length === 0) ? false : this.component;
17752 this.hasInput = this.component && this.inputEL().length;
17754 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17756 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17758 this.picker().on('mousedown', this.onMousedown, this);
17759 this.picker().on('click', this.onClick, this);
17761 this.picker().addClass('datepicker-dropdown');
17763 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17764 v.setStyle('width', '189px');
17771 if(this.isInline) {
17777 setValue: function(v, suppressEvent)
17779 var o = this.getValue();
17781 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17785 if(suppressEvent !== true){
17786 this.fireEvent('select', this, o, v);
17791 getValue: function()
17796 onClick: function(e)
17798 e.stopPropagation();
17799 e.preventDefault();
17801 var target = e.getTarget();
17803 if(target.nodeName.toLowerCase() === 'i'){
17804 target = Roo.get(target).dom.parentNode;
17807 var nodeName = target.nodeName;
17808 var className = target.className;
17809 var html = target.innerHTML;
17811 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17815 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17817 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17823 picker : function()
17825 return this.pickerEl;
17828 fillMonths: function()
17831 var months = this.picker().select('>.datepicker-months td', true).first();
17833 months.dom.innerHTML = '';
17839 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
17842 months.createChild(month);
17851 if(typeof(this.vIndex) == 'undefined' && this.value.length){
17852 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
17855 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
17856 e.removeClass('active');
17858 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
17859 e.addClass('active');
17866 if(this.isInline) return;
17868 this.picker().removeClass(['bottom', 'top']);
17870 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17872 * place to the top of element!
17876 this.picker().addClass('top');
17877 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17882 this.picker().addClass('bottom');
17884 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17887 onFocus : function()
17889 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
17893 onBlur : function()
17895 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
17897 var d = this.inputEl().getValue();
17906 this.picker().show();
17907 this.picker().select('>.datepicker-months', true).first().show();
17911 this.fireEvent('show', this, this.date);
17916 if(this.isInline) return;
17917 this.picker().hide();
17918 this.fireEvent('hide', this, this.date);
17922 onMousedown: function(e)
17924 e.stopPropagation();
17925 e.preventDefault();
17930 Roo.bootstrap.MonthField.superclass.keyup.call(this);
17934 fireKey: function(e)
17936 if (!this.picker().isVisible()){
17937 if (e.keyCode == 27) // allow escape to hide and re-show picker
17947 e.preventDefault();
17951 dir = e.keyCode == 37 ? -1 : 1;
17953 this.vIndex = this.vIndex + dir;
17955 if(this.vIndex < 0){
17959 if(this.vIndex > 11){
17963 if(isNaN(this.vIndex)){
17967 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17973 dir = e.keyCode == 38 ? -1 : 1;
17975 this.vIndex = this.vIndex + dir * 4;
17977 if(this.vIndex < 0){
17981 if(this.vIndex > 11){
17985 if(isNaN(this.vIndex)){
17989 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17994 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17995 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17999 e.preventDefault();
18002 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18003 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18019 this.picker().remove();
18024 Roo.apply(Roo.bootstrap.MonthField, {
18043 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18044 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18049 Roo.apply(Roo.bootstrap.MonthField, {
18053 cls: 'datepicker dropdown-menu roo-dynamic',
18057 cls: 'datepicker-months',
18061 cls: 'table-condensed',
18063 Roo.bootstrap.DateField.content
18083 * @class Roo.bootstrap.CheckBox
18084 * @extends Roo.bootstrap.Input
18085 * Bootstrap CheckBox class
18087 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18088 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18089 * @cfg {String} boxLabel The text that appears beside the checkbox
18090 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18091 * @cfg {Boolean} checked initnal the element
18092 * @cfg {Boolean} inline inline the element (default false)
18093 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18096 * Create a new CheckBox
18097 * @param {Object} config The config object
18100 Roo.bootstrap.CheckBox = function(config){
18101 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18106 * Fires when the element is checked or unchecked.
18107 * @param {Roo.bootstrap.CheckBox} this This input
18108 * @param {Boolean} checked The new checked value
18115 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18117 inputType: 'checkbox',
18125 getAutoCreate : function()
18127 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18133 cfg.cls = 'form-group ' + this.inputType; //input-group
18136 cfg.cls += ' ' + this.inputType + '-inline';
18142 type : this.inputType,
18143 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18144 cls : 'roo-' + this.inputType, //'form-box',
18145 placeholder : this.placeholder || ''
18149 if (this.weight) { // Validity check?
18150 cfg.cls += " " + this.inputType + "-" + this.weight;
18153 if (this.disabled) {
18154 input.disabled=true;
18158 input.checked = this.checked;
18162 input.name = this.name;
18166 input.cls += ' input-' + this.size;
18171 ['xs','sm','md','lg'].map(function(size){
18172 if (settings[size]) {
18173 cfg.cls += ' col-' + size + '-' + settings[size];
18177 var inputblock = input;
18179 if (this.before || this.after) {
18182 cls : 'input-group',
18187 inputblock.cn.push({
18189 cls : 'input-group-addon',
18194 inputblock.cn.push(input);
18197 inputblock.cn.push({
18199 cls : 'input-group-addon',
18206 if (align ==='left' && this.fieldLabel.length) {
18207 Roo.log("left and has label");
18213 cls : 'control-label col-md-' + this.labelWidth,
18214 html : this.fieldLabel
18218 cls : "col-md-" + (12 - this.labelWidth),
18225 } else if ( this.fieldLabel.length) {
18230 tag: this.boxLabel ? 'span' : 'label',
18232 cls: 'control-label box-input-label',
18233 //cls : 'input-group-addon',
18234 html : this.fieldLabel
18244 Roo.log(" no label && no align");
18245 cfg.cn = [ inputblock ] ;
18250 var boxLabelCfg = {
18252 //'for': id, // box label is handled by onclick - so no for...
18254 html: this.boxLabel
18258 boxLabelCfg.tooltip = this.tooltip;
18261 cfg.cn.push(boxLabelCfg);
18271 * return the real input element.
18273 inputEl: function ()
18275 return this.el.select('input.roo-' + this.inputType,true).first();
18278 labelEl: function()
18280 return this.el.select('label.control-label',true).first();
18282 /* depricated... */
18286 return this.labelEl();
18289 initEvents : function()
18291 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18293 this.inputEl().on('click', this.onClick, this);
18295 if (this.boxLabel) {
18296 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18299 this.startValue = this.getValue();
18302 Roo.bootstrap.CheckBox.register(this);
18306 onClick : function()
18308 this.setChecked(!this.checked);
18311 setChecked : function(state,suppressEvent)
18313 this.startValue = this.getValue();
18315 if(this.inputType == 'radio'){
18317 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18318 e.dom.checked = false;
18321 this.inputEl().dom.checked = true;
18323 this.inputEl().dom.value = this.inputValue;
18325 if(suppressEvent !== true){
18326 this.fireEvent('check', this, true);
18334 this.checked = state;
18336 this.inputEl().dom.checked = state;
18338 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18340 if(suppressEvent !== true){
18341 this.fireEvent('check', this, state);
18347 getValue : function()
18349 if(this.inputType == 'radio'){
18350 return this.getGroupValue();
18353 return this.inputEl().getValue();
18357 getGroupValue : function()
18359 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18363 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18366 setValue : function(v,suppressEvent)
18368 if(this.inputType == 'radio'){
18369 this.setGroupValue(v, suppressEvent);
18373 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18378 setGroupValue : function(v, suppressEvent)
18380 this.startValue = this.getValue();
18382 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18383 e.dom.checked = false;
18385 if(e.dom.value == v){
18386 e.dom.checked = true;
18390 if(suppressEvent !== true){
18391 this.fireEvent('check', this, true);
18399 validate : function()
18403 (this.inputType == 'radio' && this.validateRadio()) ||
18404 (this.inputType == 'checkbox' && this.validateCheckbox())
18410 this.markInvalid();
18414 validateRadio : function()
18418 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18419 if(!e.dom.checked){
18431 validateCheckbox : function()
18434 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18437 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18445 for(var i in group){
18450 r = (group[i].getValue() == group[i].inputValue) ? true : false;
18457 * Mark this field as valid
18459 markValid : function()
18461 if(this.allowBlank){
18467 this.fireEvent('valid', this);
18469 if(this.inputType == 'radio'){
18470 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18471 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18472 e.findParent('.form-group', false, true).addClass(_this.validClass);
18479 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18480 this.el.findParent('.form-group', false, true).addClass(this.validClass);
18484 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18490 for(var i in group){
18491 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18492 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18497 * Mark this field as invalid
18498 * @param {String} msg The validation message
18500 markInvalid : function(msg)
18502 if(this.allowBlank){
18508 this.fireEvent('invalid', this, msg);
18510 if(this.inputType == 'radio'){
18511 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18512 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18513 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18520 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18521 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18525 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18531 for(var i in group){
18532 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18533 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18540 Roo.apply(Roo.bootstrap.CheckBox, {
18545 * register a CheckBox Group
18546 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18548 register : function(checkbox)
18550 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18551 this.groups[checkbox.groupId] = {};
18554 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18558 this.groups[checkbox.groupId][checkbox.name] = checkbox;
18562 * fetch a CheckBox Group based on the group ID
18563 * @param {string} the group ID
18564 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18566 get: function(groupId) {
18567 if (typeof(this.groups[groupId]) == 'undefined') {
18571 return this.groups[groupId] ;
18583 *<div class="radio">
18585 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18586 Option one is this and that—be sure to include why it's great
18593 *<label class="radio-inline">fieldLabel</label>
18594 *<label class="radio-inline">
18595 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18603 * @class Roo.bootstrap.Radio
18604 * @extends Roo.bootstrap.CheckBox
18605 * Bootstrap Radio class
18608 * Create a new Radio
18609 * @param {Object} config The config object
18612 Roo.bootstrap.Radio = function(config){
18613 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18617 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
18619 inputType: 'radio',
18623 getAutoCreate : function()
18625 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18626 align = align || 'left'; // default...
18633 tag : this.inline ? 'span' : 'div',
18638 var inline = this.inline ? ' radio-inline' : '';
18642 // does not need for, as we wrap the input with it..
18644 cls : 'control-label box-label' + inline,
18647 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18651 //cls : 'control-label' + inline,
18652 html : this.fieldLabel,
18653 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18662 type : this.inputType,
18663 //value : (!this.checked) ? this.valueOff : this.inputValue,
18664 value : this.inputValue,
18666 placeholder : this.placeholder || '' // ?? needed????
18669 if (this.weight) { // Validity check?
18670 input.cls += " radio-" + this.weight;
18672 if (this.disabled) {
18673 input.disabled=true;
18677 input.checked = this.checked;
18681 input.name = this.name;
18685 input.cls += ' input-' + this.size;
18688 //?? can span's inline have a width??
18691 ['xs','sm','md','lg'].map(function(size){
18692 if (settings[size]) {
18693 cfg.cls += ' col-' + size + '-' + settings[size];
18697 var inputblock = input;
18699 if (this.before || this.after) {
18702 cls : 'input-group',
18707 inputblock.cn.push({
18709 cls : 'input-group-addon',
18713 inputblock.cn.push(input);
18715 inputblock.cn.push({
18717 cls : 'input-group-addon',
18725 if (this.fieldLabel && this.fieldLabel.length) {
18726 cfg.cn.push(fieldLabel);
18729 // normal bootstrap puts the input inside the label.
18730 // however with our styled version - it has to go after the input.
18732 //lbl.cn.push(inputblock);
18736 cls: 'radio' + inline,
18743 cfg.cn.push( lblwrap);
18748 html: this.boxLabel
18757 initEvents : function()
18759 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18761 this.inputEl().on('click', this.onClick, this);
18762 if (this.boxLabel) {
18763 Roo.log('find label')
18764 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
18769 inputEl: function ()
18771 return this.el.select('input.roo-radio',true).first();
18773 onClick : function()
18776 this.setChecked(true);
18779 setChecked : function(state,suppressEvent)
18782 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18783 v.dom.checked = false;
18786 Roo.log(this.inputEl().dom);
18787 this.checked = state;
18788 this.inputEl().dom.checked = state;
18790 if(suppressEvent !== true){
18791 this.fireEvent('check', this, state);
18794 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18798 getGroupValue : function()
18801 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18802 if(v.dom.checked == true){
18803 value = v.dom.value;
18811 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
18812 * @return {Mixed} value The field value
18814 getValue : function(){
18815 return this.getGroupValue();
18821 //<script type="text/javascript">
18824 * Based Ext JS Library 1.1.1
18825 * Copyright(c) 2006-2007, Ext JS, LLC.
18831 * @class Roo.HtmlEditorCore
18832 * @extends Roo.Component
18833 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18835 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18838 Roo.HtmlEditorCore = function(config){
18841 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
18846 * @event initialize
18847 * Fires when the editor is fully initialized (including the iframe)
18848 * @param {Roo.HtmlEditorCore} this
18853 * Fires when the editor is first receives the focus. Any insertion must wait
18854 * until after this event.
18855 * @param {Roo.HtmlEditorCore} this
18859 * @event beforesync
18860 * Fires before the textarea is updated with content from the editor iframe. Return false
18861 * to cancel the sync.
18862 * @param {Roo.HtmlEditorCore} this
18863 * @param {String} html
18867 * @event beforepush
18868 * Fires before the iframe editor is updated with content from the textarea. Return false
18869 * to cancel the push.
18870 * @param {Roo.HtmlEditorCore} this
18871 * @param {String} html
18876 * Fires when the textarea is updated with content from the editor iframe.
18877 * @param {Roo.HtmlEditorCore} this
18878 * @param {String} html
18883 * Fires when the iframe editor is updated with content from the textarea.
18884 * @param {Roo.HtmlEditorCore} this
18885 * @param {String} html
18890 * @event editorevent
18891 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18892 * @param {Roo.HtmlEditorCore} this
18898 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
18900 // defaults : white / black...
18901 this.applyBlacklists();
18908 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
18912 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
18918 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
18923 * @cfg {Number} height (in pixels)
18927 * @cfg {Number} width (in pixels)
18932 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18935 stylesheets: false,
18940 // private properties
18941 validationEvent : false,
18943 initialized : false,
18945 sourceEditMode : false,
18946 onFocus : Roo.emptyFn,
18948 hideMode:'offsets',
18952 // blacklist + whitelisted elements..
18959 * Protected method that will not generally be called directly. It
18960 * is called when the editor initializes the iframe with HTML contents. Override this method if you
18961 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
18963 getDocMarkup : function(){
18967 // inherit styels from page...??
18968 if (this.stylesheets === false) {
18970 Roo.get(document.head).select('style').each(function(node) {
18971 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18974 Roo.get(document.head).select('link').each(function(node) {
18975 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18978 } else if (!this.stylesheets.length) {
18980 st = '<style type="text/css">' +
18981 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18987 st += '<style type="text/css">' +
18988 'IMG { cursor: pointer } ' +
18992 return '<html><head>' + st +
18993 //<style type="text/css">' +
18994 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18996 ' </head><body class="roo-htmleditor-body"></body></html>';
19000 onRender : function(ct, position)
19003 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19004 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19007 this.el.dom.style.border = '0 none';
19008 this.el.dom.setAttribute('tabIndex', -1);
19009 this.el.addClass('x-hidden hide');
19013 if(Roo.isIE){ // fix IE 1px bogus margin
19014 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19018 this.frameId = Roo.id();
19022 var iframe = this.owner.wrap.createChild({
19024 cls: 'form-control', // bootstrap..
19026 name: this.frameId,
19027 frameBorder : 'no',
19028 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19033 this.iframe = iframe.dom;
19035 this.assignDocWin();
19037 this.doc.designMode = 'on';
19040 this.doc.write(this.getDocMarkup());
19044 var task = { // must defer to wait for browser to be ready
19046 //console.log("run task?" + this.doc.readyState);
19047 this.assignDocWin();
19048 if(this.doc.body || this.doc.readyState == 'complete'){
19050 this.doc.designMode="on";
19054 Roo.TaskMgr.stop(task);
19055 this.initEditor.defer(10, this);
19062 Roo.TaskMgr.start(task);
19067 onResize : function(w, h)
19069 Roo.log('resize: ' +w + ',' + h );
19070 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19074 if(typeof w == 'number'){
19076 this.iframe.style.width = w + 'px';
19078 if(typeof h == 'number'){
19080 this.iframe.style.height = h + 'px';
19082 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19089 * Toggles the editor between standard and source edit mode.
19090 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19092 toggleSourceEdit : function(sourceEditMode){
19094 this.sourceEditMode = sourceEditMode === true;
19096 if(this.sourceEditMode){
19098 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19101 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19102 //this.iframe.className = '';
19105 //this.setSize(this.owner.wrap.getSize());
19106 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19113 * Protected method that will not generally be called directly. If you need/want
19114 * custom HTML cleanup, this is the method you should override.
19115 * @param {String} html The HTML to be cleaned
19116 * return {String} The cleaned HTML
19118 cleanHtml : function(html){
19119 html = String(html);
19120 if(html.length > 5){
19121 if(Roo.isSafari){ // strip safari nonsense
19122 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19125 if(html == ' '){
19132 * HTML Editor -> Textarea
19133 * Protected method that will not generally be called directly. Syncs the contents
19134 * of the editor iframe with the textarea.
19136 syncValue : function(){
19137 if(this.initialized){
19138 var bd = (this.doc.body || this.doc.documentElement);
19139 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19140 var html = bd.innerHTML;
19142 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19143 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19145 html = '<div style="'+m[0]+'">' + html + '</div>';
19148 html = this.cleanHtml(html);
19149 // fix up the special chars.. normaly like back quotes in word...
19150 // however we do not want to do this with chinese..
19151 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19152 var cc = b.charCodeAt();
19154 (cc >= 0x4E00 && cc < 0xA000 ) ||
19155 (cc >= 0x3400 && cc < 0x4E00 ) ||
19156 (cc >= 0xf900 && cc < 0xfb00 )
19162 if(this.owner.fireEvent('beforesync', this, html) !== false){
19163 this.el.dom.value = html;
19164 this.owner.fireEvent('sync', this, html);
19170 * Protected method that will not generally be called directly. Pushes the value of the textarea
19171 * into the iframe editor.
19173 pushValue : function(){
19174 if(this.initialized){
19175 var v = this.el.dom.value.trim();
19177 // if(v.length < 1){
19181 if(this.owner.fireEvent('beforepush', this, v) !== false){
19182 var d = (this.doc.body || this.doc.documentElement);
19184 this.cleanUpPaste();
19185 this.el.dom.value = d.innerHTML;
19186 this.owner.fireEvent('push', this, v);
19192 deferFocus : function(){
19193 this.focus.defer(10, this);
19197 focus : function(){
19198 if(this.win && !this.sourceEditMode){
19205 assignDocWin: function()
19207 var iframe = this.iframe;
19210 this.doc = iframe.contentWindow.document;
19211 this.win = iframe.contentWindow;
19213 // if (!Roo.get(this.frameId)) {
19216 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19217 // this.win = Roo.get(this.frameId).dom.contentWindow;
19219 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19223 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19224 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19229 initEditor : function(){
19230 //console.log("INIT EDITOR");
19231 this.assignDocWin();
19235 this.doc.designMode="on";
19237 this.doc.write(this.getDocMarkup());
19240 var dbody = (this.doc.body || this.doc.documentElement);
19241 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19242 // this copies styles from the containing element into thsi one..
19243 // not sure why we need all of this..
19244 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19246 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19247 //ss['background-attachment'] = 'fixed'; // w3c
19248 dbody.bgProperties = 'fixed'; // ie
19249 //Roo.DomHelper.applyStyles(dbody, ss);
19250 Roo.EventManager.on(this.doc, {
19251 //'mousedown': this.onEditorEvent,
19252 'mouseup': this.onEditorEvent,
19253 'dblclick': this.onEditorEvent,
19254 'click': this.onEditorEvent,
19255 'keyup': this.onEditorEvent,
19260 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19262 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19263 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19265 this.initialized = true;
19267 this.owner.fireEvent('initialize', this);
19272 onDestroy : function(){
19278 //for (var i =0; i < this.toolbars.length;i++) {
19279 // // fixme - ask toolbars for heights?
19280 // this.toolbars[i].onDestroy();
19283 //this.wrap.dom.innerHTML = '';
19284 //this.wrap.remove();
19289 onFirstFocus : function(){
19291 this.assignDocWin();
19294 this.activated = true;
19297 if(Roo.isGecko){ // prevent silly gecko errors
19299 var s = this.win.getSelection();
19300 if(!s.focusNode || s.focusNode.nodeType != 3){
19301 var r = s.getRangeAt(0);
19302 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19307 this.execCmd('useCSS', true);
19308 this.execCmd('styleWithCSS', false);
19311 this.owner.fireEvent('activate', this);
19315 adjustFont: function(btn){
19316 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19317 //if(Roo.isSafari){ // safari
19320 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19321 if(Roo.isSafari){ // safari
19322 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19323 v = (v < 10) ? 10 : v;
19324 v = (v > 48) ? 48 : v;
19325 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19330 v = Math.max(1, v+adjust);
19332 this.execCmd('FontSize', v );
19335 onEditorEvent : function(e)
19337 this.owner.fireEvent('editorevent', this, e);
19338 // this.updateToolbar();
19339 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19342 insertTag : function(tg)
19344 // could be a bit smarter... -> wrap the current selected tRoo..
19345 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19347 range = this.createRange(this.getSelection());
19348 var wrappingNode = this.doc.createElement(tg.toLowerCase());
19349 wrappingNode.appendChild(range.extractContents());
19350 range.insertNode(wrappingNode);
19357 this.execCmd("formatblock", tg);
19361 insertText : function(txt)
19365 var range = this.createRange();
19366 range.deleteContents();
19367 //alert(Sender.getAttribute('label'));
19369 range.insertNode(this.doc.createTextNode(txt));
19375 * Executes a Midas editor command on the editor document and performs necessary focus and
19376 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19377 * @param {String} cmd The Midas command
19378 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19380 relayCmd : function(cmd, value){
19382 this.execCmd(cmd, value);
19383 this.owner.fireEvent('editorevent', this);
19384 //this.updateToolbar();
19385 this.owner.deferFocus();
19389 * Executes a Midas editor command directly on the editor document.
19390 * For visual commands, you should use {@link #relayCmd} instead.
19391 * <b>This should only be called after the editor is initialized.</b>
19392 * @param {String} cmd The Midas command
19393 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19395 execCmd : function(cmd, value){
19396 this.doc.execCommand(cmd, false, value === undefined ? null : value);
19403 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19405 * @param {String} text | dom node..
19407 insertAtCursor : function(text)
19412 if(!this.activated){
19418 var r = this.doc.selection.createRange();
19429 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19433 // from jquery ui (MIT licenced)
19435 var win = this.win;
19437 if (win.getSelection && win.getSelection().getRangeAt) {
19438 range = win.getSelection().getRangeAt(0);
19439 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19440 range.insertNode(node);
19441 } else if (win.document.selection && win.document.selection.createRange) {
19442 // no firefox support
19443 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19444 win.document.selection.createRange().pasteHTML(txt);
19446 // no firefox support
19447 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19448 this.execCmd('InsertHTML', txt);
19457 mozKeyPress : function(e){
19459 var c = e.getCharCode(), cmd;
19462 c = String.fromCharCode(c).toLowerCase();
19476 this.cleanUpPaste.defer(100, this);
19484 e.preventDefault();
19492 fixKeys : function(){ // load time branching for fastest keydown performance
19494 return function(e){
19495 var k = e.getKey(), r;
19498 r = this.doc.selection.createRange();
19501 r.pasteHTML('    ');
19508 r = this.doc.selection.createRange();
19510 var target = r.parentElement();
19511 if(!target || target.tagName.toLowerCase() != 'li'){
19513 r.pasteHTML('<br />');
19519 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19520 this.cleanUpPaste.defer(100, this);
19526 }else if(Roo.isOpera){
19527 return function(e){
19528 var k = e.getKey();
19532 this.execCmd('InsertHTML','    ');
19535 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19536 this.cleanUpPaste.defer(100, this);
19541 }else if(Roo.isSafari){
19542 return function(e){
19543 var k = e.getKey();
19547 this.execCmd('InsertText','\t');
19551 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19552 this.cleanUpPaste.defer(100, this);
19560 getAllAncestors: function()
19562 var p = this.getSelectedNode();
19565 a.push(p); // push blank onto stack..
19566 p = this.getParentElement();
19570 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19574 a.push(this.doc.body);
19578 lastSelNode : false,
19581 getSelection : function()
19583 this.assignDocWin();
19584 return Roo.isIE ? this.doc.selection : this.win.getSelection();
19587 getSelectedNode: function()
19589 // this may only work on Gecko!!!
19591 // should we cache this!!!!
19596 var range = this.createRange(this.getSelection()).cloneRange();
19599 var parent = range.parentElement();
19601 var testRange = range.duplicate();
19602 testRange.moveToElementText(parent);
19603 if (testRange.inRange(range)) {
19606 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19609 parent = parent.parentElement;
19614 // is ancestor a text element.
19615 var ac = range.commonAncestorContainer;
19616 if (ac.nodeType == 3) {
19617 ac = ac.parentNode;
19620 var ar = ac.childNodes;
19623 var other_nodes = [];
19624 var has_other_nodes = false;
19625 for (var i=0;i<ar.length;i++) {
19626 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
19629 // fullly contained node.
19631 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19636 // probably selected..
19637 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19638 other_nodes.push(ar[i]);
19642 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
19647 has_other_nodes = true;
19649 if (!nodes.length && other_nodes.length) {
19650 nodes= other_nodes;
19652 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19658 createRange: function(sel)
19660 // this has strange effects when using with
19661 // top toolbar - not sure if it's a great idea.
19662 //this.editor.contentWindow.focus();
19663 if (typeof sel != "undefined") {
19665 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19667 return this.doc.createRange();
19670 return this.doc.createRange();
19673 getParentElement: function()
19676 this.assignDocWin();
19677 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19679 var range = this.createRange(sel);
19682 var p = range.commonAncestorContainer;
19683 while (p.nodeType == 3) { // text node
19694 * Range intersection.. the hard stuff...
19698 * [ -- selected range --- ]
19702 * if end is before start or hits it. fail.
19703 * if start is after end or hits it fail.
19705 * if either hits (but other is outside. - then it's not
19711 // @see http://www.thismuchiknow.co.uk/?p=64.
19712 rangeIntersectsNode : function(range, node)
19714 var nodeRange = node.ownerDocument.createRange();
19716 nodeRange.selectNode(node);
19718 nodeRange.selectNodeContents(node);
19721 var rangeStartRange = range.cloneRange();
19722 rangeStartRange.collapse(true);
19724 var rangeEndRange = range.cloneRange();
19725 rangeEndRange.collapse(false);
19727 var nodeStartRange = nodeRange.cloneRange();
19728 nodeStartRange.collapse(true);
19730 var nodeEndRange = nodeRange.cloneRange();
19731 nodeEndRange.collapse(false);
19733 return rangeStartRange.compareBoundaryPoints(
19734 Range.START_TO_START, nodeEndRange) == -1 &&
19735 rangeEndRange.compareBoundaryPoints(
19736 Range.START_TO_START, nodeStartRange) == 1;
19740 rangeCompareNode : function(range, node)
19742 var nodeRange = node.ownerDocument.createRange();
19744 nodeRange.selectNode(node);
19746 nodeRange.selectNodeContents(node);
19750 range.collapse(true);
19752 nodeRange.collapse(true);
19754 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19755 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
19757 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19759 var nodeIsBefore = ss == 1;
19760 var nodeIsAfter = ee == -1;
19762 if (nodeIsBefore && nodeIsAfter)
19764 if (!nodeIsBefore && nodeIsAfter)
19765 return 1; //right trailed.
19767 if (nodeIsBefore && !nodeIsAfter)
19768 return 2; // left trailed.
19773 // private? - in a new class?
19774 cleanUpPaste : function()
19776 // cleans up the whole document..
19777 Roo.log('cleanuppaste');
19779 this.cleanUpChildren(this.doc.body);
19780 var clean = this.cleanWordChars(this.doc.body.innerHTML);
19781 if (clean != this.doc.body.innerHTML) {
19782 this.doc.body.innerHTML = clean;
19787 cleanWordChars : function(input) {// change the chars to hex code
19788 var he = Roo.HtmlEditorCore;
19790 var output = input;
19791 Roo.each(he.swapCodes, function(sw) {
19792 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19794 output = output.replace(swapper, sw[1]);
19801 cleanUpChildren : function (n)
19803 if (!n.childNodes.length) {
19806 for (var i = n.childNodes.length-1; i > -1 ; i--) {
19807 this.cleanUpChild(n.childNodes[i]);
19814 cleanUpChild : function (node)
19817 //console.log(node);
19818 if (node.nodeName == "#text") {
19819 // clean up silly Windows -- stuff?
19822 if (node.nodeName == "#comment") {
19823 node.parentNode.removeChild(node);
19824 // clean up silly Windows -- stuff?
19827 var lcname = node.tagName.toLowerCase();
19828 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19829 // whitelist of tags..
19831 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19833 node.parentNode.removeChild(node);
19838 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
19840 // remove <a name=....> as rendering on yahoo mailer is borked with this.
19841 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
19843 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
19844 // remove_keep_children = true;
19847 if (remove_keep_children) {
19848 this.cleanUpChildren(node);
19849 // inserts everything just before this node...
19850 while (node.childNodes.length) {
19851 var cn = node.childNodes[0];
19852 node.removeChild(cn);
19853 node.parentNode.insertBefore(cn, node);
19855 node.parentNode.removeChild(node);
19859 if (!node.attributes || !node.attributes.length) {
19860 this.cleanUpChildren(node);
19864 function cleanAttr(n,v)
19867 if (v.match(/^\./) || v.match(/^\//)) {
19870 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19873 if (v.match(/^#/)) {
19876 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19877 node.removeAttribute(n);
19881 var cwhite = this.cwhite;
19882 var cblack = this.cblack;
19884 function cleanStyle(n,v)
19886 if (v.match(/expression/)) { //XSS?? should we even bother..
19887 node.removeAttribute(n);
19891 var parts = v.split(/;/);
19894 Roo.each(parts, function(p) {
19895 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
19899 var l = p.split(':').shift().replace(/\s+/g,'');
19900 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
19902 if ( cwhite.length && cblack.indexOf(l) > -1) {
19903 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19904 //node.removeAttribute(n);
19908 // only allow 'c whitelisted system attributes'
19909 if ( cwhite.length && cwhite.indexOf(l) < 0) {
19910 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19911 //node.removeAttribute(n);
19921 if (clean.length) {
19922 node.setAttribute(n, clean.join(';'));
19924 node.removeAttribute(n);
19930 for (var i = node.attributes.length-1; i > -1 ; i--) {
19931 var a = node.attributes[i];
19934 if (a.name.toLowerCase().substr(0,2)=='on') {
19935 node.removeAttribute(a.name);
19938 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
19939 node.removeAttribute(a.name);
19942 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
19943 cleanAttr(a.name,a.value); // fixme..
19946 if (a.name == 'style') {
19947 cleanStyle(a.name,a.value);
19950 /// clean up MS crap..
19951 // tecnically this should be a list of valid class'es..
19954 if (a.name == 'class') {
19955 if (a.value.match(/^Mso/)) {
19956 node.className = '';
19959 if (a.value.match(/body/)) {
19960 node.className = '';
19971 this.cleanUpChildren(node);
19977 * Clean up MS wordisms...
19979 cleanWord : function(node)
19984 this.cleanWord(this.doc.body);
19987 if (node.nodeName == "#text") {
19988 // clean up silly Windows -- stuff?
19991 if (node.nodeName == "#comment") {
19992 node.parentNode.removeChild(node);
19993 // clean up silly Windows -- stuff?
19997 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
19998 node.parentNode.removeChild(node);
20002 // remove - but keep children..
20003 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20004 while (node.childNodes.length) {
20005 var cn = node.childNodes[0];
20006 node.removeChild(cn);
20007 node.parentNode.insertBefore(cn, node);
20009 node.parentNode.removeChild(node);
20010 this.iterateChildren(node, this.cleanWord);
20014 if (node.className.length) {
20016 var cn = node.className.split(/\W+/);
20018 Roo.each(cn, function(cls) {
20019 if (cls.match(/Mso[a-zA-Z]+/)) {
20024 node.className = cna.length ? cna.join(' ') : '';
20026 node.removeAttribute("class");
20030 if (node.hasAttribute("lang")) {
20031 node.removeAttribute("lang");
20034 if (node.hasAttribute("style")) {
20036 var styles = node.getAttribute("style").split(";");
20038 Roo.each(styles, function(s) {
20039 if (!s.match(/:/)) {
20042 var kv = s.split(":");
20043 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20046 // what ever is left... we allow.
20049 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20050 if (!nstyle.length) {
20051 node.removeAttribute('style');
20054 this.iterateChildren(node, this.cleanWord);
20060 * iterateChildren of a Node, calling fn each time, using this as the scole..
20061 * @param {DomNode} node node to iterate children of.
20062 * @param {Function} fn method of this class to call on each item.
20064 iterateChildren : function(node, fn)
20066 if (!node.childNodes.length) {
20069 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20070 fn.call(this, node.childNodes[i])
20076 * cleanTableWidths.
20078 * Quite often pasting from word etc.. results in tables with column and widths.
20079 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20082 cleanTableWidths : function(node)
20087 this.cleanTableWidths(this.doc.body);
20092 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20095 Roo.log(node.tagName);
20096 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20097 this.iterateChildren(node, this.cleanTableWidths);
20100 if (node.hasAttribute('width')) {
20101 node.removeAttribute('width');
20105 if (node.hasAttribute("style")) {
20108 var styles = node.getAttribute("style").split(";");
20110 Roo.each(styles, function(s) {
20111 if (!s.match(/:/)) {
20114 var kv = s.split(":");
20115 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20118 // what ever is left... we allow.
20121 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20122 if (!nstyle.length) {
20123 node.removeAttribute('style');
20127 this.iterateChildren(node, this.cleanTableWidths);
20135 domToHTML : function(currentElement, depth, nopadtext) {
20137 depth = depth || 0;
20138 nopadtext = nopadtext || false;
20140 if (!currentElement) {
20141 return this.domToHTML(this.doc.body);
20144 //Roo.log(currentElement);
20146 var allText = false;
20147 var nodeName = currentElement.nodeName;
20148 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20150 if (nodeName == '#text') {
20152 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20157 if (nodeName != 'BODY') {
20160 // Prints the node tagName, such as <A>, <IMG>, etc
20163 for(i = 0; i < currentElement.attributes.length;i++) {
20165 var aname = currentElement.attributes.item(i).name;
20166 if (!currentElement.attributes.item(i).value.length) {
20169 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20172 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20181 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20184 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20189 // Traverse the tree
20191 var currentElementChild = currentElement.childNodes.item(i);
20192 var allText = true;
20193 var innerHTML = '';
20195 while (currentElementChild) {
20196 // Formatting code (indent the tree so it looks nice on the screen)
20197 var nopad = nopadtext;
20198 if (lastnode == 'SPAN') {
20202 if (currentElementChild.nodeName == '#text') {
20203 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20204 toadd = nopadtext ? toadd : toadd.trim();
20205 if (!nopad && toadd.length > 80) {
20206 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20208 innerHTML += toadd;
20211 currentElementChild = currentElement.childNodes.item(i);
20217 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20219 // Recursively traverse the tree structure of the child node
20220 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20221 lastnode = currentElementChild.nodeName;
20223 currentElementChild=currentElement.childNodes.item(i);
20229 // The remaining code is mostly for formatting the tree
20230 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20235 ret+= "</"+tagName+">";
20241 applyBlacklists : function()
20243 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20244 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20248 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20249 if (b.indexOf(tag) > -1) {
20252 this.white.push(tag);
20256 Roo.each(w, function(tag) {
20257 if (b.indexOf(tag) > -1) {
20260 if (this.white.indexOf(tag) > -1) {
20263 this.white.push(tag);
20268 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20269 if (w.indexOf(tag) > -1) {
20272 this.black.push(tag);
20276 Roo.each(b, function(tag) {
20277 if (w.indexOf(tag) > -1) {
20280 if (this.black.indexOf(tag) > -1) {
20283 this.black.push(tag);
20288 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20289 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20293 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20294 if (b.indexOf(tag) > -1) {
20297 this.cwhite.push(tag);
20301 Roo.each(w, function(tag) {
20302 if (b.indexOf(tag) > -1) {
20305 if (this.cwhite.indexOf(tag) > -1) {
20308 this.cwhite.push(tag);
20313 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20314 if (w.indexOf(tag) > -1) {
20317 this.cblack.push(tag);
20321 Roo.each(b, function(tag) {
20322 if (w.indexOf(tag) > -1) {
20325 if (this.cblack.indexOf(tag) > -1) {
20328 this.cblack.push(tag);
20333 setStylesheets : function(stylesheets)
20335 if(typeof(stylesheets) == 'string'){
20336 Roo.get(this.iframe.contentDocument.head).createChild({
20338 rel : 'stylesheet',
20347 Roo.each(stylesheets, function(s) {
20352 Roo.get(_this.iframe.contentDocument.head).createChild({
20354 rel : 'stylesheet',
20363 removeStylesheets : function()
20367 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20372 // hide stuff that is not compatible
20386 * @event specialkey
20390 * @cfg {String} fieldClass @hide
20393 * @cfg {String} focusClass @hide
20396 * @cfg {String} autoCreate @hide
20399 * @cfg {String} inputType @hide
20402 * @cfg {String} invalidClass @hide
20405 * @cfg {String} invalidText @hide
20408 * @cfg {String} msgFx @hide
20411 * @cfg {String} validateOnBlur @hide
20415 Roo.HtmlEditorCore.white = [
20416 'area', 'br', 'img', 'input', 'hr', 'wbr',
20418 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
20419 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
20420 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
20421 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
20422 'table', 'ul', 'xmp',
20424 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
20427 'dir', 'menu', 'ol', 'ul', 'dl',
20433 Roo.HtmlEditorCore.black = [
20434 // 'embed', 'object', // enable - backend responsiblity to clean thiese
20436 'base', 'basefont', 'bgsound', 'blink', 'body',
20437 'frame', 'frameset', 'head', 'html', 'ilayer',
20438 'iframe', 'layer', 'link', 'meta', 'object',
20439 'script', 'style' ,'title', 'xml' // clean later..
20441 Roo.HtmlEditorCore.clean = [
20442 'script', 'style', 'title', 'xml'
20444 Roo.HtmlEditorCore.remove = [
20449 Roo.HtmlEditorCore.ablack = [
20453 Roo.HtmlEditorCore.aclean = [
20454 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
20458 Roo.HtmlEditorCore.pwhite= [
20459 'http', 'https', 'mailto'
20462 // white listed style attributes.
20463 Roo.HtmlEditorCore.cwhite= [
20464 // 'text-align', /// default is to allow most things..
20470 // black listed style attributes.
20471 Roo.HtmlEditorCore.cblack= [
20472 // 'font-size' -- this can be set by the project
20476 Roo.HtmlEditorCore.swapCodes =[
20495 * @class Roo.bootstrap.HtmlEditor
20496 * @extends Roo.bootstrap.TextArea
20497 * Bootstrap HtmlEditor class
20500 * Create a new HtmlEditor
20501 * @param {Object} config The config object
20504 Roo.bootstrap.HtmlEditor = function(config){
20505 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20506 if (!this.toolbars) {
20507 this.toolbars = [];
20509 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20512 * @event initialize
20513 * Fires when the editor is fully initialized (including the iframe)
20514 * @param {HtmlEditor} this
20519 * Fires when the editor is first receives the focus. Any insertion must wait
20520 * until after this event.
20521 * @param {HtmlEditor} this
20525 * @event beforesync
20526 * Fires before the textarea is updated with content from the editor iframe. Return false
20527 * to cancel the sync.
20528 * @param {HtmlEditor} this
20529 * @param {String} html
20533 * @event beforepush
20534 * Fires before the iframe editor is updated with content from the textarea. Return false
20535 * to cancel the push.
20536 * @param {HtmlEditor} this
20537 * @param {String} html
20542 * Fires when the textarea is updated with content from the editor iframe.
20543 * @param {HtmlEditor} this
20544 * @param {String} html
20549 * Fires when the iframe editor is updated with content from the textarea.
20550 * @param {HtmlEditor} this
20551 * @param {String} html
20555 * @event editmodechange
20556 * Fires when the editor switches edit modes
20557 * @param {HtmlEditor} this
20558 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20560 editmodechange: true,
20562 * @event editorevent
20563 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20564 * @param {HtmlEditor} this
20568 * @event firstfocus
20569 * Fires when on first focus - needed by toolbars..
20570 * @param {HtmlEditor} this
20575 * Auto save the htmlEditor value as a file into Events
20576 * @param {HtmlEditor} this
20580 * @event savedpreview
20581 * preview the saved version of htmlEditor
20582 * @param {HtmlEditor} this
20589 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
20593 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20598 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20603 * @cfg {Number} height (in pixels)
20607 * @cfg {Number} width (in pixels)
20612 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20615 stylesheets: false,
20620 // private properties
20621 validationEvent : false,
20623 initialized : false,
20626 onFocus : Roo.emptyFn,
20628 hideMode:'offsets',
20631 tbContainer : false,
20633 toolbarContainer :function() {
20634 return this.wrap.select('.x-html-editor-tb',true).first();
20638 * Protected method that will not generally be called directly. It
20639 * is called when the editor creates its toolbar. Override this method if you need to
20640 * add custom toolbar buttons.
20641 * @param {HtmlEditor} editor
20643 createToolbar : function(){
20645 Roo.log("create toolbars");
20647 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20648 this.toolbars[0].render(this.toolbarContainer());
20652 // if (!editor.toolbars || !editor.toolbars.length) {
20653 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20656 // for (var i =0 ; i < editor.toolbars.length;i++) {
20657 // editor.toolbars[i] = Roo.factory(
20658 // typeof(editor.toolbars[i]) == 'string' ?
20659 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
20660 // Roo.bootstrap.HtmlEditor);
20661 // editor.toolbars[i].init(editor);
20667 onRender : function(ct, position)
20669 // Roo.log("Call onRender: " + this.xtype);
20671 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20673 this.wrap = this.inputEl().wrap({
20674 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20677 this.editorcore.onRender(ct, position);
20679 if (this.resizable) {
20680 this.resizeEl = new Roo.Resizable(this.wrap, {
20684 minHeight : this.height,
20685 height: this.height,
20686 handles : this.resizable,
20689 resize : function(r, w, h) {
20690 _t.onResize(w,h); // -something
20696 this.createToolbar(this);
20699 if(!this.width && this.resizable){
20700 this.setSize(this.wrap.getSize());
20702 if (this.resizeEl) {
20703 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20704 // should trigger onReize..
20710 onResize : function(w, h)
20712 Roo.log('resize: ' +w + ',' + h );
20713 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20717 if(this.inputEl() ){
20718 if(typeof w == 'number'){
20719 var aw = w - this.wrap.getFrameWidth('lr');
20720 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20723 if(typeof h == 'number'){
20724 var tbh = -11; // fixme it needs to tool bar size!
20725 for (var i =0; i < this.toolbars.length;i++) {
20726 // fixme - ask toolbars for heights?
20727 tbh += this.toolbars[i].el.getHeight();
20728 //if (this.toolbars[i].footer) {
20729 // tbh += this.toolbars[i].footer.el.getHeight();
20737 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20738 ah -= 5; // knock a few pixes off for look..
20739 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20743 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20744 this.editorcore.onResize(ew,eh);
20749 * Toggles the editor between standard and source edit mode.
20750 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20752 toggleSourceEdit : function(sourceEditMode)
20754 this.editorcore.toggleSourceEdit(sourceEditMode);
20756 if(this.editorcore.sourceEditMode){
20757 Roo.log('editor - showing textarea');
20760 // Roo.log(this.syncValue());
20762 this.inputEl().removeClass(['hide', 'x-hidden']);
20763 this.inputEl().dom.removeAttribute('tabIndex');
20764 this.inputEl().focus();
20766 Roo.log('editor - hiding textarea');
20768 // Roo.log(this.pushValue());
20771 this.inputEl().addClass(['hide', 'x-hidden']);
20772 this.inputEl().dom.setAttribute('tabIndex', -1);
20773 //this.deferFocus();
20776 if(this.resizable){
20777 this.setSize(this.wrap.getSize());
20780 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20783 // private (for BoxComponent)
20784 adjustSize : Roo.BoxComponent.prototype.adjustSize,
20786 // private (for BoxComponent)
20787 getResizeEl : function(){
20791 // private (for BoxComponent)
20792 getPositionEl : function(){
20797 initEvents : function(){
20798 this.originalValue = this.getValue();
20802 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20805 // markInvalid : Roo.emptyFn,
20807 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20810 // clearInvalid : Roo.emptyFn,
20812 setValue : function(v){
20813 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20814 this.editorcore.pushValue();
20819 deferFocus : function(){
20820 this.focus.defer(10, this);
20824 focus : function(){
20825 this.editorcore.focus();
20831 onDestroy : function(){
20837 for (var i =0; i < this.toolbars.length;i++) {
20838 // fixme - ask toolbars for heights?
20839 this.toolbars[i].onDestroy();
20842 this.wrap.dom.innerHTML = '';
20843 this.wrap.remove();
20848 onFirstFocus : function(){
20849 //Roo.log("onFirstFocus");
20850 this.editorcore.onFirstFocus();
20851 for (var i =0; i < this.toolbars.length;i++) {
20852 this.toolbars[i].onFirstFocus();
20858 syncValue : function()
20860 this.editorcore.syncValue();
20863 pushValue : function()
20865 this.editorcore.pushValue();
20869 // hide stuff that is not compatible
20883 * @event specialkey
20887 * @cfg {String} fieldClass @hide
20890 * @cfg {String} focusClass @hide
20893 * @cfg {String} autoCreate @hide
20896 * @cfg {String} inputType @hide
20899 * @cfg {String} invalidClass @hide
20902 * @cfg {String} invalidText @hide
20905 * @cfg {String} msgFx @hide
20908 * @cfg {String} validateOnBlur @hide
20917 Roo.namespace('Roo.bootstrap.htmleditor');
20919 * @class Roo.bootstrap.HtmlEditorToolbar1
20924 new Roo.bootstrap.HtmlEditor({
20927 new Roo.bootstrap.HtmlEditorToolbar1({
20928 disable : { fonts: 1 , format: 1, ..., ... , ...],
20934 * @cfg {Object} disable List of elements to disable..
20935 * @cfg {Array} btns List of additional buttons.
20939 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
20942 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
20945 Roo.apply(this, config);
20947 // default disabled, based on 'good practice'..
20948 this.disable = this.disable || {};
20949 Roo.applyIf(this.disable, {
20952 specialElements : true
20954 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
20956 this.editor = config.editor;
20957 this.editorcore = config.editor.editorcore;
20959 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
20961 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
20962 // dont call parent... till later.
20964 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
20969 editorcore : false,
20974 "h1","h2","h3","h4","h5","h6",
20976 "abbr", "acronym", "address", "cite", "samp", "var",
20980 onRender : function(ct, position)
20982 // Roo.log("Call onRender: " + this.xtype);
20984 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
20986 this.el.dom.style.marginBottom = '0';
20988 var editorcore = this.editorcore;
20989 var editor= this.editor;
20992 var btn = function(id,cmd , toggle, handler){
20994 var event = toggle ? 'toggle' : 'click';
20999 xns: Roo.bootstrap,
21002 enableToggle:toggle !== false,
21004 pressed : toggle ? false : null,
21007 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21008 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21017 xns: Roo.bootstrap,
21018 glyphicon : 'font',
21022 xns: Roo.bootstrap,
21026 Roo.each(this.formats, function(f) {
21027 style.menu.items.push({
21029 xns: Roo.bootstrap,
21030 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21035 editorcore.insertTag(this.tagname);
21042 children.push(style);
21045 btn('bold',false,true);
21046 btn('italic',false,true);
21047 btn('align-left', 'justifyleft',true);
21048 btn('align-center', 'justifycenter',true);
21049 btn('align-right' , 'justifyright',true);
21050 btn('link', false, false, function(btn) {
21051 //Roo.log("create link?");
21052 var url = prompt(this.createLinkText, this.defaultLinkValue);
21053 if(url && url != 'http:/'+'/'){
21054 this.editorcore.relayCmd('createlink', url);
21057 btn('list','insertunorderedlist',true);
21058 btn('pencil', false,true, function(btn){
21061 this.toggleSourceEdit(btn.pressed);
21067 xns: Roo.bootstrap,
21072 xns: Roo.bootstrap,
21077 cog.menu.items.push({
21079 xns: Roo.bootstrap,
21080 html : Clean styles,
21085 editorcore.insertTag(this.tagname);
21094 this.xtype = 'NavSimplebar';
21096 for(var i=0;i< children.length;i++) {
21098 this.buttons.add(this.addxtypeChild(children[i]));
21102 editor.on('editorevent', this.updateToolbar, this);
21104 onBtnClick : function(id)
21106 this.editorcore.relayCmd(id);
21107 this.editorcore.focus();
21111 * Protected method that will not generally be called directly. It triggers
21112 * a toolbar update by reading the markup state of the current selection in the editor.
21114 updateToolbar: function(){
21116 if(!this.editorcore.activated){
21117 this.editor.onFirstFocus(); // is this neeed?
21121 var btns = this.buttons;
21122 var doc = this.editorcore.doc;
21123 btns.get('bold').setActive(doc.queryCommandState('bold'));
21124 btns.get('italic').setActive(doc.queryCommandState('italic'));
21125 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21127 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21128 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21129 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21131 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21132 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21135 var ans = this.editorcore.getAllAncestors();
21136 if (this.formatCombo) {
21139 var store = this.formatCombo.store;
21140 this.formatCombo.setValue("");
21141 for (var i =0; i < ans.length;i++) {
21142 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21144 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21152 // hides menus... - so this cant be on a menu...
21153 Roo.bootstrap.MenuMgr.hideAll();
21155 Roo.bootstrap.MenuMgr.hideAll();
21156 //this.editorsyncValue();
21158 onFirstFocus: function() {
21159 this.buttons.each(function(item){
21163 toggleSourceEdit : function(sourceEditMode){
21166 if(sourceEditMode){
21167 Roo.log("disabling buttons");
21168 this.buttons.each( function(item){
21169 if(item.cmd != 'pencil'){
21175 Roo.log("enabling buttons");
21176 if(this.editorcore.initialized){
21177 this.buttons.each( function(item){
21183 Roo.log("calling toggole on editor");
21184 // tell the editor that it's been pressed..
21185 this.editor.toggleSourceEdit(sourceEditMode);
21195 * @class Roo.bootstrap.Table.AbstractSelectionModel
21196 * @extends Roo.util.Observable
21197 * Abstract base class for grid SelectionModels. It provides the interface that should be
21198 * implemented by descendant classes. This class should not be directly instantiated.
21201 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21202 this.locked = false;
21203 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21207 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21208 /** @ignore Called by the grid automatically. Do not call directly. */
21209 init : function(grid){
21215 * Locks the selections.
21218 this.locked = true;
21222 * Unlocks the selections.
21224 unlock : function(){
21225 this.locked = false;
21229 * Returns true if the selections are locked.
21230 * @return {Boolean}
21232 isLocked : function(){
21233 return this.locked;
21237 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21238 * @class Roo.bootstrap.Table.RowSelectionModel
21239 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21240 * It supports multiple selections and keyboard selection/navigation.
21242 * @param {Object} config
21245 Roo.bootstrap.Table.RowSelectionModel = function(config){
21246 Roo.apply(this, config);
21247 this.selections = new Roo.util.MixedCollection(false, function(o){
21252 this.lastActive = false;
21256 * @event selectionchange
21257 * Fires when the selection changes
21258 * @param {SelectionModel} this
21260 "selectionchange" : true,
21262 * @event afterselectionchange
21263 * Fires after the selection changes (eg. by key press or clicking)
21264 * @param {SelectionModel} this
21266 "afterselectionchange" : true,
21268 * @event beforerowselect
21269 * Fires when a row is selected being selected, return false to cancel.
21270 * @param {SelectionModel} this
21271 * @param {Number} rowIndex The selected index
21272 * @param {Boolean} keepExisting False if other selections will be cleared
21274 "beforerowselect" : true,
21277 * Fires when a row is selected.
21278 * @param {SelectionModel} this
21279 * @param {Number} rowIndex The selected index
21280 * @param {Roo.data.Record} r The record
21282 "rowselect" : true,
21284 * @event rowdeselect
21285 * Fires when a row is deselected.
21286 * @param {SelectionModel} this
21287 * @param {Number} rowIndex The selected index
21289 "rowdeselect" : true
21291 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21292 this.locked = false;
21295 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21297 * @cfg {Boolean} singleSelect
21298 * True to allow selection of only one row at a time (defaults to false)
21300 singleSelect : false,
21303 initEvents : function(){
21305 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21306 this.grid.on("mousedown", this.handleMouseDown, this);
21307 }else{ // allow click to work like normal
21308 this.grid.on("rowclick", this.handleDragableRowClick, this);
21311 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21312 "up" : function(e){
21314 this.selectPrevious(e.shiftKey);
21315 }else if(this.last !== false && this.lastActive !== false){
21316 var last = this.last;
21317 this.selectRange(this.last, this.lastActive-1);
21318 this.grid.getView().focusRow(this.lastActive);
21319 if(last !== false){
21323 this.selectFirstRow();
21325 this.fireEvent("afterselectionchange", this);
21327 "down" : function(e){
21329 this.selectNext(e.shiftKey);
21330 }else if(this.last !== false && this.lastActive !== false){
21331 var last = this.last;
21332 this.selectRange(this.last, this.lastActive+1);
21333 this.grid.getView().focusRow(this.lastActive);
21334 if(last !== false){
21338 this.selectFirstRow();
21340 this.fireEvent("afterselectionchange", this);
21345 var view = this.grid.view;
21346 view.on("refresh", this.onRefresh, this);
21347 view.on("rowupdated", this.onRowUpdated, this);
21348 view.on("rowremoved", this.onRemove, this);
21352 onRefresh : function(){
21353 var ds = this.grid.dataSource, i, v = this.grid.view;
21354 var s = this.selections;
21355 s.each(function(r){
21356 if((i = ds.indexOfId(r.id)) != -1){
21365 onRemove : function(v, index, r){
21366 this.selections.remove(r);
21370 onRowUpdated : function(v, index, r){
21371 if(this.isSelected(r)){
21372 v.onRowSelect(index);
21378 * @param {Array} records The records to select
21379 * @param {Boolean} keepExisting (optional) True to keep existing selections
21381 selectRecords : function(records, keepExisting){
21383 this.clearSelections();
21385 var ds = this.grid.dataSource;
21386 for(var i = 0, len = records.length; i < len; i++){
21387 this.selectRow(ds.indexOf(records[i]), true);
21392 * Gets the number of selected rows.
21395 getCount : function(){
21396 return this.selections.length;
21400 * Selects the first row in the grid.
21402 selectFirstRow : function(){
21407 * Select the last row.
21408 * @param {Boolean} keepExisting (optional) True to keep existing selections
21410 selectLastRow : function(keepExisting){
21411 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21415 * Selects the row immediately following the last selected row.
21416 * @param {Boolean} keepExisting (optional) True to keep existing selections
21418 selectNext : function(keepExisting){
21419 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21420 this.selectRow(this.last+1, keepExisting);
21421 this.grid.getView().focusRow(this.last);
21426 * Selects the row that precedes the last selected row.
21427 * @param {Boolean} keepExisting (optional) True to keep existing selections
21429 selectPrevious : function(keepExisting){
21431 this.selectRow(this.last-1, keepExisting);
21432 this.grid.getView().focusRow(this.last);
21437 * Returns the selected records
21438 * @return {Array} Array of selected records
21440 getSelections : function(){
21441 return [].concat(this.selections.items);
21445 * Returns the first selected record.
21448 getSelected : function(){
21449 return this.selections.itemAt(0);
21454 * Clears all selections.
21456 clearSelections : function(fast){
21457 if(this.locked) return;
21459 var ds = this.grid.dataSource;
21460 var s = this.selections;
21461 s.each(function(r){
21462 this.deselectRow(ds.indexOfId(r.id));
21466 this.selections.clear();
21473 * Selects all rows.
21475 selectAll : function(){
21476 if(this.locked) return;
21477 this.selections.clear();
21478 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21479 this.selectRow(i, true);
21484 * Returns True if there is a selection.
21485 * @return {Boolean}
21487 hasSelection : function(){
21488 return this.selections.length > 0;
21492 * Returns True if the specified row is selected.
21493 * @param {Number/Record} record The record or index of the record to check
21494 * @return {Boolean}
21496 isSelected : function(index){
21497 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21498 return (r && this.selections.key(r.id) ? true : false);
21502 * Returns True if the specified record id is selected.
21503 * @param {String} id The id of record to check
21504 * @return {Boolean}
21506 isIdSelected : function(id){
21507 return (this.selections.key(id) ? true : false);
21511 handleMouseDown : function(e, t){
21512 var view = this.grid.getView(), rowIndex;
21513 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21516 if(e.shiftKey && this.last !== false){
21517 var last = this.last;
21518 this.selectRange(last, rowIndex, e.ctrlKey);
21519 this.last = last; // reset the last
21520 view.focusRow(rowIndex);
21522 var isSelected = this.isSelected(rowIndex);
21523 if(e.button !== 0 && isSelected){
21524 view.focusRow(rowIndex);
21525 }else if(e.ctrlKey && isSelected){
21526 this.deselectRow(rowIndex);
21527 }else if(!isSelected){
21528 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21529 view.focusRow(rowIndex);
21532 this.fireEvent("afterselectionchange", this);
21535 handleDragableRowClick : function(grid, rowIndex, e)
21537 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21538 this.selectRow(rowIndex, false);
21539 grid.view.focusRow(rowIndex);
21540 this.fireEvent("afterselectionchange", this);
21545 * Selects multiple rows.
21546 * @param {Array} rows Array of the indexes of the row to select
21547 * @param {Boolean} keepExisting (optional) True to keep existing selections
21549 selectRows : function(rows, keepExisting){
21551 this.clearSelections();
21553 for(var i = 0, len = rows.length; i < len; i++){
21554 this.selectRow(rows[i], true);
21559 * Selects a range of rows. All rows in between startRow and endRow are also selected.
21560 * @param {Number} startRow The index of the first row in the range
21561 * @param {Number} endRow The index of the last row in the range
21562 * @param {Boolean} keepExisting (optional) True to retain existing selections
21564 selectRange : function(startRow, endRow, keepExisting){
21565 if(this.locked) return;
21567 this.clearSelections();
21569 if(startRow <= endRow){
21570 for(var i = startRow; i <= endRow; i++){
21571 this.selectRow(i, true);
21574 for(var i = startRow; i >= endRow; i--){
21575 this.selectRow(i, true);
21581 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21582 * @param {Number} startRow The index of the first row in the range
21583 * @param {Number} endRow The index of the last row in the range
21585 deselectRange : function(startRow, endRow, preventViewNotify){
21586 if(this.locked) return;
21587 for(var i = startRow; i <= endRow; i++){
21588 this.deselectRow(i, preventViewNotify);
21594 * @param {Number} row The index of the row to select
21595 * @param {Boolean} keepExisting (optional) True to keep existing selections
21597 selectRow : function(index, keepExisting, preventViewNotify){
21598 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21599 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21600 if(!keepExisting || this.singleSelect){
21601 this.clearSelections();
21603 var r = this.grid.dataSource.getAt(index);
21604 this.selections.add(r);
21605 this.last = this.lastActive = index;
21606 if(!preventViewNotify){
21607 this.grid.getView().onRowSelect(index);
21609 this.fireEvent("rowselect", this, index, r);
21610 this.fireEvent("selectionchange", this);
21616 * @param {Number} row The index of the row to deselect
21618 deselectRow : function(index, preventViewNotify){
21619 if(this.locked) return;
21620 if(this.last == index){
21623 if(this.lastActive == index){
21624 this.lastActive = false;
21626 var r = this.grid.dataSource.getAt(index);
21627 this.selections.remove(r);
21628 if(!preventViewNotify){
21629 this.grid.getView().onRowDeselect(index);
21631 this.fireEvent("rowdeselect", this, index);
21632 this.fireEvent("selectionchange", this);
21636 restoreLast : function(){
21638 this.last = this._last;
21643 acceptsNav : function(row, col, cm){
21644 return !cm.isHidden(col) && cm.isCellEditable(col, row);
21648 onEditorKey : function(field, e){
21649 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21654 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21656 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21658 }else if(k == e.ENTER && !e.ctrlKey){
21662 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21664 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21666 }else if(k == e.ESC){
21670 g.startEditing(newCell[0], newCell[1]);
21675 * Ext JS Library 1.1.1
21676 * Copyright(c) 2006-2007, Ext JS, LLC.
21678 * Originally Released Under LGPL - original licence link has changed is not relivant.
21681 * <script type="text/javascript">
21685 * @class Roo.bootstrap.PagingToolbar
21686 * @extends Roo.bootstrap.NavSimplebar
21687 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21689 * Create a new PagingToolbar
21690 * @param {Object} config The config object
21691 * @param {Roo.data.Store} store
21693 Roo.bootstrap.PagingToolbar = function(config)
21695 // old args format still supported... - xtype is prefered..
21696 // created from xtype...
21698 this.ds = config.dataSource;
21700 if (config.store && !this.ds) {
21701 this.store= Roo.factory(config.store, Roo.data);
21702 this.ds = this.store;
21703 this.ds.xmodule = this.xmodule || false;
21706 this.toolbarItems = [];
21707 if (config.items) {
21708 this.toolbarItems = config.items;
21711 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21716 this.bind(this.ds);
21719 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21723 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21725 * @cfg {Roo.data.Store} dataSource
21726 * The underlying data store providing the paged data
21729 * @cfg {String/HTMLElement/Element} container
21730 * container The id or element that will contain the toolbar
21733 * @cfg {Boolean} displayInfo
21734 * True to display the displayMsg (defaults to false)
21737 * @cfg {Number} pageSize
21738 * The number of records to display per page (defaults to 20)
21742 * @cfg {String} displayMsg
21743 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21745 displayMsg : 'Displaying {0} - {1} of {2}',
21747 * @cfg {String} emptyMsg
21748 * The message to display when no records are found (defaults to "No data to display")
21750 emptyMsg : 'No data to display',
21752 * Customizable piece of the default paging text (defaults to "Page")
21755 beforePageText : "Page",
21757 * Customizable piece of the default paging text (defaults to "of %0")
21760 afterPageText : "of {0}",
21762 * Customizable piece of the default paging text (defaults to "First Page")
21765 firstText : "First Page",
21767 * Customizable piece of the default paging text (defaults to "Previous Page")
21770 prevText : "Previous Page",
21772 * Customizable piece of the default paging text (defaults to "Next Page")
21775 nextText : "Next Page",
21777 * Customizable piece of the default paging text (defaults to "Last Page")
21780 lastText : "Last Page",
21782 * Customizable piece of the default paging text (defaults to "Refresh")
21785 refreshText : "Refresh",
21789 onRender : function(ct, position)
21791 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21792 this.navgroup.parentId = this.id;
21793 this.navgroup.onRender(this.el, null);
21794 // add the buttons to the navgroup
21796 if(this.displayInfo){
21797 Roo.log(this.el.select('ul.navbar-nav',true).first());
21798 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21799 this.displayEl = this.el.select('.x-paging-info', true).first();
21800 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21801 // this.displayEl = navel.el.select('span',true).first();
21807 Roo.each(_this.buttons, function(e){ // this might need to use render????
21808 Roo.factory(e).onRender(_this.el, null);
21812 Roo.each(_this.toolbarItems, function(e) {
21813 _this.navgroup.addItem(e);
21817 this.first = this.navgroup.addItem({
21818 tooltip: this.firstText,
21820 icon : 'fa fa-backward',
21822 preventDefault: true,
21823 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21826 this.prev = this.navgroup.addItem({
21827 tooltip: this.prevText,
21829 icon : 'fa fa-step-backward',
21831 preventDefault: true,
21832 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
21834 //this.addSeparator();
21837 var field = this.navgroup.addItem( {
21839 cls : 'x-paging-position',
21841 html : this.beforePageText +
21842 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
21843 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
21846 this.field = field.el.select('input', true).first();
21847 this.field.on("keydown", this.onPagingKeydown, this);
21848 this.field.on("focus", function(){this.dom.select();});
21851 this.afterTextEl = field.el.select('.x-paging-after',true).first();
21852 //this.field.setHeight(18);
21853 //this.addSeparator();
21854 this.next = this.navgroup.addItem({
21855 tooltip: this.nextText,
21857 html : ' <i class="fa fa-step-forward">',
21859 preventDefault: true,
21860 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
21862 this.last = this.navgroup.addItem({
21863 tooltip: this.lastText,
21864 icon : 'fa fa-forward',
21867 preventDefault: true,
21868 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
21870 //this.addSeparator();
21871 this.loading = this.navgroup.addItem({
21872 tooltip: this.refreshText,
21873 icon: 'fa fa-refresh',
21874 preventDefault: true,
21875 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
21881 updateInfo : function(){
21882 if(this.displayEl){
21883 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
21884 var msg = count == 0 ?
21888 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
21890 this.displayEl.update(msg);
21895 onLoad : function(ds, r, o){
21896 this.cursor = o.params ? o.params.start : 0;
21897 var d = this.getPageData(),
21901 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
21902 this.field.dom.value = ap;
21903 this.first.setDisabled(ap == 1);
21904 this.prev.setDisabled(ap == 1);
21905 this.next.setDisabled(ap == ps);
21906 this.last.setDisabled(ap == ps);
21907 this.loading.enable();
21912 getPageData : function(){
21913 var total = this.ds.getTotalCount();
21916 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
21917 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
21922 onLoadError : function(){
21923 this.loading.enable();
21927 onPagingKeydown : function(e){
21928 var k = e.getKey();
21929 var d = this.getPageData();
21931 var v = this.field.dom.value, pageNum;
21932 if(!v || isNaN(pageNum = parseInt(v, 10))){
21933 this.field.dom.value = d.activePage;
21936 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
21937 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21940 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))
21942 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
21943 this.field.dom.value = pageNum;
21944 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
21947 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21949 var v = this.field.dom.value, pageNum;
21950 var increment = (e.shiftKey) ? 10 : 1;
21951 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21953 if(!v || isNaN(pageNum = parseInt(v, 10))) {
21954 this.field.dom.value = d.activePage;
21957 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
21959 this.field.dom.value = parseInt(v, 10) + increment;
21960 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
21961 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21968 beforeLoad : function(){
21970 this.loading.disable();
21975 onClick : function(which){
21984 ds.load({params:{start: 0, limit: this.pageSize}});
21987 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
21990 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
21993 var total = ds.getTotalCount();
21994 var extra = total % this.pageSize;
21995 var lastStart = extra ? (total - extra) : total-this.pageSize;
21996 ds.load({params:{start: lastStart, limit: this.pageSize}});
21999 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22005 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22006 * @param {Roo.data.Store} store The data store to unbind
22008 unbind : function(ds){
22009 ds.un("beforeload", this.beforeLoad, this);
22010 ds.un("load", this.onLoad, this);
22011 ds.un("loadexception", this.onLoadError, this);
22012 ds.un("remove", this.updateInfo, this);
22013 ds.un("add", this.updateInfo, this);
22014 this.ds = undefined;
22018 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22019 * @param {Roo.data.Store} store The data store to bind
22021 bind : function(ds){
22022 ds.on("beforeload", this.beforeLoad, this);
22023 ds.on("load", this.onLoad, this);
22024 ds.on("loadexception", this.onLoadError, this);
22025 ds.on("remove", this.updateInfo, this);
22026 ds.on("add", this.updateInfo, this);
22037 * @class Roo.bootstrap.MessageBar
22038 * @extends Roo.bootstrap.Component
22039 * Bootstrap MessageBar class
22040 * @cfg {String} html contents of the MessageBar
22041 * @cfg {String} weight (info | success | warning | danger) default info
22042 * @cfg {String} beforeClass insert the bar before the given class
22043 * @cfg {Boolean} closable (true | false) default false
22044 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22047 * Create a new Element
22048 * @param {Object} config The config object
22051 Roo.bootstrap.MessageBar = function(config){
22052 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22055 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22061 beforeClass: 'bootstrap-sticky-wrap',
22063 getAutoCreate : function(){
22067 cls: 'alert alert-dismissable alert-' + this.weight,
22072 html: this.html || ''
22078 cfg.cls += ' alert-messages-fixed';
22092 onRender : function(ct, position)
22094 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22097 var cfg = Roo.apply({}, this.getAutoCreate());
22101 cfg.cls += ' ' + this.cls;
22104 cfg.style = this.style;
22106 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22108 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22111 this.el.select('>button.close').on('click', this.hide, this);
22117 if (!this.rendered) {
22123 this.fireEvent('show', this);
22129 if (!this.rendered) {
22135 this.fireEvent('hide', this);
22138 update : function()
22140 // var e = this.el.dom.firstChild;
22142 // if(this.closable){
22143 // e = e.nextSibling;
22146 // e.data = this.html || '';
22148 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22164 * @class Roo.bootstrap.Graph
22165 * @extends Roo.bootstrap.Component
22166 * Bootstrap Graph class
22170 @cfg {String} graphtype bar | vbar | pie
22171 @cfg {number} g_x coodinator | centre x (pie)
22172 @cfg {number} g_y coodinator | centre y (pie)
22173 @cfg {number} g_r radius (pie)
22174 @cfg {number} g_height height of the chart (respected by all elements in the set)
22175 @cfg {number} g_width width of the chart (respected by all elements in the set)
22176 @cfg {Object} title The title of the chart
22179 -opts (object) options for the chart
22181 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22182 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22184 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.
22185 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22187 o stretch (boolean)
22189 -opts (object) options for the pie
22192 o startAngle (number)
22193 o endAngle (number)
22197 * Create a new Input
22198 * @param {Object} config The config object
22201 Roo.bootstrap.Graph = function(config){
22202 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22208 * The img click event for the img.
22209 * @param {Roo.EventObject} e
22215 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22226 //g_colors: this.colors,
22233 getAutoCreate : function(){
22244 onRender : function(ct,position){
22245 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22246 this.raphael = Raphael(this.el.dom);
22248 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22249 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22250 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22251 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22253 r.text(160, 10, "Single Series Chart").attr(txtattr);
22254 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22255 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22256 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22258 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22259 r.barchart(330, 10, 300, 220, data1);
22260 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22261 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22264 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22265 // r.barchart(30, 30, 560, 250, xdata, {
22266 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22267 // axis : "0 0 1 1",
22268 // axisxlabels : xdata
22269 // //yvalues : cols,
22272 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22274 // this.load(null,xdata,{
22275 // axis : "0 0 1 1",
22276 // axisxlabels : xdata
22281 load : function(graphtype,xdata,opts){
22282 this.raphael.clear();
22284 graphtype = this.graphtype;
22289 var r = this.raphael,
22290 fin = function () {
22291 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22293 fout = function () {
22294 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22296 pfin = function() {
22297 this.sector.stop();
22298 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22301 this.label[0].stop();
22302 this.label[0].attr({ r: 7.5 });
22303 this.label[1].attr({ "font-weight": 800 });
22306 pfout = function() {
22307 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22310 this.label[0].animate({ r: 5 }, 500, "bounce");
22311 this.label[1].attr({ "font-weight": 400 });
22317 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22320 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22323 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
22324 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22326 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22333 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22338 setTitle: function(o)
22343 initEvents: function() {
22346 this.el.on('click', this.onClick, this);
22350 onClick : function(e)
22352 Roo.log('img onclick');
22353 this.fireEvent('click', this, e);
22365 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22368 * @class Roo.bootstrap.dash.NumberBox
22369 * @extends Roo.bootstrap.Component
22370 * Bootstrap NumberBox class
22371 * @cfg {String} headline Box headline
22372 * @cfg {String} content Box content
22373 * @cfg {String} icon Box icon
22374 * @cfg {String} footer Footer text
22375 * @cfg {String} fhref Footer href
22378 * Create a new NumberBox
22379 * @param {Object} config The config object
22383 Roo.bootstrap.dash.NumberBox = function(config){
22384 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22388 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
22397 getAutoCreate : function(){
22401 cls : 'small-box ',
22409 cls : 'roo-headline',
22410 html : this.headline
22414 cls : 'roo-content',
22415 html : this.content
22429 cls : 'ion ' + this.icon
22438 cls : 'small-box-footer',
22439 href : this.fhref || '#',
22443 cfg.cn.push(footer);
22450 onRender : function(ct,position){
22451 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22458 setHeadline: function (value)
22460 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22463 setFooter: function (value, href)
22465 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22468 this.el.select('a.small-box-footer',true).first().attr('href', href);
22473 setContent: function (value)
22475 this.el.select('.roo-content',true).first().dom.innerHTML = value;
22478 initEvents: function()
22492 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22495 * @class Roo.bootstrap.dash.TabBox
22496 * @extends Roo.bootstrap.Component
22497 * Bootstrap TabBox class
22498 * @cfg {String} title Title of the TabBox
22499 * @cfg {String} icon Icon of the TabBox
22500 * @cfg {Boolean} showtabs (true|false) show the tabs default true
22501 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22504 * Create a new TabBox
22505 * @param {Object} config The config object
22509 Roo.bootstrap.dash.TabBox = function(config){
22510 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22515 * When a pane is added
22516 * @param {Roo.bootstrap.dash.TabPane} pane
22520 * @event activatepane
22521 * When a pane is activated
22522 * @param {Roo.bootstrap.dash.TabPane} pane
22524 "activatepane" : true
22532 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
22537 tabScrollable : false,
22539 getChildContainer : function()
22541 return this.el.select('.tab-content', true).first();
22544 getAutoCreate : function(){
22548 cls: 'pull-left header',
22556 cls: 'fa ' + this.icon
22562 cls: 'nav nav-tabs pull-right',
22568 if(this.tabScrollable){
22575 cls: 'nav nav-tabs pull-right',
22586 cls: 'nav-tabs-custom',
22591 cls: 'tab-content no-padding',
22599 initEvents : function()
22601 //Roo.log('add add pane handler');
22602 this.on('addpane', this.onAddPane, this);
22605 * Updates the box title
22606 * @param {String} html to set the title to.
22608 setTitle : function(value)
22610 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22612 onAddPane : function(pane)
22614 this.panes.push(pane);
22615 //Roo.log('addpane');
22617 // tabs are rendere left to right..
22618 if(!this.showtabs){
22622 var ctr = this.el.select('.nav-tabs', true).first();
22625 var existing = ctr.select('.nav-tab',true);
22626 var qty = existing.getCount();;
22629 var tab = ctr.createChild({
22631 cls : 'nav-tab' + (qty ? '' : ' active'),
22639 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22642 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22644 pane.el.addClass('active');
22649 onTabClick : function(ev,un,ob,pane)
22651 //Roo.log('tab - prev default');
22652 ev.preventDefault();
22655 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22656 pane.tab.addClass('active');
22657 //Roo.log(pane.title);
22658 this.getChildContainer().select('.tab-pane',true).removeClass('active');
22659 // technically we should have a deactivate event.. but maybe add later.
22660 // and it should not de-activate the selected tab...
22661 this.fireEvent('activatepane', pane);
22662 pane.el.addClass('active');
22663 pane.fireEvent('activate');
22668 getActivePane : function()
22671 Roo.each(this.panes, function(p) {
22672 if(p.el.hasClass('active')){
22693 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22695 * @class Roo.bootstrap.TabPane
22696 * @extends Roo.bootstrap.Component
22697 * Bootstrap TabPane class
22698 * @cfg {Boolean} active (false | true) Default false
22699 * @cfg {String} title title of panel
22703 * Create a new TabPane
22704 * @param {Object} config The config object
22707 Roo.bootstrap.dash.TabPane = function(config){
22708 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22714 * When a pane is activated
22715 * @param {Roo.bootstrap.dash.TabPane} pane
22722 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
22727 // the tabBox that this is attached to.
22730 getAutoCreate : function()
22738 cfg.cls += ' active';
22743 initEvents : function()
22745 //Roo.log('trigger add pane handler');
22746 this.parent().fireEvent('addpane', this)
22750 * Updates the tab title
22751 * @param {String} html to set the title to.
22753 setTitle: function(str)
22759 this.tab.select('a', true).first().dom.innerHTML = str;
22776 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22779 * @class Roo.bootstrap.menu.Menu
22780 * @extends Roo.bootstrap.Component
22781 * Bootstrap Menu class - container for Menu
22782 * @cfg {String} html Text of the menu
22783 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22784 * @cfg {String} icon Font awesome icon
22785 * @cfg {String} pos Menu align to (top | bottom) default bottom
22789 * Create a new Menu
22790 * @param {Object} config The config object
22794 Roo.bootstrap.menu.Menu = function(config){
22795 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22799 * @event beforeshow
22800 * Fires before this menu is displayed
22801 * @param {Roo.bootstrap.menu.Menu} this
22805 * @event beforehide
22806 * Fires before this menu is hidden
22807 * @param {Roo.bootstrap.menu.Menu} this
22812 * Fires after this menu is displayed
22813 * @param {Roo.bootstrap.menu.Menu} this
22818 * Fires after this menu is hidden
22819 * @param {Roo.bootstrap.menu.Menu} this
22824 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22825 * @param {Roo.bootstrap.menu.Menu} this
22826 * @param {Roo.EventObject} e
22833 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
22837 weight : 'default',
22842 getChildContainer : function() {
22843 if(this.isSubMenu){
22847 return this.el.select('ul.dropdown-menu', true).first();
22850 getAutoCreate : function()
22855 cls : 'roo-menu-text',
22863 cls : 'fa ' + this.icon
22874 cls : 'dropdown-button btn btn-' + this.weight,
22879 cls : 'dropdown-toggle btn btn-' + this.weight,
22889 cls : 'dropdown-menu'
22895 if(this.pos == 'top'){
22896 cfg.cls += ' dropup';
22899 if(this.isSubMenu){
22902 cls : 'dropdown-menu'
22909 onRender : function(ct, position)
22911 this.isSubMenu = ct.hasClass('dropdown-submenu');
22913 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
22916 initEvents : function()
22918 if(this.isSubMenu){
22922 this.hidden = true;
22924 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
22925 this.triggerEl.on('click', this.onTriggerPress, this);
22927 this.buttonEl = this.el.select('button.dropdown-button', true).first();
22928 this.buttonEl.on('click', this.onClick, this);
22934 if(this.isSubMenu){
22938 return this.el.select('ul.dropdown-menu', true).first();
22941 onClick : function(e)
22943 this.fireEvent("click", this, e);
22946 onTriggerPress : function(e)
22948 if (this.isVisible()) {
22955 isVisible : function(){
22956 return !this.hidden;
22961 this.fireEvent("beforeshow", this);
22963 this.hidden = false;
22964 this.el.addClass('open');
22966 Roo.get(document).on("mouseup", this.onMouseUp, this);
22968 this.fireEvent("show", this);
22975 this.fireEvent("beforehide", this);
22977 this.hidden = true;
22978 this.el.removeClass('open');
22980 Roo.get(document).un("mouseup", this.onMouseUp);
22982 this.fireEvent("hide", this);
22985 onMouseUp : function()
22999 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23002 * @class Roo.bootstrap.menu.Item
23003 * @extends Roo.bootstrap.Component
23004 * Bootstrap MenuItem class
23005 * @cfg {Boolean} submenu (true | false) default false
23006 * @cfg {String} html text of the item
23007 * @cfg {String} href the link
23008 * @cfg {Boolean} disable (true | false) default false
23009 * @cfg {Boolean} preventDefault (true | false) default true
23010 * @cfg {String} icon Font awesome icon
23011 * @cfg {String} pos Submenu align to (left | right) default right
23015 * Create a new Item
23016 * @param {Object} config The config object
23020 Roo.bootstrap.menu.Item = function(config){
23021 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23025 * Fires when the mouse is hovering over this menu
23026 * @param {Roo.bootstrap.menu.Item} this
23027 * @param {Roo.EventObject} e
23032 * Fires when the mouse exits this menu
23033 * @param {Roo.bootstrap.menu.Item} this
23034 * @param {Roo.EventObject} e
23040 * The raw click event for the entire grid.
23041 * @param {Roo.EventObject} e
23047 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23052 preventDefault: true,
23057 getAutoCreate : function()
23062 cls : 'roo-menu-item-text',
23070 cls : 'fa ' + this.icon
23079 href : this.href || '#',
23086 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23090 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23092 if(this.pos == 'left'){
23093 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23100 initEvents : function()
23102 this.el.on('mouseover', this.onMouseOver, this);
23103 this.el.on('mouseout', this.onMouseOut, this);
23105 this.el.select('a', true).first().on('click', this.onClick, this);
23109 onClick : function(e)
23111 if(this.preventDefault){
23112 e.preventDefault();
23115 this.fireEvent("click", this, e);
23118 onMouseOver : function(e)
23120 if(this.submenu && this.pos == 'left'){
23121 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23124 this.fireEvent("mouseover", this, e);
23127 onMouseOut : function(e)
23129 this.fireEvent("mouseout", this, e);
23141 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23144 * @class Roo.bootstrap.menu.Separator
23145 * @extends Roo.bootstrap.Component
23146 * Bootstrap Separator class
23149 * Create a new Separator
23150 * @param {Object} config The config object
23154 Roo.bootstrap.menu.Separator = function(config){
23155 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23158 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23160 getAutoCreate : function(){
23181 * @class Roo.bootstrap.Tooltip
23182 * Bootstrap Tooltip class
23183 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23184 * to determine which dom element triggers the tooltip.
23186 * It needs to add support for additional attributes like tooltip-position
23189 * Create a new Toolti
23190 * @param {Object} config The config object
23193 Roo.bootstrap.Tooltip = function(config){
23194 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23197 Roo.apply(Roo.bootstrap.Tooltip, {
23199 * @function init initialize tooltip monitoring.
23203 currentTip : false,
23204 currentRegion : false,
23210 Roo.get(document).on('mouseover', this.enter ,this);
23211 Roo.get(document).on('mouseout', this.leave, this);
23214 this.currentTip = new Roo.bootstrap.Tooltip();
23217 enter : function(ev)
23219 var dom = ev.getTarget();
23221 //Roo.log(['enter',dom]);
23222 var el = Roo.fly(dom);
23223 if (this.currentEl) {
23225 //Roo.log(this.currentEl);
23226 //Roo.log(this.currentEl.contains(dom));
23227 if (this.currentEl == el) {
23230 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23236 if (this.currentTip.el) {
23237 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23242 // you can not look for children, as if el is the body.. then everythign is the child..
23243 if (!el.attr('tooltip')) { //
23244 if (!el.select("[tooltip]").elements.length) {
23247 // is the mouse over this child...?
23248 bindEl = el.select("[tooltip]").first();
23249 var xy = ev.getXY();
23250 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23251 //Roo.log("not in region.");
23254 //Roo.log("child element over..");
23257 this.currentEl = bindEl;
23258 this.currentTip.bind(bindEl);
23259 this.currentRegion = Roo.lib.Region.getRegion(dom);
23260 this.currentTip.enter();
23263 leave : function(ev)
23265 var dom = ev.getTarget();
23266 //Roo.log(['leave',dom]);
23267 if (!this.currentEl) {
23272 if (dom != this.currentEl.dom) {
23275 var xy = ev.getXY();
23276 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23279 // only activate leave if mouse cursor is outside... bounding box..
23284 if (this.currentTip) {
23285 this.currentTip.leave();
23287 //Roo.log('clear currentEl');
23288 this.currentEl = false;
23293 'left' : ['r-l', [-2,0], 'right'],
23294 'right' : ['l-r', [2,0], 'left'],
23295 'bottom' : ['t-b', [0,2], 'top'],
23296 'top' : [ 'b-t', [0,-2], 'bottom']
23302 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
23307 delay : null, // can be { show : 300 , hide: 500}
23311 hoverState : null, //???
23313 placement : 'bottom',
23315 getAutoCreate : function(){
23322 cls : 'tooltip-arrow'
23325 cls : 'tooltip-inner'
23332 bind : function(el)
23338 enter : function () {
23340 if (this.timeout != null) {
23341 clearTimeout(this.timeout);
23344 this.hoverState = 'in';
23345 //Roo.log("enter - show");
23346 if (!this.delay || !this.delay.show) {
23351 this.timeout = setTimeout(function () {
23352 if (_t.hoverState == 'in') {
23355 }, this.delay.show);
23359 clearTimeout(this.timeout);
23361 this.hoverState = 'out';
23362 if (!this.delay || !this.delay.hide) {
23368 this.timeout = setTimeout(function () {
23369 //Roo.log("leave - timeout");
23371 if (_t.hoverState == 'out') {
23373 Roo.bootstrap.Tooltip.currentEl = false;
23381 this.render(document.body);
23384 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23386 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23388 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23390 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23392 var placement = typeof this.placement == 'function' ?
23393 this.placement.call(this, this.el, on_el) :
23396 var autoToken = /\s?auto?\s?/i;
23397 var autoPlace = autoToken.test(placement);
23399 placement = placement.replace(autoToken, '') || 'top';
23403 //this.el.setXY([0,0]);
23405 //this.el.dom.style.display='block';
23407 //this.el.appendTo(on_el);
23409 var p = this.getPosition();
23410 var box = this.el.getBox();
23416 var align = Roo.bootstrap.Tooltip.alignment[placement];
23418 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23420 if(placement == 'top' || placement == 'bottom'){
23422 placement = 'right';
23425 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23426 placement = 'left';
23430 align = Roo.bootstrap.Tooltip.alignment[placement];
23432 this.el.alignTo(this.bindEl, align[0],align[1]);
23433 //var arrow = this.el.select('.arrow',true).first();
23434 //arrow.set(align[2],
23436 this.el.addClass(placement);
23438 this.el.addClass('in fade');
23440 this.hoverState = null;
23442 if (this.el.hasClass('fade')) {
23453 //this.el.setXY([0,0]);
23454 this.el.removeClass('in');
23470 * @class Roo.bootstrap.LocationPicker
23471 * @extends Roo.bootstrap.Component
23472 * Bootstrap LocationPicker class
23473 * @cfg {Number} latitude Position when init default 0
23474 * @cfg {Number} longitude Position when init default 0
23475 * @cfg {Number} zoom default 15
23476 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23477 * @cfg {Boolean} mapTypeControl default false
23478 * @cfg {Boolean} disableDoubleClickZoom default false
23479 * @cfg {Boolean} scrollwheel default true
23480 * @cfg {Boolean} streetViewControl default false
23481 * @cfg {Number} radius default 0
23482 * @cfg {String} locationName
23483 * @cfg {Boolean} draggable default true
23484 * @cfg {Boolean} enableAutocomplete default false
23485 * @cfg {Boolean} enableReverseGeocode default true
23486 * @cfg {String} markerTitle
23489 * Create a new LocationPicker
23490 * @param {Object} config The config object
23494 Roo.bootstrap.LocationPicker = function(config){
23496 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23501 * Fires when the picker initialized.
23502 * @param {Roo.bootstrap.LocationPicker} this
23503 * @param {Google Location} location
23507 * @event positionchanged
23508 * Fires when the picker position changed.
23509 * @param {Roo.bootstrap.LocationPicker} this
23510 * @param {Google Location} location
23512 positionchanged : true,
23515 * Fires when the map resize.
23516 * @param {Roo.bootstrap.LocationPicker} this
23521 * Fires when the map show.
23522 * @param {Roo.bootstrap.LocationPicker} this
23527 * Fires when the map hide.
23528 * @param {Roo.bootstrap.LocationPicker} this
23533 * Fires when click the map.
23534 * @param {Roo.bootstrap.LocationPicker} this
23535 * @param {Map event} e
23539 * @event mapRightClick
23540 * Fires when right click the map.
23541 * @param {Roo.bootstrap.LocationPicker} this
23542 * @param {Map event} e
23544 mapRightClick : true,
23546 * @event markerClick
23547 * Fires when click the marker.
23548 * @param {Roo.bootstrap.LocationPicker} this
23549 * @param {Map event} e
23551 markerClick : true,
23553 * @event markerRightClick
23554 * Fires when right click the marker.
23555 * @param {Roo.bootstrap.LocationPicker} this
23556 * @param {Map event} e
23558 markerRightClick : true,
23560 * @event OverlayViewDraw
23561 * Fires when OverlayView Draw
23562 * @param {Roo.bootstrap.LocationPicker} this
23564 OverlayViewDraw : true,
23566 * @event OverlayViewOnAdd
23567 * Fires when OverlayView Draw
23568 * @param {Roo.bootstrap.LocationPicker} this
23570 OverlayViewOnAdd : true,
23572 * @event OverlayViewOnRemove
23573 * Fires when OverlayView Draw
23574 * @param {Roo.bootstrap.LocationPicker} this
23576 OverlayViewOnRemove : true,
23578 * @event OverlayViewShow
23579 * Fires when OverlayView Draw
23580 * @param {Roo.bootstrap.LocationPicker} this
23581 * @param {Pixel} cpx
23583 OverlayViewShow : true,
23585 * @event OverlayViewHide
23586 * Fires when OverlayView Draw
23587 * @param {Roo.bootstrap.LocationPicker} this
23589 OverlayViewHide : true
23594 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
23596 gMapContext: false,
23602 mapTypeControl: false,
23603 disableDoubleClickZoom: false,
23605 streetViewControl: false,
23609 enableAutocomplete: false,
23610 enableReverseGeocode: true,
23613 getAutoCreate: function()
23618 cls: 'roo-location-picker'
23624 initEvents: function(ct, position)
23626 if(!this.el.getWidth() || this.isApplied()){
23630 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23635 initial: function()
23637 if(!this.mapTypeId){
23638 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23641 this.gMapContext = this.GMapContext();
23643 this.initOverlayView();
23645 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23649 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23650 _this.setPosition(_this.gMapContext.marker.position);
23653 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23654 _this.fireEvent('mapClick', this, event);
23658 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23659 _this.fireEvent('mapRightClick', this, event);
23663 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23664 _this.fireEvent('markerClick', this, event);
23668 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23669 _this.fireEvent('markerRightClick', this, event);
23673 this.setPosition(this.gMapContext.location);
23675 this.fireEvent('initial', this, this.gMapContext.location);
23678 initOverlayView: function()
23682 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23686 _this.fireEvent('OverlayViewDraw', _this);
23691 _this.fireEvent('OverlayViewOnAdd', _this);
23694 onRemove: function()
23696 _this.fireEvent('OverlayViewOnRemove', _this);
23699 show: function(cpx)
23701 _this.fireEvent('OverlayViewShow', _this, cpx);
23706 _this.fireEvent('OverlayViewHide', _this);
23712 fromLatLngToContainerPixel: function(event)
23714 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23717 isApplied: function()
23719 return this.getGmapContext() == false ? false : true;
23722 getGmapContext: function()
23724 return this.gMapContext
23727 GMapContext: function()
23729 var position = new google.maps.LatLng(this.latitude, this.longitude);
23731 var _map = new google.maps.Map(this.el.dom, {
23734 mapTypeId: this.mapTypeId,
23735 mapTypeControl: this.mapTypeControl,
23736 disableDoubleClickZoom: this.disableDoubleClickZoom,
23737 scrollwheel: this.scrollwheel,
23738 streetViewControl: this.streetViewControl,
23739 locationName: this.locationName,
23740 draggable: this.draggable,
23741 enableAutocomplete: this.enableAutocomplete,
23742 enableReverseGeocode: this.enableReverseGeocode
23745 var _marker = new google.maps.Marker({
23746 position: position,
23748 title: this.markerTitle,
23749 draggable: this.draggable
23756 location: position,
23757 radius: this.radius,
23758 locationName: this.locationName,
23759 addressComponents: {
23760 formatted_address: null,
23761 addressLine1: null,
23762 addressLine2: null,
23764 streetNumber: null,
23768 stateOrProvince: null
23771 domContainer: this.el.dom,
23772 geodecoder: new google.maps.Geocoder()
23776 drawCircle: function(center, radius, options)
23778 if (this.gMapContext.circle != null) {
23779 this.gMapContext.circle.setMap(null);
23783 options = Roo.apply({}, options, {
23784 strokeColor: "#0000FF",
23785 strokeOpacity: .35,
23787 fillColor: "#0000FF",
23791 options.map = this.gMapContext.map;
23792 options.radius = radius;
23793 options.center = center;
23794 this.gMapContext.circle = new google.maps.Circle(options);
23795 return this.gMapContext.circle;
23801 setPosition: function(location)
23803 this.gMapContext.location = location;
23804 this.gMapContext.marker.setPosition(location);
23805 this.gMapContext.map.panTo(location);
23806 this.drawCircle(location, this.gMapContext.radius, {});
23810 if (this.gMapContext.settings.enableReverseGeocode) {
23811 this.gMapContext.geodecoder.geocode({
23812 latLng: this.gMapContext.location
23813 }, function(results, status) {
23815 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23816 _this.gMapContext.locationName = results[0].formatted_address;
23817 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23819 _this.fireEvent('positionchanged', this, location);
23826 this.fireEvent('positionchanged', this, location);
23831 google.maps.event.trigger(this.gMapContext.map, "resize");
23833 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
23835 this.fireEvent('resize', this);
23838 setPositionByLatLng: function(latitude, longitude)
23840 this.setPosition(new google.maps.LatLng(latitude, longitude));
23843 getCurrentPosition: function()
23846 latitude: this.gMapContext.location.lat(),
23847 longitude: this.gMapContext.location.lng()
23851 getAddressName: function()
23853 return this.gMapContext.locationName;
23856 getAddressComponents: function()
23858 return this.gMapContext.addressComponents;
23861 address_component_from_google_geocode: function(address_components)
23865 for (var i = 0; i < address_components.length; i++) {
23866 var component = address_components[i];
23867 if (component.types.indexOf("postal_code") >= 0) {
23868 result.postalCode = component.short_name;
23869 } else if (component.types.indexOf("street_number") >= 0) {
23870 result.streetNumber = component.short_name;
23871 } else if (component.types.indexOf("route") >= 0) {
23872 result.streetName = component.short_name;
23873 } else if (component.types.indexOf("neighborhood") >= 0) {
23874 result.city = component.short_name;
23875 } else if (component.types.indexOf("locality") >= 0) {
23876 result.city = component.short_name;
23877 } else if (component.types.indexOf("sublocality") >= 0) {
23878 result.district = component.short_name;
23879 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
23880 result.stateOrProvince = component.short_name;
23881 } else if (component.types.indexOf("country") >= 0) {
23882 result.country = component.short_name;
23886 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
23887 result.addressLine2 = "";
23891 setZoomLevel: function(zoom)
23893 this.gMapContext.map.setZoom(zoom);
23906 this.fireEvent('show', this);
23917 this.fireEvent('hide', this);
23922 Roo.apply(Roo.bootstrap.LocationPicker, {
23924 OverlayView : function(map, options)
23926 options = options || {};
23940 * @class Roo.bootstrap.Alert
23941 * @extends Roo.bootstrap.Component
23942 * Bootstrap Alert class
23943 * @cfg {String} title The title of alert
23944 * @cfg {String} html The content of alert
23945 * @cfg {String} weight ( success | info | warning | danger )
23946 * @cfg {String} faicon font-awesomeicon
23949 * Create a new alert
23950 * @param {Object} config The config object
23954 Roo.bootstrap.Alert = function(config){
23955 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
23959 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
23966 getAutoCreate : function()
23975 cls : 'roo-alert-icon'
23980 cls : 'roo-alert-title',
23985 cls : 'roo-alert-text',
23992 cfg.cn[0].cls += ' fa ' + this.faicon;
23996 cfg.cls += ' alert-' + this.weight;
24002 initEvents: function()
24004 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24007 setTitle : function(str)
24009 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24012 setText : function(str)
24014 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24017 setWeight : function(weight)
24020 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24023 this.weight = weight;
24025 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24028 setIcon : function(icon)
24031 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24036 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24057 * @class Roo.bootstrap.UploadCropbox
24058 * @extends Roo.bootstrap.Component
24059 * Bootstrap UploadCropbox class
24060 * @cfg {String} emptyText show when image has been loaded
24061 * @cfg {String} rotateNotify show when image too small to rotate
24062 * @cfg {Number} errorTimeout default 3000
24063 * @cfg {Number} minWidth default 300
24064 * @cfg {Number} minHeight default 300
24065 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24066 * @cfg {Boolean} isDocument (true|false) default false
24067 * @cfg {String} url action url
24068 * @cfg {String} paramName default 'imageUpload'
24069 * @cfg {String} method default POST
24072 * Create a new UploadCropbox
24073 * @param {Object} config The config object
24076 Roo.bootstrap.UploadCropbox = function(config){
24077 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24081 * @event beforeselectfile
24082 * Fire before select file
24083 * @param {Roo.bootstrap.UploadCropbox} this
24085 "beforeselectfile" : true,
24088 * Fire after initEvent
24089 * @param {Roo.bootstrap.UploadCropbox} this
24094 * Fire after initEvent
24095 * @param {Roo.bootstrap.UploadCropbox} this
24096 * @param {String} data
24101 * Fire when preparing the file data
24102 * @param {Roo.bootstrap.UploadCropbox} this
24103 * @param {Object} file
24108 * Fire when get exception
24109 * @param {Roo.bootstrap.UploadCropbox} this
24110 * @param {XMLHttpRequest} xhr
24112 "exception" : true,
24114 * @event beforeloadcanvas
24115 * Fire before load the canvas
24116 * @param {Roo.bootstrap.UploadCropbox} this
24117 * @param {String} src
24119 "beforeloadcanvas" : true,
24122 * Fire when trash image
24123 * @param {Roo.bootstrap.UploadCropbox} this
24128 * Fire when download the image
24129 * @param {Roo.bootstrap.UploadCropbox} this
24133 * @event footerbuttonclick
24134 * Fire when footerbuttonclick
24135 * @param {Roo.bootstrap.UploadCropbox} this
24136 * @param {String} type
24138 "footerbuttonclick" : true,
24142 * @param {Roo.bootstrap.UploadCropbox} this
24147 * Fire when rotate the image
24148 * @param {Roo.bootstrap.UploadCropbox} this
24149 * @param {String} pos
24154 * Fire when inspect the file
24155 * @param {Roo.bootstrap.UploadCropbox} this
24156 * @param {Object} file
24161 * Fire when xhr upload the file
24162 * @param {Roo.bootstrap.UploadCropbox} this
24163 * @param {Object} data
24168 * Fire when arrange the file data
24169 * @param {Roo.bootstrap.UploadCropbox} this
24170 * @param {Object} formData
24175 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24178 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24180 emptyText : 'Click to upload image',
24181 rotateNotify : 'Image is too small to rotate',
24182 errorTimeout : 3000,
24196 cropType : 'image/jpeg',
24198 canvasLoaded : false,
24199 isDocument : false,
24201 paramName : 'imageUpload',
24203 getAutoCreate : function()
24207 cls : 'roo-upload-cropbox',
24211 cls : 'roo-upload-cropbox-selector',
24216 cls : 'roo-upload-cropbox-body',
24217 style : 'cursor:pointer',
24221 cls : 'roo-upload-cropbox-preview'
24225 cls : 'roo-upload-cropbox-thumb'
24229 cls : 'roo-upload-cropbox-empty-notify',
24230 html : this.emptyText
24234 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24235 html : this.rotateNotify
24241 cls : 'roo-upload-cropbox-footer',
24244 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24254 onRender : function(ct, position)
24256 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24258 if (this.buttons.length) {
24260 Roo.each(this.buttons, function(bb) {
24262 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24264 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24270 initEvents : function()
24272 this.urlAPI = (window.createObjectURL && window) ||
24273 (window.URL && URL.revokeObjectURL && URL) ||
24274 (window.webkitURL && webkitURL);
24276 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24277 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24279 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24280 this.selectorEl.hide();
24282 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24283 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24285 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24286 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24287 this.thumbEl.hide();
24289 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24290 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24292 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24293 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24294 this.errorEl.hide();
24296 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24297 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24298 this.footerEl.hide();
24300 this.setThumbBoxSize();
24306 this.fireEvent('initial', this);
24313 window.addEventListener("resize", function() { _this.resize(); } );
24315 this.bodyEl.on('click', this.beforeSelectFile, this);
24318 this.bodyEl.on('touchstart', this.onTouchStart, this);
24319 this.bodyEl.on('touchmove', this.onTouchMove, this);
24320 this.bodyEl.on('touchend', this.onTouchEnd, this);
24324 this.bodyEl.on('mousedown', this.onMouseDown, this);
24325 this.bodyEl.on('mousemove', this.onMouseMove, this);
24326 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24327 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24328 Roo.get(document).on('mouseup', this.onMouseUp, this);
24331 this.selectorEl.on('change', this.onFileSelected, this);
24337 this.baseScale = 1;
24339 this.baseRotate = 1;
24340 this.dragable = false;
24341 this.pinching = false;
24344 this.cropData = false;
24345 this.notifyEl.dom.innerHTML = this.emptyText;
24347 this.selectorEl.dom.value = '';
24351 resize : function()
24353 if(this.fireEvent('resize', this) != false){
24354 this.setThumbBoxPosition();
24355 this.setCanvasPosition();
24359 onFooterButtonClick : function(e, el, o, type)
24362 case 'rotate-left' :
24363 this.onRotateLeft(e);
24365 case 'rotate-right' :
24366 this.onRotateRight(e);
24369 this.beforeSelectFile(e);
24384 this.fireEvent('footerbuttonclick', this, type);
24387 beforeSelectFile : function(e)
24389 e.preventDefault();
24391 if(this.fireEvent('beforeselectfile', this) != false){
24392 this.selectorEl.dom.click();
24396 onFileSelected : function(e)
24398 e.preventDefault();
24400 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24404 var file = this.selectorEl.dom.files[0];
24406 if(this.fireEvent('inspect', this, file) != false){
24407 this.prepare(file);
24412 trash : function(e)
24414 this.fireEvent('trash', this);
24417 download : function(e)
24419 this.fireEvent('download', this);
24422 loadCanvas : function(src)
24424 if(this.fireEvent('beforeloadcanvas', this, src) != false){
24428 this.imageEl = document.createElement('img');
24432 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24434 this.imageEl.src = src;
24438 onLoadCanvas : function()
24440 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24441 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24443 this.bodyEl.un('click', this.beforeSelectFile, this);
24445 this.notifyEl.hide();
24446 this.thumbEl.show();
24447 this.footerEl.show();
24449 this.baseRotateLevel();
24451 if(this.isDocument){
24452 this.setThumbBoxSize();
24455 this.setThumbBoxPosition();
24457 this.baseScaleLevel();
24463 this.canvasLoaded = true;
24467 setCanvasPosition : function()
24469 if(!this.canvasEl){
24473 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24474 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24476 this.previewEl.setLeft(pw);
24477 this.previewEl.setTop(ph);
24481 onMouseDown : function(e)
24485 this.dragable = true;
24486 this.pinching = false;
24488 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24489 this.dragable = false;
24493 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24494 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24498 onMouseMove : function(e)
24502 if(!this.canvasLoaded){
24506 if (!this.dragable){
24510 var minX = Math.ceil(this.thumbEl.getLeft(true));
24511 var minY = Math.ceil(this.thumbEl.getTop(true));
24513 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24514 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24516 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24517 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24519 x = x - this.mouseX;
24520 y = y - this.mouseY;
24522 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24523 var bgY = Math.ceil(y + this.previewEl.getTop(true));
24525 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24526 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24528 this.previewEl.setLeft(bgX);
24529 this.previewEl.setTop(bgY);
24531 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24532 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24535 onMouseUp : function(e)
24539 this.dragable = false;
24542 onMouseWheel : function(e)
24546 this.startScale = this.scale;
24548 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24550 if(!this.zoomable()){
24551 this.scale = this.startScale;
24560 zoomable : function()
24562 var minScale = this.thumbEl.getWidth() / this.minWidth;
24564 if(this.minWidth < this.minHeight){
24565 minScale = this.thumbEl.getHeight() / this.minHeight;
24568 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
24569 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
24573 (this.rotate == 0 || this.rotate == 180) &&
24575 width > this.imageEl.OriginWidth ||
24576 height > this.imageEl.OriginHeight ||
24577 (width < this.minWidth && height < this.minHeight)
24585 (this.rotate == 90 || this.rotate == 270) &&
24587 width > this.imageEl.OriginWidth ||
24588 height > this.imageEl.OriginHeight ||
24589 (width < this.minHeight && height < this.minWidth)
24596 !this.isDocument &&
24597 (this.rotate == 0 || this.rotate == 180) &&
24599 width < this.minWidth ||
24600 width > this.imageEl.OriginWidth ||
24601 height < this.minHeight ||
24602 height > this.imageEl.OriginHeight
24609 !this.isDocument &&
24610 (this.rotate == 90 || this.rotate == 270) &&
24612 width < this.minHeight ||
24613 width > this.imageEl.OriginWidth ||
24614 height < this.minWidth ||
24615 height > this.imageEl.OriginHeight
24625 onRotateLeft : function(e)
24627 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24629 var minScale = this.thumbEl.getWidth() / this.minWidth;
24631 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24632 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24634 this.startScale = this.scale;
24636 while (this.getScaleLevel() < minScale){
24638 this.scale = this.scale + 1;
24640 if(!this.zoomable()){
24645 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24646 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24651 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24658 this.scale = this.startScale;
24660 this.onRotateFail();
24665 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24667 if(this.isDocument){
24668 this.setThumbBoxSize();
24669 this.setThumbBoxPosition();
24670 this.setCanvasPosition();
24675 this.fireEvent('rotate', this, 'left');
24679 onRotateRight : function(e)
24681 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24683 var minScale = this.thumbEl.getWidth() / this.minWidth;
24685 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24686 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24688 this.startScale = this.scale;
24690 while (this.getScaleLevel() < minScale){
24692 this.scale = this.scale + 1;
24694 if(!this.zoomable()){
24699 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24700 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24705 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24712 this.scale = this.startScale;
24714 this.onRotateFail();
24719 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24721 if(this.isDocument){
24722 this.setThumbBoxSize();
24723 this.setThumbBoxPosition();
24724 this.setCanvasPosition();
24729 this.fireEvent('rotate', this, 'right');
24732 onRotateFail : function()
24734 this.errorEl.show(true);
24738 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
24743 this.previewEl.dom.innerHTML = '';
24745 var canvasEl = document.createElement("canvas");
24747 var contextEl = canvasEl.getContext("2d");
24749 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24750 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24751 var center = this.imageEl.OriginWidth / 2;
24753 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
24754 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24755 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24756 center = this.imageEl.OriginHeight / 2;
24759 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
24761 contextEl.translate(center, center);
24762 contextEl.rotate(this.rotate * Math.PI / 180);
24764 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24766 this.canvasEl = document.createElement("canvas");
24768 this.contextEl = this.canvasEl.getContext("2d");
24770 switch (this.rotate) {
24773 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24774 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24776 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24781 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24782 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24784 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24785 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);
24789 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24794 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24795 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24797 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24798 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);
24802 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);
24807 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24808 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24810 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24811 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24815 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);
24822 this.previewEl.appendChild(this.canvasEl);
24824 this.setCanvasPosition();
24829 if(!this.canvasLoaded){
24833 var imageCanvas = document.createElement("canvas");
24835 var imageContext = imageCanvas.getContext("2d");
24837 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
24838 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
24840 var center = imageCanvas.width / 2;
24842 imageContext.translate(center, center);
24844 imageContext.rotate(this.rotate * Math.PI / 180);
24846 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24848 var canvas = document.createElement("canvas");
24850 var context = canvas.getContext("2d");
24852 canvas.width = this.minWidth;
24853 canvas.height = this.minHeight;
24855 switch (this.rotate) {
24858 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
24859 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
24861 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
24862 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
24864 var targetWidth = this.minWidth - 2 * x;
24865 var targetHeight = this.minHeight - 2 * y;
24869 if((x == 0 && y == 0) || (x == 0 && y > 0)){
24870 scale = targetWidth / width;
24873 if(x > 0 && y == 0){
24874 scale = targetHeight / height;
24877 if(x > 0 && y > 0){
24878 scale = targetWidth / width;
24880 if(width < height){
24881 scale = targetHeight / height;
24885 context.scale(scale, scale);
24887 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
24888 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
24890 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
24891 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
24893 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
24898 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
24899 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
24901 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
24902 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
24904 var targetWidth = this.minWidth - 2 * x;
24905 var targetHeight = this.minHeight - 2 * y;
24909 if((x == 0 && y == 0) || (x == 0 && y > 0)){
24910 scale = targetWidth / width;
24913 if(x > 0 && y == 0){
24914 scale = targetHeight / height;
24917 if(x > 0 && y > 0){
24918 scale = targetWidth / width;
24920 if(width < height){
24921 scale = targetHeight / height;
24925 context.scale(scale, scale);
24927 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
24928 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
24930 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
24931 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
24933 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
24935 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
24940 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
24941 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
24943 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
24944 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
24946 var targetWidth = this.minWidth - 2 * x;
24947 var targetHeight = this.minHeight - 2 * y;
24951 if((x == 0 && y == 0) || (x == 0 && y > 0)){
24952 scale = targetWidth / width;
24955 if(x > 0 && y == 0){
24956 scale = targetHeight / height;
24959 if(x > 0 && y > 0){
24960 scale = targetWidth / width;
24962 if(width < height){
24963 scale = targetHeight / height;
24967 context.scale(scale, scale);
24969 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
24970 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
24972 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
24973 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
24975 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
24976 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
24978 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
24983 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
24984 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
24986 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
24987 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
24989 var targetWidth = this.minWidth - 2 * x;
24990 var targetHeight = this.minHeight - 2 * y;
24994 if((x == 0 && y == 0) || (x == 0 && y > 0)){
24995 scale = targetWidth / width;
24998 if(x > 0 && y == 0){
24999 scale = targetHeight / height;
25002 if(x > 0 && y > 0){
25003 scale = targetWidth / width;
25005 if(width < height){
25006 scale = targetHeight / height;
25010 context.scale(scale, scale);
25012 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25013 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25015 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25016 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25018 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25020 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25027 this.cropData = canvas.toDataURL(this.cropType);
25029 if(this.fireEvent('crop', this, this.cropData) !== false){
25030 this.process(this.file, this.cropData);
25037 setThumbBoxSize : function()
25041 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25042 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25043 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25045 this.minWidth = width;
25046 this.minHeight = height;
25048 if(this.rotate == 90 || this.rotate == 270){
25049 this.minWidth = height;
25050 this.minHeight = width;
25055 width = Math.ceil(this.minWidth * height / this.minHeight);
25057 if(this.minWidth > this.minHeight){
25059 height = Math.ceil(this.minHeight * width / this.minWidth);
25062 this.thumbEl.setStyle({
25063 width : width + 'px',
25064 height : height + 'px'
25071 setThumbBoxPosition : function()
25073 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25074 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25076 this.thumbEl.setLeft(x);
25077 this.thumbEl.setTop(y);
25081 baseRotateLevel : function()
25083 this.baseRotate = 1;
25086 typeof(this.exif) != 'undefined' &&
25087 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25088 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25090 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25093 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25097 baseScaleLevel : function()
25101 if(this.isDocument){
25103 if(this.baseRotate == 6 || this.baseRotate == 8){
25105 height = this.thumbEl.getHeight();
25106 this.baseScale = height / this.imageEl.OriginWidth;
25108 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25109 width = this.thumbEl.getWidth();
25110 this.baseScale = width / this.imageEl.OriginHeight;
25116 height = this.thumbEl.getHeight();
25117 this.baseScale = height / this.imageEl.OriginHeight;
25119 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25120 width = this.thumbEl.getWidth();
25121 this.baseScale = width / this.imageEl.OriginWidth;
25127 if(this.baseRotate == 6 || this.baseRotate == 8){
25129 width = this.thumbEl.getHeight();
25130 this.baseScale = width / this.imageEl.OriginHeight;
25132 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25133 height = this.thumbEl.getWidth();
25134 this.baseScale = height / this.imageEl.OriginHeight;
25137 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25138 height = this.thumbEl.getWidth();
25139 this.baseScale = height / this.imageEl.OriginHeight;
25141 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25142 width = this.thumbEl.getHeight();
25143 this.baseScale = width / this.imageEl.OriginWidth;
25150 width = this.thumbEl.getWidth();
25151 this.baseScale = width / this.imageEl.OriginWidth;
25153 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25154 height = this.thumbEl.getHeight();
25155 this.baseScale = height / this.imageEl.OriginHeight;
25158 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25160 height = this.thumbEl.getHeight();
25161 this.baseScale = height / this.imageEl.OriginHeight;
25163 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25164 width = this.thumbEl.getWidth();
25165 this.baseScale = width / this.imageEl.OriginWidth;
25173 getScaleLevel : function()
25175 return this.baseScale * Math.pow(1.1, this.scale);
25178 onTouchStart : function(e)
25180 if(!this.canvasLoaded){
25181 this.beforeSelectFile(e);
25185 var touches = e.browserEvent.touches;
25191 if(touches.length == 1){
25192 this.onMouseDown(e);
25196 if(touches.length != 2){
25202 for(var i = 0, finger; finger = touches[i]; i++){
25203 coords.push(finger.pageX, finger.pageY);
25206 var x = Math.pow(coords[0] - coords[2], 2);
25207 var y = Math.pow(coords[1] - coords[3], 2);
25209 this.startDistance = Math.sqrt(x + y);
25211 this.startScale = this.scale;
25213 this.pinching = true;
25214 this.dragable = false;
25218 onTouchMove : function(e)
25220 if(!this.pinching && !this.dragable){
25224 var touches = e.browserEvent.touches;
25231 this.onMouseMove(e);
25237 for(var i = 0, finger; finger = touches[i]; i++){
25238 coords.push(finger.pageX, finger.pageY);
25241 var x = Math.pow(coords[0] - coords[2], 2);
25242 var y = Math.pow(coords[1] - coords[3], 2);
25244 this.endDistance = Math.sqrt(x + y);
25246 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25248 if(!this.zoomable()){
25249 this.scale = this.startScale;
25257 onTouchEnd : function(e)
25259 this.pinching = false;
25260 this.dragable = false;
25264 process : function(file, crop)
25266 this.xhr = new XMLHttpRequest();
25268 file.xhr = this.xhr;
25270 this.xhr.open(this.method, this.url, true);
25273 "Accept": "application/json",
25274 "Cache-Control": "no-cache",
25275 "X-Requested-With": "XMLHttpRequest"
25278 for (var headerName in headers) {
25279 var headerValue = headers[headerName];
25281 this.xhr.setRequestHeader(headerName, headerValue);
25287 this.xhr.onload = function()
25289 _this.xhrOnLoad(_this.xhr);
25292 this.xhr.onerror = function()
25294 _this.xhrOnError(_this.xhr);
25297 var formData = new FormData();
25299 formData.append('returnHTML', 'NO');
25302 formData.append('crop', crop);
25305 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25306 formData.append(this.paramName, file, file.name);
25309 if(typeof(file.filename) != 'undefined'){
25310 formData.append('filename', file.filename);
25313 if(typeof(file.mimetype) != 'undefined'){
25314 formData.append('mimetype', file.mimetype);
25317 if(this.fireEvent('arrange', this, formData) != false){
25318 this.xhr.send(formData);
25322 xhrOnLoad : function(xhr)
25324 if (xhr.readyState !== 4) {
25325 this.fireEvent('exception', this, xhr);
25329 var response = Roo.decode(xhr.responseText);
25331 if(!response.success){
25332 this.fireEvent('exception', this, xhr);
25336 var response = Roo.decode(xhr.responseText);
25338 this.fireEvent('upload', this, response);
25342 xhrOnError : function()
25344 Roo.log('xhr on error');
25346 var response = Roo.decode(xhr.responseText);
25352 prepare : function(file)
25357 if(typeof(file) === 'string'){
25358 this.loadCanvas(file);
25362 if(!file || !this.urlAPI){
25367 this.cropType = file.type;
25371 if(this.fireEvent('prepare', this, this.file) != false){
25373 var reader = new FileReader();
25375 reader.onload = function (e) {
25376 if (e.target.error) {
25377 Roo.log(e.target.error);
25381 var buffer = e.target.result,
25382 dataView = new DataView(buffer),
25384 maxOffset = dataView.byteLength - 4,
25388 if (dataView.getUint16(0) === 0xffd8) {
25389 while (offset < maxOffset) {
25390 markerBytes = dataView.getUint16(offset);
25392 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25393 markerLength = dataView.getUint16(offset + 2) + 2;
25394 if (offset + markerLength > dataView.byteLength) {
25395 Roo.log('Invalid meta data: Invalid segment size.');
25399 if(markerBytes == 0xffe1){
25400 _this.parseExifData(
25407 offset += markerLength;
25417 var url = _this.urlAPI.createObjectURL(_this.file);
25419 _this.loadCanvas(url);
25424 reader.readAsArrayBuffer(this.file);
25430 parseExifData : function(dataView, offset, length)
25432 var tiffOffset = offset + 10,
25436 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25437 // No Exif data, might be XMP data instead
25441 // Check for the ASCII code for "Exif" (0x45786966):
25442 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25443 // No Exif data, might be XMP data instead
25446 if (tiffOffset + 8 > dataView.byteLength) {
25447 Roo.log('Invalid Exif data: Invalid segment size.');
25450 // Check for the two null bytes:
25451 if (dataView.getUint16(offset + 8) !== 0x0000) {
25452 Roo.log('Invalid Exif data: Missing byte alignment offset.');
25455 // Check the byte alignment:
25456 switch (dataView.getUint16(tiffOffset)) {
25458 littleEndian = true;
25461 littleEndian = false;
25464 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25467 // Check for the TIFF tag marker (0x002A):
25468 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25469 Roo.log('Invalid Exif data: Missing TIFF marker.');
25472 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25473 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25475 this.parseExifTags(
25478 tiffOffset + dirOffset,
25483 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25488 if (dirOffset + 6 > dataView.byteLength) {
25489 Roo.log('Invalid Exif data: Invalid directory offset.');
25492 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25493 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25494 if (dirEndOffset + 4 > dataView.byteLength) {
25495 Roo.log('Invalid Exif data: Invalid directory size.');
25498 for (i = 0; i < tagsNumber; i += 1) {
25502 dirOffset + 2 + 12 * i, // tag offset
25506 // Return the offset to the next directory:
25507 return dataView.getUint32(dirEndOffset, littleEndian);
25510 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
25512 var tag = dataView.getUint16(offset, littleEndian);
25514 this.exif[tag] = this.getExifValue(
25518 dataView.getUint16(offset + 2, littleEndian), // tag type
25519 dataView.getUint32(offset + 4, littleEndian), // tag length
25524 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25526 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25535 Roo.log('Invalid Exif data: Invalid tag type.');
25539 tagSize = tagType.size * length;
25540 // Determine if the value is contained in the dataOffset bytes,
25541 // or if the value at the dataOffset is a pointer to the actual data:
25542 dataOffset = tagSize > 4 ?
25543 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25544 if (dataOffset + tagSize > dataView.byteLength) {
25545 Roo.log('Invalid Exif data: Invalid data offset.');
25548 if (length === 1) {
25549 return tagType.getValue(dataView, dataOffset, littleEndian);
25552 for (i = 0; i < length; i += 1) {
25553 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
25556 if (tagType.ascii) {
25558 // Concatenate the chars:
25559 for (i = 0; i < values.length; i += 1) {
25561 // Ignore the terminating NULL byte(s):
25562 if (c === '\u0000') {
25574 Roo.apply(Roo.bootstrap.UploadCropbox, {
25576 'Orientation': 0x0112
25580 1: 0, //'top-left',
25582 3: 180, //'bottom-right',
25583 // 4: 'bottom-left',
25585 6: 90, //'right-top',
25586 // 7: 'right-bottom',
25587 8: 270 //'left-bottom'
25591 // byte, 8-bit unsigned int:
25593 getValue: function (dataView, dataOffset) {
25594 return dataView.getUint8(dataOffset);
25598 // ascii, 8-bit byte:
25600 getValue: function (dataView, dataOffset) {
25601 return String.fromCharCode(dataView.getUint8(dataOffset));
25606 // short, 16 bit int:
25608 getValue: function (dataView, dataOffset, littleEndian) {
25609 return dataView.getUint16(dataOffset, littleEndian);
25613 // long, 32 bit int:
25615 getValue: function (dataView, dataOffset, littleEndian) {
25616 return dataView.getUint32(dataOffset, littleEndian);
25620 // rational = two long values, first is numerator, second is denominator:
25622 getValue: function (dataView, dataOffset, littleEndian) {
25623 return dataView.getUint32(dataOffset, littleEndian) /
25624 dataView.getUint32(dataOffset + 4, littleEndian);
25628 // slong, 32 bit signed int:
25630 getValue: function (dataView, dataOffset, littleEndian) {
25631 return dataView.getInt32(dataOffset, littleEndian);
25635 // srational, two slongs, first is numerator, second is denominator:
25637 getValue: function (dataView, dataOffset, littleEndian) {
25638 return dataView.getInt32(dataOffset, littleEndian) /
25639 dataView.getInt32(dataOffset + 4, littleEndian);
25649 cls : 'btn-group roo-upload-cropbox-rotate-left',
25650 action : 'rotate-left',
25654 cls : 'btn btn-default',
25655 html : '<i class="fa fa-undo"></i>'
25661 cls : 'btn-group roo-upload-cropbox-picture',
25662 action : 'picture',
25666 cls : 'btn btn-default',
25667 html : '<i class="fa fa-picture-o"></i>'
25673 cls : 'btn-group roo-upload-cropbox-rotate-right',
25674 action : 'rotate-right',
25678 cls : 'btn btn-default',
25679 html : '<i class="fa fa-repeat"></i>'
25687 cls : 'btn-group roo-upload-cropbox-rotate-left',
25688 action : 'rotate-left',
25692 cls : 'btn btn-default',
25693 html : '<i class="fa fa-undo"></i>'
25699 cls : 'btn-group roo-upload-cropbox-download',
25700 action : 'download',
25704 cls : 'btn btn-default',
25705 html : '<i class="fa fa-download"></i>'
25711 cls : 'btn-group roo-upload-cropbox-crop',
25716 cls : 'btn btn-default',
25717 html : '<i class="fa fa-crop"></i>'
25723 cls : 'btn-group roo-upload-cropbox-trash',
25728 cls : 'btn btn-default',
25729 html : '<i class="fa fa-trash"></i>'
25735 cls : 'btn-group roo-upload-cropbox-rotate-right',
25736 action : 'rotate-right',
25740 cls : 'btn btn-default',
25741 html : '<i class="fa fa-repeat"></i>'
25749 cls : 'btn-group roo-upload-cropbox-rotate-left',
25750 action : 'rotate-left',
25754 cls : 'btn btn-default',
25755 html : '<i class="fa fa-undo"></i>'
25761 cls : 'btn-group roo-upload-cropbox-rotate-right',
25762 action : 'rotate-right',
25766 cls : 'btn btn-default',
25767 html : '<i class="fa fa-repeat"></i>'
25780 * @class Roo.bootstrap.DocumentManager
25781 * @extends Roo.bootstrap.Component
25782 * Bootstrap DocumentManager class
25783 * @cfg {String} paramName default 'imageUpload'
25784 * @cfg {String} method default POST
25785 * @cfg {String} url action url
25786 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
25787 * @cfg {Boolean} multiple multiple upload default true
25788 * @cfg {Number} thumbSize default 300
25789 * @cfg {String} fieldLabel
25790 * @cfg {Number} labelWidth default 4
25791 * @cfg {String} labelAlign (left|top) default left
25792 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
25795 * Create a new DocumentManager
25796 * @param {Object} config The config object
25799 Roo.bootstrap.DocumentManager = function(config){
25800 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
25805 * Fire when initial the DocumentManager
25806 * @param {Roo.bootstrap.DocumentManager} this
25811 * inspect selected file
25812 * @param {Roo.bootstrap.DocumentManager} this
25813 * @param {File} file
25818 * Fire when xhr load exception
25819 * @param {Roo.bootstrap.DocumentManager} this
25820 * @param {XMLHttpRequest} xhr
25822 "exception" : true,
25825 * prepare the form data
25826 * @param {Roo.bootstrap.DocumentManager} this
25827 * @param {Object} formData
25832 * Fire when remove the file
25833 * @param {Roo.bootstrap.DocumentManager} this
25834 * @param {Object} file
25839 * Fire after refresh the file
25840 * @param {Roo.bootstrap.DocumentManager} this
25845 * Fire after click the image
25846 * @param {Roo.bootstrap.DocumentManager} this
25847 * @param {Object} file
25852 * Fire when upload a image and editable set to true
25853 * @param {Roo.bootstrap.DocumentManager} this
25854 * @param {Object} file
25858 * @event beforeselectfile
25859 * Fire before select file
25860 * @param {Roo.bootstrap.DocumentManager} this
25862 "beforeselectfile" : true,
25865 * Fire before process file
25866 * @param {Roo.bootstrap.DocumentManager} this
25867 * @param {Object} file
25874 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
25883 paramName : 'imageUpload',
25886 labelAlign : 'left',
25890 getAutoCreate : function()
25892 var managerWidget = {
25894 cls : 'roo-document-manager',
25898 cls : 'roo-document-manager-selector',
25903 cls : 'roo-document-manager-uploader',
25907 cls : 'roo-document-manager-upload-btn',
25908 html : '<i class="fa fa-plus"></i>'
25919 cls : 'column col-md-12',
25924 if(this.fieldLabel.length){
25929 cls : 'column col-md-12',
25930 html : this.fieldLabel
25934 cls : 'column col-md-12',
25939 if(this.labelAlign == 'left'){
25943 cls : 'column col-md-' + this.labelWidth,
25944 html : this.fieldLabel
25948 cls : 'column col-md-' + (12 - this.labelWidth),
25958 cls : 'row clearfix',
25966 initEvents : function()
25968 this.managerEl = this.el.select('.roo-document-manager', true).first();
25969 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25971 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
25972 this.selectorEl.hide();
25975 this.selectorEl.attr('multiple', 'multiple');
25978 this.selectorEl.on('change', this.onFileSelected, this);
25980 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
25981 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25983 this.uploader.on('click', this.onUploaderClick, this);
25985 this.renderProgressDialog();
25989 window.addEventListener("resize", function() { _this.refresh(); } );
25991 this.fireEvent('initial', this);
25994 renderProgressDialog : function()
25998 this.progressDialog = new Roo.bootstrap.Modal({
25999 cls : 'roo-document-manager-progress-dialog',
26000 allow_close : false,
26010 btnclick : function() {
26011 _this.uploadCancel();
26017 this.progressDialog.render(Roo.get(document.body));
26019 this.progress = new Roo.bootstrap.Progress({
26020 cls : 'roo-document-manager-progress',
26025 this.progress.render(this.progressDialog.getChildContainer());
26027 this.progressBar = new Roo.bootstrap.ProgressBar({
26028 cls : 'roo-document-manager-progress-bar',
26031 aria_valuemax : 12,
26035 this.progressBar.render(this.progress.getChildContainer());
26038 onUploaderClick : function(e)
26040 e.preventDefault();
26042 if(this.fireEvent('beforeselectfile', this) != false){
26043 this.selectorEl.dom.click();
26048 onFileSelected : function(e)
26050 e.preventDefault();
26052 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26056 Roo.each(this.selectorEl.dom.files, function(file){
26057 if(this.fireEvent('inspect', this, file) != false){
26058 this.files.push(file);
26068 this.selectorEl.dom.value = '';
26070 if(!this.files.length){
26074 if(this.boxes > 0 && this.files.length > this.boxes){
26075 this.files = this.files.slice(0, this.boxes);
26078 this.uploader.show();
26080 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26081 this.uploader.hide();
26090 Roo.each(this.files, function(file){
26092 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26093 var f = this.renderPreview(file);
26098 if(file.type.indexOf('image') != -1){
26099 this.delegates.push(
26101 _this.process(file);
26102 }).createDelegate(this)
26110 _this.process(file);
26111 }).createDelegate(this)
26116 this.files = files;
26118 this.delegates = this.delegates.concat(docs);
26120 if(!this.delegates.length){
26125 this.progressBar.aria_valuemax = this.delegates.length;
26132 arrange : function()
26134 if(!this.delegates.length){
26135 this.progressDialog.hide();
26140 var delegate = this.delegates.shift();
26142 this.progressDialog.show();
26144 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26146 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26151 refresh : function()
26153 this.uploader.show();
26155 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26156 this.uploader.hide();
26159 Roo.isTouch ? this.closable(false) : this.closable(true);
26161 this.fireEvent('refresh', this);
26164 onRemove : function(e, el, o)
26166 e.preventDefault();
26168 this.fireEvent('remove', this, o);
26172 remove : function(o)
26176 Roo.each(this.files, function(file){
26177 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26186 this.files = files;
26193 Roo.each(this.files, function(file){
26198 file.target.remove();
26207 onClick : function(e, el, o)
26209 e.preventDefault();
26211 this.fireEvent('click', this, o);
26215 closable : function(closable)
26217 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26219 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26231 xhrOnLoad : function(xhr)
26233 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26237 if (xhr.readyState !== 4) {
26239 this.fireEvent('exception', this, xhr);
26243 var response = Roo.decode(xhr.responseText);
26245 if(!response.success){
26247 this.fireEvent('exception', this, xhr);
26251 var file = this.renderPreview(response.data);
26253 this.files.push(file);
26259 xhrOnError : function()
26261 Roo.log('xhr on error');
26263 var response = Roo.decode(xhr.responseText);
26270 process : function(file)
26272 if(this.fireEvent('process', this, file) !== false){
26273 if(this.editable && file.type.indexOf('image') != -1){
26274 this.fireEvent('edit', this, file);
26278 this.uploadStart(file, false);
26285 uploadStart : function(file, crop)
26287 this.xhr = new XMLHttpRequest();
26289 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26294 file.xhr = this.xhr;
26296 this.managerEl.createChild({
26298 cls : 'roo-document-manager-loading',
26302 tooltip : file.name,
26303 cls : 'roo-document-manager-thumb',
26304 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26310 this.xhr.open(this.method, this.url, true);
26313 "Accept": "application/json",
26314 "Cache-Control": "no-cache",
26315 "X-Requested-With": "XMLHttpRequest"
26318 for (var headerName in headers) {
26319 var headerValue = headers[headerName];
26321 this.xhr.setRequestHeader(headerName, headerValue);
26327 this.xhr.onload = function()
26329 _this.xhrOnLoad(_this.xhr);
26332 this.xhr.onerror = function()
26334 _this.xhrOnError(_this.xhr);
26337 var formData = new FormData();
26339 formData.append('returnHTML', 'NO');
26342 formData.append('crop', crop);
26345 formData.append(this.paramName, file, file.name);
26347 if(this.fireEvent('prepare', this, formData) != false){
26348 this.xhr.send(formData);
26352 uploadCancel : function()
26356 this.delegates = [];
26358 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26365 renderPreview : function(file)
26367 if(typeof(file.target) != 'undefined' && file.target){
26371 var previewEl = this.managerEl.createChild({
26373 cls : 'roo-document-manager-preview',
26377 tooltip : file.filename,
26378 cls : 'roo-document-manager-thumb',
26379 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26384 html : '<i class="fa fa-times-circle"></i>'
26389 var close = previewEl.select('button.close', true).first();
26391 close.on('click', this.onRemove, this, file);
26393 file.target = previewEl;
26395 var image = previewEl.select('img', true).first();
26399 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26401 image.on('click', this.onClick, this, file);
26407 onPreviewLoad : function(file, image)
26409 if(typeof(file.target) == 'undefined' || !file.target){
26413 var width = image.dom.naturalWidth || image.dom.width;
26414 var height = image.dom.naturalHeight || image.dom.height;
26416 if(width > height){
26417 file.target.addClass('wide');
26421 file.target.addClass('tall');
26426 uploadFromSource : function(file, crop)
26428 this.xhr = new XMLHttpRequest();
26430 this.managerEl.createChild({
26432 cls : 'roo-document-manager-loading',
26436 tooltip : file.name,
26437 cls : 'roo-document-manager-thumb',
26438 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26444 this.xhr.open(this.method, this.url, true);
26447 "Accept": "application/json",
26448 "Cache-Control": "no-cache",
26449 "X-Requested-With": "XMLHttpRequest"
26452 for (var headerName in headers) {
26453 var headerValue = headers[headerName];
26455 this.xhr.setRequestHeader(headerName, headerValue);
26461 this.xhr.onload = function()
26463 _this.xhrOnLoad(_this.xhr);
26466 this.xhr.onerror = function()
26468 _this.xhrOnError(_this.xhr);
26471 var formData = new FormData();
26473 formData.append('returnHTML', 'NO');
26475 formData.append('crop', crop);
26477 if(typeof(file.filename) != 'undefined'){
26478 formData.append('filename', file.filename);
26481 if(typeof(file.mimetype) != 'undefined'){
26482 formData.append('mimetype', file.mimetype);
26485 if(this.fireEvent('prepare', this, formData) != false){
26486 this.xhr.send(formData);
26496 * @class Roo.bootstrap.DocumentViewer
26497 * @extends Roo.bootstrap.Component
26498 * Bootstrap DocumentViewer class
26501 * Create a new DocumentViewer
26502 * @param {Object} config The config object
26505 Roo.bootstrap.DocumentViewer = function(config){
26506 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26511 * Fire after initEvent
26512 * @param {Roo.bootstrap.DocumentViewer} this
26518 * @param {Roo.bootstrap.DocumentViewer} this
26523 * Fire after trash button
26524 * @param {Roo.bootstrap.DocumentViewer} this
26531 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
26533 getAutoCreate : function()
26537 cls : 'roo-document-viewer',
26541 cls : 'roo-document-viewer-body',
26545 cls : 'roo-document-viewer-thumb',
26549 cls : 'roo-document-viewer-image'
26557 cls : 'roo-document-viewer-footer',
26560 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
26568 cls : 'btn btn-default roo-document-viewer-trash',
26569 html : '<i class="fa fa-trash"></i>'
26582 initEvents : function()
26585 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
26586 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26588 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
26589 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26591 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
26592 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26594 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
26595 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26597 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
26598 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26600 this.bodyEl.on('click', this.onClick, this);
26602 this.trashBtn.on('click', this.onTrash, this);
26606 initial : function()
26608 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
26611 this.fireEvent('initial', this);
26615 onClick : function(e)
26617 e.preventDefault();
26619 this.fireEvent('click', this);
26622 onTrash : function(e)
26624 e.preventDefault();
26626 this.fireEvent('trash', this);
26638 * @class Roo.bootstrap.NavProgressBar
26639 * @extends Roo.bootstrap.Component
26640 * Bootstrap NavProgressBar class
26643 * Create a new nav progress bar
26644 * @param {Object} config The config object
26647 Roo.bootstrap.NavProgressBar = function(config){
26648 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
26650 this.bullets = this.bullets || [];
26652 // Roo.bootstrap.NavProgressBar.register(this);
26656 * Fires when the active item changes
26657 * @param {Roo.bootstrap.NavProgressBar} this
26658 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
26659 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
26666 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
26671 getAutoCreate : function()
26673 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
26677 cls : 'roo-navigation-bar-group',
26681 cls : 'roo-navigation-top-bar'
26685 cls : 'roo-navigation-bullets-bar',
26689 cls : 'roo-navigation-bar'
26696 cls : 'roo-navigation-bottom-bar'
26706 initEvents: function()
26711 onRender : function(ct, position)
26713 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
26715 if(this.bullets.length){
26716 Roo.each(this.bullets, function(b){
26725 addItem : function(cfg)
26727 var item = new Roo.bootstrap.NavProgressItem(cfg);
26729 item.parentId = this.id;
26730 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
26733 var top = new Roo.bootstrap.Element({
26735 cls : 'roo-navigation-bar-text'
26738 var bottom = new Roo.bootstrap.Element({
26740 cls : 'roo-navigation-bar-text'
26743 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
26744 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
26746 var topText = new Roo.bootstrap.Element({
26748 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
26751 var bottomText = new Roo.bootstrap.Element({
26753 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
26756 topText.onRender(top.el, null);
26757 bottomText.onRender(bottom.el, null);
26760 item.bottomEl = bottom;
26763 this.barItems.push(item);
26768 getActive : function()
26770 var active = false;
26772 Roo.each(this.barItems, function(v){
26774 if (!v.isActive()) {
26786 setActiveItem : function(item)
26790 Roo.each(this.barItems, function(v){
26791 if (v.rid == item.rid) {
26795 if (v.isActive()) {
26796 v.setActive(false);
26801 item.setActive(true);
26803 this.fireEvent('changed', this, item, prev);
26806 getBarItem: function(rid)
26810 Roo.each(this.barItems, function(e) {
26811 if (e.rid != rid) {
26822 indexOfItem : function(item)
26826 Roo.each(this.barItems, function(v, i){
26828 if (v.rid != item.rid) {
26839 setActiveNext : function()
26841 var i = this.indexOfItem(this.getActive());
26843 if (i > this.barItems.length) {
26847 this.setActiveItem(this.barItems[i+1]);
26850 setActivePrev : function()
26852 var i = this.indexOfItem(this.getActive());
26858 this.setActiveItem(this.barItems[i-1]);
26861 format : function()
26863 if(!this.barItems.length){
26867 var width = 100 / this.barItems.length;
26869 Roo.each(this.barItems, function(i){
26870 i.el.setStyle('width', width + '%');
26871 i.topEl.el.setStyle('width', width + '%');
26872 i.bottomEl.el.setStyle('width', width + '%');
26881 * Nav Progress Item
26886 * @class Roo.bootstrap.NavProgressItem
26887 * @extends Roo.bootstrap.Component
26888 * Bootstrap NavProgressItem class
26889 * @cfg {String} rid the reference id
26890 * @cfg {Boolean} active (true|false) Is item active default false
26891 * @cfg {Boolean} disabled (true|false) Is item active default false
26892 * @cfg {String} html
26893 * @cfg {String} position (top|bottom) text position default bottom
26894 * @cfg {String} icon show icon instead of number
26897 * Create a new NavProgressItem
26898 * @param {Object} config The config object
26900 Roo.bootstrap.NavProgressItem = function(config){
26901 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
26906 * The raw click event for the entire grid.
26907 * @param {Roo.bootstrap.NavProgressItem} this
26908 * @param {Roo.EventObject} e
26915 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
26921 position : 'bottom',
26924 getAutoCreate : function()
26926 var iconCls = 'roo-navigation-bar-item-icon';
26928 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
26932 cls: 'roo-navigation-bar-item',
26942 cfg.cls += ' active';
26945 cfg.cls += ' disabled';
26951 disable : function()
26953 this.setDisabled(true);
26956 enable : function()
26958 this.setDisabled(false);
26961 initEvents: function()
26963 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
26965 this.iconEl.on('click', this.onClick, this);
26968 onClick : function(e)
26970 e.preventDefault();
26976 if(this.fireEvent('click', this, e) === false){
26980 this.parent().setActiveItem(this);
26983 isActive: function ()
26985 return this.active;
26988 setActive : function(state)
26990 if(this.active == state){
26994 this.active = state;
26997 this.el.addClass('active');
27001 this.el.removeClass('active');
27006 setDisabled : function(state)
27008 if(this.disabled == state){
27012 this.disabled = state;
27015 this.el.addClass('disabled');
27019 this.el.removeClass('disabled');
27022 tooltipEl : function()
27024 return this.el.select('.roo-navigation-bar-item-icon', true).first();;