4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
23 * Do not use directly - it does not do anything..
24 * @param {Object} config The config object
29 Roo.bootstrap.Component = function(config){
30 Roo.bootstrap.Component.superclass.constructor.call(this, config);
34 * @event childrenrendered
35 * Fires when the children have been rendered..
36 * @param {Roo.bootstrap.Component} this
38 "childrenrendered" : true
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
50 allowDomMove : false, // to stop relocations in parent onRender...
60 * Initialize Events for the element
62 initEvents : function() { },
68 can_build_overlaid : true,
70 container_method : false,
77 // returns the parent component..
78 return Roo.ComponentMgr.get(this.parentId)
84 onRender : function(ct, position)
86 // Roo.log("Call onRender: " + this.xtype);
88 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
91 if (this.el.attr('xtype')) {
92 this.el.attr('xtypex', this.el.attr('xtype'));
93 this.el.dom.removeAttribute('xtype');
103 var cfg = Roo.apply({}, this.getAutoCreate());
105 cfg.id = this.id || Roo.id();
107 // fill in the extra attributes
108 if (this.xattr && typeof(this.xattr) =='object') {
109 for (var i in this.xattr) {
110 cfg[i] = this.xattr[i];
115 cfg.dataId = this.dataId;
119 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
122 if (this.style) { // fixme needs to support more complex style data.
123 cfg.style = this.style;
127 cfg.name = this.name;
130 this.el = ct.createChild(cfg, position);
133 this.tooltipEl().attr('tooltip', this.tooltip);
136 if(this.tabIndex !== undefined){
137 this.el.dom.setAttribute('tabIndex', this.tabIndex);
144 * Fetch the element to add children to
145 * @return {Roo.Element} defaults to this.el
147 getChildContainer : function()
152 * Fetch the element to display the tooltip on.
153 * @return {Roo.Element} defaults to this.el
155 tooltipEl : function()
160 addxtype : function(tree,cntr)
164 cn = Roo.factory(tree);
166 cn.parentType = this.xtype; //??
167 cn.parentId = this.id;
169 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
170 if (typeof(cn.container_method) == 'string') {
171 cntr = cn.container_method;
175 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
177 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
179 var build_from_html = Roo.XComponent.build_from_html;
181 var is_body = (tree.xtype == 'Body') ;
183 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185 var self_cntr_el = Roo.get(this[cntr](false));
187 // do not try and build conditional elements
188 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
192 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
193 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
194 return this.addxtypeChild(tree,cntr);
197 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
200 return this.addxtypeChild(Roo.apply({}, tree),cntr);
203 Roo.log('skipping render');
209 if (!build_from_html) {
213 // this i think handles overlaying multiple children of the same type
214 // with the sam eelement.. - which might be buggy..
216 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
222 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
226 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
231 addxtypeChild : function (tree, cntr)
233 Roo.debug && Roo.log('addxtypeChild:' + cntr);
235 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
238 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
239 (typeof(tree['flexy:foreach']) != 'undefined');
243 skip_children = false;
244 // render the element if it's not BODY.
245 if (tree.xtype != 'Body') {
247 cn = Roo.factory(tree);
249 cn.parentType = this.xtype; //??
250 cn.parentId = this.id;
252 var build_from_html = Roo.XComponent.build_from_html;
255 // does the container contain child eleemnts with 'xtype' attributes.
256 // that match this xtype..
257 // note - when we render we create these as well..
258 // so we should check to see if body has xtype set.
259 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
261 var self_cntr_el = Roo.get(this[cntr](false));
262 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
264 //Roo.log(Roo.XComponent.build_from_html);
265 //Roo.log("got echild:");
268 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
269 // and are not displayed -this causes this to use up the wrong element when matching.
270 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
273 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
274 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
280 //echild.dom.removeAttribute('xtype');
282 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
283 Roo.debug && Roo.log(self_cntr_el);
284 Roo.debug && Roo.log(echild);
285 Roo.debug && Roo.log(cn);
291 // if object has flexy:if - then it may or may not be rendered.
292 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
293 // skip a flexy if element.
294 Roo.debug && Roo.log('skipping render');
295 Roo.debug && Roo.log(tree);
297 Roo.debug && Roo.log('skipping all children');
298 skip_children = true;
303 // actually if flexy:foreach is found, we really want to create
304 // multiple copies here...
306 //Roo.log(this[cntr]());
307 cn.render(this[cntr](true));
309 // then add the element..
317 if (typeof (tree.menu) != 'undefined') {
318 tree.menu.parentType = cn.xtype;
319 tree.menu.triggerEl = cn.el;
320 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
324 if (!tree.items || !tree.items.length) {
328 var items = tree.items;
331 //Roo.log(items.length);
333 if (!skip_children) {
334 for(var i =0;i < items.length;i++) {
335 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
341 this.fireEvent('childrenrendered', this);
346 * Show a component - removes 'hidden' class
351 this.el.removeClass('hidden');
355 * Hide a component - adds 'hidden' class
359 if (this.el && !this.el.hasClass('hidden')) {
360 this.el.addClass('hidden');
374 * @class Roo.bootstrap.Body
375 * @extends Roo.bootstrap.Component
376 * Bootstrap Body class
380 * @param {Object} config The config object
383 Roo.bootstrap.Body = function(config){
384 Roo.bootstrap.Body.superclass.constructor.call(this, config);
385 this.el = Roo.get(document.body);
386 if (this.cls && this.cls.length) {
387 Roo.get(document.body).addClass(this.cls);
391 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
396 onRender : function(ct, position)
398 /* Roo.log("Roo.bootstrap.Body - onRender");
399 if (this.cls && this.cls.length) {
400 Roo.get(document.body).addClass(this.cls);
420 * @class Roo.bootstrap.ButtonGroup
421 * @extends Roo.bootstrap.Component
422 * Bootstrap ButtonGroup class
423 * @cfg {String} size lg | sm | xs (default empty normal)
424 * @cfg {String} align vertical | justified (default none)
425 * @cfg {String} direction up | down (default down)
426 * @cfg {Boolean} toolbar false | true
427 * @cfg {Boolean} btn true | false
432 * @param {Object} config The config object
435 Roo.bootstrap.ButtonGroup = function(config){
436 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
439 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
447 getAutoCreate : function(){
453 cfg.html = this.html || cfg.html;
464 if (['vertical','justified'].indexOf(this.align)!==-1) {
465 cfg.cls = 'btn-group-' + this.align;
467 if (this.align == 'justified') {
468 console.log(this.items);
472 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
473 cfg.cls += ' btn-group-' + this.size;
476 if (this.direction == 'up') {
477 cfg.cls += ' dropup' ;
493 * @class Roo.bootstrap.Button
494 * @extends Roo.bootstrap.Component
495 * Bootstrap Button class
496 * @cfg {String} html The button content
497 * @cfg {String} weight ( primary | success | info | warning | danger | link ) default
498 * @cfg {String} size ( lg | sm | xs)
499 * @cfg {String} tag ( a | input | submit)
500 * @cfg {String} href empty or href
501 * @cfg {Boolean} disabled default false;
502 * @cfg {Boolean} isClose default false;
503 * @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)
504 * @cfg {String} badge text for badge
505 * @cfg {String} theme default
506 * @cfg {Boolean} inverse
507 * @cfg {Boolean} toggle
508 * @cfg {String} ontext text for on toggle state
509 * @cfg {String} offtext text for off toggle state
510 * @cfg {Boolean} defaulton
511 * @cfg {Boolean} preventDefault default true
512 * @cfg {Boolean} removeClass remove the standard class..
513 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
516 * Create a new button
517 * @param {Object} config The config object
521 Roo.bootstrap.Button = function(config){
522 Roo.bootstrap.Button.superclass.constructor.call(this, config);
527 * When a butotn is pressed
528 * @param {Roo.bootstrap.Button} this
529 * @param {Roo.EventObject} e
534 * After the button has been toggles
535 * @param {Roo.EventObject} e
536 * @param {boolean} pressed (also available as button.pressed)
542 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
560 preventDefault: true,
569 getAutoCreate : function(){
577 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
578 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
583 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
585 if (this.toggle == true) {
588 cls: 'slider-frame roo-button',
593 'data-off-text':'OFF',
594 cls: 'slider-button',
600 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
601 cfg.cls += ' '+this.weight;
610 cfg["aria-hidden"] = true;
612 cfg.html = "×";
618 if (this.theme==='default') {
619 cfg.cls = 'btn roo-button';
621 //if (this.parentType != 'Navbar') {
622 this.weight = this.weight.length ? this.weight : 'default';
624 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
626 cfg.cls += ' btn-' + this.weight;
628 } else if (this.theme==='glow') {
631 cfg.cls = 'btn-glow roo-button';
633 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
635 cfg.cls += ' ' + this.weight;
641 this.cls += ' inverse';
646 cfg.cls += ' active';
650 cfg.disabled = 'disabled';
654 Roo.log('changing to ul' );
656 this.glyphicon = 'caret';
659 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
661 //gsRoo.log(this.parentType);
662 if (this.parentType === 'Navbar' && !this.parent().bar) {
663 Roo.log('changing to li?');
672 href : this.href || '#'
675 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
676 cfg.cls += ' dropdown';
683 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
685 if (this.glyphicon) {
686 cfg.html = ' ' + cfg.html;
691 cls: 'glyphicon glyphicon-' + this.glyphicon
701 // cfg.cls='btn roo-button';
705 var value = cfg.html;
710 cls: 'glyphicon glyphicon-' + this.glyphicon,
729 cfg.cls += ' dropdown';
730 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
733 if (cfg.tag !== 'a' && this.href !== '') {
734 throw "Tag must be a to set href.";
735 } else if (this.href.length > 0) {
736 cfg.href = this.href;
739 if(this.removeClass){
744 cfg.target = this.target;
749 initEvents: function() {
750 // Roo.log('init events?');
751 // Roo.log(this.el.dom);
754 if (typeof (this.menu) != 'undefined') {
755 this.menu.parentType = this.xtype;
756 this.menu.triggerEl = this.el;
757 this.addxtype(Roo.apply({}, this.menu));
761 if (this.el.hasClass('roo-button')) {
762 this.el.on('click', this.onClick, this);
764 this.el.select('.roo-button').on('click', this.onClick, this);
767 if(this.removeClass){
768 this.el.on('click', this.onClick, this);
771 this.el.enableDisplayMode();
774 onClick : function(e)
781 Roo.log('button on click ');
782 if(this.preventDefault){
785 if (this.pressed === true || this.pressed === false) {
786 this.pressed = !this.pressed;
787 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
788 this.fireEvent('toggle', this, e, this.pressed);
792 this.fireEvent('click', this, e);
796 * Enables this button
800 this.disabled = false;
801 this.el.removeClass('disabled');
805 * Disable this button
809 this.disabled = true;
810 this.el.addClass('disabled');
813 * sets the active state on/off,
814 * @param {Boolean} state (optional) Force a particular state
816 setActive : function(v) {
818 this.el[v ? 'addClass' : 'removeClass']('active');
821 * toggles the current active state
823 toggleActive : function()
825 var active = this.el.hasClass('active');
826 this.setActive(!active);
830 setText : function(str)
832 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
836 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
859 * @class Roo.bootstrap.Column
860 * @extends Roo.bootstrap.Component
861 * Bootstrap Column class
862 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
863 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
864 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
865 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
866 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
867 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
868 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
869 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
872 * @cfg {Boolean} hidden (true|false) hide the element
873 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
874 * @cfg {String} fa (ban|check|...) font awesome icon
875 * @cfg {Number} fasize (1|2|....) font awsome size
877 * @cfg {String} icon (info-sign|check|...) glyphicon name
879 * @cfg {String} html content of column.
882 * Create a new Column
883 * @param {Object} config The config object
886 Roo.bootstrap.Column = function(config){
887 Roo.bootstrap.Column.superclass.constructor.call(this, config);
890 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
908 getAutoCreate : function(){
909 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
917 ['xs','sm','md','lg'].map(function(size){
918 //Roo.log( size + ':' + settings[size]);
920 if (settings[size+'off'] !== false) {
921 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
924 if (settings[size] === false) {
928 if (!settings[size]) { // 0 = hidden
929 cfg.cls += ' hidden-' + size;
932 cfg.cls += ' col-' + size + '-' + settings[size];
937 cfg.cls += ' hidden';
940 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
941 cfg.cls +=' alert alert-' + this.alert;
945 if (this.html.length) {
946 cfg.html = this.html;
950 if (this.fasize > 1) {
951 fasize = ' fa-' + this.fasize + 'x';
953 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
958 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
977 * @class Roo.bootstrap.Container
978 * @extends Roo.bootstrap.Component
979 * Bootstrap Container class
980 * @cfg {Boolean} jumbotron is it a jumbotron element
981 * @cfg {String} html content of element
982 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
983 * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
984 * @cfg {String} header content of header (for panel)
985 * @cfg {String} footer content of footer (for panel)
986 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
987 * @cfg {String} tag (header|aside|section) type of HTML tag.
988 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
989 * @cfg {String} fa font awesome icon
990 * @cfg {String} icon (info-sign|check|...) glyphicon name
991 * @cfg {Boolean} hidden (true|false) hide the element
992 * @cfg {Boolean} expandable (true|false) default false
993 * @cfg {Boolean} expanded (true|false) default true
994 * @cfg {String} rheader contet on the right of header
995 * @cfg {Boolean} clickable (true|false) default false
999 * Create a new Container
1000 * @param {Object} config The config object
1003 Roo.bootstrap.Container = function(config){
1004 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1010 * After the panel has been expand
1012 * @param {Roo.bootstrap.Container} this
1017 * After the panel has been collapsed
1019 * @param {Roo.bootstrap.Container} this
1024 * When a element is chick
1025 * @param {Roo.bootstrap.Container} this
1026 * @param {Roo.EventObject} e
1032 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1050 getChildContainer : function() {
1056 if (this.panel.length) {
1057 return this.el.select('.panel-body',true).first();
1064 getAutoCreate : function(){
1067 tag : this.tag || 'div',
1071 if (this.jumbotron) {
1072 cfg.cls = 'jumbotron';
1077 // - this is applied by the parent..
1079 // cfg.cls = this.cls + '';
1082 if (this.sticky.length) {
1084 var bd = Roo.get(document.body);
1085 if (!bd.hasClass('bootstrap-sticky')) {
1086 bd.addClass('bootstrap-sticky');
1087 Roo.select('html',true).setStyle('height', '100%');
1090 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1094 if (this.well.length) {
1095 switch (this.well) {
1098 cfg.cls +=' well well-' +this.well;
1107 cfg.cls += ' hidden';
1111 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1112 cfg.cls +=' alert alert-' + this.alert;
1117 if (this.panel.length) {
1118 cfg.cls += ' panel panel-' + this.panel;
1120 if (this.header.length) {
1124 if(this.expandable){
1126 cfg.cls = cfg.cls + ' expandable';
1130 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1138 cls : 'panel-title',
1139 html : (this.expandable ? ' ' : '') + this.header
1143 cls: 'panel-header-right',
1149 cls : 'panel-heading',
1150 style : this.expandable ? 'cursor: pointer' : '',
1158 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1163 if (this.footer.length) {
1165 cls : 'panel-footer',
1174 body.html = this.html || cfg.html;
1175 // prefix with the icons..
1177 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1180 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1185 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1186 cfg.cls = 'container';
1192 initEvents: function()
1194 if(this.expandable){
1195 var headerEl = this.headerEl();
1198 headerEl.on('click', this.onToggleClick, this);
1203 this.el.on('click', this.onClick, this);
1208 onToggleClick : function()
1210 var headerEl = this.headerEl();
1226 if(this.fireEvent('expand', this)) {
1228 this.expanded = true;
1230 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1232 this.el.select('.panel-body',true).first().removeClass('hide');
1234 var toggleEl = this.toggleEl();
1240 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1245 collapse : function()
1247 if(this.fireEvent('collapse', this)) {
1249 this.expanded = false;
1251 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1252 this.el.select('.panel-body',true).first().addClass('hide');
1254 var toggleEl = this.toggleEl();
1260 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1264 toggleEl : function()
1266 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1270 return this.el.select('.panel-heading .fa',true).first();
1273 headerEl : function()
1275 if(!this.el || !this.panel.length || !this.header.length){
1279 return this.el.select('.panel-heading',true).first()
1282 titleEl : function()
1284 if(!this.el || !this.panel.length || !this.header.length){
1288 return this.el.select('.panel-title',true).first();
1291 setTitle : function(v)
1293 var titleEl = this.titleEl();
1299 titleEl.dom.innerHTML = v;
1302 getTitle : function()
1305 var titleEl = this.titleEl();
1311 return titleEl.dom.innerHTML;
1314 setRightTitle : function(v)
1316 var t = this.el.select('.panel-header-right',true).first();
1322 t.dom.innerHTML = v;
1325 onClick : function(e)
1329 this.fireEvent('click', this, e);
1343 * @class Roo.bootstrap.Img
1344 * @extends Roo.bootstrap.Component
1345 * Bootstrap Img class
1346 * @cfg {Boolean} imgResponsive false | true
1347 * @cfg {String} border rounded | circle | thumbnail
1348 * @cfg {String} src image source
1349 * @cfg {String} alt image alternative text
1350 * @cfg {String} href a tag href
1351 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1352 * @cfg {String} xsUrl xs image source
1353 * @cfg {String} smUrl sm image source
1354 * @cfg {String} mdUrl md image source
1355 * @cfg {String} lgUrl lg image source
1358 * Create a new Input
1359 * @param {Object} config The config object
1362 Roo.bootstrap.Img = function(config){
1363 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1369 * The img click event for the img.
1370 * @param {Roo.EventObject} e
1376 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1378 imgResponsive: true,
1388 getAutoCreate : function()
1390 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1391 return this.createSingleImg();
1396 cls: 'roo-image-responsive-group',
1401 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1403 if(!_this[size + 'Url']){
1409 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1410 html: _this.html || cfg.html,
1411 src: _this[size + 'Url']
1414 img.cls += ' roo-image-responsive-' + size;
1416 var s = ['xs', 'sm', 'md', 'lg'];
1418 s.splice(s.indexOf(size), 1);
1420 Roo.each(s, function(ss){
1421 img.cls += ' hidden-' + ss;
1424 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1425 cfg.cls += ' img-' + _this.border;
1429 cfg.alt = _this.alt;
1442 a.target = _this.target;
1446 cfg.cn.push((_this.href) ? a : img);
1453 createSingleImg : function()
1457 cls: (this.imgResponsive) ? 'img-responsive' : '',
1459 src : 'about:blank' // just incase src get's set to undefined?!?
1462 cfg.html = this.html || cfg.html;
1464 cfg.src = this.src || cfg.src;
1466 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1467 cfg.cls += ' img-' + this.border;
1484 a.target = this.target;
1489 return (this.href) ? a : cfg;
1492 initEvents: function()
1495 this.el.on('click', this.onClick, this);
1500 onClick : function(e)
1502 Roo.log('img onclick');
1503 this.fireEvent('click', this, e);
1517 * @class Roo.bootstrap.Link
1518 * @extends Roo.bootstrap.Component
1519 * Bootstrap Link Class
1520 * @cfg {String} alt image alternative text
1521 * @cfg {String} href a tag href
1522 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1523 * @cfg {String} html the content of the link.
1524 * @cfg {String} anchor name for the anchor link
1526 * @cfg {Boolean} preventDefault (true | false) default false
1530 * Create a new Input
1531 * @param {Object} config The config object
1534 Roo.bootstrap.Link = function(config){
1535 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1541 * The img click event for the img.
1542 * @param {Roo.EventObject} e
1548 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1552 preventDefault: false,
1556 getAutoCreate : function()
1562 // anchor's do not require html/href...
1563 if (this.anchor === false) {
1564 cfg.html = this.html || '';
1565 cfg.href = this.href || '#';
1567 cfg.name = this.anchor;
1568 if (this.html !== false) {
1569 cfg.html = this.html;
1571 if (this.href !== false) {
1572 cfg.href = this.href;
1576 if(this.alt !== false){
1581 if(this.target !== false) {
1582 cfg.target = this.target;
1588 initEvents: function() {
1590 if(!this.href || this.preventDefault){
1591 this.el.on('click', this.onClick, this);
1595 onClick : function(e)
1597 if(this.preventDefault){
1600 //Roo.log('img onclick');
1601 this.fireEvent('click', this, e);
1614 * @class Roo.bootstrap.Header
1615 * @extends Roo.bootstrap.Component
1616 * Bootstrap Header class
1617 * @cfg {String} html content of header
1618 * @cfg {Number} level (1|2|3|4|5|6) default 1
1621 * Create a new Header
1622 * @param {Object} config The config object
1626 Roo.bootstrap.Header = function(config){
1627 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1630 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1638 getAutoCreate : function(){
1643 tag: 'h' + (1 *this.level),
1644 html: this.html || ''
1656 * Ext JS Library 1.1.1
1657 * Copyright(c) 2006-2007, Ext JS, LLC.
1659 * Originally Released Under LGPL - original licence link has changed is not relivant.
1662 * <script type="text/javascript">
1666 * @class Roo.bootstrap.MenuMgr
1667 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1670 Roo.bootstrap.MenuMgr = function(){
1671 var menus, active, groups = {}, attached = false, lastShow = new Date();
1673 // private - called when first menu is created
1676 active = new Roo.util.MixedCollection();
1677 Roo.get(document).addKeyListener(27, function(){
1678 if(active.length > 0){
1686 if(active && active.length > 0){
1687 var c = active.clone();
1697 if(active.length < 1){
1698 Roo.get(document).un("mouseup", onMouseDown);
1706 var last = active.last();
1707 lastShow = new Date();
1710 Roo.get(document).on("mouseup", onMouseDown);
1715 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1716 m.parentMenu.activeChild = m;
1717 }else if(last && last.isVisible()){
1718 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1723 function onBeforeHide(m){
1725 m.activeChild.hide();
1727 if(m.autoHideTimer){
1728 clearTimeout(m.autoHideTimer);
1729 delete m.autoHideTimer;
1734 function onBeforeShow(m){
1735 var pm = m.parentMenu;
1736 if(!pm && !m.allowOtherMenus){
1738 }else if(pm && pm.activeChild && active != m){
1739 pm.activeChild.hide();
1743 // private this should really trigger on mouseup..
1744 function onMouseDown(e){
1745 Roo.log("on Mouse Up");
1746 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1756 function onBeforeCheck(mi, state){
1758 var g = groups[mi.group];
1759 for(var i = 0, l = g.length; i < l; i++){
1761 g[i].setChecked(false);
1770 * Hides all menus that are currently visible
1772 hideAll : function(){
1777 register : function(menu){
1781 menus[menu.id] = menu;
1782 menu.on("beforehide", onBeforeHide);
1783 menu.on("hide", onHide);
1784 menu.on("beforeshow", onBeforeShow);
1785 menu.on("show", onShow);
1787 if(g && menu.events["checkchange"]){
1791 groups[g].push(menu);
1792 menu.on("checkchange", onCheck);
1797 * Returns a {@link Roo.menu.Menu} object
1798 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1799 * be used to generate and return a new Menu instance.
1801 get : function(menu){
1802 if(typeof menu == "string"){ // menu id
1804 }else if(menu.events){ // menu instance
1807 /*else if(typeof menu.length == 'number'){ // array of menu items?
1808 return new Roo.bootstrap.Menu({items:menu});
1809 }else{ // otherwise, must be a config
1810 return new Roo.bootstrap.Menu(menu);
1817 unregister : function(menu){
1818 delete menus[menu.id];
1819 menu.un("beforehide", onBeforeHide);
1820 menu.un("hide", onHide);
1821 menu.un("beforeshow", onBeforeShow);
1822 menu.un("show", onShow);
1824 if(g && menu.events["checkchange"]){
1825 groups[g].remove(menu);
1826 menu.un("checkchange", onCheck);
1831 registerCheckable : function(menuItem){
1832 var g = menuItem.group;
1837 groups[g].push(menuItem);
1838 menuItem.on("beforecheckchange", onBeforeCheck);
1843 unregisterCheckable : function(menuItem){
1844 var g = menuItem.group;
1846 groups[g].remove(menuItem);
1847 menuItem.un("beforecheckchange", onBeforeCheck);
1859 * @class Roo.bootstrap.Menu
1860 * @extends Roo.bootstrap.Component
1861 * Bootstrap Menu class - container for MenuItems
1862 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1866 * @param {Object} config The config object
1870 Roo.bootstrap.Menu = function(config){
1871 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1872 if (this.registerMenu) {
1873 Roo.bootstrap.MenuMgr.register(this);
1878 * Fires before this menu is displayed
1879 * @param {Roo.menu.Menu} this
1884 * Fires before this menu is hidden
1885 * @param {Roo.menu.Menu} this
1890 * Fires after this menu is displayed
1891 * @param {Roo.menu.Menu} this
1896 * Fires after this menu is hidden
1897 * @param {Roo.menu.Menu} this
1902 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1903 * @param {Roo.menu.Menu} this
1904 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1905 * @param {Roo.EventObject} e
1910 * Fires when the mouse is hovering over this menu
1911 * @param {Roo.menu.Menu} this
1912 * @param {Roo.EventObject} e
1913 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1918 * Fires when the mouse exits this menu
1919 * @param {Roo.menu.Menu} this
1920 * @param {Roo.EventObject} e
1921 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1926 * Fires when a menu item contained in this menu is clicked
1927 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1928 * @param {Roo.EventObject} e
1932 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1935 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1939 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1942 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1944 registerMenu : true,
1946 menuItems :false, // stores the menu items..
1952 getChildContainer : function() {
1956 getAutoCreate : function(){
1958 //if (['right'].indexOf(this.align)!==-1) {
1959 // cfg.cn[1].cls += ' pull-right'
1965 cls : 'dropdown-menu' ,
1966 style : 'z-index:1000'
1970 if (this.type === 'submenu') {
1971 cfg.cls = 'submenu active';
1973 if (this.type === 'treeview') {
1974 cfg.cls = 'treeview-menu';
1979 initEvents : function() {
1981 // Roo.log("ADD event");
1982 // Roo.log(this.triggerEl.dom);
1983 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1985 this.triggerEl.addClass('dropdown-toggle');
1988 this.el.on('touchstart' , this.onTouch, this);
1990 this.el.on('click' , this.onClick, this);
1992 this.el.on("mouseover", this.onMouseOver, this);
1993 this.el.on("mouseout", this.onMouseOut, this);
1997 findTargetItem : function(e)
1999 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2003 //Roo.log(t); Roo.log(t.id);
2005 //Roo.log(this.menuitems);
2006 return this.menuitems.get(t.id);
2008 //return this.items.get(t.menuItemId);
2014 onTouch : function(e)
2016 //e.stopEvent(); this make the user popdown broken
2020 onClick : function(e)
2022 Roo.log("menu.onClick");
2023 var t = this.findTargetItem(e);
2024 if(!t || t.isContainer){
2029 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2030 if(t == this.activeItem && t.shouldDeactivate(e)){
2031 this.activeItem.deactivate();
2032 delete this.activeItem;
2036 this.setActiveItem(t, true);
2044 Roo.log('pass click event');
2048 this.fireEvent("click", this, t, e);
2052 onMouseOver : function(e){
2053 var t = this.findTargetItem(e);
2056 // if(t.canActivate && !t.disabled){
2057 // this.setActiveItem(t, true);
2061 this.fireEvent("mouseover", this, e, t);
2063 isVisible : function(){
2064 return !this.hidden;
2066 onMouseOut : function(e){
2067 var t = this.findTargetItem(e);
2070 // if(t == this.activeItem && t.shouldDeactivate(e)){
2071 // this.activeItem.deactivate();
2072 // delete this.activeItem;
2075 this.fireEvent("mouseout", this, e, t);
2080 * Displays this menu relative to another element
2081 * @param {String/HTMLElement/Roo.Element} element The element to align to
2082 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2083 * the element (defaults to this.defaultAlign)
2084 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2086 show : function(el, pos, parentMenu){
2087 this.parentMenu = parentMenu;
2091 this.fireEvent("beforeshow", this);
2092 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2095 * Displays this menu at a specific xy position
2096 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2097 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2099 showAt : function(xy, parentMenu, /* private: */_e){
2100 this.parentMenu = parentMenu;
2105 this.fireEvent("beforeshow", this);
2106 //xy = this.el.adjustForConstraints(xy);
2110 this.hideMenuItems();
2111 this.hidden = false;
2112 this.triggerEl.addClass('open');
2114 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2115 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2120 this.fireEvent("show", this);
2126 this.doFocus.defer(50, this);
2130 doFocus : function(){
2132 this.focusEl.focus();
2137 * Hides this menu and optionally all parent menus
2138 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2140 hide : function(deep){
2142 this.hideMenuItems();
2143 if(this.el && this.isVisible()){
2144 this.fireEvent("beforehide", this);
2145 if(this.activeItem){
2146 this.activeItem.deactivate();
2147 this.activeItem = null;
2149 this.triggerEl.removeClass('open');;
2151 this.fireEvent("hide", this);
2153 if(deep === true && this.parentMenu){
2154 this.parentMenu.hide(true);
2158 onTriggerPress : function(e)
2161 Roo.log('trigger press');
2162 //Roo.log(e.getTarget());
2163 // Roo.log(this.triggerEl.dom);
2164 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2168 if (this.isVisible()) {
2173 this.show(this.triggerEl, false, false);
2182 hideMenuItems : function()
2184 //$(backdrop).remove()
2185 Roo.select('.open',true).each(function(aa) {
2187 aa.removeClass('open');
2188 //var parent = getParent($(this))
2189 //var relatedTarget = { relatedTarget: this }
2191 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2192 //if (e.isDefaultPrevented()) return
2193 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2196 addxtypeChild : function (tree, cntr) {
2197 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2199 this.menuitems.add(comp);
2220 * @class Roo.bootstrap.MenuItem
2221 * @extends Roo.bootstrap.Component
2222 * Bootstrap MenuItem class
2223 * @cfg {String} html the menu label
2224 * @cfg {String} href the link
2225 * @cfg {Boolean} preventDefault (true | false) default true
2226 * @cfg {Boolean} isContainer (true | false) default false
2230 * Create a new MenuItem
2231 * @param {Object} config The config object
2235 Roo.bootstrap.MenuItem = function(config){
2236 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2241 * The raw click event for the entire grid.
2242 * @param {Roo.bootstrap.MenuItem} this
2243 * @param {Roo.EventObject} e
2249 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2253 preventDefault: true,
2254 isContainer : false,
2256 getAutoCreate : function(){
2258 if(this.isContainer){
2261 cls: 'dropdown-menu-item'
2267 cls: 'dropdown-menu-item',
2276 if (this.parent().type == 'treeview') {
2277 cfg.cls = 'treeview-menu';
2280 cfg.cn[0].href = this.href || cfg.cn[0].href ;
2281 cfg.cn[0].html = this.html || cfg.cn[0].html ;
2285 initEvents: function() {
2287 //this.el.select('a').on('click', this.onClick, this);
2290 onClick : function(e)
2292 Roo.log('item on click ');
2293 //if(this.preventDefault){
2294 // e.preventDefault();
2296 //this.parent().hideMenuItems();
2298 this.fireEvent('click', this, e);
2317 * @class Roo.bootstrap.MenuSeparator
2318 * @extends Roo.bootstrap.Component
2319 * Bootstrap MenuSeparator class
2322 * Create a new MenuItem
2323 * @param {Object} config The config object
2327 Roo.bootstrap.MenuSeparator = function(config){
2328 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2331 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2333 getAutoCreate : function(){
2352 * @class Roo.bootstrap.Modal
2353 * @extends Roo.bootstrap.Component
2354 * Bootstrap Modal class
2355 * @cfg {String} title Title of dialog
2356 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2357 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2358 * @cfg {Boolean} specificTitle default false
2359 * @cfg {Array} buttons Array of buttons or standard button set..
2360 * @cfg {String} buttonPosition (left|right|center) default right
2361 * @cfg {Boolean} animate default true
2362 * @cfg {Boolean} allow_close default true
2365 * Create a new Modal Dialog
2366 * @param {Object} config The config object
2369 Roo.bootstrap.Modal = function(config){
2370 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2375 * The raw btnclick event for the button
2376 * @param {Roo.EventObject} e
2380 this.buttons = this.buttons || [];
2383 this.tmpl = Roo.factory(this.tmpl);
2388 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2390 title : 'test dialog',
2400 specificTitle: false,
2402 buttonPosition: 'right',
2416 onRender : function(ct, position)
2418 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2421 var cfg = Roo.apply({}, this.getAutoCreate());
2424 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2426 //if (!cfg.name.length) {
2430 cfg.cls += ' ' + this.cls;
2433 cfg.style = this.style;
2435 this.el = Roo.get(document.body).createChild(cfg, position);
2437 //var type = this.el.dom.type;
2440 if(this.tabIndex !== undefined){
2441 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2445 this.bodyEl = this.el.select('.modal-body',true).first();
2446 this.closeEl = this.el.select('.modal-header .close', true).first();
2447 this.footerEl = this.el.select('.modal-footer',true).first();
2448 this.titleEl = this.el.select('.modal-title',true).first();
2452 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2453 this.maskEl.enableDisplayMode("block");
2455 //this.el.addClass("x-dlg-modal");
2457 if (this.buttons.length) {
2458 Roo.each(this.buttons, function(bb) {
2459 var b = Roo.apply({}, bb);
2460 b.xns = b.xns || Roo.bootstrap;
2461 b.xtype = b.xtype || 'Button';
2462 if (typeof(b.listeners) == 'undefined') {
2463 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2466 var btn = Roo.factory(b);
2468 btn.onRender(this.el.select('.modal-footer div').first());
2472 // render the children.
2475 if(typeof(this.items) != 'undefined'){
2476 var items = this.items;
2479 for(var i =0;i < items.length;i++) {
2480 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2484 this.items = nitems;
2486 // where are these used - they used to be body/close/footer
2490 //this.el.addClass([this.fieldClass, this.cls]);
2494 getAutoCreate : function(){
2499 html : this.html || ''
2504 cls : 'modal-title',
2508 if(this.specificTitle){
2514 if (this.allow_close) {
2525 style : 'display: none',
2528 cls: "modal-dialog",
2531 cls : "modal-content",
2534 cls : 'modal-header',
2539 cls : 'modal-footer',
2543 cls: 'btn-' + this.buttonPosition
2560 modal.cls += ' fade';
2566 getChildContainer : function() {
2571 getButtonContainer : function() {
2572 return this.el.select('.modal-footer div',true).first();
2575 initEvents : function()
2577 if (this.allow_close) {
2578 this.closeEl.on('click', this.hide, this);
2583 window.addEventListener("resize", function() { _this.resize(); } );
2589 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2594 if (!this.rendered) {
2598 this.el.setStyle('display', 'block');
2600 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2603 this.el.addClass('in');
2606 this.el.addClass('in');
2610 // not sure how we can show data in here..
2612 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2615 Roo.get(document.body).addClass("x-body-masked");
2616 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2618 this.el.setStyle('zIndex', '10001');
2620 this.fireEvent('show', this);
2628 Roo.get(document.body).removeClass("x-body-masked");
2629 this.el.removeClass('in');
2630 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2632 if(this.animate){ // why
2634 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2636 this.el.setStyle('display', 'none');
2639 this.fireEvent('hide', this);
2642 addButton : function(str, cb)
2646 var b = Roo.apply({}, { html : str } );
2647 b.xns = b.xns || Roo.bootstrap;
2648 b.xtype = b.xtype || 'Button';
2649 if (typeof(b.listeners) == 'undefined') {
2650 b.listeners = { click : cb.createDelegate(this) };
2653 var btn = Roo.factory(b);
2655 btn.onRender(this.el.select('.modal-footer div').first());
2661 setDefaultButton : function(btn)
2663 //this.el.select('.modal-footer').()
2665 resizeTo: function(w,h)
2669 setContentSize : function(w, h)
2673 onButtonClick: function(btn,e)
2676 this.fireEvent('btnclick', btn.name, e);
2679 * Set the title of the Dialog
2680 * @param {String} str new Title
2682 setTitle: function(str) {
2683 this.titleEl.dom.innerHTML = str;
2686 * Set the body of the Dialog
2687 * @param {String} str new Title
2689 setBody: function(str) {
2690 this.bodyEl.dom.innerHTML = str;
2693 * Set the body of the Dialog using the template
2694 * @param {Obj} data - apply this data to the template and replace the body contents.
2696 applyBody: function(obj)
2699 Roo.log("Error - using apply Body without a template");
2702 this.tmpl.overwrite(this.bodyEl, obj);
2708 Roo.apply(Roo.bootstrap.Modal, {
2710 * Button config that displays a single OK button
2719 * Button config that displays Yes and No buttons
2735 * Button config that displays OK and Cancel buttons
2750 * Button config that displays Yes, No and Cancel buttons
2773 * messagebox - can be used as a replace
2777 * @class Roo.MessageBox
2778 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2782 Roo.Msg.alert('Status', 'Changes saved successfully.');
2784 // Prompt for user data:
2785 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2787 // process text value...
2791 // Show a dialog using config options:
2793 title:'Save Changes?',
2794 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2795 buttons: Roo.Msg.YESNOCANCEL,
2802 Roo.bootstrap.MessageBox = function(){
2803 var dlg, opt, mask, waitTimer;
2804 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2805 var buttons, activeTextEl, bwidth;
2809 var handleButton = function(button){
2811 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2815 var handleHide = function(){
2817 dlg.el.removeClass(opt.cls);
2820 // Roo.TaskMgr.stop(waitTimer);
2821 // waitTimer = null;
2826 var updateButtons = function(b){
2829 buttons["ok"].hide();
2830 buttons["cancel"].hide();
2831 buttons["yes"].hide();
2832 buttons["no"].hide();
2833 //dlg.footer.dom.style.display = 'none';
2836 dlg.footerEl.dom.style.display = '';
2837 for(var k in buttons){
2838 if(typeof buttons[k] != "function"){
2841 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2842 width += buttons[k].el.getWidth()+15;
2852 var handleEsc = function(d, k, e){
2853 if(opt && opt.closable !== false){
2863 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2864 * @return {Roo.BasicDialog} The BasicDialog element
2866 getDialog : function(){
2868 dlg = new Roo.bootstrap.Modal( {
2871 //constraintoviewport:false,
2873 //collapsible : false,
2878 //buttonAlign:"center",
2879 closeClick : function(){
2880 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2883 handleButton("cancel");
2888 dlg.on("hide", handleHide);
2890 //dlg.addKeyListener(27, handleEsc);
2892 this.buttons = buttons;
2893 var bt = this.buttonText;
2894 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2895 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2896 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2897 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2899 bodyEl = dlg.bodyEl.createChild({
2901 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2902 '<textarea class="roo-mb-textarea"></textarea>' +
2903 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2905 msgEl = bodyEl.dom.firstChild;
2906 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2907 textboxEl.enableDisplayMode();
2908 textboxEl.addKeyListener([10,13], function(){
2909 if(dlg.isVisible() && opt && opt.buttons){
2912 }else if(opt.buttons.yes){
2913 handleButton("yes");
2917 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2918 textareaEl.enableDisplayMode();
2919 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2920 progressEl.enableDisplayMode();
2921 var pf = progressEl.dom.firstChild;
2923 pp = Roo.get(pf.firstChild);
2924 pp.setHeight(pf.offsetHeight);
2932 * Updates the message box body text
2933 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2934 * the XHTML-compliant non-breaking space character '&#160;')
2935 * @return {Roo.MessageBox} This message box
2937 updateText : function(text){
2938 if(!dlg.isVisible() && !opt.width){
2939 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2941 msgEl.innerHTML = text || ' ';
2943 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2944 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2946 Math.min(opt.width || cw , this.maxWidth),
2947 Math.max(opt.minWidth || this.minWidth, bwidth)
2950 activeTextEl.setWidth(w);
2952 if(dlg.isVisible()){
2953 dlg.fixedcenter = false;
2955 // to big, make it scroll. = But as usual stupid IE does not support
2958 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2959 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2960 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2962 bodyEl.dom.style.height = '';
2963 bodyEl.dom.style.overflowY = '';
2966 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2968 bodyEl.dom.style.overflowX = '';
2971 dlg.setContentSize(w, bodyEl.getHeight());
2972 if(dlg.isVisible()){
2973 dlg.fixedcenter = true;
2979 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
2980 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2981 * @param {Number} value Any number between 0 and 1 (e.g., .5)
2982 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2983 * @return {Roo.MessageBox} This message box
2985 updateProgress : function(value, text){
2987 this.updateText(text);
2989 if (pp) { // weird bug on my firefox - for some reason this is not defined
2990 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2996 * Returns true if the message box is currently displayed
2997 * @return {Boolean} True if the message box is visible, else false
2999 isVisible : function(){
3000 return dlg && dlg.isVisible();
3004 * Hides the message box if it is displayed
3007 if(this.isVisible()){
3013 * Displays a new message box, or reinitializes an existing message box, based on the config options
3014 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3015 * The following config object properties are supported:
3017 Property Type Description
3018 ---------- --------------- ------------------------------------------------------------------------------------
3019 animEl String/Element An id or Element from which the message box should animate as it opens and
3020 closes (defaults to undefined)
3021 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3022 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3023 closable Boolean False to hide the top-right close button (defaults to true). Note that
3024 progress and wait dialogs will ignore this property and always hide the
3025 close button as they can only be closed programmatically.
3026 cls String A custom CSS class to apply to the message box element
3027 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3028 displayed (defaults to 75)
3029 fn Function A callback function to execute after closing the dialog. The arguments to the
3030 function will be btn (the name of the button that was clicked, if applicable,
3031 e.g. "ok"), and text (the value of the active text field, if applicable).
3032 Progress and wait dialogs will ignore this option since they do not respond to
3033 user actions and can only be closed programmatically, so any required function
3034 should be called by the same code after it closes the dialog.
3035 icon String A CSS class that provides a background image to be used as an icon for
3036 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3037 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3038 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3039 modal Boolean False to allow user interaction with the page while the message box is
3040 displayed (defaults to true)
3041 msg String A string that will replace the existing message box body text (defaults
3042 to the XHTML-compliant non-breaking space character ' ')
3043 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3044 progress Boolean True to display a progress bar (defaults to false)
3045 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3046 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3047 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3048 title String The title text
3049 value String The string value to set into the active textbox element if displayed
3050 wait Boolean True to display a progress bar (defaults to false)
3051 width Number The width of the dialog in pixels
3058 msg: 'Please enter your address:',
3060 buttons: Roo.MessageBox.OKCANCEL,
3063 animEl: 'addAddressBtn'
3066 * @param {Object} config Configuration options
3067 * @return {Roo.MessageBox} This message box
3069 show : function(options)
3072 // this causes nightmares if you show one dialog after another
3073 // especially on callbacks..
3075 if(this.isVisible()){
3078 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3079 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3080 Roo.log("New Dialog Message:" + options.msg )
3081 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3082 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3085 var d = this.getDialog();
3087 d.setTitle(opt.title || " ");
3088 d.closeEl.setDisplayed(opt.closable !== false);
3089 activeTextEl = textboxEl;
3090 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3095 textareaEl.setHeight(typeof opt.multiline == "number" ?
3096 opt.multiline : this.defaultTextHeight);
3097 activeTextEl = textareaEl;
3106 progressEl.setDisplayed(opt.progress === true);
3107 this.updateProgress(0);
3108 activeTextEl.dom.value = opt.value || "";
3110 dlg.setDefaultButton(activeTextEl);
3112 var bs = opt.buttons;
3116 }else if(bs && bs.yes){
3117 db = buttons["yes"];
3119 dlg.setDefaultButton(db);
3121 bwidth = updateButtons(opt.buttons);
3122 this.updateText(opt.msg);
3124 d.el.addClass(opt.cls);
3126 d.proxyDrag = opt.proxyDrag === true;
3127 d.modal = opt.modal !== false;
3128 d.mask = opt.modal !== false ? mask : false;
3130 // force it to the end of the z-index stack so it gets a cursor in FF
3131 document.body.appendChild(dlg.el.dom);
3132 d.animateTarget = null;
3133 d.show(options.animEl);
3139 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3140 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3141 * and closing the message box when the process is complete.
3142 * @param {String} title The title bar text
3143 * @param {String} msg The message box body text
3144 * @return {Roo.MessageBox} This message box
3146 progress : function(title, msg){
3153 minWidth: this.minProgressWidth,
3160 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3161 * If a callback function is passed it will be called after the user clicks the button, and the
3162 * id of the button that was clicked will be passed as the only parameter to the callback
3163 * (could also be the top-right close button).
3164 * @param {String} title The title bar text
3165 * @param {String} msg The message box body text
3166 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3167 * @param {Object} scope (optional) The scope of the callback function
3168 * @return {Roo.MessageBox} This message box
3170 alert : function(title, msg, fn, scope){
3183 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3184 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3185 * You are responsible for closing the message box when the process is complete.
3186 * @param {String} msg The message box body text
3187 * @param {String} title (optional) The title bar text
3188 * @return {Roo.MessageBox} This message box
3190 wait : function(msg, title){
3201 waitTimer = Roo.TaskMgr.start({
3203 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3211 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3212 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3213 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3214 * @param {String} title The title bar text
3215 * @param {String} msg The message box body text
3216 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3217 * @param {Object} scope (optional) The scope of the callback function
3218 * @return {Roo.MessageBox} This message box
3220 confirm : function(title, msg, fn, scope){
3224 buttons: this.YESNO,
3233 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3234 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3235 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3236 * (could also be the top-right close button) and the text that was entered will be passed as the two
3237 * parameters to the callback.
3238 * @param {String} title The title bar text
3239 * @param {String} msg The message box body text
3240 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3241 * @param {Object} scope (optional) The scope of the callback function
3242 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3243 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3244 * @return {Roo.MessageBox} This message box
3246 prompt : function(title, msg, fn, scope, multiline){
3250 buttons: this.OKCANCEL,
3255 multiline: multiline,
3262 * Button config that displays a single OK button
3267 * Button config that displays Yes and No buttons
3270 YESNO : {yes:true, no:true},
3272 * Button config that displays OK and Cancel buttons
3275 OKCANCEL : {ok:true, cancel:true},
3277 * Button config that displays Yes, No and Cancel buttons
3280 YESNOCANCEL : {yes:true, no:true, cancel:true},
3283 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3286 defaultTextHeight : 75,
3288 * The maximum width in pixels of the message box (defaults to 600)
3293 * The minimum width in pixels of the message box (defaults to 100)
3298 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3299 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3302 minProgressWidth : 250,
3304 * An object containing the default button text strings that can be overriden for localized language support.
3305 * Supported properties are: ok, cancel, yes and no.
3306 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3319 * Shorthand for {@link Roo.MessageBox}
3321 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3322 Roo.Msg = Roo.Msg || Roo.MessageBox;
3331 * @class Roo.bootstrap.Navbar
3332 * @extends Roo.bootstrap.Component
3333 * Bootstrap Navbar class
3336 * Create a new Navbar
3337 * @param {Object} config The config object
3341 Roo.bootstrap.Navbar = function(config){
3342 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3346 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3355 getAutoCreate : function(){
3358 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3362 initEvents :function ()
3364 //Roo.log(this.el.select('.navbar-toggle',true));
3365 this.el.select('.navbar-toggle',true).on('click', function() {
3366 // Roo.log('click');
3367 this.el.select('.navbar-collapse',true).toggleClass('in');
3375 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3377 var size = this.el.getSize();
3378 this.maskEl.setSize(size.width, size.height);
3379 this.maskEl.enableDisplayMode("block");
3388 getChildContainer : function()
3390 if (this.el.select('.collapse').getCount()) {
3391 return this.el.select('.collapse',true).first();
3424 * @class Roo.bootstrap.NavSimplebar
3425 * @extends Roo.bootstrap.Navbar
3426 * Bootstrap Sidebar class
3428 * @cfg {Boolean} inverse is inverted color
3430 * @cfg {String} type (nav | pills | tabs)
3431 * @cfg {Boolean} arrangement stacked | justified
3432 * @cfg {String} align (left | right) alignment
3434 * @cfg {Boolean} main (true|false) main nav bar? default false
3435 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3437 * @cfg {String} tag (header|footer|nav|div) default is nav
3443 * Create a new Sidebar
3444 * @param {Object} config The config object
3448 Roo.bootstrap.NavSimplebar = function(config){
3449 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3452 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3468 getAutoCreate : function(){
3472 tag : this.tag || 'div',
3485 this.type = this.type || 'nav';
3486 if (['tabs','pills'].indexOf(this.type)!==-1) {
3487 cfg.cn[0].cls += ' nav-' + this.type
3491 if (this.type!=='nav') {
3492 Roo.log('nav type must be nav/tabs/pills')
3494 cfg.cn[0].cls += ' navbar-nav'
3500 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3501 cfg.cn[0].cls += ' nav-' + this.arrangement;
3505 if (this.align === 'right') {
3506 cfg.cn[0].cls += ' navbar-right';
3510 cfg.cls += ' navbar-inverse';
3537 * @class Roo.bootstrap.NavHeaderbar
3538 * @extends Roo.bootstrap.NavSimplebar
3539 * Bootstrap Sidebar class
3541 * @cfg {String} brand what is brand
3542 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3543 * @cfg {String} brand_href href of the brand
3544 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3545 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3546 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3547 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3550 * Create a new Sidebar
3551 * @param {Object} config The config object
3555 Roo.bootstrap.NavHeaderbar = function(config){
3556 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3560 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3567 desktopCenter : false,
3570 getAutoCreate : function(){
3573 tag: this.nav || 'nav',
3580 if (this.desktopCenter) {
3581 cn.push({cls : 'container', cn : []});
3588 cls: 'navbar-header',
3593 cls: 'navbar-toggle',
3594 'data-toggle': 'collapse',
3599 html: 'Toggle navigation'
3621 cls: 'collapse navbar-collapse',
3625 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3627 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3628 cfg.cls += ' navbar-' + this.position;
3630 // tag can override this..
3632 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3635 if (this.brand !== '') {
3638 href: this.brand_href ? this.brand_href : '#',
3639 cls: 'navbar-brand',
3647 cfg.cls += ' main-nav';
3655 getHeaderChildContainer : function()
3657 if (this.el.select('.navbar-header').getCount()) {
3658 return this.el.select('.navbar-header',true).first();
3661 return this.getChildContainer();
3665 initEvents : function()
3667 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3669 if (this.autohide) {
3674 Roo.get(document).on('scroll',function(e) {
3675 var ns = Roo.get(document).getScroll().top;
3676 var os = prevScroll;
3680 ft.removeClass('slideDown');
3681 ft.addClass('slideUp');
3684 ft.removeClass('slideUp');
3685 ft.addClass('slideDown');
3706 * @class Roo.bootstrap.NavSidebar
3707 * @extends Roo.bootstrap.Navbar
3708 * Bootstrap Sidebar class
3711 * Create a new Sidebar
3712 * @param {Object} config The config object
3716 Roo.bootstrap.NavSidebar = function(config){
3717 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3720 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3722 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3724 getAutoCreate : function(){
3729 cls: 'sidebar sidebar-nav'
3751 * @class Roo.bootstrap.NavGroup
3752 * @extends Roo.bootstrap.Component
3753 * Bootstrap NavGroup class
3754 * @cfg {String} align (left|right)
3755 * @cfg {Boolean} inverse
3756 * @cfg {String} type (nav|pills|tab) default nav
3757 * @cfg {String} navId - reference Id for navbar.
3761 * Create a new nav group
3762 * @param {Object} config The config object
3765 Roo.bootstrap.NavGroup = function(config){
3766 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3769 Roo.bootstrap.NavGroup.register(this);
3773 * Fires when the active item changes
3774 * @param {Roo.bootstrap.NavGroup} this
3775 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3776 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3783 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3794 getAutoCreate : function()
3796 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3803 if (['tabs','pills'].indexOf(this.type)!==-1) {
3804 cfg.cls += ' nav-' + this.type
3806 if (this.type!=='nav') {
3807 Roo.log('nav type must be nav/tabs/pills')
3809 cfg.cls += ' navbar-nav'
3812 if (this.parent().sidebar) {
3815 cls: 'dashboard-menu sidebar-menu'
3821 if (this.form === true) {
3827 if (this.align === 'right') {
3828 cfg.cls += ' navbar-right';
3830 cfg.cls += ' navbar-left';
3834 if (this.align === 'right') {
3835 cfg.cls += ' navbar-right';
3839 cfg.cls += ' navbar-inverse';
3847 * sets the active Navigation item
3848 * @param {Roo.bootstrap.NavItem} the new current navitem
3850 setActiveItem : function(item)
3853 Roo.each(this.navItems, function(v){
3858 v.setActive(false, true);
3865 item.setActive(true, true);
3866 this.fireEvent('changed', this, item, prev);
3871 * gets the active Navigation item
3872 * @return {Roo.bootstrap.NavItem} the current navitem
3874 getActive : function()
3878 Roo.each(this.navItems, function(v){
3889 indexOfNav : function()
3893 Roo.each(this.navItems, function(v,i){
3904 * adds a Navigation item
3905 * @param {Roo.bootstrap.NavItem} the navitem to add
3907 addItem : function(cfg)
3909 var cn = new Roo.bootstrap.NavItem(cfg);
3911 cn.parentId = this.id;
3912 cn.onRender(this.el, null);
3916 * register a Navigation item
3917 * @param {Roo.bootstrap.NavItem} the navitem to add
3919 register : function(item)
3921 this.navItems.push( item);
3922 item.navId = this.navId;
3927 * clear all the Navigation item
3930 clearAll : function()
3933 this.el.dom.innerHTML = '';
3936 getNavItem: function(tabId)
3939 Roo.each(this.navItems, function(e) {
3940 if (e.tabId == tabId) {
3950 setActiveNext : function()
3952 var i = this.indexOfNav(this.getActive());
3953 if (i > this.navItems.length) {
3956 this.setActiveItem(this.navItems[i+1]);
3958 setActivePrev : function()
3960 var i = this.indexOfNav(this.getActive());
3964 this.setActiveItem(this.navItems[i-1]);
3966 clearWasActive : function(except) {
3967 Roo.each(this.navItems, function(e) {
3968 if (e.tabId != except.tabId && e.was_active) {
3969 e.was_active = false;
3976 getWasActive : function ()
3979 Roo.each(this.navItems, function(e) {
3994 Roo.apply(Roo.bootstrap.NavGroup, {
3998 * register a Navigation Group
3999 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4001 register : function(navgrp)
4003 this.groups[navgrp.navId] = navgrp;
4007 * fetch a Navigation Group based on the navigation ID
4008 * @param {string} the navgroup to add
4009 * @returns {Roo.bootstrap.NavGroup} the navgroup
4011 get: function(navId) {
4012 if (typeof(this.groups[navId]) == 'undefined') {
4014 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4016 return this.groups[navId] ;
4031 * @class Roo.bootstrap.NavItem
4032 * @extends Roo.bootstrap.Component
4033 * Bootstrap Navbar.NavItem class
4034 * @cfg {String} href link to
4035 * @cfg {String} html content of button
4036 * @cfg {String} badge text inside badge
4037 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4038 * @cfg {String} glyphicon name of glyphicon
4039 * @cfg {String} icon name of font awesome icon
4040 * @cfg {Boolean} active Is item active
4041 * @cfg {Boolean} disabled Is item disabled
4043 * @cfg {Boolean} preventDefault (true | false) default false
4044 * @cfg {String} tabId the tab that this item activates.
4045 * @cfg {String} tagtype (a|span) render as a href or span?
4046 * @cfg {Boolean} animateRef (true|false) link to element default false
4049 * Create a new Navbar Item
4050 * @param {Object} config The config object
4052 Roo.bootstrap.NavItem = function(config){
4053 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4058 * The raw click event for the entire grid.
4059 * @param {Roo.EventObject} e
4064 * Fires when the active item active state changes
4065 * @param {Roo.bootstrap.NavItem} this
4066 * @param {boolean} state the new state
4072 * Fires when scroll to element
4073 * @param {Roo.bootstrap.NavItem} this
4074 * @param {Object} options
4075 * @param {Roo.EventObject} e
4083 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4091 preventDefault : false,
4098 getAutoCreate : function(){
4107 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4109 if (this.disabled) {
4110 cfg.cls += ' disabled';
4113 if (this.href || this.html || this.glyphicon || this.icon) {
4117 href : this.href || "#",
4118 html: this.html || ''
4123 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4126 if(this.glyphicon) {
4127 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4132 cfg.cn[0].html += " <span class='caret'></span>";
4136 if (this.badge !== '') {
4138 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4146 initEvents: function()
4148 if (typeof (this.menu) != 'undefined') {
4149 this.menu.parentType = this.xtype;
4150 this.menu.triggerEl = this.el;
4151 this.menu = this.addxtype(Roo.apply({}, this.menu));
4154 this.el.select('a',true).on('click', this.onClick, this);
4156 if(this.tagtype == 'span'){
4157 this.el.select('span',true).on('click', this.onClick, this);
4160 // at this point parent should be available..
4161 this.parent().register(this);
4164 onClick : function(e)
4167 this.preventDefault ||
4174 if (this.disabled) {
4178 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4179 if (tg && tg.transition) {
4180 Roo.log("waiting for the transitionend");
4186 //Roo.log("fire event clicked");
4187 if(this.fireEvent('click', this, e) === false){
4191 if(this.tagtype == 'span'){
4195 //Roo.log(this.href);
4196 var ael = this.el.select('a',true).first();
4199 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4200 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4201 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4202 return; // ignore... - it's a 'hash' to another page.
4206 this.scrollToElement(e);
4210 var p = this.parent();
4212 if (['tabs','pills'].indexOf(p.type)!==-1) {
4213 if (typeof(p.setActiveItem) !== 'undefined') {
4214 p.setActiveItem(this);
4218 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4219 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4220 // remove the collapsed menu expand...
4221 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4225 isActive: function () {
4228 setActive : function(state, fire, is_was_active)
4230 if (this.active && !state && this.navId) {
4231 this.was_active = true;
4232 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4234 nv.clearWasActive(this);
4238 this.active = state;
4241 this.el.removeClass('active');
4242 } else if (!this.el.hasClass('active')) {
4243 this.el.addClass('active');
4246 this.fireEvent('changed', this, state);
4249 // show a panel if it's registered and related..
4251 if (!this.navId || !this.tabId || !state || is_was_active) {
4255 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4259 var pan = tg.getPanelByName(this.tabId);
4263 // if we can not flip to new panel - go back to old nav highlight..
4264 if (false == tg.showPanel(pan)) {
4265 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4267 var onav = nv.getWasActive();
4269 onav.setActive(true, false, true);
4278 // this should not be here...
4279 setDisabled : function(state)
4281 this.disabled = state;
4283 this.el.removeClass('disabled');
4284 } else if (!this.el.hasClass('disabled')) {
4285 this.el.addClass('disabled');
4291 * Fetch the element to display the tooltip on.
4292 * @return {Roo.Element} defaults to this.el
4294 tooltipEl : function()
4296 return this.el.select('' + this.tagtype + '', true).first();
4299 scrollToElement : function(e)
4301 var c = document.body;
4304 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4306 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4307 c = document.documentElement;
4310 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4316 var o = target.calcOffsetsTo(c);
4323 this.fireEvent('scrollto', this, options, e);
4325 Roo.get(c).scrollTo('top', options.value, true);
4338 * <span> icon </span>
4339 * <span> text </span>
4340 * <span>badge </span>
4344 * @class Roo.bootstrap.NavSidebarItem
4345 * @extends Roo.bootstrap.NavItem
4346 * Bootstrap Navbar.NavSidebarItem class
4347 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4349 * Create a new Navbar Button
4350 * @param {Object} config The config object
4352 Roo.bootstrap.NavSidebarItem = function(config){
4353 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4358 * The raw click event for the entire grid.
4359 * @param {Roo.EventObject} e
4364 * Fires when the active item active state changes
4365 * @param {Roo.bootstrap.NavSidebarItem} this
4366 * @param {boolean} state the new state
4374 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4376 badgeWeight : 'default',
4378 getAutoCreate : function(){
4383 href : this.href || '#',
4395 html : this.html || ''
4400 cfg.cls += ' active';
4403 if (this.disabled) {
4404 cfg.cls += ' disabled';
4408 if (this.glyphicon || this.icon) {
4409 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4410 a.cn.push({ tag : 'i', cls : c }) ;
4415 if (this.badge !== '') {
4417 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4421 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4422 a.cls += 'dropdown-toggle treeview' ;
4433 initEvents : function()
4435 this.el.on('click', this.onClick, this);
4438 if(this.badge !== ''){
4440 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4445 onClick : function(e)
4452 if(this.preventDefault){
4456 this.fireEvent('click', this);
4459 disable : function()
4461 this.setDisabled(true);
4466 this.setDisabled(false);
4469 setDisabled : function(state)
4471 if(this.disabled == state){
4475 this.disabled = state;
4478 this.el.addClass('disabled');
4482 this.el.removeClass('disabled');
4487 setActive : function(state)
4489 if(this.active == state){
4493 this.active = state;
4496 this.el.addClass('active');
4500 this.el.removeClass('active');
4505 isActive: function ()
4510 setBadge : function(str)
4516 this.badgeEl.dom.innerHTML = str;
4533 * @class Roo.bootstrap.Row
4534 * @extends Roo.bootstrap.Component
4535 * Bootstrap Row class (contains columns...)
4539 * @param {Object} config The config object
4542 Roo.bootstrap.Row = function(config){
4543 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4546 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4548 getAutoCreate : function(){
4567 * @class Roo.bootstrap.Element
4568 * @extends Roo.bootstrap.Component
4569 * Bootstrap Element class
4570 * @cfg {String} html contents of the element
4571 * @cfg {String} tag tag of the element
4572 * @cfg {String} cls class of the element
4573 * @cfg {Boolean} preventDefault (true|false) default false
4574 * @cfg {Boolean} clickable (true|false) default false
4577 * Create a new Element
4578 * @param {Object} config The config object
4581 Roo.bootstrap.Element = function(config){
4582 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4588 * When a element is chick
4589 * @param {Roo.bootstrap.Element} this
4590 * @param {Roo.EventObject} e
4596 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4601 preventDefault: false,
4604 getAutoCreate : function(){
4615 initEvents: function()
4617 Roo.bootstrap.Element.superclass.initEvents.call(this);
4620 this.el.on('click', this.onClick, this);
4625 onClick : function(e)
4627 if(this.preventDefault){
4631 this.fireEvent('click', this, e);
4634 getValue : function()
4636 return this.el.dom.innerHTML;
4639 setValue : function(value)
4641 this.el.dom.innerHTML = value;
4656 * @class Roo.bootstrap.Pagination
4657 * @extends Roo.bootstrap.Component
4658 * Bootstrap Pagination class
4659 * @cfg {String} size xs | sm | md | lg
4660 * @cfg {Boolean} inverse false | true
4663 * Create a new Pagination
4664 * @param {Object} config The config object
4667 Roo.bootstrap.Pagination = function(config){
4668 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4671 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4677 getAutoCreate : function(){
4683 cfg.cls += ' inverse';
4689 cfg.cls += " " + this.cls;
4707 * @class Roo.bootstrap.PaginationItem
4708 * @extends Roo.bootstrap.Component
4709 * Bootstrap PaginationItem class
4710 * @cfg {String} html text
4711 * @cfg {String} href the link
4712 * @cfg {Boolean} preventDefault (true | false) default true
4713 * @cfg {Boolean} active (true | false) default false
4714 * @cfg {Boolean} disabled default false
4718 * Create a new PaginationItem
4719 * @param {Object} config The config object
4723 Roo.bootstrap.PaginationItem = function(config){
4724 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4729 * The raw click event for the entire grid.
4730 * @param {Roo.EventObject} e
4736 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4740 preventDefault: true,
4745 getAutoCreate : function(){
4751 href : this.href ? this.href : '#',
4752 html : this.html ? this.html : ''
4762 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4766 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4772 initEvents: function() {
4774 this.el.on('click', this.onClick, this);
4777 onClick : function(e)
4779 Roo.log('PaginationItem on click ');
4780 if(this.preventDefault){
4788 this.fireEvent('click', this, e);
4804 * @class Roo.bootstrap.Slider
4805 * @extends Roo.bootstrap.Component
4806 * Bootstrap Slider class
4809 * Create a new Slider
4810 * @param {Object} config The config object
4813 Roo.bootstrap.Slider = function(config){
4814 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4817 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4819 getAutoCreate : function(){
4823 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4827 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4839 * Ext JS Library 1.1.1
4840 * Copyright(c) 2006-2007, Ext JS, LLC.
4842 * Originally Released Under LGPL - original licence link has changed is not relivant.
4845 * <script type="text/javascript">
4850 * @class Roo.grid.ColumnModel
4851 * @extends Roo.util.Observable
4852 * This is the default implementation of a ColumnModel used by the Grid. It defines
4853 * the columns in the grid.
4856 var colModel = new Roo.grid.ColumnModel([
4857 {header: "Ticker", width: 60, sortable: true, locked: true},
4858 {header: "Company Name", width: 150, sortable: true},
4859 {header: "Market Cap.", width: 100, sortable: true},
4860 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4861 {header: "Employees", width: 100, sortable: true, resizable: false}
4866 * The config options listed for this class are options which may appear in each
4867 * individual column definition.
4868 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4870 * @param {Object} config An Array of column config objects. See this class's
4871 * config objects for details.
4873 Roo.grid.ColumnModel = function(config){
4875 * The config passed into the constructor
4877 this.config = config;
4880 // if no id, create one
4881 // if the column does not have a dataIndex mapping,
4882 // map it to the order it is in the config
4883 for(var i = 0, len = config.length; i < len; i++){
4885 if(typeof c.dataIndex == "undefined"){
4888 if(typeof c.renderer == "string"){
4889 c.renderer = Roo.util.Format[c.renderer];
4891 if(typeof c.id == "undefined"){
4894 if(c.editor && c.editor.xtype){
4895 c.editor = Roo.factory(c.editor, Roo.grid);
4897 if(c.editor && c.editor.isFormField){
4898 c.editor = new Roo.grid.GridEditor(c.editor);
4900 this.lookup[c.id] = c;
4904 * The width of columns which have no width specified (defaults to 100)
4907 this.defaultWidth = 100;
4910 * Default sortable of columns which have no sortable specified (defaults to false)
4913 this.defaultSortable = false;
4917 * @event widthchange
4918 * Fires when the width of a column changes.
4919 * @param {ColumnModel} this
4920 * @param {Number} columnIndex The column index
4921 * @param {Number} newWidth The new width
4923 "widthchange": true,
4925 * @event headerchange
4926 * Fires when the text of a header changes.
4927 * @param {ColumnModel} this
4928 * @param {Number} columnIndex The column index
4929 * @param {Number} newText The new header text
4931 "headerchange": true,
4933 * @event hiddenchange
4934 * Fires when a column is hidden or "unhidden".
4935 * @param {ColumnModel} this
4936 * @param {Number} columnIndex The column index
4937 * @param {Boolean} hidden true if hidden, false otherwise
4939 "hiddenchange": true,
4941 * @event columnmoved
4942 * Fires when a column is moved.
4943 * @param {ColumnModel} this
4944 * @param {Number} oldIndex
4945 * @param {Number} newIndex
4947 "columnmoved" : true,
4949 * @event columlockchange
4950 * Fires when a column's locked state is changed
4951 * @param {ColumnModel} this
4952 * @param {Number} colIndex
4953 * @param {Boolean} locked true if locked
4955 "columnlockchange" : true
4957 Roo.grid.ColumnModel.superclass.constructor.call(this);
4959 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4961 * @cfg {String} header The header text to display in the Grid view.
4964 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4965 * {@link Roo.data.Record} definition from which to draw the column's value. If not
4966 * specified, the column's index is used as an index into the Record's data Array.
4969 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4970 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4973 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4974 * Defaults to the value of the {@link #defaultSortable} property.
4975 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4978 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
4981 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
4984 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4987 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4990 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4991 * given the cell's data value. See {@link #setRenderer}. If not specified, the
4992 * default renderer uses the raw data value. If an object is returned (bootstrap only)
4993 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4996 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
4999 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5002 * @cfg {String} cursor (Optional)
5005 * @cfg {String} tooltip (Optional)
5008 * @cfg {Number} xs (Optional)
5011 * @cfg {Number} sm (Optional)
5014 * @cfg {Number} md (Optional)
5017 * @cfg {Number} lg (Optional)
5020 * Returns the id of the column at the specified index.
5021 * @param {Number} index The column index
5022 * @return {String} the id
5024 getColumnId : function(index){
5025 return this.config[index].id;
5029 * Returns the column for a specified id.
5030 * @param {String} id The column id
5031 * @return {Object} the column
5033 getColumnById : function(id){
5034 return this.lookup[id];
5039 * Returns the column for a specified dataIndex.
5040 * @param {String} dataIndex The column dataIndex
5041 * @return {Object|Boolean} the column or false if not found
5043 getColumnByDataIndex: function(dataIndex){
5044 var index = this.findColumnIndex(dataIndex);
5045 return index > -1 ? this.config[index] : false;
5049 * Returns the index for a specified column id.
5050 * @param {String} id The column id
5051 * @return {Number} the index, or -1 if not found
5053 getIndexById : function(id){
5054 for(var i = 0, len = this.config.length; i < len; i++){
5055 if(this.config[i].id == id){
5063 * Returns the index for a specified column dataIndex.
5064 * @param {String} dataIndex The column dataIndex
5065 * @return {Number} the index, or -1 if not found
5068 findColumnIndex : function(dataIndex){
5069 for(var i = 0, len = this.config.length; i < len; i++){
5070 if(this.config[i].dataIndex == dataIndex){
5078 moveColumn : function(oldIndex, newIndex){
5079 var c = this.config[oldIndex];
5080 this.config.splice(oldIndex, 1);
5081 this.config.splice(newIndex, 0, c);
5082 this.dataMap = null;
5083 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5086 isLocked : function(colIndex){
5087 return this.config[colIndex].locked === true;
5090 setLocked : function(colIndex, value, suppressEvent){
5091 if(this.isLocked(colIndex) == value){
5094 this.config[colIndex].locked = value;
5096 this.fireEvent("columnlockchange", this, colIndex, value);
5100 getTotalLockedWidth : function(){
5102 for(var i = 0; i < this.config.length; i++){
5103 if(this.isLocked(i) && !this.isHidden(i)){
5104 this.totalWidth += this.getColumnWidth(i);
5110 getLockedCount : function(){
5111 for(var i = 0, len = this.config.length; i < len; i++){
5112 if(!this.isLocked(i)){
5119 * Returns the number of columns.
5122 getColumnCount : function(visibleOnly){
5123 if(visibleOnly === true){
5125 for(var i = 0, len = this.config.length; i < len; i++){
5126 if(!this.isHidden(i)){
5132 return this.config.length;
5136 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5137 * @param {Function} fn
5138 * @param {Object} scope (optional)
5139 * @return {Array} result
5141 getColumnsBy : function(fn, scope){
5143 for(var i = 0, len = this.config.length; i < len; i++){
5144 var c = this.config[i];
5145 if(fn.call(scope||this, c, i) === true){
5153 * Returns true if the specified column is sortable.
5154 * @param {Number} col The column index
5157 isSortable : function(col){
5158 if(typeof this.config[col].sortable == "undefined"){
5159 return this.defaultSortable;
5161 return this.config[col].sortable;
5165 * Returns the rendering (formatting) function defined for the column.
5166 * @param {Number} col The column index.
5167 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5169 getRenderer : function(col){
5170 if(!this.config[col].renderer){
5171 return Roo.grid.ColumnModel.defaultRenderer;
5173 return this.config[col].renderer;
5177 * Sets the rendering (formatting) function for a column.
5178 * @param {Number} col The column index
5179 * @param {Function} fn The function to use to process the cell's raw data
5180 * to return HTML markup for the grid view. The render function is called with
5181 * the following parameters:<ul>
5182 * <li>Data value.</li>
5183 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5184 * <li>css A CSS style string to apply to the table cell.</li>
5185 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5186 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5187 * <li>Row index</li>
5188 * <li>Column index</li>
5189 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5191 setRenderer : function(col, fn){
5192 this.config[col].renderer = fn;
5196 * Returns the width for the specified column.
5197 * @param {Number} col The column index
5200 getColumnWidth : function(col){
5201 return this.config[col].width * 1 || this.defaultWidth;
5205 * Sets the width for a column.
5206 * @param {Number} col The column index
5207 * @param {Number} width The new width
5209 setColumnWidth : function(col, width, suppressEvent){
5210 this.config[col].width = width;
5211 this.totalWidth = null;
5213 this.fireEvent("widthchange", this, col, width);
5218 * Returns the total width of all columns.
5219 * @param {Boolean} includeHidden True to include hidden column widths
5222 getTotalWidth : function(includeHidden){
5223 if(!this.totalWidth){
5224 this.totalWidth = 0;
5225 for(var i = 0, len = this.config.length; i < len; i++){
5226 if(includeHidden || !this.isHidden(i)){
5227 this.totalWidth += this.getColumnWidth(i);
5231 return this.totalWidth;
5235 * Returns the header for the specified column.
5236 * @param {Number} col The column index
5239 getColumnHeader : function(col){
5240 return this.config[col].header;
5244 * Sets the header for a column.
5245 * @param {Number} col The column index
5246 * @param {String} header The new header
5248 setColumnHeader : function(col, header){
5249 this.config[col].header = header;
5250 this.fireEvent("headerchange", this, col, header);
5254 * Returns the tooltip for the specified column.
5255 * @param {Number} col The column index
5258 getColumnTooltip : function(col){
5259 return this.config[col].tooltip;
5262 * Sets the tooltip for a column.
5263 * @param {Number} col The column index
5264 * @param {String} tooltip The new tooltip
5266 setColumnTooltip : function(col, tooltip){
5267 this.config[col].tooltip = tooltip;
5271 * Returns the dataIndex for the specified column.
5272 * @param {Number} col The column index
5275 getDataIndex : function(col){
5276 return this.config[col].dataIndex;
5280 * Sets the dataIndex for a column.
5281 * @param {Number} col The column index
5282 * @param {Number} dataIndex The new dataIndex
5284 setDataIndex : function(col, dataIndex){
5285 this.config[col].dataIndex = dataIndex;
5291 * Returns true if the cell is editable.
5292 * @param {Number} colIndex The column index
5293 * @param {Number} rowIndex The row index - this is nto actually used..?
5296 isCellEditable : function(colIndex, rowIndex){
5297 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5301 * Returns the editor defined for the cell/column.
5302 * return false or null to disable editing.
5303 * @param {Number} colIndex The column index
5304 * @param {Number} rowIndex The row index
5307 getCellEditor : function(colIndex, rowIndex){
5308 return this.config[colIndex].editor;
5312 * Sets if a column is editable.
5313 * @param {Number} col The column index
5314 * @param {Boolean} editable True if the column is editable
5316 setEditable : function(col, editable){
5317 this.config[col].editable = editable;
5322 * Returns true if the column is hidden.
5323 * @param {Number} colIndex The column index
5326 isHidden : function(colIndex){
5327 return this.config[colIndex].hidden;
5332 * Returns true if the column width cannot be changed
5334 isFixed : function(colIndex){
5335 return this.config[colIndex].fixed;
5339 * Returns true if the column can be resized
5342 isResizable : function(colIndex){
5343 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5346 * Sets if a column is hidden.
5347 * @param {Number} colIndex The column index
5348 * @param {Boolean} hidden True if the column is hidden
5350 setHidden : function(colIndex, hidden){
5351 this.config[colIndex].hidden = hidden;
5352 this.totalWidth = null;
5353 this.fireEvent("hiddenchange", this, colIndex, hidden);
5357 * Sets the editor for a column.
5358 * @param {Number} col The column index
5359 * @param {Object} editor The editor object
5361 setEditor : function(col, editor){
5362 this.config[col].editor = editor;
5366 Roo.grid.ColumnModel.defaultRenderer = function(value){
5367 if(typeof value == "string" && value.length < 1){
5373 // Alias for backwards compatibility
5374 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5377 * Ext JS Library 1.1.1
5378 * Copyright(c) 2006-2007, Ext JS, LLC.
5380 * Originally Released Under LGPL - original licence link has changed is not relivant.
5383 * <script type="text/javascript">
5387 * @class Roo.LoadMask
5388 * A simple utility class for generically masking elements while loading data. If the element being masked has
5389 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5390 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5391 * element's UpdateManager load indicator and will be destroyed after the initial load.
5393 * Create a new LoadMask
5394 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5395 * @param {Object} config The config object
5397 Roo.LoadMask = function(el, config){
5398 this.el = Roo.get(el);
5399 Roo.apply(this, config);
5401 this.store.on('beforeload', this.onBeforeLoad, this);
5402 this.store.on('load', this.onLoad, this);
5403 this.store.on('loadexception', this.onLoadException, this);
5404 this.removeMask = false;
5406 var um = this.el.getUpdateManager();
5407 um.showLoadIndicator = false; // disable the default indicator
5408 um.on('beforeupdate', this.onBeforeLoad, this);
5409 um.on('update', this.onLoad, this);
5410 um.on('failure', this.onLoad, this);
5411 this.removeMask = true;
5415 Roo.LoadMask.prototype = {
5417 * @cfg {Boolean} removeMask
5418 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5419 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5423 * The text to display in a centered loading message box (defaults to 'Loading...')
5427 * @cfg {String} msgCls
5428 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5430 msgCls : 'x-mask-loading',
5433 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5439 * Disables the mask to prevent it from being displayed
5441 disable : function(){
5442 this.disabled = true;
5446 * Enables the mask so that it can be displayed
5448 enable : function(){
5449 this.disabled = false;
5452 onLoadException : function()
5456 if (typeof(arguments[3]) != 'undefined') {
5457 Roo.MessageBox.alert("Error loading",arguments[3]);
5461 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5462 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5471 this.el.unmask(this.removeMask);
5476 this.el.unmask(this.removeMask);
5480 onBeforeLoad : function(){
5482 this.el.mask(this.msg, this.msgCls);
5487 destroy : function(){
5489 this.store.un('beforeload', this.onBeforeLoad, this);
5490 this.store.un('load', this.onLoad, this);
5491 this.store.un('loadexception', this.onLoadException, this);
5493 var um = this.el.getUpdateManager();
5494 um.un('beforeupdate', this.onBeforeLoad, this);
5495 um.un('update', this.onLoad, this);
5496 um.un('failure', this.onLoad, this);
5507 * @class Roo.bootstrap.Table
5508 * @extends Roo.bootstrap.Component
5509 * Bootstrap Table class
5510 * @cfg {String} cls table class
5511 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5512 * @cfg {String} bgcolor Specifies the background color for a table
5513 * @cfg {Number} border Specifies whether the table cells should have borders or not
5514 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5515 * @cfg {Number} cellspacing Specifies the space between cells
5516 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5517 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5518 * @cfg {String} sortable Specifies that the table should be sortable
5519 * @cfg {String} summary Specifies a summary of the content of a table
5520 * @cfg {Number} width Specifies the width of a table
5521 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5523 * @cfg {boolean} striped Should the rows be alternative striped
5524 * @cfg {boolean} bordered Add borders to the table
5525 * @cfg {boolean} hover Add hover highlighting
5526 * @cfg {boolean} condensed Format condensed
5527 * @cfg {boolean} responsive Format condensed
5528 * @cfg {Boolean} loadMask (true|false) default false
5529 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5530 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5531 * @cfg {Boolean} rowSelection (true|false) default false
5532 * @cfg {Boolean} cellSelection (true|false) default false
5533 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5537 * Create a new Table
5538 * @param {Object} config The config object
5541 Roo.bootstrap.Table = function(config){
5542 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5545 this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5546 this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5547 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5548 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5552 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5553 this.sm = this.selModel;
5554 this.sm.xmodule = this.xmodule || false;
5556 if (this.cm && typeof(this.cm.config) == 'undefined') {
5557 this.colModel = new Roo.grid.ColumnModel(this.cm);
5558 this.cm = this.colModel;
5559 this.cm.xmodule = this.xmodule || false;
5562 this.store= Roo.factory(this.store, Roo.data);
5563 this.ds = this.store;
5564 this.ds.xmodule = this.xmodule || false;
5567 if (this.footer && this.store) {
5568 this.footer.dataSource = this.ds;
5569 this.footer = Roo.factory(this.footer);
5576 * Fires when a cell is clicked
5577 * @param {Roo.bootstrap.Table} this
5578 * @param {Roo.Element} el
5579 * @param {Number} rowIndex
5580 * @param {Number} columnIndex
5581 * @param {Roo.EventObject} e
5585 * @event celldblclick
5586 * Fires when a cell is double clicked
5587 * @param {Roo.bootstrap.Table} this
5588 * @param {Roo.Element} el
5589 * @param {Number} rowIndex
5590 * @param {Number} columnIndex
5591 * @param {Roo.EventObject} e
5593 "celldblclick" : true,
5596 * Fires when a row is clicked
5597 * @param {Roo.bootstrap.Table} this
5598 * @param {Roo.Element} el
5599 * @param {Number} rowIndex
5600 * @param {Roo.EventObject} e
5604 * @event rowdblclick
5605 * Fires when a row is double clicked
5606 * @param {Roo.bootstrap.Table} this
5607 * @param {Roo.Element} el
5608 * @param {Number} rowIndex
5609 * @param {Roo.EventObject} e
5611 "rowdblclick" : true,
5614 * Fires when a mouseover occur
5615 * @param {Roo.bootstrap.Table} this
5616 * @param {Roo.Element} el
5617 * @param {Number} rowIndex
5618 * @param {Number} columnIndex
5619 * @param {Roo.EventObject} e
5624 * Fires when a mouseout occur
5625 * @param {Roo.bootstrap.Table} this
5626 * @param {Roo.Element} el
5627 * @param {Number} rowIndex
5628 * @param {Number} columnIndex
5629 * @param {Roo.EventObject} e
5634 * Fires when a row is rendered, so you can change add a style to it.
5635 * @param {Roo.bootstrap.Table} this
5636 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5640 * @event rowsrendered
5641 * Fires when all the rows have been rendered
5642 * @param {Roo.bootstrap.Table} this
5644 'rowsrendered' : true
5649 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5674 rowSelection : false,
5675 cellSelection : false,
5678 // Roo.Element - the tbody
5681 getAutoCreate : function(){
5682 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5691 cfg.cls += ' table-striped';
5695 cfg.cls += ' table-hover';
5697 if (this.bordered) {
5698 cfg.cls += ' table-bordered';
5700 if (this.condensed) {
5701 cfg.cls += ' table-condensed';
5703 if (this.responsive) {
5704 cfg.cls += ' table-responsive';
5708 cfg.cls+= ' ' +this.cls;
5711 // this lot should be simplifed...
5714 cfg.align=this.align;
5717 cfg.bgcolor=this.bgcolor;
5720 cfg.border=this.border;
5722 if (this.cellpadding) {
5723 cfg.cellpadding=this.cellpadding;
5725 if (this.cellspacing) {
5726 cfg.cellspacing=this.cellspacing;
5729 cfg.frame=this.frame;
5732 cfg.rules=this.rules;
5734 if (this.sortable) {
5735 cfg.sortable=this.sortable;
5738 cfg.summary=this.summary;
5741 cfg.width=this.width;
5744 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5747 if(this.store || this.cm){
5748 if(this.headerShow){
5749 cfg.cn.push(this.renderHeader());
5752 cfg.cn.push(this.renderBody());
5754 if(this.footerShow){
5755 cfg.cn.push(this.renderFooter());
5758 cfg.cls+= ' TableGrid';
5761 return { cn : [ cfg ] };
5764 initEvents : function()
5766 if(!this.store || !this.cm){
5770 //Roo.log('initEvents with ds!!!!');
5772 this.mainBody = this.el.select('tbody', true).first();
5777 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5778 e.on('click', _this.sort, _this);
5781 this.el.on("click", this.onClick, this);
5782 this.el.on("dblclick", this.onDblClick, this);
5784 // why is this done????? = it breaks dialogs??
5785 //this.parent().el.setStyle('position', 'relative');
5789 this.footer.parentId = this.id;
5790 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5793 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5795 this.store.on('load', this.onLoad, this);
5796 this.store.on('beforeload', this.onBeforeLoad, this);
5797 this.store.on('update', this.onUpdate, this);
5798 this.store.on('add', this.onAdd, this);
5802 onMouseover : function(e, el)
5804 var cell = Roo.get(el);
5810 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5811 cell = cell.findParent('td', false, true);
5814 var row = cell.findParent('tr', false, true);
5815 var cellIndex = cell.dom.cellIndex;
5816 var rowIndex = row.dom.rowIndex - 1; // start from 0
5818 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5822 onMouseout : function(e, el)
5824 var cell = Roo.get(el);
5830 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5831 cell = cell.findParent('td', false, true);
5834 var row = cell.findParent('tr', false, true);
5835 var cellIndex = cell.dom.cellIndex;
5836 var rowIndex = row.dom.rowIndex - 1; // start from 0
5838 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5842 onClick : function(e, el)
5844 var cell = Roo.get(el);
5846 if(!cell || (!this.cellSelection && !this.rowSelection)){
5850 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5851 cell = cell.findParent('td', false, true);
5854 if(!cell || typeof(cell) == 'undefined'){
5858 var row = cell.findParent('tr', false, true);
5860 if(!row || typeof(row) == 'undefined'){
5864 var cellIndex = cell.dom.cellIndex;
5865 var rowIndex = this.getRowIndex(row);
5867 // why??? - should these not be based on SelectionModel?
5868 if(this.cellSelection){
5869 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5872 if(this.rowSelection){
5873 this.fireEvent('rowclick', this, row, rowIndex, e);
5879 onDblClick : function(e,el)
5881 var cell = Roo.get(el);
5883 if(!cell || (!this.CellSelection && !this.RowSelection)){
5887 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5888 cell = cell.findParent('td', false, true);
5891 if(!cell || typeof(cell) == 'undefined'){
5895 var row = cell.findParent('tr', false, true);
5897 if(!row || typeof(row) == 'undefined'){
5901 var cellIndex = cell.dom.cellIndex;
5902 var rowIndex = this.getRowIndex(row);
5904 if(this.CellSelection){
5905 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5908 if(this.RowSelection){
5909 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5913 sort : function(e,el)
5915 var col = Roo.get(el);
5917 if(!col.hasClass('sortable')){
5921 var sort = col.attr('sort');
5924 if(col.hasClass('glyphicon-arrow-up')){
5928 this.store.sortInfo = {field : sort, direction : dir};
5931 Roo.log("calling footer first");
5932 this.footer.onClick('first');
5935 this.store.load({ params : { start : 0 } });
5939 renderHeader : function()
5948 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5950 var config = cm.config[i];
5955 html: cm.getColumnHeader(i)
5960 if(typeof(config.lgHeader) != 'undefined'){
5961 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5964 if(typeof(config.mdHeader) != 'undefined'){
5965 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5968 if(typeof(config.smHeader) != 'undefined'){
5969 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5972 if(typeof(config.xsHeader) != 'undefined'){
5973 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5980 if(typeof(config.tooltip) != 'undefined'){
5981 c.tooltip = config.tooltip;
5984 if(typeof(config.colspan) != 'undefined'){
5985 c.colspan = config.colspan;
5988 if(typeof(config.hidden) != 'undefined' && config.hidden){
5989 c.style += ' display:none;';
5992 if(typeof(config.dataIndex) != 'undefined'){
5993 c.sort = config.dataIndex;
5996 if(typeof(config.sortable) != 'undefined' && config.sortable){
6000 if(typeof(config.align) != 'undefined' && config.align.length){
6001 c.style += ' text-align:' + config.align + ';';
6004 if(typeof(config.width) != 'undefined'){
6005 c.style += ' width:' + config.width + 'px;';
6008 if(typeof(config.cls) != 'undefined'){
6009 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6012 ['xs','sm','md','lg'].map(function(size){
6014 if(typeof(config[size]) == 'undefined'){
6018 if (!config[size]) { // 0 = hidden
6019 c.cls += ' hidden-' + size;
6023 c.cls += ' col-' + size + '-' + config[size];
6033 renderBody : function()
6043 colspan : this.cm.getColumnCount()
6053 renderFooter : function()
6063 colspan : this.cm.getColumnCount()
6077 // Roo.log('ds onload');
6082 var ds = this.store;
6084 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6085 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6087 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6088 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6091 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6092 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6096 var tbody = this.mainBody;
6098 if(ds.getCount() > 0){
6099 ds.data.each(function(d,rowIndex){
6100 var row = this.renderRow(cm, ds, rowIndex);
6102 tbody.createChild(row);
6106 if(row.cellObjects.length){
6107 Roo.each(row.cellObjects, function(r){
6108 _this.renderCellObject(r);
6115 Roo.each(this.el.select('tbody td', true).elements, function(e){
6116 e.on('mouseover', _this.onMouseover, _this);
6119 Roo.each(this.el.select('tbody td', true).elements, function(e){
6120 e.on('mouseout', _this.onMouseout, _this);
6122 this.fireEvent('rowsrendered', this);
6123 //if(this.loadMask){
6124 // this.maskEl.hide();
6129 onUpdate : function(ds,record)
6131 this.refreshRow(record);
6134 onRemove : function(ds, record, index, isUpdate){
6135 if(isUpdate !== true){
6136 this.fireEvent("beforerowremoved", this, index, record);
6138 var bt = this.mainBody.dom;
6140 var rows = this.el.select('tbody > tr', true).elements;
6142 if(typeof(rows[index]) != 'undefined'){
6143 bt.removeChild(rows[index].dom);
6146 // if(bt.rows[index]){
6147 // bt.removeChild(bt.rows[index]);
6150 if(isUpdate !== true){
6151 //this.stripeRows(index);
6152 //this.syncRowHeights(index, index);
6154 this.fireEvent("rowremoved", this, index, record);
6158 onAdd : function(ds, records, rowIndex)
6160 //Roo.log('on Add called');
6161 // - note this does not handle multiple adding very well..
6162 var bt = this.mainBody.dom;
6163 for (var i =0 ; i < records.length;i++) {
6164 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6165 //Roo.log(records[i]);
6166 //Roo.log(this.store.getAt(rowIndex+i));
6167 this.insertRow(this.store, rowIndex + i, false);
6174 refreshRow : function(record){
6175 var ds = this.store, index;
6176 if(typeof record == 'number'){
6178 record = ds.getAt(index);
6180 index = ds.indexOf(record);
6182 this.insertRow(ds, index, true);
6183 this.onRemove(ds, record, index+1, true);
6184 //this.syncRowHeights(index, index);
6186 this.fireEvent("rowupdated", this, index, record);
6189 insertRow : function(dm, rowIndex, isUpdate){
6192 this.fireEvent("beforerowsinserted", this, rowIndex);
6194 //var s = this.getScrollState();
6195 var row = this.renderRow(this.cm, this.store, rowIndex);
6196 // insert before rowIndex..
6197 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6201 if(row.cellObjects.length){
6202 Roo.each(row.cellObjects, function(r){
6203 _this.renderCellObject(r);
6208 this.fireEvent("rowsinserted", this, rowIndex);
6209 //this.syncRowHeights(firstRow, lastRow);
6210 //this.stripeRows(firstRow);
6217 getRowDom : function(rowIndex)
6219 var rows = this.el.select('tbody > tr', true).elements;
6221 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6224 // returns the object tree for a tr..
6227 renderRow : function(cm, ds, rowIndex)
6230 var d = ds.getAt(rowIndex);
6237 var cellObjects = [];
6239 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6240 var config = cm.config[i];
6242 var renderer = cm.getRenderer(i);
6246 if(typeof(renderer) !== 'undefined'){
6247 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6249 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6250 // and are rendered into the cells after the row is rendered - using the id for the element.
6252 if(typeof(value) === 'object'){
6262 rowIndex : rowIndex,
6267 this.fireEvent('rowclass', this, rowcfg);
6271 cls : rowcfg.rowClass,
6273 html: (typeof(value) === 'object') ? '' : value
6280 if(typeof(config.colspan) != 'undefined'){
6281 td.colspan = config.colspan;
6284 if(typeof(config.hidden) != 'undefined' && config.hidden){
6285 td.style += ' display:none;';
6288 if(typeof(config.align) != 'undefined' && config.align.length){
6289 td.style += ' text-align:' + config.align + ';';
6292 if(typeof(config.width) != 'undefined'){
6293 td.style += ' width:' + config.width + 'px;';
6296 if(typeof(config.cursor) != 'undefined'){
6297 td.style += ' cursor:' + config.cursor + ';';
6300 if(typeof(config.cls) != 'undefined'){
6301 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6304 ['xs','sm','md','lg'].map(function(size){
6306 if(typeof(config[size]) == 'undefined'){
6310 if (!config[size]) { // 0 = hidden
6311 td.cls += ' hidden-' + size;
6315 td.cls += ' col-' + size + '-' + config[size];
6323 row.cellObjects = cellObjects;
6331 onBeforeLoad : function()
6333 //Roo.log('ds onBeforeLoad');
6337 //if(this.loadMask){
6338 // this.maskEl.show();
6346 this.el.select('tbody', true).first().dom.innerHTML = '';
6349 * Show or hide a row.
6350 * @param {Number} rowIndex to show or hide
6351 * @param {Boolean} state hide
6353 setRowVisibility : function(rowIndex, state)
6355 var bt = this.mainBody.dom;
6357 var rows = this.el.select('tbody > tr', true).elements;
6359 if(typeof(rows[rowIndex]) == 'undefined'){
6362 rows[rowIndex].dom.style.display = state ? '' : 'none';
6366 getSelectionModel : function(){
6368 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6370 return this.selModel;
6373 * Render the Roo.bootstrap object from renderder
6375 renderCellObject : function(r)
6379 var t = r.cfg.render(r.container);
6382 Roo.each(r.cfg.cn, function(c){
6384 container: t.getChildContainer(),
6387 _this.renderCellObject(child);
6392 getRowIndex : function(row)
6396 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6419 * @class Roo.bootstrap.TableCell
6420 * @extends Roo.bootstrap.Component
6421 * Bootstrap TableCell class
6422 * @cfg {String} html cell contain text
6423 * @cfg {String} cls cell class
6424 * @cfg {String} tag cell tag (td|th) default td
6425 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6426 * @cfg {String} align Aligns the content in a cell
6427 * @cfg {String} axis Categorizes cells
6428 * @cfg {String} bgcolor Specifies the background color of a cell
6429 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6430 * @cfg {Number} colspan Specifies the number of columns a cell should span
6431 * @cfg {String} headers Specifies one or more header cells a cell is related to
6432 * @cfg {Number} height Sets the height of a cell
6433 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6434 * @cfg {Number} rowspan Sets the number of rows a cell should span
6435 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6436 * @cfg {String} valign Vertical aligns the content in a cell
6437 * @cfg {Number} width Specifies the width of a cell
6440 * Create a new TableCell
6441 * @param {Object} config The config object
6444 Roo.bootstrap.TableCell = function(config){
6445 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6448 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6468 getAutoCreate : function(){
6469 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6489 cfg.align=this.align
6495 cfg.bgcolor=this.bgcolor
6498 cfg.charoff=this.charoff
6501 cfg.colspan=this.colspan
6504 cfg.headers=this.headers
6507 cfg.height=this.height
6510 cfg.nowrap=this.nowrap
6513 cfg.rowspan=this.rowspan
6516 cfg.scope=this.scope
6519 cfg.valign=this.valign
6522 cfg.width=this.width
6541 * @class Roo.bootstrap.TableRow
6542 * @extends Roo.bootstrap.Component
6543 * Bootstrap TableRow class
6544 * @cfg {String} cls row class
6545 * @cfg {String} align Aligns the content in a table row
6546 * @cfg {String} bgcolor Specifies a background color for a table row
6547 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6548 * @cfg {String} valign Vertical aligns the content in a table row
6551 * Create a new TableRow
6552 * @param {Object} config The config object
6555 Roo.bootstrap.TableRow = function(config){
6556 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6559 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6567 getAutoCreate : function(){
6568 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6578 cfg.align = this.align;
6581 cfg.bgcolor = this.bgcolor;
6584 cfg.charoff = this.charoff;
6587 cfg.valign = this.valign;
6605 * @class Roo.bootstrap.TableBody
6606 * @extends Roo.bootstrap.Component
6607 * Bootstrap TableBody class
6608 * @cfg {String} cls element class
6609 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6610 * @cfg {String} align Aligns the content inside the element
6611 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6612 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6615 * Create a new TableBody
6616 * @param {Object} config The config object
6619 Roo.bootstrap.TableBody = function(config){
6620 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6623 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6631 getAutoCreate : function(){
6632 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6646 cfg.align = this.align;
6649 cfg.charoff = this.charoff;
6652 cfg.valign = this.valign;
6659 // initEvents : function()
6666 // this.store = Roo.factory(this.store, Roo.data);
6667 // this.store.on('load', this.onLoad, this);
6669 // this.store.load();
6673 // onLoad: function ()
6675 // this.fireEvent('load', this);
6685 * Ext JS Library 1.1.1
6686 * Copyright(c) 2006-2007, Ext JS, LLC.
6688 * Originally Released Under LGPL - original licence link has changed is not relivant.
6691 * <script type="text/javascript">
6694 // as we use this in bootstrap.
6695 Roo.namespace('Roo.form');
6697 * @class Roo.form.Action
6698 * Internal Class used to handle form actions
6700 * @param {Roo.form.BasicForm} el The form element or its id
6701 * @param {Object} config Configuration options
6706 // define the action interface
6707 Roo.form.Action = function(form, options){
6709 this.options = options || {};
6712 * Client Validation Failed
6715 Roo.form.Action.CLIENT_INVALID = 'client';
6717 * Server Validation Failed
6720 Roo.form.Action.SERVER_INVALID = 'server';
6722 * Connect to Server Failed
6725 Roo.form.Action.CONNECT_FAILURE = 'connect';
6727 * Reading Data from Server Failed
6730 Roo.form.Action.LOAD_FAILURE = 'load';
6732 Roo.form.Action.prototype = {
6734 failureType : undefined,
6735 response : undefined,
6739 run : function(options){
6744 success : function(response){
6749 handleResponse : function(response){
6753 // default connection failure
6754 failure : function(response){
6756 this.response = response;
6757 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6758 this.form.afterAction(this, false);
6761 processResponse : function(response){
6762 this.response = response;
6763 if(!response.responseText){
6766 this.result = this.handleResponse(response);
6770 // utility functions used internally
6771 getUrl : function(appendParams){
6772 var url = this.options.url || this.form.url || this.form.el.dom.action;
6774 var p = this.getParams();
6776 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6782 getMethod : function(){
6783 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6786 getParams : function(){
6787 var bp = this.form.baseParams;
6788 var p = this.options.params;
6790 if(typeof p == "object"){
6791 p = Roo.urlEncode(Roo.applyIf(p, bp));
6792 }else if(typeof p == 'string' && bp){
6793 p += '&' + Roo.urlEncode(bp);
6796 p = Roo.urlEncode(bp);
6801 createCallback : function(){
6803 success: this.success,
6804 failure: this.failure,
6806 timeout: (this.form.timeout*1000),
6807 upload: this.form.fileUpload ? this.success : undefined
6812 Roo.form.Action.Submit = function(form, options){
6813 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6816 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6819 haveProgress : false,
6820 uploadComplete : false,
6822 // uploadProgress indicator.
6823 uploadProgress : function()
6825 if (!this.form.progressUrl) {
6829 if (!this.haveProgress) {
6830 Roo.MessageBox.progress("Uploading", "Uploading");
6832 if (this.uploadComplete) {
6833 Roo.MessageBox.hide();
6837 this.haveProgress = true;
6839 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6841 var c = new Roo.data.Connection();
6843 url : this.form.progressUrl,
6848 success : function(req){
6849 //console.log(data);
6853 rdata = Roo.decode(req.responseText)
6855 Roo.log("Invalid data from server..");
6859 if (!rdata || !rdata.success) {
6861 Roo.MessageBox.alert(Roo.encode(rdata));
6864 var data = rdata.data;
6866 if (this.uploadComplete) {
6867 Roo.MessageBox.hide();
6872 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6873 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6876 this.uploadProgress.defer(2000,this);
6879 failure: function(data) {
6880 Roo.log('progress url failed ');
6891 // run get Values on the form, so it syncs any secondary forms.
6892 this.form.getValues();
6894 var o = this.options;
6895 var method = this.getMethod();
6896 var isPost = method == 'POST';
6897 if(o.clientValidation === false || this.form.isValid()){
6899 if (this.form.progressUrl) {
6900 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6901 (new Date() * 1) + '' + Math.random());
6906 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6907 form:this.form.el.dom,
6908 url:this.getUrl(!isPost),
6910 params:isPost ? this.getParams() : null,
6911 isUpload: this.form.fileUpload
6914 this.uploadProgress();
6916 }else if (o.clientValidation !== false){ // client validation failed
6917 this.failureType = Roo.form.Action.CLIENT_INVALID;
6918 this.form.afterAction(this, false);
6922 success : function(response)
6924 this.uploadComplete= true;
6925 if (this.haveProgress) {
6926 Roo.MessageBox.hide();
6930 var result = this.processResponse(response);
6931 if(result === true || result.success){
6932 this.form.afterAction(this, true);
6936 this.form.markInvalid(result.errors);
6937 this.failureType = Roo.form.Action.SERVER_INVALID;
6939 this.form.afterAction(this, false);
6941 failure : function(response)
6943 this.uploadComplete= true;
6944 if (this.haveProgress) {
6945 Roo.MessageBox.hide();
6948 this.response = response;
6949 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6950 this.form.afterAction(this, false);
6953 handleResponse : function(response){
6954 if(this.form.errorReader){
6955 var rs = this.form.errorReader.read(response);
6958 for(var i = 0, len = rs.records.length; i < len; i++) {
6959 var r = rs.records[i];
6963 if(errors.length < 1){
6967 success : rs.success,
6973 ret = Roo.decode(response.responseText);
6977 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6987 Roo.form.Action.Load = function(form, options){
6988 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6989 this.reader = this.form.reader;
6992 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6997 Roo.Ajax.request(Roo.apply(
6998 this.createCallback(), {
6999 method:this.getMethod(),
7000 url:this.getUrl(false),
7001 params:this.getParams()
7005 success : function(response){
7007 var result = this.processResponse(response);
7008 if(result === true || !result.success || !result.data){
7009 this.failureType = Roo.form.Action.LOAD_FAILURE;
7010 this.form.afterAction(this, false);
7013 this.form.clearInvalid();
7014 this.form.setValues(result.data);
7015 this.form.afterAction(this, true);
7018 handleResponse : function(response){
7019 if(this.form.reader){
7020 var rs = this.form.reader.read(response);
7021 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7023 success : rs.success,
7027 return Roo.decode(response.responseText);
7031 Roo.form.Action.ACTION_TYPES = {
7032 'load' : Roo.form.Action.Load,
7033 'submit' : Roo.form.Action.Submit
7042 * @class Roo.bootstrap.Form
7043 * @extends Roo.bootstrap.Component
7044 * Bootstrap Form class
7045 * @cfg {String} method GET | POST (default POST)
7046 * @cfg {String} labelAlign top | left (default top)
7047 * @cfg {String} align left | right - for navbars
7048 * @cfg {Boolean} loadMask load mask when submit (default true)
7053 * @param {Object} config The config object
7057 Roo.bootstrap.Form = function(config){
7058 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7061 * @event clientvalidation
7062 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7063 * @param {Form} this
7064 * @param {Boolean} valid true if the form has passed client-side validation
7066 clientvalidation: true,
7068 * @event beforeaction
7069 * Fires before any action is performed. Return false to cancel the action.
7070 * @param {Form} this
7071 * @param {Action} action The action to be performed
7075 * @event actionfailed
7076 * Fires when an action fails.
7077 * @param {Form} this
7078 * @param {Action} action The action that failed
7080 actionfailed : true,
7082 * @event actioncomplete
7083 * Fires when an action is completed.
7084 * @param {Form} this
7085 * @param {Action} action The action that completed
7087 actioncomplete : true
7092 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7095 * @cfg {String} method
7096 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7101 * The URL to use for form actions if one isn't supplied in the action options.
7104 * @cfg {Boolean} fileUpload
7105 * Set to true if this form is a file upload.
7109 * @cfg {Object} baseParams
7110 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7114 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7118 * @cfg {Sting} align (left|right) for navbar forms
7123 activeAction : null,
7126 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7127 * element by passing it or its id or mask the form itself by passing in true.
7130 waitMsgTarget : false,
7134 getAutoCreate : function(){
7138 method : this.method || 'POST',
7139 id : this.id || Roo.id(),
7142 if (this.parent().xtype.match(/^Nav/)) {
7143 cfg.cls = 'navbar-form navbar-' + this.align;
7147 if (this.labelAlign == 'left' ) {
7148 cfg.cls += ' form-horizontal';
7154 initEvents : function()
7156 this.el.on('submit', this.onSubmit, this);
7157 // this was added as random key presses on the form where triggering form submit.
7158 this.el.on('keypress', function(e) {
7159 if (e.getCharCode() != 13) {
7162 // we might need to allow it for textareas.. and some other items.
7163 // check e.getTarget().
7165 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7169 Roo.log("keypress blocked");
7177 onSubmit : function(e){
7182 * Returns true if client-side validation on the form is successful.
7185 isValid : function(){
7186 var items = this.getItems();
7188 items.each(function(f){
7197 * Returns true if any fields in this form have changed since their original load.
7200 isDirty : function(){
7202 var items = this.getItems();
7203 items.each(function(f){
7213 * Performs a predefined action (submit or load) or custom actions you define on this form.
7214 * @param {String} actionName The name of the action type
7215 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7216 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7217 * accept other config options):
7219 Property Type Description
7220 ---------------- --------------- ----------------------------------------------------------------------------------
7221 url String The url for the action (defaults to the form's url)
7222 method String The form method to use (defaults to the form's method, or POST if not defined)
7223 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7224 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7225 validate the form on the client (defaults to false)
7227 * @return {BasicForm} this
7229 doAction : function(action, options){
7230 if(typeof action == 'string'){
7231 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7233 if(this.fireEvent('beforeaction', this, action) !== false){
7234 this.beforeAction(action);
7235 action.run.defer(100, action);
7241 beforeAction : function(action){
7242 var o = action.options;
7245 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7247 // not really supported yet.. ??
7249 //if(this.waitMsgTarget === true){
7250 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7251 //}else if(this.waitMsgTarget){
7252 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7253 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7255 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7261 afterAction : function(action, success){
7262 this.activeAction = null;
7263 var o = action.options;
7265 //if(this.waitMsgTarget === true){
7267 //}else if(this.waitMsgTarget){
7268 // this.waitMsgTarget.unmask();
7270 // Roo.MessageBox.updateProgress(1);
7271 // Roo.MessageBox.hide();
7278 Roo.callback(o.success, o.scope, [this, action]);
7279 this.fireEvent('actioncomplete', this, action);
7283 // failure condition..
7284 // we have a scenario where updates need confirming.
7285 // eg. if a locking scenario exists..
7286 // we look for { errors : { needs_confirm : true }} in the response.
7288 (typeof(action.result) != 'undefined') &&
7289 (typeof(action.result.errors) != 'undefined') &&
7290 (typeof(action.result.errors.needs_confirm) != 'undefined')
7293 Roo.log("not supported yet");
7296 Roo.MessageBox.confirm(
7297 "Change requires confirmation",
7298 action.result.errorMsg,
7303 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7313 Roo.callback(o.failure, o.scope, [this, action]);
7314 // show an error message if no failed handler is set..
7315 if (!this.hasListener('actionfailed')) {
7316 Roo.log("need to add dialog support");
7318 Roo.MessageBox.alert("Error",
7319 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7320 action.result.errorMsg :
7321 "Saving Failed, please check your entries or try again"
7326 this.fireEvent('actionfailed', this, action);
7331 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7332 * @param {String} id The value to search for
7335 findField : function(id){
7336 var items = this.getItems();
7337 var field = items.get(id);
7339 items.each(function(f){
7340 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7347 return field || null;
7350 * Mark fields in this form invalid in bulk.
7351 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7352 * @return {BasicForm} this
7354 markInvalid : function(errors){
7355 if(errors instanceof Array){
7356 for(var i = 0, len = errors.length; i < len; i++){
7357 var fieldError = errors[i];
7358 var f = this.findField(fieldError.id);
7360 f.markInvalid(fieldError.msg);
7366 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7367 field.markInvalid(errors[id]);
7371 //Roo.each(this.childForms || [], function (f) {
7372 // f.markInvalid(errors);
7379 * Set values for fields in this form in bulk.
7380 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7381 * @return {BasicForm} this
7383 setValues : function(values){
7384 if(values instanceof Array){ // array of objects
7385 for(var i = 0, len = values.length; i < len; i++){
7387 var f = this.findField(v.id);
7389 f.setValue(v.value);
7390 if(this.trackResetOnLoad){
7391 f.originalValue = f.getValue();
7395 }else{ // object hash
7398 if(typeof values[id] != 'function' && (field = this.findField(id))){
7400 if (field.setFromData &&
7402 field.displayField &&
7403 // combos' with local stores can
7404 // be queried via setValue()
7405 // to set their value..
7406 (field.store && !field.store.isLocal)
7410 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7411 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7412 field.setFromData(sd);
7415 field.setValue(values[id]);
7419 if(this.trackResetOnLoad){
7420 field.originalValue = field.getValue();
7426 //Roo.each(this.childForms || [], function (f) {
7427 // f.setValues(values);
7434 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7435 * they are returned as an array.
7436 * @param {Boolean} asString
7439 getValues : function(asString){
7440 //if (this.childForms) {
7441 // copy values from the child forms
7442 // Roo.each(this.childForms, function (f) {
7443 // this.setValues(f.getValues());
7449 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7450 if(asString === true){
7453 return Roo.urlDecode(fs);
7457 * Returns the fields in this form as an object with key/value pairs.
7458 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7461 getFieldValues : function(with_hidden)
7463 var items = this.getItems();
7465 items.each(function(f){
7469 var v = f.getValue();
7470 if (f.inputType =='radio') {
7471 if (typeof(ret[f.getName()]) == 'undefined') {
7472 ret[f.getName()] = ''; // empty..
7475 if (!f.el.dom.checked) {
7483 // not sure if this supported any more..
7484 if ((typeof(v) == 'object') && f.getRawValue) {
7485 v = f.getRawValue() ; // dates..
7487 // combo boxes where name != hiddenName...
7488 if (f.name != f.getName()) {
7489 ret[f.name] = f.getRawValue();
7491 ret[f.getName()] = v;
7498 * Clears all invalid messages in this form.
7499 * @return {BasicForm} this
7501 clearInvalid : function(){
7502 var items = this.getItems();
7504 items.each(function(f){
7515 * @return {BasicForm} this
7518 var items = this.getItems();
7519 items.each(function(f){
7523 Roo.each(this.childForms || [], function (f) {
7530 getItems : function()
7532 var r=new Roo.util.MixedCollection(false, function(o){
7533 return o.id || (o.id = Roo.id());
7535 var iter = function(el) {
7542 Roo.each(el.items,function(e) {
7562 * Ext JS Library 1.1.1
7563 * Copyright(c) 2006-2007, Ext JS, LLC.
7565 * Originally Released Under LGPL - original licence link has changed is not relivant.
7568 * <script type="text/javascript">
7571 * @class Roo.form.VTypes
7572 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7575 Roo.form.VTypes = function(){
7576 // closure these in so they are only created once.
7577 var alpha = /^[a-zA-Z_]+$/;
7578 var alphanum = /^[a-zA-Z0-9_]+$/;
7579 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7580 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7582 // All these messages and functions are configurable
7585 * The function used to validate email addresses
7586 * @param {String} value The email address
7588 'email' : function(v){
7589 return email.test(v);
7592 * The error text to display when the email validation function returns false
7595 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7597 * The keystroke filter mask to be applied on email input
7600 'emailMask' : /[a-z0-9_\.\-@]/i,
7603 * The function used to validate URLs
7604 * @param {String} value The URL
7606 'url' : function(v){
7610 * The error text to display when the url validation function returns false
7613 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7616 * The function used to validate alpha values
7617 * @param {String} value The value
7619 'alpha' : function(v){
7620 return alpha.test(v);
7623 * The error text to display when the alpha validation function returns false
7626 'alphaText' : 'This field should only contain letters and _',
7628 * The keystroke filter mask to be applied on alpha input
7631 'alphaMask' : /[a-z_]/i,
7634 * The function used to validate alphanumeric values
7635 * @param {String} value The value
7637 'alphanum' : function(v){
7638 return alphanum.test(v);
7641 * The error text to display when the alphanumeric validation function returns false
7644 'alphanumText' : 'This field should only contain letters, numbers and _',
7646 * The keystroke filter mask to be applied on alphanumeric input
7649 'alphanumMask' : /[a-z0-9_]/i
7659 * @class Roo.bootstrap.Input
7660 * @extends Roo.bootstrap.Component
7661 * Bootstrap Input class
7662 * @cfg {Boolean} disabled is it disabled
7663 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7664 * @cfg {String} name name of the input
7665 * @cfg {string} fieldLabel - the label associated
7666 * @cfg {string} placeholder - placeholder to put in text.
7667 * @cfg {string} before - input group add on before
7668 * @cfg {string} after - input group add on after
7669 * @cfg {string} size - (lg|sm) or leave empty..
7670 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7671 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7672 * @cfg {Number} md colspan out of 12 for computer-sized screens
7673 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7674 * @cfg {string} value default value of the input
7675 * @cfg {Number} labelWidth set the width of label (0-12)
7676 * @cfg {String} labelAlign (top|left)
7677 * @cfg {Boolean} readOnly Specifies that the field should be read-only
7678 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7680 * @cfg {String} align (left|center|right) Default left
7681 * @cfg {Boolean} forceFeedback (true|false) Default false
7687 * Create a new Input
7688 * @param {Object} config The config object
7691 Roo.bootstrap.Input = function(config){
7692 Roo.bootstrap.Input.superclass.constructor.call(this, config);
7697 * Fires when this field receives input focus.
7698 * @param {Roo.form.Field} this
7703 * Fires when this field loses input focus.
7704 * @param {Roo.form.Field} this
7709 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
7710 * {@link Roo.EventObject#getKey} to determine which key was pressed.
7711 * @param {Roo.form.Field} this
7712 * @param {Roo.EventObject} e The event object
7717 * Fires just before the field blurs if the field value has changed.
7718 * @param {Roo.form.Field} this
7719 * @param {Mixed} newValue The new value
7720 * @param {Mixed} oldValue The original value
7725 * Fires after the field has been marked as invalid.
7726 * @param {Roo.form.Field} this
7727 * @param {String} msg The validation message
7732 * Fires after the field has been validated with no errors.
7733 * @param {Roo.form.Field} this
7738 * Fires after the key up
7739 * @param {Roo.form.Field} this
7740 * @param {Roo.EventObject} e The event Object
7746 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
7748 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7749 automatic validation (defaults to "keyup").
7751 validationEvent : "keyup",
7753 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7755 validateOnBlur : true,
7757 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7759 validationDelay : 250,
7761 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7763 focusClass : "x-form-focus", // not needed???
7767 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7769 invalidClass : "has-warning",
7772 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7774 validClass : "has-success",
7777 * @cfg {Boolean} hasFeedback (true|false) default true
7782 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7784 invalidFeedbackClass : "glyphicon-warning-sign",
7787 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7789 validFeedbackClass : "glyphicon-ok",
7792 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7794 selectOnFocus : false,
7797 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7801 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7806 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7808 disableKeyFilter : false,
7811 * @cfg {Boolean} disabled True to disable the field (defaults to false).
7815 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7819 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7821 blankText : "This field is required",
7824 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7828 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7830 maxLength : Number.MAX_VALUE,
7832 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7834 minLengthText : "The minimum length for this field is {0}",
7836 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7838 maxLengthText : "The maximum length for this field is {0}",
7842 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7843 * If available, this function will be called only after the basic validators all return true, and will be passed the
7844 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7848 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7849 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7850 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
7854 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7858 autocomplete: false,
7877 formatedValue : false,
7878 forceFeedback : false,
7880 parentLabelAlign : function()
7883 while (parent.parent()) {
7884 parent = parent.parent();
7885 if (typeof(parent.labelAlign) !='undefined') {
7886 return parent.labelAlign;
7893 getAutoCreate : function(){
7895 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7901 if(this.inputType != 'hidden'){
7902 cfg.cls = 'form-group' //input-group
7908 type : this.inputType,
7910 cls : 'form-control',
7911 placeholder : this.placeholder || '',
7912 autocomplete : this.autocomplete || 'new-password'
7917 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7920 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7921 input.maxLength = this.maxLength;
7924 if (this.disabled) {
7925 input.disabled=true;
7928 if (this.readOnly) {
7929 input.readonly=true;
7933 input.name = this.name;
7936 input.cls += ' input-' + this.size;
7939 ['xs','sm','md','lg'].map(function(size){
7940 if (settings[size]) {
7941 cfg.cls += ' col-' + size + '-' + settings[size];
7945 var inputblock = input;
7949 cls: 'glyphicon form-control-feedback'
7952 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7955 cls : 'has-feedback',
7963 if (this.before || this.after) {
7966 cls : 'input-group',
7970 if (this.before && typeof(this.before) == 'string') {
7972 inputblock.cn.push({
7974 cls : 'roo-input-before input-group-addon',
7978 if (this.before && typeof(this.before) == 'object') {
7979 this.before = Roo.factory(this.before);
7981 inputblock.cn.push({
7983 cls : 'roo-input-before input-group-' +
7984 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7988 inputblock.cn.push(input);
7990 if (this.after && typeof(this.after) == 'string') {
7991 inputblock.cn.push({
7993 cls : 'roo-input-after input-group-addon',
7997 if (this.after && typeof(this.after) == 'object') {
7998 this.after = Roo.factory(this.after);
8000 inputblock.cn.push({
8002 cls : 'roo-input-after input-group-' +
8003 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8007 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8008 inputblock.cls += ' has-feedback';
8009 inputblock.cn.push(feedback);
8013 if (align ==='left' && this.fieldLabel.length) {
8020 cls : 'control-label col-sm-' + this.labelWidth,
8021 html : this.fieldLabel
8025 cls : "col-sm-" + (12 - this.labelWidth),
8032 } else if ( this.fieldLabel.length) {
8038 //cls : 'input-group-addon',
8039 html : this.fieldLabel
8058 if (this.parentType === 'Navbar' && this.parent().bar) {
8059 cfg.cls += ' navbar-form';
8066 * return the real input element.
8068 inputEl: function ()
8070 return this.el.select('input.form-control',true).first();
8073 tooltipEl : function()
8075 return this.inputEl();
8078 setDisabled : function(v)
8080 var i = this.inputEl().dom;
8082 i.removeAttribute('disabled');
8086 i.setAttribute('disabled','true');
8088 initEvents : function()
8091 this.inputEl().on("keydown" , this.fireKey, this);
8092 this.inputEl().on("focus", this.onFocus, this);
8093 this.inputEl().on("blur", this.onBlur, this);
8095 this.inputEl().relayEvent('keyup', this);
8097 // reference to original value for reset
8098 this.originalValue = this.getValue();
8099 //Roo.form.TextField.superclass.initEvents.call(this);
8100 if(this.validationEvent == 'keyup'){
8101 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8102 this.inputEl().on('keyup', this.filterValidation, this);
8104 else if(this.validationEvent !== false){
8105 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8108 if(this.selectOnFocus){
8109 this.on("focus", this.preFocus, this);
8112 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8113 this.inputEl().on("keypress", this.filterKeys, this);
8116 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8117 this.el.on("click", this.autoSize, this);
8120 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8121 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8124 if (typeof(this.before) == 'object') {
8125 this.before.render(this.el.select('.roo-input-before',true).first());
8127 if (typeof(this.after) == 'object') {
8128 this.after.render(this.el.select('.roo-input-after',true).first());
8133 filterValidation : function(e){
8134 if(!e.isNavKeyPress()){
8135 this.validationTask.delay(this.validationDelay);
8139 * Validates the field value
8140 * @return {Boolean} True if the value is valid, else false
8142 validate : function(){
8143 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8144 if(this.disabled || this.validateValue(this.getRawValue())){
8155 * Validates a value according to the field's validation rules and marks the field as invalid
8156 * if the validation fails
8157 * @param {Mixed} value The value to validate
8158 * @return {Boolean} True if the value is valid, else false
8160 validateValue : function(value){
8161 if(value.length < 1) { // if it's blank
8162 if(this.allowBlank){
8168 if(value.length < this.minLength){
8171 if(value.length > this.maxLength){
8175 var vt = Roo.form.VTypes;
8176 if(!vt[this.vtype](value, this)){
8180 if(typeof this.validator == "function"){
8181 var msg = this.validator(value);
8187 if(this.regex && !this.regex.test(value)){
8197 fireKey : function(e){
8198 //Roo.log('field ' + e.getKey());
8199 if(e.isNavKeyPress()){
8200 this.fireEvent("specialkey", this, e);
8203 focus : function (selectText){
8205 this.inputEl().focus();
8206 if(selectText === true){
8207 this.inputEl().dom.select();
8213 onFocus : function(){
8214 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8215 // this.el.addClass(this.focusClass);
8218 this.hasFocus = true;
8219 this.startValue = this.getValue();
8220 this.fireEvent("focus", this);
8224 beforeBlur : Roo.emptyFn,
8228 onBlur : function(){
8230 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8231 //this.el.removeClass(this.focusClass);
8233 this.hasFocus = false;
8234 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8237 var v = this.getValue();
8238 if(String(v) !== String(this.startValue)){
8239 this.fireEvent('change', this, v, this.startValue);
8241 this.fireEvent("blur", this);
8245 * Resets the current field value to the originally loaded value and clears any validation messages
8248 this.setValue(this.originalValue);
8252 * Returns the name of the field
8253 * @return {Mixed} name The name field
8255 getName: function(){
8259 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8260 * @return {Mixed} value The field value
8262 getValue : function(){
8264 var v = this.inputEl().getValue();
8269 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8270 * @return {Mixed} value The field value
8272 getRawValue : function(){
8273 var v = this.inputEl().getValue();
8279 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8280 * @param {Mixed} value The value to set
8282 setRawValue : function(v){
8283 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8286 selectText : function(start, end){
8287 var v = this.getRawValue();
8289 start = start === undefined ? 0 : start;
8290 end = end === undefined ? v.length : end;
8291 var d = this.inputEl().dom;
8292 if(d.setSelectionRange){
8293 d.setSelectionRange(start, end);
8294 }else if(d.createTextRange){
8295 var range = d.createTextRange();
8296 range.moveStart("character", start);
8297 range.moveEnd("character", v.length-end);
8304 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8305 * @param {Mixed} value The value to set
8307 setValue : function(v){
8310 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8316 processValue : function(value){
8317 if(this.stripCharsRe){
8318 var newValue = value.replace(this.stripCharsRe, '');
8319 if(newValue !== value){
8320 this.setRawValue(newValue);
8327 preFocus : function(){
8329 if(this.selectOnFocus){
8330 this.inputEl().dom.select();
8333 filterKeys : function(e){
8335 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8338 var c = e.getCharCode(), cc = String.fromCharCode(c);
8339 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8342 if(!this.maskRe.test(cc)){
8347 * Clear any invalid styles/messages for this field
8349 clearInvalid : function(){
8351 if(!this.el || this.preventMark){ // not rendered
8354 this.el.removeClass(this.invalidClass);
8356 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8358 var feedback = this.el.select('.form-control-feedback', true).first();
8361 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8366 this.fireEvent('valid', this);
8370 * Mark this field as valid
8372 markValid : function()
8374 if(!this.el || this.preventMark){ // not rendered
8378 this.el.removeClass([this.invalidClass, this.validClass]);
8380 var feedback = this.el.select('.form-control-feedback', true).first();
8383 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8386 if(this.disabled || this.allowBlank){
8390 var formGroup = this.el.findParent('.form-group', false, true);
8394 var label = formGroup.select('label', true).first();
8395 var icon = formGroup.select('i.fa-star', true).first();
8402 this.el.addClass(this.validClass);
8404 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8406 var feedback = this.el.select('.form-control-feedback', true).first();
8409 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8410 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8415 this.fireEvent('valid', this);
8419 * Mark this field as invalid
8420 * @param {String} msg The validation message
8422 markInvalid : function(msg)
8424 if(!this.el || this.preventMark){ // not rendered
8428 this.el.removeClass([this.invalidClass, this.validClass]);
8430 var feedback = this.el.select('.form-control-feedback', true).first();
8433 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8436 if(this.disabled || this.allowBlank){
8440 var formGroup = this.el.findParent('.form-group', false, true);
8443 var label = formGroup.select('label', true).first();
8444 var icon = formGroup.select('i.fa-star', true).first();
8446 if(!this.getValue().length && label && !icon){
8447 this.el.findParent('.form-group', false, true).createChild({
8449 cls : 'text-danger fa fa-lg fa-star',
8450 tooltip : 'This field is required',
8451 style : 'margin-right:5px;'
8457 this.el.addClass(this.invalidClass);
8459 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8461 var feedback = this.el.select('.form-control-feedback', true).first();
8464 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8466 if(this.getValue().length || this.forceFeedback){
8467 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8474 this.fireEvent('invalid', this, msg);
8477 SafariOnKeyDown : function(event)
8479 // this is a workaround for a password hang bug on chrome/ webkit.
8481 var isSelectAll = false;
8483 if(this.inputEl().dom.selectionEnd > 0){
8484 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8486 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8487 event.preventDefault();
8492 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8494 event.preventDefault();
8495 // this is very hacky as keydown always get's upper case.
8497 var cc = String.fromCharCode(event.getCharCode());
8498 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8502 adjustWidth : function(tag, w){
8503 tag = tag.toLowerCase();
8504 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8505 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8509 if(tag == 'textarea'){
8512 }else if(Roo.isOpera){
8516 if(tag == 'textarea'){
8535 * @class Roo.bootstrap.TextArea
8536 * @extends Roo.bootstrap.Input
8537 * Bootstrap TextArea class
8538 * @cfg {Number} cols Specifies the visible width of a text area
8539 * @cfg {Number} rows Specifies the visible number of lines in a text area
8540 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8541 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8542 * @cfg {string} html text
8545 * Create a new TextArea
8546 * @param {Object} config The config object
8549 Roo.bootstrap.TextArea = function(config){
8550 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8554 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8564 getAutoCreate : function(){
8566 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8577 value : this.value || '',
8578 html: this.html || '',
8579 cls : 'form-control',
8580 placeholder : this.placeholder || ''
8584 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8585 input.maxLength = this.maxLength;
8589 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8593 input.cols = this.cols;
8596 if (this.readOnly) {
8597 input.readonly = true;
8601 input.name = this.name;
8605 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8609 ['xs','sm','md','lg'].map(function(size){
8610 if (settings[size]) {
8611 cfg.cls += ' col-' + size + '-' + settings[size];
8615 var inputblock = input;
8617 if(this.hasFeedback && !this.allowBlank){
8621 cls: 'glyphicon form-control-feedback'
8625 cls : 'has-feedback',
8634 if (this.before || this.after) {
8637 cls : 'input-group',
8641 inputblock.cn.push({
8643 cls : 'input-group-addon',
8648 inputblock.cn.push(input);
8650 if(this.hasFeedback && !this.allowBlank){
8651 inputblock.cls += ' has-feedback';
8652 inputblock.cn.push(feedback);
8656 inputblock.cn.push({
8658 cls : 'input-group-addon',
8665 if (align ==='left' && this.fieldLabel.length) {
8666 // Roo.log("left and has label");
8672 cls : 'control-label col-sm-' + this.labelWidth,
8673 html : this.fieldLabel
8677 cls : "col-sm-" + (12 - this.labelWidth),
8684 } else if ( this.fieldLabel.length) {
8685 // Roo.log(" label");
8690 //cls : 'input-group-addon',
8691 html : this.fieldLabel
8701 // Roo.log(" no label && no align");
8711 if (this.disabled) {
8712 input.disabled=true;
8719 * return the real textarea element.
8721 inputEl: function ()
8723 return this.el.select('textarea.form-control',true).first();
8727 * Clear any invalid styles/messages for this field
8729 clearInvalid : function()
8732 if(!this.el || this.preventMark){ // not rendered
8736 var label = this.el.select('label', true).first();
8737 var icon = this.el.select('i.fa-star', true).first();
8743 this.el.removeClass(this.invalidClass);
8745 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8747 var feedback = this.el.select('.form-control-feedback', true).first();
8750 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8755 this.fireEvent('valid', this);
8759 * Mark this field as valid
8761 markValid : function()
8763 if(!this.el || this.preventMark){ // not rendered
8767 this.el.removeClass([this.invalidClass, this.validClass]);
8769 var feedback = this.el.select('.form-control-feedback', true).first();
8772 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8775 if(this.disabled || this.allowBlank){
8779 var label = this.el.select('label', true).first();
8780 var icon = this.el.select('i.fa-star', true).first();
8786 this.el.addClass(this.validClass);
8788 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8790 var feedback = this.el.select('.form-control-feedback', true).first();
8793 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8794 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8799 this.fireEvent('valid', this);
8803 * Mark this field as invalid
8804 * @param {String} msg The validation message
8806 markInvalid : function(msg)
8808 if(!this.el || this.preventMark){ // not rendered
8812 this.el.removeClass([this.invalidClass, this.validClass]);
8814 var feedback = this.el.select('.form-control-feedback', true).first();
8817 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8820 if(this.disabled || this.allowBlank){
8824 var label = this.el.select('label', true).first();
8825 var icon = this.el.select('i.fa-star', true).first();
8827 if(!this.getValue().length && label && !icon){
8828 this.el.createChild({
8830 cls : 'text-danger fa fa-lg fa-star',
8831 tooltip : 'This field is required',
8832 style : 'margin-right:5px;'
8836 this.el.addClass(this.invalidClass);
8838 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8840 var feedback = this.el.select('.form-control-feedback', true).first();
8843 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8845 if(this.getValue().length || this.forceFeedback){
8846 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8853 this.fireEvent('invalid', this, msg);
8861 * trigger field - base class for combo..
8866 * @class Roo.bootstrap.TriggerField
8867 * @extends Roo.bootstrap.Input
8868 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8869 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8870 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8871 * for which you can provide a custom implementation. For example:
8873 var trigger = new Roo.bootstrap.TriggerField();
8874 trigger.onTriggerClick = myTriggerFn;
8875 trigger.applyTo('my-field');
8878 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8879 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8880 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
8881 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8882 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8885 * Create a new TriggerField.
8886 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8887 * to the base TextField)
8889 Roo.bootstrap.TriggerField = function(config){
8890 this.mimicing = false;
8891 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8894 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
8896 * @cfg {String} triggerClass A CSS class to apply to the trigger
8899 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8904 * @cfg {Boolean} removable (true|false) special filter default false
8908 /** @cfg {Boolean} grow @hide */
8909 /** @cfg {Number} growMin @hide */
8910 /** @cfg {Number} growMax @hide */
8916 autoSize: Roo.emptyFn,
8923 actionMode : 'wrap',
8928 getAutoCreate : function(){
8930 var align = this.labelAlign || this.parentLabelAlign();
8935 cls: 'form-group' //input-group
8942 type : this.inputType,
8943 cls : 'form-control',
8944 autocomplete: 'new-password',
8945 placeholder : this.placeholder || ''
8949 input.name = this.name;
8952 input.cls += ' input-' + this.size;
8955 if (this.disabled) {
8956 input.disabled=true;
8959 var inputblock = input;
8961 if(this.hasFeedback && !this.allowBlank){
8965 cls: 'glyphicon form-control-feedback'
8968 if(this.removable && !this.editable && !this.tickable){
8970 cls : 'has-feedback',
8976 cls : 'roo-combo-removable-btn close'
8983 cls : 'has-feedback',
8992 if(this.removable && !this.editable && !this.tickable){
8994 cls : 'roo-removable',
9000 cls : 'roo-combo-removable-btn close'
9007 if (this.before || this.after) {
9010 cls : 'input-group',
9014 inputblock.cn.push({
9016 cls : 'input-group-addon',
9021 inputblock.cn.push(input);
9023 if(this.hasFeedback && !this.allowBlank){
9024 inputblock.cls += ' has-feedback';
9025 inputblock.cn.push(feedback);
9029 inputblock.cn.push({
9031 cls : 'input-group-addon',
9044 cls: 'form-hidden-field'
9058 cls: 'form-hidden-field'
9062 cls: 'select2-choices',
9066 cls: 'select2-search-field',
9079 cls: 'select2-container input-group',
9084 // cls: 'typeahead typeahead-long dropdown-menu',
9085 // style: 'display:none'
9090 if(!this.multiple && this.showToggleBtn){
9096 if (this.caret != false) {
9099 cls: 'fa fa-' + this.caret
9106 cls : 'input-group-addon btn dropdown-toggle',
9111 cls: 'combobox-clear',
9125 combobox.cls += ' select2-container-multi';
9128 if (align ==='left' && this.fieldLabel.length) {
9130 // Roo.log("left and has label");
9136 cls : 'control-label col-sm-' + this.labelWidth,
9137 html : this.fieldLabel
9141 cls : "col-sm-" + (12 - this.labelWidth),
9148 } else if ( this.fieldLabel.length) {
9149 // Roo.log(" label");
9154 //cls : 'input-group-addon',
9155 html : this.fieldLabel
9165 // Roo.log(" no label && no align");
9172 ['xs','sm','md','lg'].map(function(size){
9173 if (settings[size]) {
9174 cfg.cls += ' col-' + size + '-' + settings[size];
9185 onResize : function(w, h){
9186 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9187 // if(typeof w == 'number'){
9188 // var x = w - this.trigger.getWidth();
9189 // this.inputEl().setWidth(this.adjustWidth('input', x));
9190 // this.trigger.setStyle('left', x+'px');
9195 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9198 getResizeEl : function(){
9199 return this.inputEl();
9203 getPositionEl : function(){
9204 return this.inputEl();
9208 alignErrorIcon : function(){
9209 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9213 initEvents : function(){
9217 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9218 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9219 if(!this.multiple && this.showToggleBtn){
9220 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9221 if(this.hideTrigger){
9222 this.trigger.setDisplayed(false);
9224 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9228 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9231 if(this.removable && !this.editable && !this.tickable){
9232 var close = this.closeTriggerEl();
9235 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9236 close.on('click', this.removeBtnClick, this, close);
9240 //this.trigger.addClassOnOver('x-form-trigger-over');
9241 //this.trigger.addClassOnClick('x-form-trigger-click');
9244 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9248 closeTriggerEl : function()
9250 var close = this.el.select('.roo-combo-removable-btn', true).first();
9251 return close ? close : false;
9254 removeBtnClick : function(e, h, el)
9258 if(this.fireEvent("remove", this) !== false){
9263 createList : function()
9265 this.list = Roo.get(document.body).createChild({
9267 cls: 'typeahead typeahead-long dropdown-menu',
9268 style: 'display:none'
9271 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9276 initTrigger : function(){
9281 onDestroy : function(){
9283 this.trigger.removeAllListeners();
9284 // this.trigger.remove();
9287 // this.wrap.remove();
9289 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9293 onFocus : function(){
9294 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9297 this.wrap.addClass('x-trigger-wrap-focus');
9298 this.mimicing = true;
9299 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9300 if(this.monitorTab){
9301 this.el.on("keydown", this.checkTab, this);
9308 checkTab : function(e){
9309 if(e.getKey() == e.TAB){
9315 onBlur : function(){
9320 mimicBlur : function(e, t){
9322 if(!this.wrap.contains(t) && this.validateBlur()){
9329 triggerBlur : function(){
9330 this.mimicing = false;
9331 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9332 if(this.monitorTab){
9333 this.el.un("keydown", this.checkTab, this);
9335 //this.wrap.removeClass('x-trigger-wrap-focus');
9336 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9340 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9341 validateBlur : function(e, t){
9346 onDisable : function(){
9347 this.inputEl().dom.disabled = true;
9348 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9350 // this.wrap.addClass('x-item-disabled');
9355 onEnable : function(){
9356 this.inputEl().dom.disabled = false;
9357 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9359 // this.el.removeClass('x-item-disabled');
9364 onShow : function(){
9365 var ae = this.getActionEl();
9368 ae.dom.style.display = '';
9369 ae.dom.style.visibility = 'visible';
9375 onHide : function(){
9376 var ae = this.getActionEl();
9377 ae.dom.style.display = 'none';
9381 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9382 * by an implementing function.
9384 * @param {EventObject} e
9386 onTriggerClick : Roo.emptyFn
9390 * Ext JS Library 1.1.1
9391 * Copyright(c) 2006-2007, Ext JS, LLC.
9393 * Originally Released Under LGPL - original licence link has changed is not relivant.
9396 * <script type="text/javascript">
9401 * @class Roo.data.SortTypes
9403 * Defines the default sorting (casting?) comparison functions used when sorting data.
9405 Roo.data.SortTypes = {
9407 * Default sort that does nothing
9408 * @param {Mixed} s The value being converted
9409 * @return {Mixed} The comparison value
9416 * The regular expression used to strip tags
9420 stripTagsRE : /<\/?[^>]+>/gi,
9423 * Strips all HTML tags to sort on text only
9424 * @param {Mixed} s The value being converted
9425 * @return {String} The comparison value
9427 asText : function(s){
9428 return String(s).replace(this.stripTagsRE, "");
9432 * Strips all HTML tags to sort on text only - Case insensitive
9433 * @param {Mixed} s The value being converted
9434 * @return {String} The comparison value
9436 asUCText : function(s){
9437 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9441 * Case insensitive string
9442 * @param {Mixed} s The value being converted
9443 * @return {String} The comparison value
9445 asUCString : function(s) {
9446 return String(s).toUpperCase();
9451 * @param {Mixed} s The value being converted
9452 * @return {Number} The comparison value
9454 asDate : function(s) {
9458 if(s instanceof Date){
9461 return Date.parse(String(s));
9466 * @param {Mixed} s The value being converted
9467 * @return {Float} The comparison value
9469 asFloat : function(s) {
9470 var val = parseFloat(String(s).replace(/,/g, ""));
9479 * @param {Mixed} s The value being converted
9480 * @return {Number} The comparison value
9482 asInt : function(s) {
9483 var val = parseInt(String(s).replace(/,/g, ""));
9491 * Ext JS Library 1.1.1
9492 * Copyright(c) 2006-2007, Ext JS, LLC.
9494 * Originally Released Under LGPL - original licence link has changed is not relivant.
9497 * <script type="text/javascript">
9501 * @class Roo.data.Record
9502 * Instances of this class encapsulate both record <em>definition</em> information, and record
9503 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9504 * to access Records cached in an {@link Roo.data.Store} object.<br>
9506 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9507 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9510 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9512 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9513 * {@link #create}. The parameters are the same.
9514 * @param {Array} data An associative Array of data values keyed by the field name.
9515 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9516 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9517 * not specified an integer id is generated.
9519 Roo.data.Record = function(data, id){
9520 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9525 * Generate a constructor for a specific record layout.
9526 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9527 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9528 * Each field definition object may contain the following properties: <ul>
9529 * <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,
9530 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9531 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9532 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9533 * is being used, then this is a string containing the javascript expression to reference the data relative to
9534 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9535 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9536 * this may be omitted.</p></li>
9537 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9538 * <ul><li>auto (Default, implies no conversion)</li>
9543 * <li>date</li></ul></p></li>
9544 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9545 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9546 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9547 * by the Reader into an object that will be stored in the Record. It is passed the
9548 * following parameters:<ul>
9549 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9551 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9553 * <br>usage:<br><pre><code>
9554 var TopicRecord = Roo.data.Record.create(
9555 {name: 'title', mapping: 'topic_title'},
9556 {name: 'author', mapping: 'username'},
9557 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9558 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9559 {name: 'lastPoster', mapping: 'user2'},
9560 {name: 'excerpt', mapping: 'post_text'}
9563 var myNewRecord = new TopicRecord({
9564 title: 'Do my job please',
9567 lastPost: new Date(),
9568 lastPoster: 'Animal',
9569 excerpt: 'No way dude!'
9571 myStore.add(myNewRecord);
9576 Roo.data.Record.create = function(o){
9578 f.superclass.constructor.apply(this, arguments);
9580 Roo.extend(f, Roo.data.Record);
9581 var p = f.prototype;
9582 p.fields = new Roo.util.MixedCollection(false, function(field){
9585 for(var i = 0, len = o.length; i < len; i++){
9586 p.fields.add(new Roo.data.Field(o[i]));
9588 f.getField = function(name){
9589 return p.fields.get(name);
9594 Roo.data.Record.AUTO_ID = 1000;
9595 Roo.data.Record.EDIT = 'edit';
9596 Roo.data.Record.REJECT = 'reject';
9597 Roo.data.Record.COMMIT = 'commit';
9599 Roo.data.Record.prototype = {
9601 * Readonly flag - true if this record has been modified.
9610 join : function(store){
9615 * Set the named field to the specified value.
9616 * @param {String} name The name of the field to set.
9617 * @param {Object} value The value to set the field to.
9619 set : function(name, value){
9620 if(this.data[name] == value){
9627 if(typeof this.modified[name] == 'undefined'){
9628 this.modified[name] = this.data[name];
9630 this.data[name] = value;
9631 if(!this.editing && this.store){
9632 this.store.afterEdit(this);
9637 * Get the value of the named field.
9638 * @param {String} name The name of the field to get the value of.
9639 * @return {Object} The value of the field.
9641 get : function(name){
9642 return this.data[name];
9646 beginEdit : function(){
9647 this.editing = true;
9652 cancelEdit : function(){
9653 this.editing = false;
9654 delete this.modified;
9658 endEdit : function(){
9659 this.editing = false;
9660 if(this.dirty && this.store){
9661 this.store.afterEdit(this);
9666 * Usually called by the {@link Roo.data.Store} which owns the Record.
9667 * Rejects all changes made to the Record since either creation, or the last commit operation.
9668 * Modified fields are reverted to their original values.
9670 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9671 * of reject operations.
9673 reject : function(){
9674 var m = this.modified;
9676 if(typeof m[n] != "function"){
9677 this.data[n] = m[n];
9681 delete this.modified;
9682 this.editing = false;
9684 this.store.afterReject(this);
9689 * Usually called by the {@link Roo.data.Store} which owns the Record.
9690 * Commits all changes made to the Record since either creation, or the last commit operation.
9692 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9693 * of commit operations.
9695 commit : function(){
9697 delete this.modified;
9698 this.editing = false;
9700 this.store.afterCommit(this);
9705 hasError : function(){
9706 return this.error != null;
9710 clearError : function(){
9715 * Creates a copy of this record.
9716 * @param {String} id (optional) A new record id if you don't want to use this record's id
9719 copy : function(newId) {
9720 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9724 * Ext JS Library 1.1.1
9725 * Copyright(c) 2006-2007, Ext JS, LLC.
9727 * Originally Released Under LGPL - original licence link has changed is not relivant.
9730 * <script type="text/javascript">
9736 * @class Roo.data.Store
9737 * @extends Roo.util.Observable
9738 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9739 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9741 * 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
9742 * has no knowledge of the format of the data returned by the Proxy.<br>
9744 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9745 * instances from the data object. These records are cached and made available through accessor functions.
9747 * Creates a new Store.
9748 * @param {Object} config A config object containing the objects needed for the Store to access data,
9749 * and read the data into Records.
9751 Roo.data.Store = function(config){
9752 this.data = new Roo.util.MixedCollection(false);
9753 this.data.getKey = function(o){
9756 this.baseParams = {};
9763 "multisort" : "_multisort"
9766 if(config && config.data){
9767 this.inlineData = config.data;
9771 Roo.apply(this, config);
9773 if(this.reader){ // reader passed
9774 this.reader = Roo.factory(this.reader, Roo.data);
9775 this.reader.xmodule = this.xmodule || false;
9776 if(!this.recordType){
9777 this.recordType = this.reader.recordType;
9779 if(this.reader.onMetaChange){
9780 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9784 if(this.recordType){
9785 this.fields = this.recordType.prototype.fields;
9791 * @event datachanged
9792 * Fires when the data cache has changed, and a widget which is using this Store
9793 * as a Record cache should refresh its view.
9794 * @param {Store} this
9799 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9800 * @param {Store} this
9801 * @param {Object} meta The JSON metadata
9806 * Fires when Records have been added to the Store
9807 * @param {Store} this
9808 * @param {Roo.data.Record[]} records The array of Records added
9809 * @param {Number} index The index at which the record(s) were added
9814 * Fires when a Record has been removed from the Store
9815 * @param {Store} this
9816 * @param {Roo.data.Record} record The Record that was removed
9817 * @param {Number} index The index at which the record was removed
9822 * Fires when a Record has been updated
9823 * @param {Store} this
9824 * @param {Roo.data.Record} record The Record that was updated
9825 * @param {String} operation The update operation being performed. Value may be one of:
9827 Roo.data.Record.EDIT
9828 Roo.data.Record.REJECT
9829 Roo.data.Record.COMMIT
9835 * Fires when the data cache has been cleared.
9836 * @param {Store} this
9841 * Fires before a request is made for a new data object. If the beforeload handler returns false
9842 * the load action will be canceled.
9843 * @param {Store} this
9844 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9848 * @event beforeloadadd
9849 * Fires after a new set of Records has been loaded.
9850 * @param {Store} this
9851 * @param {Roo.data.Record[]} records The Records that were loaded
9852 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9854 beforeloadadd : true,
9857 * Fires after a new set of Records has been loaded, before they are added to the store.
9858 * @param {Store} this
9859 * @param {Roo.data.Record[]} records The Records that were loaded
9860 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9861 * @params {Object} return from reader
9865 * @event loadexception
9866 * Fires if an exception occurs in the Proxy during loading.
9867 * Called with the signature of the Proxy's "loadexception" event.
9868 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9871 * @param {Object} return from JsonData.reader() - success, totalRecords, records
9872 * @param {Object} load options
9873 * @param {Object} jsonData from your request (normally this contains the Exception)
9875 loadexception : true
9879 this.proxy = Roo.factory(this.proxy, Roo.data);
9880 this.proxy.xmodule = this.xmodule || false;
9881 this.relayEvents(this.proxy, ["loadexception"]);
9883 this.sortToggle = {};
9884 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9886 Roo.data.Store.superclass.constructor.call(this);
9888 if(this.inlineData){
9889 this.loadData(this.inlineData);
9890 delete this.inlineData;
9894 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9896 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
9897 * without a remote query - used by combo/forms at present.
9901 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9904 * @cfg {Array} data Inline data to be loaded when the store is initialized.
9907 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9908 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9911 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9912 * on any HTTP request
9915 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9918 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9922 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9923 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9928 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9929 * loaded or when a record is removed. (defaults to false).
9931 pruneModifiedRecords : false,
9937 * Add Records to the Store and fires the add event.
9938 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9940 add : function(records){
9941 records = [].concat(records);
9942 for(var i = 0, len = records.length; i < len; i++){
9943 records[i].join(this);
9945 var index = this.data.length;
9946 this.data.addAll(records);
9947 this.fireEvent("add", this, records, index);
9951 * Remove a Record from the Store and fires the remove event.
9952 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9954 remove : function(record){
9955 var index = this.data.indexOf(record);
9956 this.data.removeAt(index);
9957 if(this.pruneModifiedRecords){
9958 this.modified.remove(record);
9960 this.fireEvent("remove", this, record, index);
9964 * Remove all Records from the Store and fires the clear event.
9966 removeAll : function(){
9968 if(this.pruneModifiedRecords){
9971 this.fireEvent("clear", this);
9975 * Inserts Records to the Store at the given index and fires the add event.
9976 * @param {Number} index The start index at which to insert the passed Records.
9977 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9979 insert : function(index, records){
9980 records = [].concat(records);
9981 for(var i = 0, len = records.length; i < len; i++){
9982 this.data.insert(index, records[i]);
9983 records[i].join(this);
9985 this.fireEvent("add", this, records, index);
9989 * Get the index within the cache of the passed Record.
9990 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9991 * @return {Number} The index of the passed Record. Returns -1 if not found.
9993 indexOf : function(record){
9994 return this.data.indexOf(record);
9998 * Get the index within the cache of the Record with the passed id.
9999 * @param {String} id The id of the Record to find.
10000 * @return {Number} The index of the Record. Returns -1 if not found.
10002 indexOfId : function(id){
10003 return this.data.indexOfKey(id);
10007 * Get the Record with the specified id.
10008 * @param {String} id The id of the Record to find.
10009 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10011 getById : function(id){
10012 return this.data.key(id);
10016 * Get the Record at the specified index.
10017 * @param {Number} index The index of the Record to find.
10018 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10020 getAt : function(index){
10021 return this.data.itemAt(index);
10025 * Returns a range of Records between specified indices.
10026 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10027 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10028 * @return {Roo.data.Record[]} An array of Records
10030 getRange : function(start, end){
10031 return this.data.getRange(start, end);
10035 storeOptions : function(o){
10036 o = Roo.apply({}, o);
10039 this.lastOptions = o;
10043 * Loads the Record cache from the configured Proxy using the configured Reader.
10045 * If using remote paging, then the first load call must specify the <em>start</em>
10046 * and <em>limit</em> properties in the options.params property to establish the initial
10047 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10049 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10050 * and this call will return before the new data has been loaded. Perform any post-processing
10051 * in a callback function, or in a "load" event handler.</strong>
10053 * @param {Object} options An object containing properties which control loading options:<ul>
10054 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10055 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10056 * passed the following arguments:<ul>
10057 * <li>r : Roo.data.Record[]</li>
10058 * <li>options: Options object from the load call</li>
10059 * <li>success: Boolean success indicator</li></ul></li>
10060 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10061 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10064 load : function(options){
10065 options = options || {};
10066 if(this.fireEvent("beforeload", this, options) !== false){
10067 this.storeOptions(options);
10068 var p = Roo.apply(options.params || {}, this.baseParams);
10069 // if meta was not loaded from remote source.. try requesting it.
10070 if (!this.reader.metaFromRemote) {
10071 p._requestMeta = 1;
10073 if(this.sortInfo && this.remoteSort){
10074 var pn = this.paramNames;
10075 p[pn["sort"]] = this.sortInfo.field;
10076 p[pn["dir"]] = this.sortInfo.direction;
10078 if (this.multiSort) {
10079 var pn = this.paramNames;
10080 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10083 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10088 * Reloads the Record cache from the configured Proxy using the configured Reader and
10089 * the options from the last load operation performed.
10090 * @param {Object} options (optional) An object containing properties which may override the options
10091 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10092 * the most recently used options are reused).
10094 reload : function(options){
10095 this.load(Roo.applyIf(options||{}, this.lastOptions));
10099 // Called as a callback by the Reader during a load operation.
10100 loadRecords : function(o, options, success){
10101 if(!o || success === false){
10102 if(success !== false){
10103 this.fireEvent("load", this, [], options, o);
10105 if(options.callback){
10106 options.callback.call(options.scope || this, [], options, false);
10110 // if data returned failure - throw an exception.
10111 if (o.success === false) {
10112 // show a message if no listener is registered.
10113 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10114 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10116 // loadmask wil be hooked into this..
10117 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10120 var r = o.records, t = o.totalRecords || r.length;
10122 this.fireEvent("beforeloadadd", this, r, options, o);
10124 if(!options || options.add !== true){
10125 if(this.pruneModifiedRecords){
10126 this.modified = [];
10128 for(var i = 0, len = r.length; i < len; i++){
10132 this.data = this.snapshot;
10133 delete this.snapshot;
10136 this.data.addAll(r);
10137 this.totalLength = t;
10139 this.fireEvent("datachanged", this);
10141 this.totalLength = Math.max(t, this.data.length+r.length);
10144 this.fireEvent("load", this, r, options, o);
10145 if(options.callback){
10146 options.callback.call(options.scope || this, r, options, true);
10152 * Loads data from a passed data block. A Reader which understands the format of the data
10153 * must have been configured in the constructor.
10154 * @param {Object} data The data block from which to read the Records. The format of the data expected
10155 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10156 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10158 loadData : function(o, append){
10159 var r = this.reader.readRecords(o);
10160 this.loadRecords(r, {add: append}, true);
10164 * Gets the number of cached records.
10166 * <em>If using paging, this may not be the total size of the dataset. If the data object
10167 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10168 * the data set size</em>
10170 getCount : function(){
10171 return this.data.length || 0;
10175 * Gets the total number of records in the dataset as returned by the server.
10177 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10178 * the dataset size</em>
10180 getTotalCount : function(){
10181 return this.totalLength || 0;
10185 * Returns the sort state of the Store as an object with two properties:
10187 field {String} The name of the field by which the Records are sorted
10188 direction {String} The sort order, "ASC" or "DESC"
10191 getSortState : function(){
10192 return this.sortInfo;
10196 applySort : function(){
10197 if(this.sortInfo && !this.remoteSort){
10198 var s = this.sortInfo, f = s.field;
10199 var st = this.fields.get(f).sortType;
10200 var fn = function(r1, r2){
10201 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10202 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10204 this.data.sort(s.direction, fn);
10205 if(this.snapshot && this.snapshot != this.data){
10206 this.snapshot.sort(s.direction, fn);
10212 * Sets the default sort column and order to be used by the next load operation.
10213 * @param {String} fieldName The name of the field to sort by.
10214 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10216 setDefaultSort : function(field, dir){
10217 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10221 * Sort the Records.
10222 * If remote sorting is used, the sort is performed on the server, and the cache is
10223 * reloaded. If local sorting is used, the cache is sorted internally.
10224 * @param {String} fieldName The name of the field to sort by.
10225 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10227 sort : function(fieldName, dir){
10228 var f = this.fields.get(fieldName);
10230 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10232 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10233 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10238 this.sortToggle[f.name] = dir;
10239 this.sortInfo = {field: f.name, direction: dir};
10240 if(!this.remoteSort){
10242 this.fireEvent("datachanged", this);
10244 this.load(this.lastOptions);
10249 * Calls the specified function for each of the Records in the cache.
10250 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10251 * Returning <em>false</em> aborts and exits the iteration.
10252 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10254 each : function(fn, scope){
10255 this.data.each(fn, scope);
10259 * Gets all records modified since the last commit. Modified records are persisted across load operations
10260 * (e.g., during paging).
10261 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10263 getModifiedRecords : function(){
10264 return this.modified;
10268 createFilterFn : function(property, value, anyMatch){
10269 if(!value.exec){ // not a regex
10270 value = String(value);
10271 if(value.length == 0){
10274 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10276 return function(r){
10277 return value.test(r.data[property]);
10282 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10283 * @param {String} property A field on your records
10284 * @param {Number} start The record index to start at (defaults to 0)
10285 * @param {Number} end The last record index to include (defaults to length - 1)
10286 * @return {Number} The sum
10288 sum : function(property, start, end){
10289 var rs = this.data.items, v = 0;
10290 start = start || 0;
10291 end = (end || end === 0) ? end : rs.length-1;
10293 for(var i = start; i <= end; i++){
10294 v += (rs[i].data[property] || 0);
10300 * Filter the records by a specified property.
10301 * @param {String} field A field on your records
10302 * @param {String/RegExp} value Either a string that the field
10303 * should start with or a RegExp to test against the field
10304 * @param {Boolean} anyMatch True to match any part not just the beginning
10306 filter : function(property, value, anyMatch){
10307 var fn = this.createFilterFn(property, value, anyMatch);
10308 return fn ? this.filterBy(fn) : this.clearFilter();
10312 * Filter by a function. The specified function will be called with each
10313 * record in this data source. If the function returns true the record is included,
10314 * otherwise it is filtered.
10315 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10316 * @param {Object} scope (optional) The scope of the function (defaults to this)
10318 filterBy : function(fn, scope){
10319 this.snapshot = this.snapshot || this.data;
10320 this.data = this.queryBy(fn, scope||this);
10321 this.fireEvent("datachanged", this);
10325 * Query the records by a specified property.
10326 * @param {String} field A field on your records
10327 * @param {String/RegExp} value Either a string that the field
10328 * should start with or a RegExp to test against the field
10329 * @param {Boolean} anyMatch True to match any part not just the beginning
10330 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10332 query : function(property, value, anyMatch){
10333 var fn = this.createFilterFn(property, value, anyMatch);
10334 return fn ? this.queryBy(fn) : this.data.clone();
10338 * Query by a function. The specified function will be called with each
10339 * record in this data source. If the function returns true the record is included
10341 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10342 * @param {Object} scope (optional) The scope of the function (defaults to this)
10343 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10345 queryBy : function(fn, scope){
10346 var data = this.snapshot || this.data;
10347 return data.filterBy(fn, scope||this);
10351 * Collects unique values for a particular dataIndex from this store.
10352 * @param {String} dataIndex The property to collect
10353 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10354 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10355 * @return {Array} An array of the unique values
10357 collect : function(dataIndex, allowNull, bypassFilter){
10358 var d = (bypassFilter === true && this.snapshot) ?
10359 this.snapshot.items : this.data.items;
10360 var v, sv, r = [], l = {};
10361 for(var i = 0, len = d.length; i < len; i++){
10362 v = d[i].data[dataIndex];
10364 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10373 * Revert to a view of the Record cache with no filtering applied.
10374 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10376 clearFilter : function(suppressEvent){
10377 if(this.snapshot && this.snapshot != this.data){
10378 this.data = this.snapshot;
10379 delete this.snapshot;
10380 if(suppressEvent !== true){
10381 this.fireEvent("datachanged", this);
10387 afterEdit : function(record){
10388 if(this.modified.indexOf(record) == -1){
10389 this.modified.push(record);
10391 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10395 afterReject : function(record){
10396 this.modified.remove(record);
10397 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10401 afterCommit : function(record){
10402 this.modified.remove(record);
10403 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10407 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10408 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10410 commitChanges : function(){
10411 var m = this.modified.slice(0);
10412 this.modified = [];
10413 for(var i = 0, len = m.length; i < len; i++){
10419 * Cancel outstanding changes on all changed records.
10421 rejectChanges : function(){
10422 var m = this.modified.slice(0);
10423 this.modified = [];
10424 for(var i = 0, len = m.length; i < len; i++){
10429 onMetaChange : function(meta, rtype, o){
10430 this.recordType = rtype;
10431 this.fields = rtype.prototype.fields;
10432 delete this.snapshot;
10433 this.sortInfo = meta.sortInfo || this.sortInfo;
10434 this.modified = [];
10435 this.fireEvent('metachange', this, this.reader.meta);
10438 moveIndex : function(data, type)
10440 var index = this.indexOf(data);
10442 var newIndex = index + type;
10446 this.insert(newIndex, data);
10451 * Ext JS Library 1.1.1
10452 * Copyright(c) 2006-2007, Ext JS, LLC.
10454 * Originally Released Under LGPL - original licence link has changed is not relivant.
10457 * <script type="text/javascript">
10461 * @class Roo.data.SimpleStore
10462 * @extends Roo.data.Store
10463 * Small helper class to make creating Stores from Array data easier.
10464 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10465 * @cfg {Array} fields An array of field definition objects, or field name strings.
10466 * @cfg {Array} data The multi-dimensional array of data
10468 * @param {Object} config
10470 Roo.data.SimpleStore = function(config){
10471 Roo.data.SimpleStore.superclass.constructor.call(this, {
10473 reader: new Roo.data.ArrayReader({
10476 Roo.data.Record.create(config.fields)
10478 proxy : new Roo.data.MemoryProxy(config.data)
10482 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10484 * Ext JS Library 1.1.1
10485 * Copyright(c) 2006-2007, Ext JS, LLC.
10487 * Originally Released Under LGPL - original licence link has changed is not relivant.
10490 * <script type="text/javascript">
10495 * @extends Roo.data.Store
10496 * @class Roo.data.JsonStore
10497 * Small helper class to make creating Stores for JSON data easier. <br/>
10499 var store = new Roo.data.JsonStore({
10500 url: 'get-images.php',
10502 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10505 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10506 * JsonReader and HttpProxy (unless inline data is provided).</b>
10507 * @cfg {Array} fields An array of field definition objects, or field name strings.
10509 * @param {Object} config
10511 Roo.data.JsonStore = function(c){
10512 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10513 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10514 reader: new Roo.data.JsonReader(c, c.fields)
10517 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10519 * Ext JS Library 1.1.1
10520 * Copyright(c) 2006-2007, Ext JS, LLC.
10522 * Originally Released Under LGPL - original licence link has changed is not relivant.
10525 * <script type="text/javascript">
10529 Roo.data.Field = function(config){
10530 if(typeof config == "string"){
10531 config = {name: config};
10533 Roo.apply(this, config);
10536 this.type = "auto";
10539 var st = Roo.data.SortTypes;
10540 // named sortTypes are supported, here we look them up
10541 if(typeof this.sortType == "string"){
10542 this.sortType = st[this.sortType];
10545 // set default sortType for strings and dates
10546 if(!this.sortType){
10549 this.sortType = st.asUCString;
10552 this.sortType = st.asDate;
10555 this.sortType = st.none;
10560 var stripRe = /[\$,%]/g;
10562 // prebuilt conversion function for this field, instead of
10563 // switching every time we're reading a value
10565 var cv, dateFormat = this.dateFormat;
10570 cv = function(v){ return v; };
10573 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10577 return v !== undefined && v !== null && v !== '' ?
10578 parseInt(String(v).replace(stripRe, ""), 10) : '';
10583 return v !== undefined && v !== null && v !== '' ?
10584 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10589 cv = function(v){ return v === true || v === "true" || v == 1; };
10596 if(v instanceof Date){
10600 if(dateFormat == "timestamp"){
10601 return new Date(v*1000);
10603 return Date.parseDate(v, dateFormat);
10605 var parsed = Date.parse(v);
10606 return parsed ? new Date(parsed) : null;
10615 Roo.data.Field.prototype = {
10623 * Ext JS Library 1.1.1
10624 * Copyright(c) 2006-2007, Ext JS, LLC.
10626 * Originally Released Under LGPL - original licence link has changed is not relivant.
10629 * <script type="text/javascript">
10632 // Base class for reading structured data from a data source. This class is intended to be
10633 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10636 * @class Roo.data.DataReader
10637 * Base class for reading structured data from a data source. This class is intended to be
10638 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10641 Roo.data.DataReader = function(meta, recordType){
10645 this.recordType = recordType instanceof Array ?
10646 Roo.data.Record.create(recordType) : recordType;
10649 Roo.data.DataReader.prototype = {
10651 * Create an empty record
10652 * @param {Object} data (optional) - overlay some values
10653 * @return {Roo.data.Record} record created.
10655 newRow : function(d) {
10657 this.recordType.prototype.fields.each(function(c) {
10659 case 'int' : da[c.name] = 0; break;
10660 case 'date' : da[c.name] = new Date(); break;
10661 case 'float' : da[c.name] = 0.0; break;
10662 case 'boolean' : da[c.name] = false; break;
10663 default : da[c.name] = ""; break;
10667 return new this.recordType(Roo.apply(da, d));
10672 * Ext JS Library 1.1.1
10673 * Copyright(c) 2006-2007, Ext JS, LLC.
10675 * Originally Released Under LGPL - original licence link has changed is not relivant.
10678 * <script type="text/javascript">
10682 * @class Roo.data.DataProxy
10683 * @extends Roo.data.Observable
10684 * This class is an abstract base class for implementations which provide retrieval of
10685 * unformatted data objects.<br>
10687 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10688 * (of the appropriate type which knows how to parse the data object) to provide a block of
10689 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10691 * Custom implementations must implement the load method as described in
10692 * {@link Roo.data.HttpProxy#load}.
10694 Roo.data.DataProxy = function(){
10697 * @event beforeload
10698 * Fires before a network request is made to retrieve a data object.
10699 * @param {Object} This DataProxy object.
10700 * @param {Object} params The params parameter to the load function.
10705 * Fires before the load method's callback is called.
10706 * @param {Object} This DataProxy object.
10707 * @param {Object} o The data object.
10708 * @param {Object} arg The callback argument object passed to the load function.
10712 * @event loadexception
10713 * Fires if an Exception occurs during data retrieval.
10714 * @param {Object} This DataProxy object.
10715 * @param {Object} o The data object.
10716 * @param {Object} arg The callback argument object passed to the load function.
10717 * @param {Object} e The Exception.
10719 loadexception : true
10721 Roo.data.DataProxy.superclass.constructor.call(this);
10724 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10727 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10731 * Ext JS Library 1.1.1
10732 * Copyright(c) 2006-2007, Ext JS, LLC.
10734 * Originally Released Under LGPL - original licence link has changed is not relivant.
10737 * <script type="text/javascript">
10740 * @class Roo.data.MemoryProxy
10741 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10742 * to the Reader when its load method is called.
10744 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10746 Roo.data.MemoryProxy = function(data){
10750 Roo.data.MemoryProxy.superclass.constructor.call(this);
10754 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10756 * Load data from the requested source (in this case an in-memory
10757 * data object passed to the constructor), read the data object into
10758 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10759 * process that block using the passed callback.
10760 * @param {Object} params This parameter is not used by the MemoryProxy class.
10761 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10762 * object into a block of Roo.data.Records.
10763 * @param {Function} callback The function into which to pass the block of Roo.data.records.
10764 * The function must be passed <ul>
10765 * <li>The Record block object</li>
10766 * <li>The "arg" argument from the load function</li>
10767 * <li>A boolean success indicator</li>
10769 * @param {Object} scope The scope in which to call the callback
10770 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10772 load : function(params, reader, callback, scope, arg){
10773 params = params || {};
10776 result = reader.readRecords(this.data);
10778 this.fireEvent("loadexception", this, arg, null, e);
10779 callback.call(scope, null, arg, false);
10782 callback.call(scope, result, arg, true);
10786 update : function(params, records){
10791 * Ext JS Library 1.1.1
10792 * Copyright(c) 2006-2007, Ext JS, LLC.
10794 * Originally Released Under LGPL - original licence link has changed is not relivant.
10797 * <script type="text/javascript">
10800 * @class Roo.data.HttpProxy
10801 * @extends Roo.data.DataProxy
10802 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10803 * configured to reference a certain URL.<br><br>
10805 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10806 * from which the running page was served.<br><br>
10808 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10810 * Be aware that to enable the browser to parse an XML document, the server must set
10811 * the Content-Type header in the HTTP response to "text/xml".
10813 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10814 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
10815 * will be used to make the request.
10817 Roo.data.HttpProxy = function(conn){
10818 Roo.data.HttpProxy.superclass.constructor.call(this);
10819 // is conn a conn config or a real conn?
10821 this.useAjax = !conn || !conn.events;
10825 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10826 // thse are take from connection...
10829 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10832 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10833 * extra parameters to each request made by this object. (defaults to undefined)
10836 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10837 * to each request made by this object. (defaults to undefined)
10840 * @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)
10843 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10846 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10852 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10856 * Return the {@link Roo.data.Connection} object being used by this Proxy.
10857 * @return {Connection} The Connection object. This object may be used to subscribe to events on
10858 * a finer-grained basis than the DataProxy events.
10860 getConnection : function(){
10861 return this.useAjax ? Roo.Ajax : this.conn;
10865 * Load data from the configured {@link Roo.data.Connection}, read the data object into
10866 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10867 * process that block using the passed callback.
10868 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10869 * for the request to the remote server.
10870 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10871 * object into a block of Roo.data.Records.
10872 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10873 * The function must be passed <ul>
10874 * <li>The Record block object</li>
10875 * <li>The "arg" argument from the load function</li>
10876 * <li>A boolean success indicator</li>
10878 * @param {Object} scope The scope in which to call the callback
10879 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10881 load : function(params, reader, callback, scope, arg){
10882 if(this.fireEvent("beforeload", this, params) !== false){
10884 params : params || {},
10886 callback : callback,
10891 callback : this.loadResponse,
10895 Roo.applyIf(o, this.conn);
10896 if(this.activeRequest){
10897 Roo.Ajax.abort(this.activeRequest);
10899 this.activeRequest = Roo.Ajax.request(o);
10901 this.conn.request(o);
10904 callback.call(scope||this, null, arg, false);
10909 loadResponse : function(o, success, response){
10910 delete this.activeRequest;
10912 this.fireEvent("loadexception", this, o, response);
10913 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10918 result = o.reader.read(response);
10920 this.fireEvent("loadexception", this, o, response, e);
10921 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10925 this.fireEvent("load", this, o, o.request.arg);
10926 o.request.callback.call(o.request.scope, result, o.request.arg, true);
10930 update : function(dataSet){
10935 updateResponse : function(dataSet){
10940 * Ext JS Library 1.1.1
10941 * Copyright(c) 2006-2007, Ext JS, LLC.
10943 * Originally Released Under LGPL - original licence link has changed is not relivant.
10946 * <script type="text/javascript">
10950 * @class Roo.data.ScriptTagProxy
10951 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10952 * other than the originating domain of the running page.<br><br>
10954 * <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
10955 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10957 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10958 * source code that is used as the source inside a <script> tag.<br><br>
10960 * In order for the browser to process the returned data, the server must wrap the data object
10961 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10962 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10963 * depending on whether the callback name was passed:
10966 boolean scriptTag = false;
10967 String cb = request.getParameter("callback");
10970 response.setContentType("text/javascript");
10972 response.setContentType("application/x-json");
10974 Writer out = response.getWriter();
10976 out.write(cb + "(");
10978 out.print(dataBlock.toJsonString());
10985 * @param {Object} config A configuration object.
10987 Roo.data.ScriptTagProxy = function(config){
10988 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10989 Roo.apply(this, config);
10990 this.head = document.getElementsByTagName("head")[0];
10993 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10995 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10997 * @cfg {String} url The URL from which to request the data object.
11000 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11004 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11005 * the server the name of the callback function set up by the load call to process the returned data object.
11006 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11007 * javascript output which calls this named function passing the data object as its only parameter.
11009 callbackParam : "callback",
11011 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11012 * name to the request.
11017 * Load data from the configured URL, read the data object into
11018 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11019 * process that block using the passed callback.
11020 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11021 * for the request to the remote server.
11022 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11023 * object into a block of Roo.data.Records.
11024 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11025 * The function must be passed <ul>
11026 * <li>The Record block object</li>
11027 * <li>The "arg" argument from the load function</li>
11028 * <li>A boolean success indicator</li>
11030 * @param {Object} scope The scope in which to call the callback
11031 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11033 load : function(params, reader, callback, scope, arg){
11034 if(this.fireEvent("beforeload", this, params) !== false){
11036 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11038 var url = this.url;
11039 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11041 url += "&_dc=" + (new Date().getTime());
11043 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11046 cb : "stcCallback"+transId,
11047 scriptId : "stcScript"+transId,
11051 callback : callback,
11057 window[trans.cb] = function(o){
11058 conn.handleResponse(o, trans);
11061 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11063 if(this.autoAbort !== false){
11067 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11069 var script = document.createElement("script");
11070 script.setAttribute("src", url);
11071 script.setAttribute("type", "text/javascript");
11072 script.setAttribute("id", trans.scriptId);
11073 this.head.appendChild(script);
11075 this.trans = trans;
11077 callback.call(scope||this, null, arg, false);
11082 isLoading : function(){
11083 return this.trans ? true : false;
11087 * Abort the current server request.
11089 abort : function(){
11090 if(this.isLoading()){
11091 this.destroyTrans(this.trans);
11096 destroyTrans : function(trans, isLoaded){
11097 this.head.removeChild(document.getElementById(trans.scriptId));
11098 clearTimeout(trans.timeoutId);
11100 window[trans.cb] = undefined;
11102 delete window[trans.cb];
11105 // if hasn't been loaded, wait for load to remove it to prevent script error
11106 window[trans.cb] = function(){
11107 window[trans.cb] = undefined;
11109 delete window[trans.cb];
11116 handleResponse : function(o, trans){
11117 this.trans = false;
11118 this.destroyTrans(trans, true);
11121 result = trans.reader.readRecords(o);
11123 this.fireEvent("loadexception", this, o, trans.arg, e);
11124 trans.callback.call(trans.scope||window, null, trans.arg, false);
11127 this.fireEvent("load", this, o, trans.arg);
11128 trans.callback.call(trans.scope||window, result, trans.arg, true);
11132 handleFailure : function(trans){
11133 this.trans = false;
11134 this.destroyTrans(trans, false);
11135 this.fireEvent("loadexception", this, null, trans.arg);
11136 trans.callback.call(trans.scope||window, null, trans.arg, false);
11140 * Ext JS Library 1.1.1
11141 * Copyright(c) 2006-2007, Ext JS, LLC.
11143 * Originally Released Under LGPL - original licence link has changed is not relivant.
11146 * <script type="text/javascript">
11150 * @class Roo.data.JsonReader
11151 * @extends Roo.data.DataReader
11152 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11153 * based on mappings in a provided Roo.data.Record constructor.
11155 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11156 * in the reply previously.
11161 var RecordDef = Roo.data.Record.create([
11162 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11163 {name: 'occupation'} // This field will use "occupation" as the mapping.
11165 var myReader = new Roo.data.JsonReader({
11166 totalProperty: "results", // The property which contains the total dataset size (optional)
11167 root: "rows", // The property which contains an Array of row objects
11168 id: "id" // The property within each row object that provides an ID for the record (optional)
11172 * This would consume a JSON file like this:
11174 { 'results': 2, 'rows': [
11175 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11176 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11179 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11180 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11181 * paged from the remote server.
11182 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11183 * @cfg {String} root name of the property which contains the Array of row objects.
11184 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11185 * @cfg {Array} fields Array of field definition objects
11187 * Create a new JsonReader
11188 * @param {Object} meta Metadata configuration options
11189 * @param {Object} recordType Either an Array of field definition objects,
11190 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11192 Roo.data.JsonReader = function(meta, recordType){
11195 // set some defaults:
11196 Roo.applyIf(meta, {
11197 totalProperty: 'total',
11198 successProperty : 'success',
11203 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11205 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11208 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11209 * Used by Store query builder to append _requestMeta to params.
11212 metaFromRemote : false,
11214 * This method is only used by a DataProxy which has retrieved data from a remote server.
11215 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11216 * @return {Object} data A data block which is used by an Roo.data.Store object as
11217 * a cache of Roo.data.Records.
11219 read : function(response){
11220 var json = response.responseText;
11222 var o = /* eval:var:o */ eval("("+json+")");
11224 throw {message: "JsonReader.read: Json object not found"};
11230 this.metaFromRemote = true;
11231 this.meta = o.metaData;
11232 this.recordType = Roo.data.Record.create(o.metaData.fields);
11233 this.onMetaChange(this.meta, this.recordType, o);
11235 return this.readRecords(o);
11238 // private function a store will implement
11239 onMetaChange : function(meta, recordType, o){
11246 simpleAccess: function(obj, subsc) {
11253 getJsonAccessor: function(){
11255 return function(expr) {
11257 return(re.test(expr))
11258 ? new Function("obj", "return obj." + expr)
11263 return Roo.emptyFn;
11268 * Create a data block containing Roo.data.Records from an XML document.
11269 * @param {Object} o An object which contains an Array of row objects in the property specified
11270 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11271 * which contains the total size of the dataset.
11272 * @return {Object} data A data block which is used by an Roo.data.Store object as
11273 * a cache of Roo.data.Records.
11275 readRecords : function(o){
11277 * After any data loads, the raw JSON data is available for further custom processing.
11281 var s = this.meta, Record = this.recordType,
11282 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11284 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11286 if(s.totalProperty) {
11287 this.getTotal = this.getJsonAccessor(s.totalProperty);
11289 if(s.successProperty) {
11290 this.getSuccess = this.getJsonAccessor(s.successProperty);
11292 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11294 var g = this.getJsonAccessor(s.id);
11295 this.getId = function(rec) {
11297 return (r === undefined || r === "") ? null : r;
11300 this.getId = function(){return null;};
11303 for(var jj = 0; jj < fl; jj++){
11305 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11306 this.ef[jj] = this.getJsonAccessor(map);
11310 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11311 if(s.totalProperty){
11312 var vt = parseInt(this.getTotal(o), 10);
11317 if(s.successProperty){
11318 var vs = this.getSuccess(o);
11319 if(vs === false || vs === 'false'){
11324 for(var i = 0; i < c; i++){
11327 var id = this.getId(n);
11328 for(var j = 0; j < fl; j++){
11330 var v = this.ef[j](n);
11332 Roo.log('missing convert for ' + f.name);
11336 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11338 var record = new Record(values, id);
11340 records[i] = record;
11346 totalRecords : totalRecords
11351 * Ext JS Library 1.1.1
11352 * Copyright(c) 2006-2007, Ext JS, LLC.
11354 * Originally Released Under LGPL - original licence link has changed is not relivant.
11357 * <script type="text/javascript">
11361 * @class Roo.data.ArrayReader
11362 * @extends Roo.data.DataReader
11363 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11364 * Each element of that Array represents a row of data fields. The
11365 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11366 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11370 var RecordDef = Roo.data.Record.create([
11371 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11372 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11374 var myReader = new Roo.data.ArrayReader({
11375 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11379 * This would consume an Array like this:
11381 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11383 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11385 * Create a new JsonReader
11386 * @param {Object} meta Metadata configuration options.
11387 * @param {Object} recordType Either an Array of field definition objects
11388 * as specified to {@link Roo.data.Record#create},
11389 * or an {@link Roo.data.Record} object
11390 * created using {@link Roo.data.Record#create}.
11392 Roo.data.ArrayReader = function(meta, recordType){
11393 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11396 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11398 * Create a data block containing Roo.data.Records from an XML document.
11399 * @param {Object} o An Array of row objects which represents the dataset.
11400 * @return {Object} data A data block which is used by an Roo.data.Store object as
11401 * a cache of Roo.data.Records.
11403 readRecords : function(o){
11404 var sid = this.meta ? this.meta.id : null;
11405 var recordType = this.recordType, fields = recordType.prototype.fields;
11408 for(var i = 0; i < root.length; i++){
11411 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11412 for(var j = 0, jlen = fields.length; j < jlen; j++){
11413 var f = fields.items[j];
11414 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11415 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11417 values[f.name] = v;
11419 var record = new recordType(values, id);
11421 records[records.length] = record;
11425 totalRecords : records.length
11434 * @class Roo.bootstrap.ComboBox
11435 * @extends Roo.bootstrap.TriggerField
11436 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11437 * @cfg {Boolean} append (true|false) default false
11438 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11439 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11440 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11441 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11442 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11443 * @cfg {Boolean} animate default true
11444 * @cfg {Boolean} emptyResultText only for touch device
11445 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11447 * Create a new ComboBox.
11448 * @param {Object} config Configuration options
11450 Roo.bootstrap.ComboBox = function(config){
11451 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11455 * Fires when the dropdown list is expanded
11456 * @param {Roo.bootstrap.ComboBox} combo This combo box
11461 * Fires when the dropdown list is collapsed
11462 * @param {Roo.bootstrap.ComboBox} combo This combo box
11466 * @event beforeselect
11467 * Fires before a list item is selected. Return false to cancel the selection.
11468 * @param {Roo.bootstrap.ComboBox} combo This combo box
11469 * @param {Roo.data.Record} record The data record returned from the underlying store
11470 * @param {Number} index The index of the selected item in the dropdown list
11472 'beforeselect' : true,
11475 * Fires when a list item is selected
11476 * @param {Roo.bootstrap.ComboBox} combo This combo box
11477 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11478 * @param {Number} index The index of the selected item in the dropdown list
11482 * @event beforequery
11483 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11484 * The event object passed has these properties:
11485 * @param {Roo.bootstrap.ComboBox} combo This combo box
11486 * @param {String} query The query
11487 * @param {Boolean} forceAll true to force "all" query
11488 * @param {Boolean} cancel true to cancel the query
11489 * @param {Object} e The query event object
11491 'beforequery': true,
11494 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11495 * @param {Roo.bootstrap.ComboBox} combo This combo box
11500 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11501 * @param {Roo.bootstrap.ComboBox} combo This combo box
11502 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11507 * Fires when the remove value from the combobox array
11508 * @param {Roo.bootstrap.ComboBox} combo This combo box
11512 * @event specialfilter
11513 * Fires when specialfilter
11514 * @param {Roo.bootstrap.ComboBox} combo This combo box
11516 'specialfilter' : true,
11519 * Fires when tick the element
11520 * @param {Roo.bootstrap.ComboBox} combo This combo box
11524 * @event touchviewdisplay
11525 * Fires when touch view require special display (default is using displayField)
11526 * @param {Roo.bootstrap.ComboBox} combo This combo box
11527 * @param {Object} cfg set html .
11529 'touchviewdisplay' : true
11534 this.tickItems = [];
11536 this.selectedIndex = -1;
11537 if(this.mode == 'local'){
11538 if(config.queryDelay === undefined){
11539 this.queryDelay = 10;
11541 if(config.minChars === undefined){
11547 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11550 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11551 * rendering into an Roo.Editor, defaults to false)
11554 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11555 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11558 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11561 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11562 * the dropdown list (defaults to undefined, with no header element)
11566 * @cfg {String/Roo.Template} tpl The template to use to render the output
11570 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11572 listWidth: undefined,
11574 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11575 * mode = 'remote' or 'text' if mode = 'local')
11577 displayField: undefined,
11580 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11581 * mode = 'remote' or 'value' if mode = 'local').
11582 * Note: use of a valueField requires the user make a selection
11583 * in order for a value to be mapped.
11585 valueField: undefined,
11589 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11590 * field's data value (defaults to the underlying DOM element's name)
11592 hiddenName: undefined,
11594 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11598 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11600 selectedClass: 'active',
11603 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11607 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11608 * anchor positions (defaults to 'tl-bl')
11610 listAlign: 'tl-bl?',
11612 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11616 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11617 * query specified by the allQuery config option (defaults to 'query')
11619 triggerAction: 'query',
11621 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11622 * (defaults to 4, does not apply if editable = false)
11626 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11627 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11631 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11632 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11636 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11637 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11641 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11642 * when editable = true (defaults to false)
11644 selectOnFocus:false,
11646 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11648 queryParam: 'query',
11650 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
11651 * when mode = 'remote' (defaults to 'Loading...')
11653 loadingText: 'Loading...',
11655 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11659 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11663 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11664 * traditional select (defaults to true)
11668 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11672 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11676 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11677 * listWidth has a higher value)
11681 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11682 * allow the user to set arbitrary text into the field (defaults to false)
11684 forceSelection:false,
11686 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11687 * if typeAhead = true (defaults to 250)
11689 typeAheadDelay : 250,
11691 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11692 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11694 valueNotFoundText : undefined,
11696 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11698 blockFocus : false,
11701 * @cfg {Boolean} disableClear Disable showing of clear button.
11703 disableClear : false,
11705 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
11707 alwaysQuery : false,
11710 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
11715 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11717 invalidClass : "has-warning",
11720 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11722 validClass : "has-success",
11725 * @cfg {Boolean} specialFilter (true|false) special filter default false
11727 specialFilter : false,
11730 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11732 mobileTouchView : true,
11744 btnPosition : 'right',
11745 triggerList : true,
11746 showToggleBtn : true,
11748 emptyResultText: 'Empty',
11749 triggerText : 'Select',
11751 // element that contains real text value.. (when hidden is used..)
11753 getAutoCreate : function()
11761 if(Roo.isTouch && this.mobileTouchView){
11762 cfg = this.getAutoCreateTouchView();
11769 if(!this.tickable){
11770 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11775 * ComboBox with tickable selections
11778 var align = this.labelAlign || this.parentLabelAlign();
11781 cls : 'form-group roo-combobox-tickable' //input-group
11786 cls : 'tickable-buttons',
11791 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11792 html : this.triggerText
11798 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11805 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11812 buttons.cn.unshift({
11814 cls: 'select2-search-field-input'
11820 Roo.each(buttons.cn, function(c){
11822 c.cls += ' btn-' + _this.size;
11825 if (_this.disabled) {
11836 cls: 'form-hidden-field'
11840 cls: 'select2-choices',
11844 cls: 'select2-search-field',
11856 cls: 'select2-container input-group select2-container-multi',
11861 // cls: 'typeahead typeahead-long dropdown-menu',
11862 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
11867 if(this.hasFeedback && !this.allowBlank){
11871 cls: 'glyphicon form-control-feedback'
11874 combobox.cn.push(feedback);
11877 if (align ==='left' && this.fieldLabel.length) {
11879 // Roo.log("left and has label");
11885 cls : 'control-label col-sm-' + this.labelWidth,
11886 html : this.fieldLabel
11890 cls : "col-sm-" + (12 - this.labelWidth),
11897 } else if ( this.fieldLabel.length) {
11898 // Roo.log(" label");
11903 //cls : 'input-group-addon',
11904 html : this.fieldLabel
11914 // Roo.log(" no label && no align");
11921 ['xs','sm','md','lg'].map(function(size){
11922 if (settings[size]) {
11923 cfg.cls += ' col-' + size + '-' + settings[size];
11931 _initEventsCalled : false,
11934 initEvents: function()
11937 if (this._initEventsCalled) { // as we call render... prevent looping...
11940 this._initEventsCalled = true;
11943 throw "can not find store for combo";
11946 this.store = Roo.factory(this.store, Roo.data);
11948 // if we are building from html. then this element is so complex, that we can not really
11949 // use the rendered HTML.
11950 // so we have to trash and replace the previous code.
11951 if (Roo.XComponent.build_from_html) {
11953 // remove this element....
11954 var e = this.el.dom, k=0;
11955 while (e ) { e = e.previousSibling; ++k;}
11960 this.rendered = false;
11962 this.render(this.parent().getChildContainer(true), k);
11973 if(Roo.isTouch && this.mobileTouchView){
11974 this.initTouchView();
11979 this.initTickableEvents();
11983 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11985 if(this.hiddenName){
11987 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11989 this.hiddenField.dom.value =
11990 this.hiddenValue !== undefined ? this.hiddenValue :
11991 this.value !== undefined ? this.value : '';
11993 // prevent input submission
11994 this.el.dom.removeAttribute('name');
11995 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12000 // this.el.dom.setAttribute('autocomplete', 'off');
12003 var cls = 'x-combo-list';
12005 //this.list = new Roo.Layer({
12006 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12012 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12013 _this.list.setWidth(lw);
12016 this.list.on('mouseover', this.onViewOver, this);
12017 this.list.on('mousemove', this.onViewMove, this);
12019 this.list.on('scroll', this.onViewScroll, this);
12022 this.list.swallowEvent('mousewheel');
12023 this.assetHeight = 0;
12026 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12027 this.assetHeight += this.header.getHeight();
12030 this.innerList = this.list.createChild({cls:cls+'-inner'});
12031 this.innerList.on('mouseover', this.onViewOver, this);
12032 this.innerList.on('mousemove', this.onViewMove, this);
12033 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12035 if(this.allowBlank && !this.pageSize && !this.disableClear){
12036 this.footer = this.list.createChild({cls:cls+'-ft'});
12037 this.pageTb = new Roo.Toolbar(this.footer);
12041 this.footer = this.list.createChild({cls:cls+'-ft'});
12042 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12043 {pageSize: this.pageSize});
12047 if (this.pageTb && this.allowBlank && !this.disableClear) {
12049 this.pageTb.add(new Roo.Toolbar.Fill(), {
12050 cls: 'x-btn-icon x-btn-clear',
12052 handler: function()
12055 _this.clearValue();
12056 _this.onSelect(false, -1);
12061 this.assetHeight += this.footer.getHeight();
12066 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12069 this.view = new Roo.View(this.list, this.tpl, {
12070 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12072 //this.view.wrapEl.setDisplayed(false);
12073 this.view.on('click', this.onViewClick, this);
12077 this.store.on('beforeload', this.onBeforeLoad, this);
12078 this.store.on('load', this.onLoad, this);
12079 this.store.on('loadexception', this.onLoadException, this);
12081 if(this.resizable){
12082 this.resizer = new Roo.Resizable(this.list, {
12083 pinned:true, handles:'se'
12085 this.resizer.on('resize', function(r, w, h){
12086 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12087 this.listWidth = w;
12088 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12089 this.restrictHeight();
12091 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12094 if(!this.editable){
12095 this.editable = true;
12096 this.setEditable(false);
12101 if (typeof(this.events.add.listeners) != 'undefined') {
12103 this.addicon = this.wrap.createChild(
12104 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12106 this.addicon.on('click', function(e) {
12107 this.fireEvent('add', this);
12110 if (typeof(this.events.edit.listeners) != 'undefined') {
12112 this.editicon = this.wrap.createChild(
12113 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12114 if (this.addicon) {
12115 this.editicon.setStyle('margin-left', '40px');
12117 this.editicon.on('click', function(e) {
12119 // we fire even if inothing is selected..
12120 this.fireEvent('edit', this, this.lastData );
12126 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12127 "up" : function(e){
12128 this.inKeyMode = true;
12132 "down" : function(e){
12133 if(!this.isExpanded()){
12134 this.onTriggerClick();
12136 this.inKeyMode = true;
12141 "enter" : function(e){
12142 // this.onViewClick();
12146 if(this.fireEvent("specialkey", this, e)){
12147 this.onViewClick(false);
12153 "esc" : function(e){
12157 "tab" : function(e){
12160 if(this.fireEvent("specialkey", this, e)){
12161 this.onViewClick(false);
12169 doRelay : function(foo, bar, hname){
12170 if(hname == 'down' || this.scope.isExpanded()){
12171 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12180 this.queryDelay = Math.max(this.queryDelay || 10,
12181 this.mode == 'local' ? 10 : 250);
12184 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12186 if(this.typeAhead){
12187 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12189 if(this.editable !== false){
12190 this.inputEl().on("keyup", this.onKeyUp, this);
12192 if(this.forceSelection){
12193 this.inputEl().on('blur', this.doForce, this);
12197 this.choices = this.el.select('ul.select2-choices', true).first();
12198 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12202 initTickableEvents: function()
12206 if(this.hiddenName){
12208 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12210 this.hiddenField.dom.value =
12211 this.hiddenValue !== undefined ? this.hiddenValue :
12212 this.value !== undefined ? this.value : '';
12214 // prevent input submission
12215 this.el.dom.removeAttribute('name');
12216 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12221 // this.list = this.el.select('ul.dropdown-menu',true).first();
12223 this.choices = this.el.select('ul.select2-choices', true).first();
12224 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12225 if(this.triggerList){
12226 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12229 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12230 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12232 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12233 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12235 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12236 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12238 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12239 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12240 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12243 this.cancelBtn.hide();
12248 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12249 _this.list.setWidth(lw);
12252 this.list.on('mouseover', this.onViewOver, this);
12253 this.list.on('mousemove', this.onViewMove, this);
12255 this.list.on('scroll', this.onViewScroll, this);
12258 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>';
12261 this.view = new Roo.View(this.list, this.tpl, {
12262 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12265 //this.view.wrapEl.setDisplayed(false);
12266 this.view.on('click', this.onViewClick, this);
12270 this.store.on('beforeload', this.onBeforeLoad, this);
12271 this.store.on('load', this.onLoad, this);
12272 this.store.on('loadexception', this.onLoadException, this);
12275 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12276 "up" : function(e){
12277 this.inKeyMode = true;
12281 "down" : function(e){
12282 this.inKeyMode = true;
12286 "enter" : function(e){
12287 if(this.fireEvent("specialkey", this, e)){
12288 this.onViewClick(false);
12294 "esc" : function(e){
12295 this.onTickableFooterButtonClick(e, false, false);
12298 "tab" : function(e){
12299 this.fireEvent("specialkey", this, e);
12301 this.onTickableFooterButtonClick(e, false, false);
12308 doRelay : function(e, fn, key){
12309 if(this.scope.isExpanded()){
12310 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12319 this.queryDelay = Math.max(this.queryDelay || 10,
12320 this.mode == 'local' ? 10 : 250);
12323 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12325 if(this.typeAhead){
12326 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12329 if(this.editable !== false){
12330 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12335 onDestroy : function(){
12337 this.view.setStore(null);
12338 this.view.el.removeAllListeners();
12339 this.view.el.remove();
12340 this.view.purgeListeners();
12343 this.list.dom.innerHTML = '';
12347 this.store.un('beforeload', this.onBeforeLoad, this);
12348 this.store.un('load', this.onLoad, this);
12349 this.store.un('loadexception', this.onLoadException, this);
12351 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12355 fireKey : function(e){
12356 if(e.isNavKeyPress() && !this.list.isVisible()){
12357 this.fireEvent("specialkey", this, e);
12362 onResize: function(w, h){
12363 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12365 // if(typeof w != 'number'){
12366 // // we do not handle it!?!?
12369 // var tw = this.trigger.getWidth();
12370 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12371 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12373 // this.inputEl().setWidth( this.adjustWidth('input', x));
12375 // //this.trigger.setStyle('left', x+'px');
12377 // if(this.list && this.listWidth === undefined){
12378 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12379 // this.list.setWidth(lw);
12380 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12388 * Allow or prevent the user from directly editing the field text. If false is passed,
12389 * the user will only be able to select from the items defined in the dropdown list. This method
12390 * is the runtime equivalent of setting the 'editable' config option at config time.
12391 * @param {Boolean} value True to allow the user to directly edit the field text
12393 setEditable : function(value){
12394 if(value == this.editable){
12397 this.editable = value;
12399 this.inputEl().dom.setAttribute('readOnly', true);
12400 this.inputEl().on('mousedown', this.onTriggerClick, this);
12401 this.inputEl().addClass('x-combo-noedit');
12403 this.inputEl().dom.setAttribute('readOnly', false);
12404 this.inputEl().un('mousedown', this.onTriggerClick, this);
12405 this.inputEl().removeClass('x-combo-noedit');
12411 onBeforeLoad : function(combo,opts){
12412 if(!this.hasFocus){
12416 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12418 this.restrictHeight();
12419 this.selectedIndex = -1;
12423 onLoad : function(){
12425 this.hasQuery = false;
12427 if(!this.hasFocus){
12431 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12432 this.loading.hide();
12435 if(this.store.getCount() > 0){
12437 this.restrictHeight();
12438 if(this.lastQuery == this.allQuery){
12439 if(this.editable && !this.tickable){
12440 this.inputEl().dom.select();
12444 !this.selectByValue(this.value, true) &&
12447 !this.store.lastOptions ||
12448 typeof(this.store.lastOptions.add) == 'undefined' ||
12449 this.store.lastOptions.add != true
12452 this.select(0, true);
12455 if(this.autoFocus){
12458 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12459 this.taTask.delay(this.typeAheadDelay);
12463 this.onEmptyResults();
12469 onLoadException : function()
12471 this.hasQuery = false;
12473 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12474 this.loading.hide();
12477 if(this.tickable && this.editable){
12482 // only causes errors at present
12483 //Roo.log(this.store.reader.jsonData);
12484 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12486 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12492 onTypeAhead : function(){
12493 if(this.store.getCount() > 0){
12494 var r = this.store.getAt(0);
12495 var newValue = r.data[this.displayField];
12496 var len = newValue.length;
12497 var selStart = this.getRawValue().length;
12499 if(selStart != len){
12500 this.setRawValue(newValue);
12501 this.selectText(selStart, newValue.length);
12507 onSelect : function(record, index){
12509 if(this.fireEvent('beforeselect', this, record, index) !== false){
12511 this.setFromData(index > -1 ? record.data : false);
12514 this.fireEvent('select', this, record, index);
12519 * Returns the currently selected field value or empty string if no value is set.
12520 * @return {String} value The selected value
12522 getValue : function(){
12525 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12528 if(this.valueField){
12529 return typeof this.value != 'undefined' ? this.value : '';
12531 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12536 * Clears any text/value currently set in the field
12538 clearValue : function(){
12539 if(this.hiddenField){
12540 this.hiddenField.dom.value = '';
12543 this.setRawValue('');
12544 this.lastSelectionText = '';
12545 this.lastData = false;
12547 var close = this.closeTriggerEl();
12556 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12557 * will be displayed in the field. If the value does not match the data value of an existing item,
12558 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12559 * Otherwise the field will be blank (although the value will still be set).
12560 * @param {String} value The value to match
12562 setValue : function(v){
12569 if(this.valueField){
12570 var r = this.findRecord(this.valueField, v);
12572 text = r.data[this.displayField];
12573 }else if(this.valueNotFoundText !== undefined){
12574 text = this.valueNotFoundText;
12577 this.lastSelectionText = text;
12578 if(this.hiddenField){
12579 this.hiddenField.dom.value = v;
12581 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12584 var close = this.closeTriggerEl();
12587 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12591 * @property {Object} the last set data for the element
12596 * Sets the value of the field based on a object which is related to the record format for the store.
12597 * @param {Object} value the value to set as. or false on reset?
12599 setFromData : function(o){
12606 var dv = ''; // display value
12607 var vv = ''; // value value..
12609 if (this.displayField) {
12610 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12612 // this is an error condition!!!
12613 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12616 if(this.valueField){
12617 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12620 var close = this.closeTriggerEl();
12623 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12626 if(this.hiddenField){
12627 this.hiddenField.dom.value = vv;
12629 this.lastSelectionText = dv;
12630 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12634 // no hidden field.. - we store the value in 'value', but still display
12635 // display field!!!!
12636 this.lastSelectionText = dv;
12637 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12644 reset : function(){
12645 // overridden so that last data is reset..
12652 this.setValue(this.originalValue);
12653 this.clearInvalid();
12654 this.lastData = false;
12656 this.view.clearSelections();
12660 findRecord : function(prop, value){
12662 if(this.store.getCount() > 0){
12663 this.store.each(function(r){
12664 if(r.data[prop] == value){
12674 getName: function()
12676 // returns hidden if it's set..
12677 if (!this.rendered) {return ''};
12678 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
12682 onViewMove : function(e, t){
12683 this.inKeyMode = false;
12687 onViewOver : function(e, t){
12688 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12691 var item = this.view.findItemFromChild(t);
12694 var index = this.view.indexOf(item);
12695 this.select(index, false);
12700 onViewClick : function(view, doFocus, el, e)
12702 var index = this.view.getSelectedIndexes()[0];
12704 var r = this.store.getAt(index);
12708 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12715 Roo.each(this.tickItems, function(v,k){
12717 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12719 _this.tickItems.splice(k, 1);
12721 if(typeof(e) == 'undefined' && view == false){
12722 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12734 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
12735 this.tickItems.push(r.data);
12738 if(typeof(e) == 'undefined' && view == false){
12739 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12746 this.onSelect(r, index);
12748 if(doFocus !== false && !this.blockFocus){
12749 this.inputEl().focus();
12754 restrictHeight : function(){
12755 //this.innerList.dom.style.height = '';
12756 //var inner = this.innerList.dom;
12757 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12758 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12759 //this.list.beginUpdate();
12760 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12761 this.list.alignTo(this.inputEl(), this.listAlign);
12762 this.list.alignTo(this.inputEl(), this.listAlign);
12763 //this.list.endUpdate();
12767 onEmptyResults : function(){
12769 if(this.tickable && this.editable){
12770 this.restrictHeight();
12778 * Returns true if the dropdown list is expanded, else false.
12780 isExpanded : function(){
12781 return this.list.isVisible();
12785 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12786 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12787 * @param {String} value The data value of the item to select
12788 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12789 * selected item if it is not currently in view (defaults to true)
12790 * @return {Boolean} True if the value matched an item in the list, else false
12792 selectByValue : function(v, scrollIntoView){
12793 if(v !== undefined && v !== null){
12794 var r = this.findRecord(this.valueField || this.displayField, v);
12796 this.select(this.store.indexOf(r), scrollIntoView);
12804 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12805 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12806 * @param {Number} index The zero-based index of the list item to select
12807 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12808 * selected item if it is not currently in view (defaults to true)
12810 select : function(index, scrollIntoView){
12811 this.selectedIndex = index;
12812 this.view.select(index);
12813 if(scrollIntoView !== false){
12814 var el = this.view.getNode(index);
12816 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12819 this.list.scrollChildIntoView(el, false);
12825 selectNext : function(){
12826 var ct = this.store.getCount();
12828 if(this.selectedIndex == -1){
12830 }else if(this.selectedIndex < ct-1){
12831 this.select(this.selectedIndex+1);
12837 selectPrev : function(){
12838 var ct = this.store.getCount();
12840 if(this.selectedIndex == -1){
12842 }else if(this.selectedIndex != 0){
12843 this.select(this.selectedIndex-1);
12849 onKeyUp : function(e){
12850 if(this.editable !== false && !e.isSpecialKey()){
12851 this.lastKey = e.getKey();
12852 this.dqTask.delay(this.queryDelay);
12857 validateBlur : function(){
12858 return !this.list || !this.list.isVisible();
12862 initQuery : function(){
12864 var v = this.getRawValue();
12866 if(this.tickable && this.editable){
12867 v = this.tickableInputEl().getValue();
12874 doForce : function(){
12875 if(this.inputEl().dom.value.length > 0){
12876 this.inputEl().dom.value =
12877 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12883 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
12884 * query allowing the query action to be canceled if needed.
12885 * @param {String} query The SQL query to execute
12886 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12887 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
12888 * saved in the current store (defaults to false)
12890 doQuery : function(q, forceAll){
12892 if(q === undefined || q === null){
12897 forceAll: forceAll,
12901 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12906 forceAll = qe.forceAll;
12907 if(forceAll === true || (q.length >= this.minChars)){
12909 this.hasQuery = true;
12911 if(this.lastQuery != q || this.alwaysQuery){
12912 this.lastQuery = q;
12913 if(this.mode == 'local'){
12914 this.selectedIndex = -1;
12916 this.store.clearFilter();
12919 if(this.specialFilter){
12920 this.fireEvent('specialfilter', this);
12925 this.store.filter(this.displayField, q);
12928 this.store.fireEvent("datachanged", this.store);
12935 this.store.baseParams[this.queryParam] = q;
12937 var options = {params : this.getParams(q)};
12940 options.add = true;
12941 options.params.start = this.page * this.pageSize;
12944 this.store.load(options);
12947 * this code will make the page width larger, at the beginning, the list not align correctly,
12948 * we should expand the list on onLoad
12949 * so command out it
12954 this.selectedIndex = -1;
12959 this.loadNext = false;
12963 getParams : function(q){
12965 //p[this.queryParam] = q;
12969 p.limit = this.pageSize;
12975 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12977 collapse : function(){
12978 if(!this.isExpanded()){
12985 this.hasFocus = false;
12987 this.cancelBtn.hide();
12988 this.trigger.show();
12991 this.tickableInputEl().dom.value = '';
12992 this.tickableInputEl().blur();
12997 Roo.get(document).un('mousedown', this.collapseIf, this);
12998 Roo.get(document).un('mousewheel', this.collapseIf, this);
12999 if (!this.editable) {
13000 Roo.get(document).un('keydown', this.listKeyPress, this);
13002 this.fireEvent('collapse', this);
13006 collapseIf : function(e){
13007 var in_combo = e.within(this.el);
13008 var in_list = e.within(this.list);
13009 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13011 if (in_combo || in_list || is_list) {
13012 //e.stopPropagation();
13017 this.onTickableFooterButtonClick(e, false, false);
13025 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13027 expand : function(){
13029 if(this.isExpanded() || !this.hasFocus){
13033 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13034 this.list.setWidth(lw);
13041 this.restrictHeight();
13045 this.tickItems = Roo.apply([], this.item);
13048 this.cancelBtn.show();
13049 this.trigger.hide();
13052 this.tickableInputEl().focus();
13057 Roo.get(document).on('mousedown', this.collapseIf, this);
13058 Roo.get(document).on('mousewheel', this.collapseIf, this);
13059 if (!this.editable) {
13060 Roo.get(document).on('keydown', this.listKeyPress, this);
13063 this.fireEvent('expand', this);
13067 // Implements the default empty TriggerField.onTriggerClick function
13068 onTriggerClick : function(e)
13070 Roo.log('trigger click');
13072 if(this.disabled || !this.triggerList){
13077 this.loadNext = false;
13079 if(this.isExpanded()){
13081 if (!this.blockFocus) {
13082 this.inputEl().focus();
13086 this.hasFocus = true;
13087 if(this.triggerAction == 'all') {
13088 this.doQuery(this.allQuery, true);
13090 this.doQuery(this.getRawValue());
13092 if (!this.blockFocus) {
13093 this.inputEl().focus();
13098 onTickableTriggerClick : function(e)
13105 this.loadNext = false;
13106 this.hasFocus = true;
13108 if(this.triggerAction == 'all') {
13109 this.doQuery(this.allQuery, true);
13111 this.doQuery(this.getRawValue());
13115 onSearchFieldClick : function(e)
13117 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13118 this.onTickableFooterButtonClick(e, false, false);
13122 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13127 this.loadNext = false;
13128 this.hasFocus = true;
13130 if(this.triggerAction == 'all') {
13131 this.doQuery(this.allQuery, true);
13133 this.doQuery(this.getRawValue());
13137 listKeyPress : function(e)
13139 //Roo.log('listkeypress');
13140 // scroll to first matching element based on key pres..
13141 if (e.isSpecialKey()) {
13144 var k = String.fromCharCode(e.getKey()).toUpperCase();
13147 var csel = this.view.getSelectedNodes();
13148 var cselitem = false;
13150 var ix = this.view.indexOf(csel[0]);
13151 cselitem = this.store.getAt(ix);
13152 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13158 this.store.each(function(v) {
13160 // start at existing selection.
13161 if (cselitem.id == v.id) {
13167 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13168 match = this.store.indexOf(v);
13174 if (match === false) {
13175 return true; // no more action?
13178 this.view.select(match);
13179 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13180 sn.scrollIntoView(sn.dom.parentNode, false);
13183 onViewScroll : function(e, t){
13185 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){
13189 this.hasQuery = true;
13191 this.loading = this.list.select('.loading', true).first();
13193 if(this.loading === null){
13194 this.list.createChild({
13196 cls: 'loading select2-more-results select2-active',
13197 html: 'Loading more results...'
13200 this.loading = this.list.select('.loading', true).first();
13202 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13204 this.loading.hide();
13207 this.loading.show();
13212 this.loadNext = true;
13214 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13219 addItem : function(o)
13221 var dv = ''; // display value
13223 if (this.displayField) {
13224 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13226 // this is an error condition!!!
13227 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13234 var choice = this.choices.createChild({
13236 cls: 'select2-search-choice',
13245 cls: 'select2-search-choice-close',
13250 }, this.searchField);
13252 var close = choice.select('a.select2-search-choice-close', true).first();
13254 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13262 this.inputEl().dom.value = '';
13267 onRemoveItem : function(e, _self, o)
13269 e.preventDefault();
13271 this.lastItem = Roo.apply([], this.item);
13273 var index = this.item.indexOf(o.data) * 1;
13276 Roo.log('not this item?!');
13280 this.item.splice(index, 1);
13285 this.fireEvent('remove', this, e);
13291 syncValue : function()
13293 if(!this.item.length){
13300 Roo.each(this.item, function(i){
13301 if(_this.valueField){
13302 value.push(i[_this.valueField]);
13309 this.value = value.join(',');
13311 if(this.hiddenField){
13312 this.hiddenField.dom.value = this.value;
13315 this.store.fireEvent("datachanged", this.store);
13318 clearItem : function()
13320 if(!this.multiple){
13326 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13334 if(this.tickable && !Roo.isTouch){
13335 this.view.refresh();
13339 inputEl: function ()
13341 if(Roo.isTouch && this.mobileTouchView){
13342 return this.el.select('input.form-control',true).first();
13346 return this.searchField;
13349 return this.el.select('input.form-control',true).first();
13353 onTickableFooterButtonClick : function(e, btn, el)
13355 e.preventDefault();
13357 this.lastItem = Roo.apply([], this.item);
13359 if(btn && btn.name == 'cancel'){
13360 this.tickItems = Roo.apply([], this.item);
13369 Roo.each(this.tickItems, function(o){
13377 validate : function()
13379 var v = this.getRawValue();
13382 v = this.getValue();
13385 if(this.disabled || this.allowBlank || v.length){
13390 this.markInvalid();
13394 tickableInputEl : function()
13396 if(!this.tickable || !this.editable){
13397 return this.inputEl();
13400 return this.inputEl().select('.select2-search-field-input', true).first();
13404 getAutoCreateTouchView : function()
13409 cls: 'form-group' //input-group
13415 type : this.inputType,
13416 cls : 'form-control x-combo-noedit',
13417 autocomplete: 'new-password',
13418 placeholder : this.placeholder || '',
13423 input.name = this.name;
13427 input.cls += ' input-' + this.size;
13430 if (this.disabled) {
13431 input.disabled = true;
13442 inputblock.cls += ' input-group';
13444 inputblock.cn.unshift({
13446 cls : 'input-group-addon',
13451 if(this.removable && !this.multiple){
13452 inputblock.cls += ' roo-removable';
13454 inputblock.cn.push({
13457 cls : 'roo-combo-removable-btn close'
13461 if(this.hasFeedback && !this.allowBlank){
13463 inputblock.cls += ' has-feedback';
13465 inputblock.cn.push({
13467 cls: 'glyphicon form-control-feedback'
13474 inputblock.cls += (this.before) ? '' : ' input-group';
13476 inputblock.cn.push({
13478 cls : 'input-group-addon',
13489 cls: 'form-hidden-field'
13503 cls: 'form-hidden-field'
13507 cls: 'select2-choices',
13511 cls: 'select2-search-field',
13524 cls: 'select2-container input-group',
13531 combobox.cls += ' select2-container-multi';
13534 var align = this.labelAlign || this.parentLabelAlign();
13538 if(this.fieldLabel.length){
13540 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13541 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13546 cls : 'control-label ' + lw,
13547 html : this.fieldLabel
13559 var settings = this;
13561 ['xs','sm','md','lg'].map(function(size){
13562 if (settings[size]) {
13563 cfg.cls += ' col-' + size + '-' + settings[size];
13570 initTouchView : function()
13572 this.renderTouchView();
13574 this.touchViewEl.on('scroll', function(){
13575 this.el.dom.scrollTop = 0;
13578 this.originalValue = this.getValue();
13580 this.inputEl().on("click", this.showTouchView, this);
13582 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13583 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13585 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13587 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13588 this.store.on('load', this.onTouchViewLoad, this);
13589 this.store.on('loadexception', this.onTouchViewLoadException, this);
13591 if(this.hiddenName){
13593 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13595 this.hiddenField.dom.value =
13596 this.hiddenValue !== undefined ? this.hiddenValue :
13597 this.value !== undefined ? this.value : '';
13599 this.el.dom.removeAttribute('name');
13600 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13604 this.choices = this.el.select('ul.select2-choices', true).first();
13605 this.searchField = this.el.select('ul li.select2-search-field', true).first();
13608 if(this.removable && !this.multiple){
13609 var close = this.closeTriggerEl();
13611 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13612 close.on('click', this.removeBtnClick, this, close);
13616 * fix the bug in Safari iOS8
13618 this.inputEl().on("focus", function(e){
13619 document.activeElement.blur();
13627 renderTouchView : function()
13629 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13630 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13632 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13633 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13635 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13636 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13637 this.touchViewBodyEl.setStyle('overflow', 'auto');
13639 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13640 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13642 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13643 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13647 showTouchView : function()
13653 this.touchViewHeaderEl.hide();
13655 if(this.fieldLabel.length){
13656 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13657 this.touchViewHeaderEl.show();
13660 this.touchViewEl.show();
13662 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13663 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13665 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13667 if(this.fieldLabel.length){
13668 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13671 this.touchViewBodyEl.setHeight(bodyHeight);
13675 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13677 this.touchViewEl.addClass('in');
13680 this.doTouchViewQuery();
13684 hideTouchView : function()
13686 this.touchViewEl.removeClass('in');
13690 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13692 this.touchViewEl.setStyle('display', 'none');
13697 setTouchViewValue : function()
13704 Roo.each(this.tickItems, function(o){
13709 this.hideTouchView();
13712 doTouchViewQuery : function()
13721 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13725 if(!this.alwaysQuery || this.mode == 'local'){
13726 this.onTouchViewLoad();
13733 onTouchViewBeforeLoad : function(combo,opts)
13739 onTouchViewLoad : function()
13741 if(this.store.getCount() < 1){
13742 this.onTouchViewEmptyResults();
13746 this.clearTouchView();
13748 var rawValue = this.getRawValue();
13750 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13752 this.tickItems = [];
13754 this.store.data.each(function(d, rowIndex){
13755 var row = this.touchViewListGroup.createChild(template);
13757 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13760 html : d.data[this.displayField]
13763 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
13764 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
13768 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13769 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13772 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13773 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13774 this.tickItems.push(d.data);
13777 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13781 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13783 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13785 if(this.fieldLabel.length){
13786 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13789 var listHeight = this.touchViewListGroup.getHeight();
13793 if(firstChecked && listHeight > bodyHeight){
13794 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13799 onTouchViewLoadException : function()
13801 this.hideTouchView();
13804 onTouchViewEmptyResults : function()
13806 this.clearTouchView();
13808 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13810 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13814 clearTouchView : function()
13816 this.touchViewListGroup.dom.innerHTML = '';
13819 onTouchViewClick : function(e, el, o)
13821 e.preventDefault();
13824 var rowIndex = o.rowIndex;
13826 var r = this.store.getAt(rowIndex);
13828 if(!this.multiple){
13829 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13830 c.dom.removeAttribute('checked');
13833 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13835 this.setFromData(r.data);
13837 var close = this.closeTriggerEl();
13843 this.hideTouchView();
13845 this.fireEvent('select', this, r, rowIndex);
13850 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13851 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13852 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13856 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13857 this.addItem(r.data);
13858 this.tickItems.push(r.data);
13864 * @cfg {Boolean} grow
13868 * @cfg {Number} growMin
13872 * @cfg {Number} growMax
13881 Roo.apply(Roo.bootstrap.ComboBox, {
13885 cls: 'modal-header',
13907 cls: 'list-group-item',
13911 cls: 'roo-combobox-list-group-item-value'
13915 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13929 listItemCheckbox : {
13931 cls: 'list-group-item',
13935 cls: 'roo-combobox-list-group-item-value'
13939 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13955 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13960 cls: 'modal-footer',
13968 cls: 'col-xs-6 text-left',
13971 cls: 'btn btn-danger roo-touch-view-cancel',
13977 cls: 'col-xs-6 text-right',
13980 cls: 'btn btn-success roo-touch-view-ok',
13991 Roo.apply(Roo.bootstrap.ComboBox, {
13993 touchViewTemplate : {
13995 cls: 'modal fade roo-combobox-touch-view',
13999 cls: 'modal-dialog',
14000 style : 'position:fixed', // we have to fix position....
14004 cls: 'modal-content',
14006 Roo.bootstrap.ComboBox.header,
14007 Roo.bootstrap.ComboBox.body,
14008 Roo.bootstrap.ComboBox.footer
14017 * Ext JS Library 1.1.1
14018 * Copyright(c) 2006-2007, Ext JS, LLC.
14020 * Originally Released Under LGPL - original licence link has changed is not relivant.
14023 * <script type="text/javascript">
14028 * @extends Roo.util.Observable
14029 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14030 * This class also supports single and multi selection modes. <br>
14031 * Create a data model bound view:
14033 var store = new Roo.data.Store(...);
14035 var view = new Roo.View({
14037 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14039 singleSelect: true,
14040 selectedClass: "ydataview-selected",
14044 // listen for node click?
14045 view.on("click", function(vw, index, node, e){
14046 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14050 dataModel.load("foobar.xml");
14052 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14054 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14055 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14057 * Note: old style constructor is still suported (container, template, config)
14060 * Create a new View
14061 * @param {Object} config The config object
14064 Roo.View = function(config, depreciated_tpl, depreciated_config){
14066 this.parent = false;
14068 if (typeof(depreciated_tpl) == 'undefined') {
14069 // new way.. - universal constructor.
14070 Roo.apply(this, config);
14071 this.el = Roo.get(this.el);
14074 this.el = Roo.get(config);
14075 this.tpl = depreciated_tpl;
14076 Roo.apply(this, depreciated_config);
14078 this.wrapEl = this.el.wrap().wrap();
14079 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14082 if(typeof(this.tpl) == "string"){
14083 this.tpl = new Roo.Template(this.tpl);
14085 // support xtype ctors..
14086 this.tpl = new Roo.factory(this.tpl, Roo);
14090 this.tpl.compile();
14095 * @event beforeclick
14096 * Fires before a click is processed. Returns false to cancel the default action.
14097 * @param {Roo.View} this
14098 * @param {Number} index The index of the target node
14099 * @param {HTMLElement} node The target node
14100 * @param {Roo.EventObject} e The raw event object
14102 "beforeclick" : true,
14105 * Fires when a template node is clicked.
14106 * @param {Roo.View} this
14107 * @param {Number} index The index of the target node
14108 * @param {HTMLElement} node The target node
14109 * @param {Roo.EventObject} e The raw event object
14114 * Fires when a template node is double clicked.
14115 * @param {Roo.View} this
14116 * @param {Number} index The index of the target node
14117 * @param {HTMLElement} node The target node
14118 * @param {Roo.EventObject} e The raw event object
14122 * @event contextmenu
14123 * Fires when a template node is right clicked.
14124 * @param {Roo.View} this
14125 * @param {Number} index The index of the target node
14126 * @param {HTMLElement} node The target node
14127 * @param {Roo.EventObject} e The raw event object
14129 "contextmenu" : true,
14131 * @event selectionchange
14132 * Fires when the selected nodes change.
14133 * @param {Roo.View} this
14134 * @param {Array} selections Array of the selected nodes
14136 "selectionchange" : true,
14139 * @event beforeselect
14140 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14141 * @param {Roo.View} this
14142 * @param {HTMLElement} node The node to be selected
14143 * @param {Array} selections Array of currently selected nodes
14145 "beforeselect" : true,
14147 * @event preparedata
14148 * Fires on every row to render, to allow you to change the data.
14149 * @param {Roo.View} this
14150 * @param {Object} data to be rendered (change this)
14152 "preparedata" : true
14160 "click": this.onClick,
14161 "dblclick": this.onDblClick,
14162 "contextmenu": this.onContextMenu,
14166 this.selections = [];
14168 this.cmp = new Roo.CompositeElementLite([]);
14170 this.store = Roo.factory(this.store, Roo.data);
14171 this.setStore(this.store, true);
14174 if ( this.footer && this.footer.xtype) {
14176 var fctr = this.wrapEl.appendChild(document.createElement("div"));
14178 this.footer.dataSource = this.store;
14179 this.footer.container = fctr;
14180 this.footer = Roo.factory(this.footer, Roo);
14181 fctr.insertFirst(this.el);
14183 // this is a bit insane - as the paging toolbar seems to detach the el..
14184 // dom.parentNode.parentNode.parentNode
14185 // they get detached?
14189 Roo.View.superclass.constructor.call(this);
14194 Roo.extend(Roo.View, Roo.util.Observable, {
14197 * @cfg {Roo.data.Store} store Data store to load data from.
14202 * @cfg {String|Roo.Element} el The container element.
14207 * @cfg {String|Roo.Template} tpl The template used by this View
14211 * @cfg {String} dataName the named area of the template to use as the data area
14212 * Works with domtemplates roo-name="name"
14216 * @cfg {String} selectedClass The css class to add to selected nodes
14218 selectedClass : "x-view-selected",
14220 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14225 * @cfg {String} text to display on mask (default Loading)
14229 * @cfg {Boolean} multiSelect Allow multiple selection
14231 multiSelect : false,
14233 * @cfg {Boolean} singleSelect Allow single selection
14235 singleSelect: false,
14238 * @cfg {Boolean} toggleSelect - selecting
14240 toggleSelect : false,
14243 * @cfg {Boolean} tickable - selecting
14248 * Returns the element this view is bound to.
14249 * @return {Roo.Element}
14251 getEl : function(){
14252 return this.wrapEl;
14258 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14260 refresh : function(){
14261 //Roo.log('refresh');
14264 // if we are using something like 'domtemplate', then
14265 // the what gets used is:
14266 // t.applySubtemplate(NAME, data, wrapping data..)
14267 // the outer template then get' applied with
14268 // the store 'extra data'
14269 // and the body get's added to the
14270 // roo-name="data" node?
14271 // <span class='roo-tpl-{name}'></span> ?????
14275 this.clearSelections();
14276 this.el.update("");
14278 var records = this.store.getRange();
14279 if(records.length < 1) {
14281 // is this valid?? = should it render a template??
14283 this.el.update(this.emptyText);
14287 if (this.dataName) {
14288 this.el.update(t.apply(this.store.meta)); //????
14289 el = this.el.child('.roo-tpl-' + this.dataName);
14292 for(var i = 0, len = records.length; i < len; i++){
14293 var data = this.prepareData(records[i].data, i, records[i]);
14294 this.fireEvent("preparedata", this, data, i, records[i]);
14296 var d = Roo.apply({}, data);
14299 Roo.apply(d, {'roo-id' : Roo.id()});
14303 Roo.each(this.parent.item, function(item){
14304 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14307 Roo.apply(d, {'roo-data-checked' : 'checked'});
14311 html[html.length] = Roo.util.Format.trim(
14313 t.applySubtemplate(this.dataName, d, this.store.meta) :
14320 el.update(html.join(""));
14321 this.nodes = el.dom.childNodes;
14322 this.updateIndexes(0);
14327 * Function to override to reformat the data that is sent to
14328 * the template for each node.
14329 * DEPRICATED - use the preparedata event handler.
14330 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14331 * a JSON object for an UpdateManager bound view).
14333 prepareData : function(data, index, record)
14335 this.fireEvent("preparedata", this, data, index, record);
14339 onUpdate : function(ds, record){
14340 // Roo.log('on update');
14341 this.clearSelections();
14342 var index = this.store.indexOf(record);
14343 var n = this.nodes[index];
14344 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14345 n.parentNode.removeChild(n);
14346 this.updateIndexes(index, index);
14352 onAdd : function(ds, records, index)
14354 //Roo.log(['on Add', ds, records, index] );
14355 this.clearSelections();
14356 if(this.nodes.length == 0){
14360 var n = this.nodes[index];
14361 for(var i = 0, len = records.length; i < len; i++){
14362 var d = this.prepareData(records[i].data, i, records[i]);
14364 this.tpl.insertBefore(n, d);
14367 this.tpl.append(this.el, d);
14370 this.updateIndexes(index);
14373 onRemove : function(ds, record, index){
14374 // Roo.log('onRemove');
14375 this.clearSelections();
14376 var el = this.dataName ?
14377 this.el.child('.roo-tpl-' + this.dataName) :
14380 el.dom.removeChild(this.nodes[index]);
14381 this.updateIndexes(index);
14385 * Refresh an individual node.
14386 * @param {Number} index
14388 refreshNode : function(index){
14389 this.onUpdate(this.store, this.store.getAt(index));
14392 updateIndexes : function(startIndex, endIndex){
14393 var ns = this.nodes;
14394 startIndex = startIndex || 0;
14395 endIndex = endIndex || ns.length - 1;
14396 for(var i = startIndex; i <= endIndex; i++){
14397 ns[i].nodeIndex = i;
14402 * Changes the data store this view uses and refresh the view.
14403 * @param {Store} store
14405 setStore : function(store, initial){
14406 if(!initial && this.store){
14407 this.store.un("datachanged", this.refresh);
14408 this.store.un("add", this.onAdd);
14409 this.store.un("remove", this.onRemove);
14410 this.store.un("update", this.onUpdate);
14411 this.store.un("clear", this.refresh);
14412 this.store.un("beforeload", this.onBeforeLoad);
14413 this.store.un("load", this.onLoad);
14414 this.store.un("loadexception", this.onLoad);
14418 store.on("datachanged", this.refresh, this);
14419 store.on("add", this.onAdd, this);
14420 store.on("remove", this.onRemove, this);
14421 store.on("update", this.onUpdate, this);
14422 store.on("clear", this.refresh, this);
14423 store.on("beforeload", this.onBeforeLoad, this);
14424 store.on("load", this.onLoad, this);
14425 store.on("loadexception", this.onLoad, this);
14433 * onbeforeLoad - masks the loading area.
14436 onBeforeLoad : function(store,opts)
14438 //Roo.log('onBeforeLoad');
14440 this.el.update("");
14442 this.el.mask(this.mask ? this.mask : "Loading" );
14444 onLoad : function ()
14451 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14452 * @param {HTMLElement} node
14453 * @return {HTMLElement} The template node
14455 findItemFromChild : function(node){
14456 var el = this.dataName ?
14457 this.el.child('.roo-tpl-' + this.dataName,true) :
14460 if(!node || node.parentNode == el){
14463 var p = node.parentNode;
14464 while(p && p != el){
14465 if(p.parentNode == el){
14474 onClick : function(e){
14475 var item = this.findItemFromChild(e.getTarget());
14477 var index = this.indexOf(item);
14478 if(this.onItemClick(item, index, e) !== false){
14479 this.fireEvent("click", this, index, item, e);
14482 this.clearSelections();
14487 onContextMenu : function(e){
14488 var item = this.findItemFromChild(e.getTarget());
14490 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14495 onDblClick : function(e){
14496 var item = this.findItemFromChild(e.getTarget());
14498 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14502 onItemClick : function(item, index, e)
14504 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14507 if (this.toggleSelect) {
14508 var m = this.isSelected(item) ? 'unselect' : 'select';
14511 _t[m](item, true, false);
14514 if(this.multiSelect || this.singleSelect){
14515 if(this.multiSelect && e.shiftKey && this.lastSelection){
14516 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14518 this.select(item, this.multiSelect && e.ctrlKey);
14519 this.lastSelection = item;
14522 if(!this.tickable){
14523 e.preventDefault();
14531 * Get the number of selected nodes.
14534 getSelectionCount : function(){
14535 return this.selections.length;
14539 * Get the currently selected nodes.
14540 * @return {Array} An array of HTMLElements
14542 getSelectedNodes : function(){
14543 return this.selections;
14547 * Get the indexes of the selected nodes.
14550 getSelectedIndexes : function(){
14551 var indexes = [], s = this.selections;
14552 for(var i = 0, len = s.length; i < len; i++){
14553 indexes.push(s[i].nodeIndex);
14559 * Clear all selections
14560 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14562 clearSelections : function(suppressEvent){
14563 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14564 this.cmp.elements = this.selections;
14565 this.cmp.removeClass(this.selectedClass);
14566 this.selections = [];
14567 if(!suppressEvent){
14568 this.fireEvent("selectionchange", this, this.selections);
14574 * Returns true if the passed node is selected
14575 * @param {HTMLElement/Number} node The node or node index
14576 * @return {Boolean}
14578 isSelected : function(node){
14579 var s = this.selections;
14583 node = this.getNode(node);
14584 return s.indexOf(node) !== -1;
14589 * @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
14590 * @param {Boolean} keepExisting (optional) true to keep existing selections
14591 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14593 select : function(nodeInfo, keepExisting, suppressEvent){
14594 if(nodeInfo instanceof Array){
14596 this.clearSelections(true);
14598 for(var i = 0, len = nodeInfo.length; i < len; i++){
14599 this.select(nodeInfo[i], true, true);
14603 var node = this.getNode(nodeInfo);
14604 if(!node || this.isSelected(node)){
14605 return; // already selected.
14608 this.clearSelections(true);
14611 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14612 Roo.fly(node).addClass(this.selectedClass);
14613 this.selections.push(node);
14614 if(!suppressEvent){
14615 this.fireEvent("selectionchange", this, this.selections);
14623 * @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
14624 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14625 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14627 unselect : function(nodeInfo, keepExisting, suppressEvent)
14629 if(nodeInfo instanceof Array){
14630 Roo.each(this.selections, function(s) {
14631 this.unselect(s, nodeInfo);
14635 var node = this.getNode(nodeInfo);
14636 if(!node || !this.isSelected(node)){
14637 //Roo.log("not selected");
14638 return; // not selected.
14642 Roo.each(this.selections, function(s) {
14644 Roo.fly(node).removeClass(this.selectedClass);
14651 this.selections= ns;
14652 this.fireEvent("selectionchange", this, this.selections);
14656 * Gets a template node.
14657 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14658 * @return {HTMLElement} The node or null if it wasn't found
14660 getNode : function(nodeInfo){
14661 if(typeof nodeInfo == "string"){
14662 return document.getElementById(nodeInfo);
14663 }else if(typeof nodeInfo == "number"){
14664 return this.nodes[nodeInfo];
14670 * Gets a range template nodes.
14671 * @param {Number} startIndex
14672 * @param {Number} endIndex
14673 * @return {Array} An array of nodes
14675 getNodes : function(start, end){
14676 var ns = this.nodes;
14677 start = start || 0;
14678 end = typeof end == "undefined" ? ns.length - 1 : end;
14681 for(var i = start; i <= end; i++){
14685 for(var i = start; i >= end; i--){
14693 * Finds the index of the passed node
14694 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14695 * @return {Number} The index of the node or -1
14697 indexOf : function(node){
14698 node = this.getNode(node);
14699 if(typeof node.nodeIndex == "number"){
14700 return node.nodeIndex;
14702 var ns = this.nodes;
14703 for(var i = 0, len = ns.length; i < len; i++){
14714 * based on jquery fullcalendar
14718 Roo.bootstrap = Roo.bootstrap || {};
14720 * @class Roo.bootstrap.Calendar
14721 * @extends Roo.bootstrap.Component
14722 * Bootstrap Calendar class
14723 * @cfg {Boolean} loadMask (true|false) default false
14724 * @cfg {Object} header generate the user specific header of the calendar, default false
14727 * Create a new Container
14728 * @param {Object} config The config object
14733 Roo.bootstrap.Calendar = function(config){
14734 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14738 * Fires when a date is selected
14739 * @param {DatePicker} this
14740 * @param {Date} date The selected date
14744 * @event monthchange
14745 * Fires when the displayed month changes
14746 * @param {DatePicker} this
14747 * @param {Date} date The selected month
14749 'monthchange': true,
14751 * @event evententer
14752 * Fires when mouse over an event
14753 * @param {Calendar} this
14754 * @param {event} Event
14756 'evententer': true,
14758 * @event eventleave
14759 * Fires when the mouse leaves an
14760 * @param {Calendar} this
14763 'eventleave': true,
14765 * @event eventclick
14766 * Fires when the mouse click an
14767 * @param {Calendar} this
14776 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
14779 * @cfg {Number} startDay
14780 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14788 getAutoCreate : function(){
14791 var fc_button = function(name, corner, style, content ) {
14792 return Roo.apply({},{
14794 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
14796 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14799 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14810 style : 'width:100%',
14817 cls : 'fc-header-left',
14819 fc_button('prev', 'left', 'arrow', '‹' ),
14820 fc_button('next', 'right', 'arrow', '›' ),
14821 { tag: 'span', cls: 'fc-header-space' },
14822 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
14830 cls : 'fc-header-center',
14834 cls: 'fc-header-title',
14837 html : 'month / year'
14845 cls : 'fc-header-right',
14847 /* fc_button('month', 'left', '', 'month' ),
14848 fc_button('week', '', '', 'week' ),
14849 fc_button('day', 'right', '', 'day' )
14861 header = this.header;
14864 var cal_heads = function() {
14866 // fixme - handle this.
14868 for (var i =0; i < Date.dayNames.length; i++) {
14869 var d = Date.dayNames[i];
14872 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14873 html : d.substring(0,3)
14877 ret[0].cls += ' fc-first';
14878 ret[6].cls += ' fc-last';
14881 var cal_cell = function(n) {
14884 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14889 cls: 'fc-day-number',
14893 cls: 'fc-day-content',
14897 style: 'position: relative;' // height: 17px;
14909 var cal_rows = function() {
14912 for (var r = 0; r < 6; r++) {
14919 for (var i =0; i < Date.dayNames.length; i++) {
14920 var d = Date.dayNames[i];
14921 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14924 row.cn[0].cls+=' fc-first';
14925 row.cn[0].cn[0].style = 'min-height:90px';
14926 row.cn[6].cls+=' fc-last';
14930 ret[0].cls += ' fc-first';
14931 ret[4].cls += ' fc-prev-last';
14932 ret[5].cls += ' fc-last';
14939 cls: 'fc-border-separate',
14940 style : 'width:100%',
14948 cls : 'fc-first fc-last',
14966 cls : 'fc-content',
14967 style : "position: relative;",
14970 cls : 'fc-view fc-view-month fc-grid',
14971 style : 'position: relative',
14972 unselectable : 'on',
14975 cls : 'fc-event-container',
14976 style : 'position:absolute;z-index:8;top:0;left:0;'
14994 initEvents : function()
14997 throw "can not find store for calendar";
15003 style: "text-align:center",
15007 style: "background-color:white;width:50%;margin:250 auto",
15011 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15022 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15024 var size = this.el.select('.fc-content', true).first().getSize();
15025 this.maskEl.setSize(size.width, size.height);
15026 this.maskEl.enableDisplayMode("block");
15027 if(!this.loadMask){
15028 this.maskEl.hide();
15031 this.store = Roo.factory(this.store, Roo.data);
15032 this.store.on('load', this.onLoad, this);
15033 this.store.on('beforeload', this.onBeforeLoad, this);
15037 this.cells = this.el.select('.fc-day',true);
15038 //Roo.log(this.cells);
15039 this.textNodes = this.el.query('.fc-day-number');
15040 this.cells.addClassOnOver('fc-state-hover');
15042 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15043 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15044 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15045 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15047 this.on('monthchange', this.onMonthChange, this);
15049 this.update(new Date().clearTime());
15052 resize : function() {
15053 var sz = this.el.getSize();
15055 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15056 this.el.select('.fc-day-content div',true).setHeight(34);
15061 showPrevMonth : function(e){
15062 this.update(this.activeDate.add("mo", -1));
15064 showToday : function(e){
15065 this.update(new Date().clearTime());
15068 showNextMonth : function(e){
15069 this.update(this.activeDate.add("mo", 1));
15073 showPrevYear : function(){
15074 this.update(this.activeDate.add("y", -1));
15078 showNextYear : function(){
15079 this.update(this.activeDate.add("y", 1));
15084 update : function(date)
15086 var vd = this.activeDate;
15087 this.activeDate = date;
15088 // if(vd && this.el){
15089 // var t = date.getTime();
15090 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15091 // Roo.log('using add remove');
15093 // this.fireEvent('monthchange', this, date);
15095 // this.cells.removeClass("fc-state-highlight");
15096 // this.cells.each(function(c){
15097 // if(c.dateValue == t){
15098 // c.addClass("fc-state-highlight");
15099 // setTimeout(function(){
15100 // try{c.dom.firstChild.focus();}catch(e){}
15110 var days = date.getDaysInMonth();
15112 var firstOfMonth = date.getFirstDateOfMonth();
15113 var startingPos = firstOfMonth.getDay()-this.startDay;
15115 if(startingPos < this.startDay){
15119 var pm = date.add(Date.MONTH, -1);
15120 var prevStart = pm.getDaysInMonth()-startingPos;
15122 this.cells = this.el.select('.fc-day',true);
15123 this.textNodes = this.el.query('.fc-day-number');
15124 this.cells.addClassOnOver('fc-state-hover');
15126 var cells = this.cells.elements;
15127 var textEls = this.textNodes;
15129 Roo.each(cells, function(cell){
15130 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15133 days += startingPos;
15135 // convert everything to numbers so it's fast
15136 var day = 86400000;
15137 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15140 //Roo.log(prevStart);
15142 var today = new Date().clearTime().getTime();
15143 var sel = date.clearTime().getTime();
15144 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15145 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15146 var ddMatch = this.disabledDatesRE;
15147 var ddText = this.disabledDatesText;
15148 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15149 var ddaysText = this.disabledDaysText;
15150 var format = this.format;
15152 var setCellClass = function(cal, cell){
15156 //Roo.log('set Cell Class');
15158 var t = d.getTime();
15162 cell.dateValue = t;
15164 cell.className += " fc-today";
15165 cell.className += " fc-state-highlight";
15166 cell.title = cal.todayText;
15169 // disable highlight in other month..
15170 //cell.className += " fc-state-highlight";
15175 cell.className = " fc-state-disabled";
15176 cell.title = cal.minText;
15180 cell.className = " fc-state-disabled";
15181 cell.title = cal.maxText;
15185 if(ddays.indexOf(d.getDay()) != -1){
15186 cell.title = ddaysText;
15187 cell.className = " fc-state-disabled";
15190 if(ddMatch && format){
15191 var fvalue = d.dateFormat(format);
15192 if(ddMatch.test(fvalue)){
15193 cell.title = ddText.replace("%0", fvalue);
15194 cell.className = " fc-state-disabled";
15198 if (!cell.initialClassName) {
15199 cell.initialClassName = cell.dom.className;
15202 cell.dom.className = cell.initialClassName + ' ' + cell.className;
15207 for(; i < startingPos; i++) {
15208 textEls[i].innerHTML = (++prevStart);
15209 d.setDate(d.getDate()+1);
15211 cells[i].className = "fc-past fc-other-month";
15212 setCellClass(this, cells[i]);
15217 for(; i < days; i++){
15218 intDay = i - startingPos + 1;
15219 textEls[i].innerHTML = (intDay);
15220 d.setDate(d.getDate()+1);
15222 cells[i].className = ''; // "x-date-active";
15223 setCellClass(this, cells[i]);
15227 for(; i < 42; i++) {
15228 textEls[i].innerHTML = (++extraDays);
15229 d.setDate(d.getDate()+1);
15231 cells[i].className = "fc-future fc-other-month";
15232 setCellClass(this, cells[i]);
15235 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15237 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15239 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15240 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15242 if(totalRows != 6){
15243 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15244 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15247 this.fireEvent('monthchange', this, date);
15251 if(!this.internalRender){
15252 var main = this.el.dom.firstChild;
15253 var w = main.offsetWidth;
15254 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15255 Roo.fly(main).setWidth(w);
15256 this.internalRender = true;
15257 // opera does not respect the auto grow header center column
15258 // then, after it gets a width opera refuses to recalculate
15259 // without a second pass
15260 if(Roo.isOpera && !this.secondPass){
15261 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15262 this.secondPass = true;
15263 this.update.defer(10, this, [date]);
15270 findCell : function(dt) {
15271 dt = dt.clearTime().getTime();
15273 this.cells.each(function(c){
15274 //Roo.log("check " +c.dateValue + '?=' + dt);
15275 if(c.dateValue == dt){
15285 findCells : function(ev) {
15286 var s = ev.start.clone().clearTime().getTime();
15288 var e= ev.end.clone().clearTime().getTime();
15291 this.cells.each(function(c){
15292 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15294 if(c.dateValue > e){
15297 if(c.dateValue < s){
15306 // findBestRow: function(cells)
15310 // for (var i =0 ; i < cells.length;i++) {
15311 // ret = Math.max(cells[i].rows || 0,ret);
15318 addItem : function(ev)
15320 // look for vertical location slot in
15321 var cells = this.findCells(ev);
15323 // ev.row = this.findBestRow(cells);
15325 // work out the location.
15329 for(var i =0; i < cells.length; i++) {
15331 cells[i].row = cells[0].row;
15334 cells[i].row = cells[i].row + 1;
15344 if (crow.start.getY() == cells[i].getY()) {
15346 crow.end = cells[i];
15363 cells[0].events.push(ev);
15365 this.calevents.push(ev);
15368 clearEvents: function() {
15370 if(!this.calevents){
15374 Roo.each(this.cells.elements, function(c){
15380 Roo.each(this.calevents, function(e) {
15381 Roo.each(e.els, function(el) {
15382 el.un('mouseenter' ,this.onEventEnter, this);
15383 el.un('mouseleave' ,this.onEventLeave, this);
15388 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15394 renderEvents: function()
15398 this.cells.each(function(c) {
15407 if(c.row != c.events.length){
15408 r = 4 - (4 - (c.row - c.events.length));
15411 c.events = ev.slice(0, r);
15412 c.more = ev.slice(r);
15414 if(c.more.length && c.more.length == 1){
15415 c.events.push(c.more.pop());
15418 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15422 this.cells.each(function(c) {
15424 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15427 for (var e = 0; e < c.events.length; e++){
15428 var ev = c.events[e];
15429 var rows = ev.rows;
15431 for(var i = 0; i < rows.length; i++) {
15433 // how many rows should it span..
15436 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15437 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15439 unselectable : "on",
15442 cls: 'fc-event-inner',
15446 // cls: 'fc-event-time',
15447 // html : cells.length > 1 ? '' : ev.time
15451 cls: 'fc-event-title',
15452 html : String.format('{0}', ev.title)
15459 cls: 'ui-resizable-handle ui-resizable-e',
15460 html : '  '
15467 cfg.cls += ' fc-event-start';
15469 if ((i+1) == rows.length) {
15470 cfg.cls += ' fc-event-end';
15473 var ctr = _this.el.select('.fc-event-container',true).first();
15474 var cg = ctr.createChild(cfg);
15476 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15477 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15479 var r = (c.more.length) ? 1 : 0;
15480 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15481 cg.setWidth(ebox.right - sbox.x -2);
15483 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15484 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15485 cg.on('click', _this.onEventClick, _this, ev);
15496 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15497 style : 'position: absolute',
15498 unselectable : "on",
15501 cls: 'fc-event-inner',
15505 cls: 'fc-event-title',
15513 cls: 'ui-resizable-handle ui-resizable-e',
15514 html : '  '
15520 var ctr = _this.el.select('.fc-event-container',true).first();
15521 var cg = ctr.createChild(cfg);
15523 var sbox = c.select('.fc-day-content',true).first().getBox();
15524 var ebox = c.select('.fc-day-content',true).first().getBox();
15526 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15527 cg.setWidth(ebox.right - sbox.x -2);
15529 cg.on('click', _this.onMoreEventClick, _this, c.more);
15539 onEventEnter: function (e, el,event,d) {
15540 this.fireEvent('evententer', this, el, event);
15543 onEventLeave: function (e, el,event,d) {
15544 this.fireEvent('eventleave', this, el, event);
15547 onEventClick: function (e, el,event,d) {
15548 this.fireEvent('eventclick', this, el, event);
15551 onMonthChange: function () {
15555 onMoreEventClick: function(e, el, more)
15559 this.calpopover.placement = 'right';
15560 this.calpopover.setTitle('More');
15562 this.calpopover.setContent('');
15564 var ctr = this.calpopover.el.select('.popover-content', true).first();
15566 Roo.each(more, function(m){
15568 cls : 'fc-event-hori fc-event-draggable',
15571 var cg = ctr.createChild(cfg);
15573 cg.on('click', _this.onEventClick, _this, m);
15576 this.calpopover.show(el);
15581 onLoad: function ()
15583 this.calevents = [];
15586 if(this.store.getCount() > 0){
15587 this.store.data.each(function(d){
15590 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15591 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15592 time : d.data.start_time,
15593 title : d.data.title,
15594 description : d.data.description,
15595 venue : d.data.venue
15600 this.renderEvents();
15602 if(this.calevents.length && this.loadMask){
15603 this.maskEl.hide();
15607 onBeforeLoad: function()
15609 this.clearEvents();
15611 this.maskEl.show();
15625 * @class Roo.bootstrap.Popover
15626 * @extends Roo.bootstrap.Component
15627 * Bootstrap Popover class
15628 * @cfg {String} html contents of the popover (or false to use children..)
15629 * @cfg {String} title of popover (or false to hide)
15630 * @cfg {String} placement how it is placed
15631 * @cfg {String} trigger click || hover (or false to trigger manually)
15632 * @cfg {String} over what (parent or false to trigger manually.)
15633 * @cfg {Number} delay - delay before showing
15636 * Create a new Popover
15637 * @param {Object} config The config object
15640 Roo.bootstrap.Popover = function(config){
15641 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15644 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
15646 title: 'Fill in a title',
15649 placement : 'right',
15650 trigger : 'hover', // hover
15656 can_build_overlaid : false,
15658 getChildContainer : function()
15660 return this.el.select('.popover-content',true).first();
15663 getAutoCreate : function(){
15666 cls : 'popover roo-dynamic',
15667 style: 'display:block',
15673 cls : 'popover-inner',
15677 cls: 'popover-title',
15681 cls : 'popover-content',
15692 setTitle: function(str)
15695 this.el.select('.popover-title',true).first().dom.innerHTML = str;
15697 setContent: function(str)
15700 this.el.select('.popover-content',true).first().dom.innerHTML = str;
15702 // as it get's added to the bottom of the page.
15703 onRender : function(ct, position)
15705 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15707 var cfg = Roo.apply({}, this.getAutoCreate());
15711 cfg.cls += ' ' + this.cls;
15714 cfg.style = this.style;
15716 //Roo.log("adding to ");
15717 this.el = Roo.get(document.body).createChild(cfg, position);
15718 // Roo.log(this.el);
15723 initEvents : function()
15725 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15726 this.el.enableDisplayMode('block');
15728 if (this.over === false) {
15731 if (this.triggers === false) {
15734 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15735 var triggers = this.trigger ? this.trigger.split(' ') : [];
15736 Roo.each(triggers, function(trigger) {
15738 if (trigger == 'click') {
15739 on_el.on('click', this.toggle, this);
15740 } else if (trigger != 'manual') {
15741 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
15742 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15744 on_el.on(eventIn ,this.enter, this);
15745 on_el.on(eventOut, this.leave, this);
15756 toggle : function () {
15757 this.hoverState == 'in' ? this.leave() : this.enter();
15760 enter : function () {
15763 clearTimeout(this.timeout);
15765 this.hoverState = 'in';
15767 if (!this.delay || !this.delay.show) {
15772 this.timeout = setTimeout(function () {
15773 if (_t.hoverState == 'in') {
15776 }, this.delay.show)
15778 leave : function() {
15779 clearTimeout(this.timeout);
15781 this.hoverState = 'out';
15783 if (!this.delay || !this.delay.hide) {
15788 this.timeout = setTimeout(function () {
15789 if (_t.hoverState == 'out') {
15792 }, this.delay.hide)
15795 show : function (on_el)
15798 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15801 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15802 if (this.html !== false) {
15803 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15805 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15806 if (!this.title.length) {
15807 this.el.select('.popover-title',true).hide();
15810 var placement = typeof this.placement == 'function' ?
15811 this.placement.call(this, this.el, on_el) :
15814 var autoToken = /\s?auto?\s?/i;
15815 var autoPlace = autoToken.test(placement);
15817 placement = placement.replace(autoToken, '') || 'top';
15821 //this.el.setXY([0,0]);
15823 this.el.dom.style.display='block';
15824 this.el.addClass(placement);
15826 //this.el.appendTo(on_el);
15828 var p = this.getPosition();
15829 var box = this.el.getBox();
15834 var align = Roo.bootstrap.Popover.alignment[placement];
15835 this.el.alignTo(on_el, align[0],align[1]);
15836 //var arrow = this.el.select('.arrow',true).first();
15837 //arrow.set(align[2],
15839 this.el.addClass('in');
15842 if (this.el.hasClass('fade')) {
15849 this.el.setXY([0,0]);
15850 this.el.removeClass('in');
15852 this.hoverState = null;
15858 Roo.bootstrap.Popover.alignment = {
15859 'left' : ['r-l', [-10,0], 'right'],
15860 'right' : ['l-r', [10,0], 'left'],
15861 'bottom' : ['t-b', [0,10], 'top'],
15862 'top' : [ 'b-t', [0,-10], 'bottom']
15873 * @class Roo.bootstrap.Progress
15874 * @extends Roo.bootstrap.Component
15875 * Bootstrap Progress class
15876 * @cfg {Boolean} striped striped of the progress bar
15877 * @cfg {Boolean} active animated of the progress bar
15881 * Create a new Progress
15882 * @param {Object} config The config object
15885 Roo.bootstrap.Progress = function(config){
15886 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15889 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
15894 getAutoCreate : function(){
15902 cfg.cls += ' progress-striped';
15906 cfg.cls += ' active';
15925 * @class Roo.bootstrap.ProgressBar
15926 * @extends Roo.bootstrap.Component
15927 * Bootstrap ProgressBar class
15928 * @cfg {Number} aria_valuenow aria-value now
15929 * @cfg {Number} aria_valuemin aria-value min
15930 * @cfg {Number} aria_valuemax aria-value max
15931 * @cfg {String} label label for the progress bar
15932 * @cfg {String} panel (success | info | warning | danger )
15933 * @cfg {String} role role of the progress bar
15934 * @cfg {String} sr_only text
15938 * Create a new ProgressBar
15939 * @param {Object} config The config object
15942 Roo.bootstrap.ProgressBar = function(config){
15943 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15946 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
15950 aria_valuemax : 100,
15956 getAutoCreate : function()
15961 cls: 'progress-bar',
15962 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15974 cfg.role = this.role;
15977 if(this.aria_valuenow){
15978 cfg['aria-valuenow'] = this.aria_valuenow;
15981 if(this.aria_valuemin){
15982 cfg['aria-valuemin'] = this.aria_valuemin;
15985 if(this.aria_valuemax){
15986 cfg['aria-valuemax'] = this.aria_valuemax;
15989 if(this.label && !this.sr_only){
15990 cfg.html = this.label;
15994 cfg.cls += ' progress-bar-' + this.panel;
16000 update : function(aria_valuenow)
16002 this.aria_valuenow = aria_valuenow;
16004 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16019 * @class Roo.bootstrap.TabGroup
16020 * @extends Roo.bootstrap.Column
16021 * Bootstrap Column class
16022 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16023 * @cfg {Boolean} carousel true to make the group behave like a carousel
16024 * @cfg {Boolean} bullets show bullets for the panels
16025 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16026 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16027 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16030 * Create a new TabGroup
16031 * @param {Object} config The config object
16034 Roo.bootstrap.TabGroup = function(config){
16035 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16037 this.navId = Roo.id();
16040 Roo.bootstrap.TabGroup.register(this);
16044 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16047 transition : false,
16052 slideOnTouch : false,
16054 getAutoCreate : function()
16056 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16058 cfg.cls += ' tab-content';
16060 if (this.carousel) {
16061 cfg.cls += ' carousel slide';
16064 cls : 'carousel-inner'
16067 if(this.bullets && !Roo.isTouch){
16070 cls : 'carousel-bullets',
16074 if(this.bullets_cls){
16075 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16078 for (var i = 0; i < this.bullets; i++){
16080 cls : 'bullet bullet-' + i
16088 cfg.cn[0].cn = bullets;
16095 initEvents: function()
16097 if(Roo.isTouch && this.slideOnTouch){
16098 this.el.on("touchstart", this.onTouchStart, this);
16101 if(this.autoslide){
16104 this.slideFn = window.setInterval(function() {
16105 _this.showPanelNext();
16111 onTouchStart : function(e, el, o)
16113 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16117 this.showPanelNext();
16120 getChildContainer : function()
16122 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16126 * register a Navigation item
16127 * @param {Roo.bootstrap.NavItem} the navitem to add
16129 register : function(item)
16131 this.tabs.push( item);
16132 item.navId = this.navId; // not really needed..
16137 getActivePanel : function()
16140 Roo.each(this.tabs, function(t) {
16150 getPanelByName : function(n)
16153 Roo.each(this.tabs, function(t) {
16154 if (t.tabId == n) {
16162 indexOfPanel : function(p)
16165 Roo.each(this.tabs, function(t,i) {
16166 if (t.tabId == p.tabId) {
16175 * show a specific panel
16176 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16177 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16179 showPanel : function (pan)
16181 if(this.transition || typeof(pan) == 'undefined'){
16182 Roo.log("waiting for the transitionend");
16186 if (typeof(pan) == 'number') {
16187 pan = this.tabs[pan];
16190 if (typeof(pan) == 'string') {
16191 pan = this.getPanelByName(pan);
16194 var cur = this.getActivePanel();
16197 Roo.log('pan or acitve pan is undefined');
16201 if (pan.tabId == this.getActivePanel().tabId) {
16205 if (false === cur.fireEvent('beforedeactivate')) {
16209 if(this.bullets > 0 && !Roo.isTouch){
16210 this.setActiveBullet(this.indexOfPanel(pan));
16213 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16215 this.transition = true;
16216 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16217 var lr = dir == 'next' ? 'left' : 'right';
16218 pan.el.addClass(dir); // or prev
16219 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16220 cur.el.addClass(lr); // or right
16221 pan.el.addClass(lr);
16224 cur.el.on('transitionend', function() {
16225 Roo.log("trans end?");
16227 pan.el.removeClass([lr,dir]);
16228 pan.setActive(true);
16230 cur.el.removeClass([lr]);
16231 cur.setActive(false);
16233 _this.transition = false;
16235 }, this, { single: true } );
16240 cur.setActive(false);
16241 pan.setActive(true);
16246 showPanelNext : function()
16248 var i = this.indexOfPanel(this.getActivePanel());
16250 if (i >= this.tabs.length - 1 && !this.autoslide) {
16254 if (i >= this.tabs.length - 1 && this.autoslide) {
16258 this.showPanel(this.tabs[i+1]);
16261 showPanelPrev : function()
16263 var i = this.indexOfPanel(this.getActivePanel());
16265 if (i < 1 && !this.autoslide) {
16269 if (i < 1 && this.autoslide) {
16270 i = this.tabs.length;
16273 this.showPanel(this.tabs[i-1]);
16277 addBullet: function()
16279 if(!this.bullets || Roo.isTouch){
16282 var ctr = this.el.select('.carousel-bullets',true).first();
16283 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16284 var bullet = ctr.createChild({
16285 cls : 'bullet bullet-' + i
16286 },ctr.dom.lastChild);
16291 bullet.on('click', (function(e, el, o, ii, t){
16293 e.preventDefault();
16295 this.showPanel(ii);
16297 if(this.autoslide && this.slideFn){
16298 clearInterval(this.slideFn);
16299 this.slideFn = window.setInterval(function() {
16300 _this.showPanelNext();
16304 }).createDelegate(this, [i, bullet], true));
16309 setActiveBullet : function(i)
16315 Roo.each(this.el.select('.bullet', true).elements, function(el){
16316 el.removeClass('selected');
16319 var bullet = this.el.select('.bullet-' + i, true).first();
16325 bullet.addClass('selected');
16336 Roo.apply(Roo.bootstrap.TabGroup, {
16340 * register a Navigation Group
16341 * @param {Roo.bootstrap.NavGroup} the navgroup to add
16343 register : function(navgrp)
16345 this.groups[navgrp.navId] = navgrp;
16349 * fetch a Navigation Group based on the navigation ID
16350 * if one does not exist , it will get created.
16351 * @param {string} the navgroup to add
16352 * @returns {Roo.bootstrap.NavGroup} the navgroup
16354 get: function(navId) {
16355 if (typeof(this.groups[navId]) == 'undefined') {
16356 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16358 return this.groups[navId] ;
16373 * @class Roo.bootstrap.TabPanel
16374 * @extends Roo.bootstrap.Component
16375 * Bootstrap TabPanel class
16376 * @cfg {Boolean} active panel active
16377 * @cfg {String} html panel content
16378 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16379 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16383 * Create a new TabPanel
16384 * @param {Object} config The config object
16387 Roo.bootstrap.TabPanel = function(config){
16388 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16392 * Fires when the active status changes
16393 * @param {Roo.bootstrap.TabPanel} this
16394 * @param {Boolean} state the new state
16399 * @event beforedeactivate
16400 * Fires before a tab is de-activated - can be used to do validation on a form.
16401 * @param {Roo.bootstrap.TabPanel} this
16402 * @return {Boolean} false if there is an error
16405 'beforedeactivate': true
16408 this.tabId = this.tabId || Roo.id();
16412 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16419 getAutoCreate : function(){
16422 // item is needed for carousel - not sure if it has any effect otherwise
16423 cls: 'tab-pane item',
16424 html: this.html || ''
16428 cfg.cls += ' active';
16432 cfg.tabId = this.tabId;
16439 initEvents: function()
16441 var p = this.parent();
16442 this.navId = this.navId || p.navId;
16444 if (typeof(this.navId) != 'undefined') {
16445 // not really needed.. but just in case.. parent should be a NavGroup.
16446 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16450 var i = tg.tabs.length - 1;
16452 if(this.active && tg.bullets > 0 && i < tg.bullets){
16453 tg.setActiveBullet(i);
16460 onRender : function(ct, position)
16462 // Roo.log("Call onRender: " + this.xtype);
16464 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16472 setActive: function(state)
16474 Roo.log("panel - set active " + this.tabId + "=" + state);
16476 this.active = state;
16478 this.el.removeClass('active');
16480 } else if (!this.el.hasClass('active')) {
16481 this.el.addClass('active');
16484 this.fireEvent('changed', this, state);
16501 * @class Roo.bootstrap.DateField
16502 * @extends Roo.bootstrap.Input
16503 * Bootstrap DateField class
16504 * @cfg {Number} weekStart default 0
16505 * @cfg {String} viewMode default empty, (months|years)
16506 * @cfg {String} minViewMode default empty, (months|years)
16507 * @cfg {Number} startDate default -Infinity
16508 * @cfg {Number} endDate default Infinity
16509 * @cfg {Boolean} todayHighlight default false
16510 * @cfg {Boolean} todayBtn default false
16511 * @cfg {Boolean} calendarWeeks default false
16512 * @cfg {Object} daysOfWeekDisabled default empty
16513 * @cfg {Boolean} singleMode default false (true | false)
16515 * @cfg {Boolean} keyboardNavigation default true
16516 * @cfg {String} language default en
16519 * Create a new DateField
16520 * @param {Object} config The config object
16523 Roo.bootstrap.DateField = function(config){
16524 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16528 * Fires when this field show.
16529 * @param {Roo.bootstrap.DateField} this
16530 * @param {Mixed} date The date value
16535 * Fires when this field hide.
16536 * @param {Roo.bootstrap.DateField} this
16537 * @param {Mixed} date The date value
16542 * Fires when select a date.
16543 * @param {Roo.bootstrap.DateField} this
16544 * @param {Mixed} date The date value
16548 * @event beforeselect
16549 * Fires when before select a date.
16550 * @param {Roo.bootstrap.DateField} this
16551 * @param {Mixed} date The date value
16553 beforeselect : true
16557 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16560 * @cfg {String} format
16561 * The default date format string which can be overriden for localization support. The format must be
16562 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16566 * @cfg {String} altFormats
16567 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16568 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16570 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16578 todayHighlight : false,
16584 keyboardNavigation: true,
16586 calendarWeeks: false,
16588 startDate: -Infinity,
16592 daysOfWeekDisabled: [],
16596 singleMode : false,
16598 UTCDate: function()
16600 return new Date(Date.UTC.apply(Date, arguments));
16603 UTCToday: function()
16605 var today = new Date();
16606 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16609 getDate: function() {
16610 var d = this.getUTCDate();
16611 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16614 getUTCDate: function() {
16618 setDate: function(d) {
16619 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16622 setUTCDate: function(d) {
16624 this.setValue(this.formatDate(this.date));
16627 onRender: function(ct, position)
16630 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16632 this.language = this.language || 'en';
16633 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16634 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16636 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16637 this.format = this.format || 'm/d/y';
16638 this.isInline = false;
16639 this.isInput = true;
16640 this.component = this.el.select('.add-on', true).first() || false;
16641 this.component = (this.component && this.component.length === 0) ? false : this.component;
16642 this.hasInput = this.component && this.inputEL().length;
16644 if (typeof(this.minViewMode === 'string')) {
16645 switch (this.minViewMode) {
16647 this.minViewMode = 1;
16650 this.minViewMode = 2;
16653 this.minViewMode = 0;
16658 if (typeof(this.viewMode === 'string')) {
16659 switch (this.viewMode) {
16672 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16674 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16676 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16678 this.picker().on('mousedown', this.onMousedown, this);
16679 this.picker().on('click', this.onClick, this);
16681 this.picker().addClass('datepicker-dropdown');
16683 this.startViewMode = this.viewMode;
16685 if(this.singleMode){
16686 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16687 v.setVisibilityMode(Roo.Element.DISPLAY);
16691 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16692 v.setStyle('width', '189px');
16696 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16697 if(!this.calendarWeeks){
16702 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16703 v.attr('colspan', function(i, val){
16704 return parseInt(val) + 1;
16709 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16711 this.setStartDate(this.startDate);
16712 this.setEndDate(this.endDate);
16714 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16721 if(this.isInline) {
16726 picker : function()
16728 return this.pickerEl;
16729 // return this.el.select('.datepicker', true).first();
16732 fillDow: function()
16734 var dowCnt = this.weekStart;
16743 if(this.calendarWeeks){
16751 while (dowCnt < this.weekStart + 7) {
16755 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16759 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16762 fillMonths: function()
16765 var months = this.picker().select('>.datepicker-months td', true).first();
16767 months.dom.innerHTML = '';
16773 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16776 months.createChild(month);
16783 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;
16785 if (this.date < this.startDate) {
16786 this.viewDate = new Date(this.startDate);
16787 } else if (this.date > this.endDate) {
16788 this.viewDate = new Date(this.endDate);
16790 this.viewDate = new Date(this.date);
16798 var d = new Date(this.viewDate),
16799 year = d.getUTCFullYear(),
16800 month = d.getUTCMonth(),
16801 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16802 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16803 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16804 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16805 currentDate = this.date && this.date.valueOf(),
16806 today = this.UTCToday();
16808 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16810 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16812 // this.picker.select('>tfoot th.today').
16813 // .text(dates[this.language].today)
16814 // .toggle(this.todayBtn !== false);
16816 this.updateNavArrows();
16819 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16821 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16823 prevMonth.setUTCDate(day);
16825 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16827 var nextMonth = new Date(prevMonth);
16829 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16831 nextMonth = nextMonth.valueOf();
16833 var fillMonths = false;
16835 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16837 while(prevMonth.valueOf() < nextMonth) {
16840 if (prevMonth.getUTCDay() === this.weekStart) {
16842 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16850 if(this.calendarWeeks){
16851 // ISO 8601: First week contains first thursday.
16852 // ISO also states week starts on Monday, but we can be more abstract here.
16854 // Start of current week: based on weekstart/current date
16855 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16856 // Thursday of this week
16857 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16858 // First Thursday of year, year from thursday
16859 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16860 // Calendar week: ms between thursdays, div ms per day, div 7 days
16861 calWeek = (th - yth) / 864e5 / 7 + 1;
16863 fillMonths.cn.push({
16871 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16873 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16876 if (this.todayHighlight &&
16877 prevMonth.getUTCFullYear() == today.getFullYear() &&
16878 prevMonth.getUTCMonth() == today.getMonth() &&
16879 prevMonth.getUTCDate() == today.getDate()) {
16880 clsName += ' today';
16883 if (currentDate && prevMonth.valueOf() === currentDate) {
16884 clsName += ' active';
16887 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16888 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16889 clsName += ' disabled';
16892 fillMonths.cn.push({
16894 cls: 'day ' + clsName,
16895 html: prevMonth.getDate()
16898 prevMonth.setDate(prevMonth.getDate()+1);
16901 var currentYear = this.date && this.date.getUTCFullYear();
16902 var currentMonth = this.date && this.date.getUTCMonth();
16904 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16906 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16907 v.removeClass('active');
16909 if(currentYear === year && k === currentMonth){
16910 v.addClass('active');
16913 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16914 v.addClass('disabled');
16920 year = parseInt(year/10, 10) * 10;
16922 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16924 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16927 for (var i = -1; i < 11; i++) {
16928 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16930 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16938 showMode: function(dir)
16941 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16944 Roo.each(this.picker().select('>div',true).elements, function(v){
16945 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16948 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16953 if(this.isInline) {
16957 this.picker().removeClass(['bottom', 'top']);
16959 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16961 * place to the top of element!
16965 this.picker().addClass('top');
16966 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16971 this.picker().addClass('bottom');
16973 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16976 parseDate : function(value)
16978 if(!value || value instanceof Date){
16981 var v = Date.parseDate(value, this.format);
16982 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16983 v = Date.parseDate(value, 'Y-m-d');
16985 if(!v && this.altFormats){
16986 if(!this.altFormatsArray){
16987 this.altFormatsArray = this.altFormats.split("|");
16989 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16990 v = Date.parseDate(value, this.altFormatsArray[i]);
16996 formatDate : function(date, fmt)
16998 return (!date || !(date instanceof Date)) ?
16999 date : date.dateFormat(fmt || this.format);
17002 onFocus : function()
17004 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17008 onBlur : function()
17010 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17012 var d = this.inputEl().getValue();
17021 this.picker().show();
17025 this.fireEvent('show', this, this.date);
17030 if(this.isInline) {
17033 this.picker().hide();
17034 this.viewMode = this.startViewMode;
17037 this.fireEvent('hide', this, this.date);
17041 onMousedown: function(e)
17043 e.stopPropagation();
17044 e.preventDefault();
17049 Roo.bootstrap.DateField.superclass.keyup.call(this);
17053 setValue: function(v)
17055 if(this.fireEvent('beforeselect', this, v) !== false){
17056 var d = new Date(this.parseDate(v) ).clearTime();
17058 if(isNaN(d.getTime())){
17059 this.date = this.viewDate = '';
17060 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17064 v = this.formatDate(d);
17066 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17068 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17072 this.fireEvent('select', this, this.date);
17076 getValue: function()
17078 return this.formatDate(this.date);
17081 fireKey: function(e)
17083 if (!this.picker().isVisible()){
17084 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17090 var dateChanged = false,
17092 newDate, newViewDate;
17097 e.preventDefault();
17101 if (!this.keyboardNavigation) {
17104 dir = e.keyCode == 37 ? -1 : 1;
17107 newDate = this.moveYear(this.date, dir);
17108 newViewDate = this.moveYear(this.viewDate, dir);
17109 } else if (e.shiftKey){
17110 newDate = this.moveMonth(this.date, dir);
17111 newViewDate = this.moveMonth(this.viewDate, dir);
17113 newDate = new Date(this.date);
17114 newDate.setUTCDate(this.date.getUTCDate() + dir);
17115 newViewDate = new Date(this.viewDate);
17116 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17118 if (this.dateWithinRange(newDate)){
17119 this.date = newDate;
17120 this.viewDate = newViewDate;
17121 this.setValue(this.formatDate(this.date));
17123 e.preventDefault();
17124 dateChanged = true;
17129 if (!this.keyboardNavigation) {
17132 dir = e.keyCode == 38 ? -1 : 1;
17134 newDate = this.moveYear(this.date, dir);
17135 newViewDate = this.moveYear(this.viewDate, dir);
17136 } else if (e.shiftKey){
17137 newDate = this.moveMonth(this.date, dir);
17138 newViewDate = this.moveMonth(this.viewDate, dir);
17140 newDate = new Date(this.date);
17141 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17142 newViewDate = new Date(this.viewDate);
17143 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17145 if (this.dateWithinRange(newDate)){
17146 this.date = newDate;
17147 this.viewDate = newViewDate;
17148 this.setValue(this.formatDate(this.date));
17150 e.preventDefault();
17151 dateChanged = true;
17155 this.setValue(this.formatDate(this.date));
17157 e.preventDefault();
17160 this.setValue(this.formatDate(this.date));
17174 onClick: function(e)
17176 e.stopPropagation();
17177 e.preventDefault();
17179 var target = e.getTarget();
17181 if(target.nodeName.toLowerCase() === 'i'){
17182 target = Roo.get(target).dom.parentNode;
17185 var nodeName = target.nodeName;
17186 var className = target.className;
17187 var html = target.innerHTML;
17188 //Roo.log(nodeName);
17190 switch(nodeName.toLowerCase()) {
17192 switch(className) {
17198 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17199 switch(this.viewMode){
17201 this.viewDate = this.moveMonth(this.viewDate, dir);
17205 this.viewDate = this.moveYear(this.viewDate, dir);
17211 var date = new Date();
17212 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17214 this.setValue(this.formatDate(this.date));
17221 if (className.indexOf('disabled') < 0) {
17222 this.viewDate.setUTCDate(1);
17223 if (className.indexOf('month') > -1) {
17224 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17226 var year = parseInt(html, 10) || 0;
17227 this.viewDate.setUTCFullYear(year);
17231 if(this.singleMode){
17232 this.setValue(this.formatDate(this.viewDate));
17243 //Roo.log(className);
17244 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17245 var day = parseInt(html, 10) || 1;
17246 var year = this.viewDate.getUTCFullYear(),
17247 month = this.viewDate.getUTCMonth();
17249 if (className.indexOf('old') > -1) {
17256 } else if (className.indexOf('new') > -1) {
17264 //Roo.log([year,month,day]);
17265 this.date = this.UTCDate(year, month, day,0,0,0,0);
17266 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17268 //Roo.log(this.formatDate(this.date));
17269 this.setValue(this.formatDate(this.date));
17276 setStartDate: function(startDate)
17278 this.startDate = startDate || -Infinity;
17279 if (this.startDate !== -Infinity) {
17280 this.startDate = this.parseDate(this.startDate);
17283 this.updateNavArrows();
17286 setEndDate: function(endDate)
17288 this.endDate = endDate || Infinity;
17289 if (this.endDate !== Infinity) {
17290 this.endDate = this.parseDate(this.endDate);
17293 this.updateNavArrows();
17296 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17298 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17299 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17300 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17302 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17303 return parseInt(d, 10);
17306 this.updateNavArrows();
17309 updateNavArrows: function()
17311 if(this.singleMode){
17315 var d = new Date(this.viewDate),
17316 year = d.getUTCFullYear(),
17317 month = d.getUTCMonth();
17319 Roo.each(this.picker().select('.prev', true).elements, function(v){
17321 switch (this.viewMode) {
17324 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17330 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17337 Roo.each(this.picker().select('.next', true).elements, function(v){
17339 switch (this.viewMode) {
17342 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17348 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17356 moveMonth: function(date, dir)
17361 var new_date = new Date(date.valueOf()),
17362 day = new_date.getUTCDate(),
17363 month = new_date.getUTCMonth(),
17364 mag = Math.abs(dir),
17366 dir = dir > 0 ? 1 : -1;
17369 // If going back one month, make sure month is not current month
17370 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17372 return new_date.getUTCMonth() == month;
17374 // If going forward one month, make sure month is as expected
17375 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17377 return new_date.getUTCMonth() != new_month;
17379 new_month = month + dir;
17380 new_date.setUTCMonth(new_month);
17381 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17382 if (new_month < 0 || new_month > 11) {
17383 new_month = (new_month + 12) % 12;
17386 // For magnitudes >1, move one month at a time...
17387 for (var i=0; i<mag; i++) {
17388 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17389 new_date = this.moveMonth(new_date, dir);
17391 // ...then reset the day, keeping it in the new month
17392 new_month = new_date.getUTCMonth();
17393 new_date.setUTCDate(day);
17395 return new_month != new_date.getUTCMonth();
17398 // Common date-resetting loop -- if date is beyond end of month, make it
17401 new_date.setUTCDate(--day);
17402 new_date.setUTCMonth(new_month);
17407 moveYear: function(date, dir)
17409 return this.moveMonth(date, dir*12);
17412 dateWithinRange: function(date)
17414 return date >= this.startDate && date <= this.endDate;
17420 this.picker().remove();
17425 Roo.apply(Roo.bootstrap.DateField, {
17436 html: '<i class="fa fa-arrow-left"/>'
17446 html: '<i class="fa fa-arrow-right"/>'
17488 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17489 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17490 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17491 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17492 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17505 navFnc: 'FullYear',
17510 navFnc: 'FullYear',
17515 Roo.apply(Roo.bootstrap.DateField, {
17519 cls: 'datepicker dropdown-menu roo-dynamic',
17523 cls: 'datepicker-days',
17527 cls: 'table-condensed',
17529 Roo.bootstrap.DateField.head,
17533 Roo.bootstrap.DateField.footer
17540 cls: 'datepicker-months',
17544 cls: 'table-condensed',
17546 Roo.bootstrap.DateField.head,
17547 Roo.bootstrap.DateField.content,
17548 Roo.bootstrap.DateField.footer
17555 cls: 'datepicker-years',
17559 cls: 'table-condensed',
17561 Roo.bootstrap.DateField.head,
17562 Roo.bootstrap.DateField.content,
17563 Roo.bootstrap.DateField.footer
17582 * @class Roo.bootstrap.TimeField
17583 * @extends Roo.bootstrap.Input
17584 * Bootstrap DateField class
17588 * Create a new TimeField
17589 * @param {Object} config The config object
17592 Roo.bootstrap.TimeField = function(config){
17593 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17597 * Fires when this field show.
17598 * @param {Roo.bootstrap.DateField} thisthis
17599 * @param {Mixed} date The date value
17604 * Fires when this field hide.
17605 * @param {Roo.bootstrap.DateField} this
17606 * @param {Mixed} date The date value
17611 * Fires when select a date.
17612 * @param {Roo.bootstrap.DateField} this
17613 * @param {Mixed} date The date value
17619 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17622 * @cfg {String} format
17623 * The default time format string which can be overriden for localization support. The format must be
17624 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17628 onRender: function(ct, position)
17631 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17633 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17635 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17637 this.pop = this.picker().select('>.datepicker-time',true).first();
17638 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17640 this.picker().on('mousedown', this.onMousedown, this);
17641 this.picker().on('click', this.onClick, this);
17643 this.picker().addClass('datepicker-dropdown');
17648 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17649 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17650 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17651 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17652 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17653 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17657 fireKey: function(e){
17658 if (!this.picker().isVisible()){
17659 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17665 e.preventDefault();
17673 this.onTogglePeriod();
17676 this.onIncrementMinutes();
17679 this.onDecrementMinutes();
17688 onClick: function(e) {
17689 e.stopPropagation();
17690 e.preventDefault();
17693 picker : function()
17695 return this.el.select('.datepicker', true).first();
17698 fillTime: function()
17700 var time = this.pop.select('tbody', true).first();
17702 time.dom.innerHTML = '';
17717 cls: 'hours-up glyphicon glyphicon-chevron-up'
17737 cls: 'minutes-up glyphicon glyphicon-chevron-up'
17758 cls: 'timepicker-hour',
17773 cls: 'timepicker-minute',
17788 cls: 'btn btn-primary period',
17810 cls: 'hours-down glyphicon glyphicon-chevron-down'
17830 cls: 'minutes-down glyphicon glyphicon-chevron-down'
17848 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17855 var hours = this.time.getHours();
17856 var minutes = this.time.getMinutes();
17869 hours = hours - 12;
17873 hours = '0' + hours;
17877 minutes = '0' + minutes;
17880 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17881 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17882 this.pop.select('button', true).first().dom.innerHTML = period;
17888 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17890 var cls = ['bottom'];
17892 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17899 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17904 this.picker().addClass(cls.join('-'));
17908 Roo.each(cls, function(c){
17910 _this.picker().setTop(_this.inputEl().getHeight());
17914 _this.picker().setTop(0 - _this.picker().getHeight());
17919 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17923 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17930 onFocus : function()
17932 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17936 onBlur : function()
17938 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17944 this.picker().show();
17949 this.fireEvent('show', this, this.date);
17954 this.picker().hide();
17957 this.fireEvent('hide', this, this.date);
17960 setTime : function()
17963 this.setValue(this.time.format(this.format));
17965 this.fireEvent('select', this, this.date);
17970 onMousedown: function(e){
17971 e.stopPropagation();
17972 e.preventDefault();
17975 onIncrementHours: function()
17977 Roo.log('onIncrementHours');
17978 this.time = this.time.add(Date.HOUR, 1);
17983 onDecrementHours: function()
17985 Roo.log('onDecrementHours');
17986 this.time = this.time.add(Date.HOUR, -1);
17990 onIncrementMinutes: function()
17992 Roo.log('onIncrementMinutes');
17993 this.time = this.time.add(Date.MINUTE, 1);
17997 onDecrementMinutes: function()
17999 Roo.log('onDecrementMinutes');
18000 this.time = this.time.add(Date.MINUTE, -1);
18004 onTogglePeriod: function()
18006 Roo.log('onTogglePeriod');
18007 this.time = this.time.add(Date.HOUR, 12);
18014 Roo.apply(Roo.bootstrap.TimeField, {
18044 cls: 'btn btn-info ok',
18056 Roo.apply(Roo.bootstrap.TimeField, {
18060 cls: 'datepicker dropdown-menu',
18064 cls: 'datepicker-time',
18068 cls: 'table-condensed',
18070 Roo.bootstrap.TimeField.content,
18071 Roo.bootstrap.TimeField.footer
18090 * @class Roo.bootstrap.MonthField
18091 * @extends Roo.bootstrap.Input
18092 * Bootstrap MonthField class
18094 * @cfg {String} language default en
18097 * Create a new MonthField
18098 * @param {Object} config The config object
18101 Roo.bootstrap.MonthField = function(config){
18102 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18107 * Fires when this field show.
18108 * @param {Roo.bootstrap.MonthField} this
18109 * @param {Mixed} date The date value
18114 * Fires when this field hide.
18115 * @param {Roo.bootstrap.MonthField} this
18116 * @param {Mixed} date The date value
18121 * Fires when select a date.
18122 * @param {Roo.bootstrap.MonthField} this
18123 * @param {String} oldvalue The old value
18124 * @param {String} newvalue The new value
18130 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
18132 onRender: function(ct, position)
18135 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18137 this.language = this.language || 'en';
18138 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18139 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18141 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18142 this.isInline = false;
18143 this.isInput = true;
18144 this.component = this.el.select('.add-on', true).first() || false;
18145 this.component = (this.component && this.component.length === 0) ? false : this.component;
18146 this.hasInput = this.component && this.inputEL().length;
18148 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18150 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18152 this.picker().on('mousedown', this.onMousedown, this);
18153 this.picker().on('click', this.onClick, this);
18155 this.picker().addClass('datepicker-dropdown');
18157 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18158 v.setStyle('width', '189px');
18165 if(this.isInline) {
18171 setValue: function(v, suppressEvent)
18173 var o = this.getValue();
18175 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18179 if(suppressEvent !== true){
18180 this.fireEvent('select', this, o, v);
18185 getValue: function()
18190 onClick: function(e)
18192 e.stopPropagation();
18193 e.preventDefault();
18195 var target = e.getTarget();
18197 if(target.nodeName.toLowerCase() === 'i'){
18198 target = Roo.get(target).dom.parentNode;
18201 var nodeName = target.nodeName;
18202 var className = target.className;
18203 var html = target.innerHTML;
18205 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18209 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18211 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18217 picker : function()
18219 return this.pickerEl;
18222 fillMonths: function()
18225 var months = this.picker().select('>.datepicker-months td', true).first();
18227 months.dom.innerHTML = '';
18233 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18236 months.createChild(month);
18245 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18246 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18249 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18250 e.removeClass('active');
18252 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18253 e.addClass('active');
18260 if(this.isInline) {
18264 this.picker().removeClass(['bottom', 'top']);
18266 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18268 * place to the top of element!
18272 this.picker().addClass('top');
18273 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18278 this.picker().addClass('bottom');
18280 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18283 onFocus : function()
18285 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18289 onBlur : function()
18291 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18293 var d = this.inputEl().getValue();
18302 this.picker().show();
18303 this.picker().select('>.datepicker-months', true).first().show();
18307 this.fireEvent('show', this, this.date);
18312 if(this.isInline) {
18315 this.picker().hide();
18316 this.fireEvent('hide', this, this.date);
18320 onMousedown: function(e)
18322 e.stopPropagation();
18323 e.preventDefault();
18328 Roo.bootstrap.MonthField.superclass.keyup.call(this);
18332 fireKey: function(e)
18334 if (!this.picker().isVisible()){
18335 if (e.keyCode == 27) {// allow escape to hide and re-show picker
18346 e.preventDefault();
18350 dir = e.keyCode == 37 ? -1 : 1;
18352 this.vIndex = this.vIndex + dir;
18354 if(this.vIndex < 0){
18358 if(this.vIndex > 11){
18362 if(isNaN(this.vIndex)){
18366 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18372 dir = e.keyCode == 38 ? -1 : 1;
18374 this.vIndex = this.vIndex + dir * 4;
18376 if(this.vIndex < 0){
18380 if(this.vIndex > 11){
18384 if(isNaN(this.vIndex)){
18388 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18393 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18394 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18398 e.preventDefault();
18401 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18402 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18418 this.picker().remove();
18423 Roo.apply(Roo.bootstrap.MonthField, {
18442 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18443 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18448 Roo.apply(Roo.bootstrap.MonthField, {
18452 cls: 'datepicker dropdown-menu roo-dynamic',
18456 cls: 'datepicker-months',
18460 cls: 'table-condensed',
18462 Roo.bootstrap.DateField.content
18482 * @class Roo.bootstrap.CheckBox
18483 * @extends Roo.bootstrap.Input
18484 * Bootstrap CheckBox class
18486 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18487 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18488 * @cfg {String} boxLabel The text that appears beside the checkbox
18489 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18490 * @cfg {Boolean} checked initnal the element
18491 * @cfg {Boolean} inline inline the element (default false)
18492 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18495 * Create a new CheckBox
18496 * @param {Object} config The config object
18499 Roo.bootstrap.CheckBox = function(config){
18500 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18505 * Fires when the element is checked or unchecked.
18506 * @param {Roo.bootstrap.CheckBox} this This input
18507 * @param {Boolean} checked The new checked value
18514 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18516 inputType: 'checkbox',
18524 getAutoCreate : function()
18526 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18532 cfg.cls = 'form-group ' + this.inputType; //input-group
18535 cfg.cls += ' ' + this.inputType + '-inline';
18541 type : this.inputType,
18542 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18543 cls : 'roo-' + this.inputType, //'form-box',
18544 placeholder : this.placeholder || ''
18548 if (this.weight) { // Validity check?
18549 cfg.cls += " " + this.inputType + "-" + this.weight;
18552 if (this.disabled) {
18553 input.disabled=true;
18557 input.checked = this.checked;
18561 input.name = this.name;
18565 input.cls += ' input-' + this.size;
18570 ['xs','sm','md','lg'].map(function(size){
18571 if (settings[size]) {
18572 cfg.cls += ' col-' + size + '-' + settings[size];
18576 var inputblock = input;
18578 if (this.before || this.after) {
18581 cls : 'input-group',
18586 inputblock.cn.push({
18588 cls : 'input-group-addon',
18593 inputblock.cn.push(input);
18596 inputblock.cn.push({
18598 cls : 'input-group-addon',
18605 if (align ==='left' && this.fieldLabel.length) {
18606 // Roo.log("left and has label");
18612 cls : 'control-label col-md-' + this.labelWidth,
18613 html : this.fieldLabel
18617 cls : "col-md-" + (12 - this.labelWidth),
18624 } else if ( this.fieldLabel.length) {
18625 // Roo.log(" label");
18629 tag: this.boxLabel ? 'span' : 'label',
18631 cls: 'control-label box-input-label',
18632 //cls : 'input-group-addon',
18633 html : this.fieldLabel
18643 // Roo.log(" no label && no align");
18644 cfg.cn = [ inputblock ] ;
18649 var boxLabelCfg = {
18651 //'for': id, // box label is handled by onclick - so no for...
18653 html: this.boxLabel
18657 boxLabelCfg.tooltip = this.tooltip;
18660 cfg.cn.push(boxLabelCfg);
18670 * return the real input element.
18672 inputEl: function ()
18674 return this.el.select('input.roo-' + this.inputType,true).first();
18677 labelEl: function()
18679 return this.el.select('label.control-label',true).first();
18681 /* depricated... */
18685 return this.labelEl();
18688 initEvents : function()
18690 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18692 this.inputEl().on('click', this.onClick, this);
18694 if (this.boxLabel) {
18695 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18698 this.startValue = this.getValue();
18701 Roo.bootstrap.CheckBox.register(this);
18705 onClick : function()
18707 this.setChecked(!this.checked);
18710 setChecked : function(state,suppressEvent)
18712 this.startValue = this.getValue();
18714 if(this.inputType == 'radio'){
18716 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18717 e.dom.checked = false;
18720 this.inputEl().dom.checked = true;
18722 this.inputEl().dom.value = this.inputValue;
18724 if(suppressEvent !== true){
18725 this.fireEvent('check', this, true);
18733 this.checked = state;
18735 this.inputEl().dom.checked = state;
18737 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18739 if(suppressEvent !== true){
18740 this.fireEvent('check', this, state);
18746 getValue : function()
18748 if(this.inputType == 'radio'){
18749 return this.getGroupValue();
18752 return this.inputEl().getValue();
18756 getGroupValue : function()
18758 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18762 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18765 setValue : function(v,suppressEvent)
18767 if(this.inputType == 'radio'){
18768 this.setGroupValue(v, suppressEvent);
18772 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18777 setGroupValue : function(v, suppressEvent)
18779 this.startValue = this.getValue();
18781 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18782 e.dom.checked = false;
18784 if(e.dom.value == v){
18785 e.dom.checked = true;
18789 if(suppressEvent !== true){
18790 this.fireEvent('check', this, true);
18798 validate : function()
18802 (this.inputType == 'radio' && this.validateRadio()) ||
18803 (this.inputType == 'checkbox' && this.validateCheckbox())
18809 this.markInvalid();
18813 validateRadio : function()
18817 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18818 if(!e.dom.checked){
18830 validateCheckbox : function()
18833 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18836 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18844 for(var i in group){
18849 r = (group[i].getValue() == group[i].inputValue) ? true : false;
18856 * Mark this field as valid
18858 markValid : function()
18860 if(this.allowBlank){
18866 this.fireEvent('valid', this);
18868 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18871 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18878 if(this.inputType == 'radio'){
18879 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18880 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18881 e.findParent('.form-group', false, true).addClass(_this.validClass);
18888 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18889 this.el.findParent('.form-group', false, true).addClass(this.validClass);
18893 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18899 for(var i in group){
18900 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18901 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18906 * Mark this field as invalid
18907 * @param {String} msg The validation message
18909 markInvalid : function(msg)
18911 if(this.allowBlank){
18917 this.fireEvent('invalid', this, msg);
18919 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18922 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18926 label.markInvalid();
18929 if(this.inputType == 'radio'){
18930 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18931 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18932 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18939 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18940 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18944 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18950 for(var i in group){
18951 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18952 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18959 Roo.apply(Roo.bootstrap.CheckBox, {
18964 * register a CheckBox Group
18965 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18967 register : function(checkbox)
18969 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18970 this.groups[checkbox.groupId] = {};
18973 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18977 this.groups[checkbox.groupId][checkbox.name] = checkbox;
18981 * fetch a CheckBox Group based on the group ID
18982 * @param {string} the group ID
18983 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18985 get: function(groupId) {
18986 if (typeof(this.groups[groupId]) == 'undefined') {
18990 return this.groups[groupId] ;
19002 *<div class="radio">
19004 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19005 Option one is this and that—be sure to include why it's great
19012 *<label class="radio-inline">fieldLabel</label>
19013 *<label class="radio-inline">
19014 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19022 * @class Roo.bootstrap.Radio
19023 * @extends Roo.bootstrap.CheckBox
19024 * Bootstrap Radio class
19027 * Create a new Radio
19028 * @param {Object} config The config object
19031 Roo.bootstrap.Radio = function(config){
19032 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19036 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
19038 inputType: 'radio',
19042 getAutoCreate : function()
19044 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19045 align = align || 'left'; // default...
19052 tag : this.inline ? 'span' : 'div',
19057 var inline = this.inline ? ' radio-inline' : '';
19061 // does not need for, as we wrap the input with it..
19063 cls : 'control-label box-label' + inline,
19066 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19070 //cls : 'control-label' + inline,
19071 html : this.fieldLabel,
19072 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19081 type : this.inputType,
19082 //value : (!this.checked) ? this.valueOff : this.inputValue,
19083 value : this.inputValue,
19085 placeholder : this.placeholder || '' // ?? needed????
19088 if (this.weight) { // Validity check?
19089 input.cls += " radio-" + this.weight;
19091 if (this.disabled) {
19092 input.disabled=true;
19096 input.checked = this.checked;
19100 input.name = this.name;
19104 input.cls += ' input-' + this.size;
19107 //?? can span's inline have a width??
19110 ['xs','sm','md','lg'].map(function(size){
19111 if (settings[size]) {
19112 cfg.cls += ' col-' + size + '-' + settings[size];
19116 var inputblock = input;
19118 if (this.before || this.after) {
19121 cls : 'input-group',
19126 inputblock.cn.push({
19128 cls : 'input-group-addon',
19132 inputblock.cn.push(input);
19134 inputblock.cn.push({
19136 cls : 'input-group-addon',
19144 if (this.fieldLabel && this.fieldLabel.length) {
19145 cfg.cn.push(fieldLabel);
19148 // normal bootstrap puts the input inside the label.
19149 // however with our styled version - it has to go after the input.
19151 //lbl.cn.push(inputblock);
19155 cls: 'radio' + inline,
19162 cfg.cn.push( lblwrap);
19167 html: this.boxLabel
19176 initEvents : function()
19178 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19180 this.inputEl().on('click', this.onClick, this);
19181 if (this.boxLabel) {
19182 //Roo.log('find label');
19183 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
19188 inputEl: function ()
19190 return this.el.select('input.roo-radio',true).first();
19192 onClick : function()
19195 this.setChecked(true);
19198 setChecked : function(state,suppressEvent)
19201 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19202 v.dom.checked = false;
19205 Roo.log(this.inputEl().dom);
19206 this.checked = state;
19207 this.inputEl().dom.checked = state;
19209 if(suppressEvent !== true){
19210 this.fireEvent('check', this, state);
19213 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19217 getGroupValue : function()
19220 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19221 if(v.dom.checked == true){
19222 value = v.dom.value;
19230 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
19231 * @return {Mixed} value The field value
19233 getValue : function(){
19234 return this.getGroupValue();
19240 //<script type="text/javascript">
19243 * Based Ext JS Library 1.1.1
19244 * Copyright(c) 2006-2007, Ext JS, LLC.
19250 * @class Roo.HtmlEditorCore
19251 * @extends Roo.Component
19252 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19254 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19257 Roo.HtmlEditorCore = function(config){
19260 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19265 * @event initialize
19266 * Fires when the editor is fully initialized (including the iframe)
19267 * @param {Roo.HtmlEditorCore} this
19272 * Fires when the editor is first receives the focus. Any insertion must wait
19273 * until after this event.
19274 * @param {Roo.HtmlEditorCore} this
19278 * @event beforesync
19279 * Fires before the textarea is updated with content from the editor iframe. Return false
19280 * to cancel the sync.
19281 * @param {Roo.HtmlEditorCore} this
19282 * @param {String} html
19286 * @event beforepush
19287 * Fires before the iframe editor is updated with content from the textarea. Return false
19288 * to cancel the push.
19289 * @param {Roo.HtmlEditorCore} this
19290 * @param {String} html
19295 * Fires when the textarea is updated with content from the editor iframe.
19296 * @param {Roo.HtmlEditorCore} this
19297 * @param {String} html
19302 * Fires when the iframe editor is updated with content from the textarea.
19303 * @param {Roo.HtmlEditorCore} this
19304 * @param {String} html
19309 * @event editorevent
19310 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19311 * @param {Roo.HtmlEditorCore} this
19317 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19319 // defaults : white / black...
19320 this.applyBlacklists();
19327 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
19331 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
19337 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
19342 * @cfg {Number} height (in pixels)
19346 * @cfg {Number} width (in pixels)
19351 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19354 stylesheets: false,
19359 // private properties
19360 validationEvent : false,
19362 initialized : false,
19364 sourceEditMode : false,
19365 onFocus : Roo.emptyFn,
19367 hideMode:'offsets',
19371 // blacklist + whitelisted elements..
19378 * Protected method that will not generally be called directly. It
19379 * is called when the editor initializes the iframe with HTML contents. Override this method if you
19380 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19382 getDocMarkup : function(){
19386 // inherit styels from page...??
19387 if (this.stylesheets === false) {
19389 Roo.get(document.head).select('style').each(function(node) {
19390 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19393 Roo.get(document.head).select('link').each(function(node) {
19394 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19397 } else if (!this.stylesheets.length) {
19399 st = '<style type="text/css">' +
19400 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19406 st += '<style type="text/css">' +
19407 'IMG { cursor: pointer } ' +
19411 return '<html><head>' + st +
19412 //<style type="text/css">' +
19413 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19415 ' </head><body class="roo-htmleditor-body"></body></html>';
19419 onRender : function(ct, position)
19422 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19423 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19426 this.el.dom.style.border = '0 none';
19427 this.el.dom.setAttribute('tabIndex', -1);
19428 this.el.addClass('x-hidden hide');
19432 if(Roo.isIE){ // fix IE 1px bogus margin
19433 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19437 this.frameId = Roo.id();
19441 var iframe = this.owner.wrap.createChild({
19443 cls: 'form-control', // bootstrap..
19445 name: this.frameId,
19446 frameBorder : 'no',
19447 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19452 this.iframe = iframe.dom;
19454 this.assignDocWin();
19456 this.doc.designMode = 'on';
19459 this.doc.write(this.getDocMarkup());
19463 var task = { // must defer to wait for browser to be ready
19465 //console.log("run task?" + this.doc.readyState);
19466 this.assignDocWin();
19467 if(this.doc.body || this.doc.readyState == 'complete'){
19469 this.doc.designMode="on";
19473 Roo.TaskMgr.stop(task);
19474 this.initEditor.defer(10, this);
19481 Roo.TaskMgr.start(task);
19486 onResize : function(w, h)
19488 Roo.log('resize: ' +w + ',' + h );
19489 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19493 if(typeof w == 'number'){
19495 this.iframe.style.width = w + 'px';
19497 if(typeof h == 'number'){
19499 this.iframe.style.height = h + 'px';
19501 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19508 * Toggles the editor between standard and source edit mode.
19509 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19511 toggleSourceEdit : function(sourceEditMode){
19513 this.sourceEditMode = sourceEditMode === true;
19515 if(this.sourceEditMode){
19517 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19520 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19521 //this.iframe.className = '';
19524 //this.setSize(this.owner.wrap.getSize());
19525 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19532 * Protected method that will not generally be called directly. If you need/want
19533 * custom HTML cleanup, this is the method you should override.
19534 * @param {String} html The HTML to be cleaned
19535 * return {String} The cleaned HTML
19537 cleanHtml : function(html){
19538 html = String(html);
19539 if(html.length > 5){
19540 if(Roo.isSafari){ // strip safari nonsense
19541 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19544 if(html == ' '){
19551 * HTML Editor -> Textarea
19552 * Protected method that will not generally be called directly. Syncs the contents
19553 * of the editor iframe with the textarea.
19555 syncValue : function(){
19556 if(this.initialized){
19557 var bd = (this.doc.body || this.doc.documentElement);
19558 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19559 var html = bd.innerHTML;
19561 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19562 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19564 html = '<div style="'+m[0]+'">' + html + '</div>';
19567 html = this.cleanHtml(html);
19568 // fix up the special chars.. normaly like back quotes in word...
19569 // however we do not want to do this with chinese..
19570 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19571 var cc = b.charCodeAt();
19573 (cc >= 0x4E00 && cc < 0xA000 ) ||
19574 (cc >= 0x3400 && cc < 0x4E00 ) ||
19575 (cc >= 0xf900 && cc < 0xfb00 )
19581 if(this.owner.fireEvent('beforesync', this, html) !== false){
19582 this.el.dom.value = html;
19583 this.owner.fireEvent('sync', this, html);
19589 * Protected method that will not generally be called directly. Pushes the value of the textarea
19590 * into the iframe editor.
19592 pushValue : function(){
19593 if(this.initialized){
19594 var v = this.el.dom.value.trim();
19596 // if(v.length < 1){
19600 if(this.owner.fireEvent('beforepush', this, v) !== false){
19601 var d = (this.doc.body || this.doc.documentElement);
19603 this.cleanUpPaste();
19604 this.el.dom.value = d.innerHTML;
19605 this.owner.fireEvent('push', this, v);
19611 deferFocus : function(){
19612 this.focus.defer(10, this);
19616 focus : function(){
19617 if(this.win && !this.sourceEditMode){
19624 assignDocWin: function()
19626 var iframe = this.iframe;
19629 this.doc = iframe.contentWindow.document;
19630 this.win = iframe.contentWindow;
19632 // if (!Roo.get(this.frameId)) {
19635 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19636 // this.win = Roo.get(this.frameId).dom.contentWindow;
19638 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19642 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19643 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19648 initEditor : function(){
19649 //console.log("INIT EDITOR");
19650 this.assignDocWin();
19654 this.doc.designMode="on";
19656 this.doc.write(this.getDocMarkup());
19659 var dbody = (this.doc.body || this.doc.documentElement);
19660 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19661 // this copies styles from the containing element into thsi one..
19662 // not sure why we need all of this..
19663 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19665 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19666 //ss['background-attachment'] = 'fixed'; // w3c
19667 dbody.bgProperties = 'fixed'; // ie
19668 //Roo.DomHelper.applyStyles(dbody, ss);
19669 Roo.EventManager.on(this.doc, {
19670 //'mousedown': this.onEditorEvent,
19671 'mouseup': this.onEditorEvent,
19672 'dblclick': this.onEditorEvent,
19673 'click': this.onEditorEvent,
19674 'keyup': this.onEditorEvent,
19679 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19681 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19682 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19684 this.initialized = true;
19686 this.owner.fireEvent('initialize', this);
19691 onDestroy : function(){
19697 //for (var i =0; i < this.toolbars.length;i++) {
19698 // // fixme - ask toolbars for heights?
19699 // this.toolbars[i].onDestroy();
19702 //this.wrap.dom.innerHTML = '';
19703 //this.wrap.remove();
19708 onFirstFocus : function(){
19710 this.assignDocWin();
19713 this.activated = true;
19716 if(Roo.isGecko){ // prevent silly gecko errors
19718 var s = this.win.getSelection();
19719 if(!s.focusNode || s.focusNode.nodeType != 3){
19720 var r = s.getRangeAt(0);
19721 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19726 this.execCmd('useCSS', true);
19727 this.execCmd('styleWithCSS', false);
19730 this.owner.fireEvent('activate', this);
19734 adjustFont: function(btn){
19735 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19736 //if(Roo.isSafari){ // safari
19739 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19740 if(Roo.isSafari){ // safari
19741 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19742 v = (v < 10) ? 10 : v;
19743 v = (v > 48) ? 48 : v;
19744 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19749 v = Math.max(1, v+adjust);
19751 this.execCmd('FontSize', v );
19754 onEditorEvent : function(e)
19756 this.owner.fireEvent('editorevent', this, e);
19757 // this.updateToolbar();
19758 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19761 insertTag : function(tg)
19763 // could be a bit smarter... -> wrap the current selected tRoo..
19764 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19766 range = this.createRange(this.getSelection());
19767 var wrappingNode = this.doc.createElement(tg.toLowerCase());
19768 wrappingNode.appendChild(range.extractContents());
19769 range.insertNode(wrappingNode);
19776 this.execCmd("formatblock", tg);
19780 insertText : function(txt)
19784 var range = this.createRange();
19785 range.deleteContents();
19786 //alert(Sender.getAttribute('label'));
19788 range.insertNode(this.doc.createTextNode(txt));
19794 * Executes a Midas editor command on the editor document and performs necessary focus and
19795 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19796 * @param {String} cmd The Midas command
19797 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19799 relayCmd : function(cmd, value){
19801 this.execCmd(cmd, value);
19802 this.owner.fireEvent('editorevent', this);
19803 //this.updateToolbar();
19804 this.owner.deferFocus();
19808 * Executes a Midas editor command directly on the editor document.
19809 * For visual commands, you should use {@link #relayCmd} instead.
19810 * <b>This should only be called after the editor is initialized.</b>
19811 * @param {String} cmd The Midas command
19812 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19814 execCmd : function(cmd, value){
19815 this.doc.execCommand(cmd, false, value === undefined ? null : value);
19822 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19824 * @param {String} text | dom node..
19826 insertAtCursor : function(text)
19831 if(!this.activated){
19837 var r = this.doc.selection.createRange();
19848 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19852 // from jquery ui (MIT licenced)
19854 var win = this.win;
19856 if (win.getSelection && win.getSelection().getRangeAt) {
19857 range = win.getSelection().getRangeAt(0);
19858 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19859 range.insertNode(node);
19860 } else if (win.document.selection && win.document.selection.createRange) {
19861 // no firefox support
19862 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19863 win.document.selection.createRange().pasteHTML(txt);
19865 // no firefox support
19866 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19867 this.execCmd('InsertHTML', txt);
19876 mozKeyPress : function(e){
19878 var c = e.getCharCode(), cmd;
19881 c = String.fromCharCode(c).toLowerCase();
19895 this.cleanUpPaste.defer(100, this);
19903 e.preventDefault();
19911 fixKeys : function(){ // load time branching for fastest keydown performance
19913 return function(e){
19914 var k = e.getKey(), r;
19917 r = this.doc.selection.createRange();
19920 r.pasteHTML('    ');
19927 r = this.doc.selection.createRange();
19929 var target = r.parentElement();
19930 if(!target || target.tagName.toLowerCase() != 'li'){
19932 r.pasteHTML('<br />');
19938 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19939 this.cleanUpPaste.defer(100, this);
19945 }else if(Roo.isOpera){
19946 return function(e){
19947 var k = e.getKey();
19951 this.execCmd('InsertHTML','    ');
19954 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19955 this.cleanUpPaste.defer(100, this);
19960 }else if(Roo.isSafari){
19961 return function(e){
19962 var k = e.getKey();
19966 this.execCmd('InsertText','\t');
19970 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19971 this.cleanUpPaste.defer(100, this);
19979 getAllAncestors: function()
19981 var p = this.getSelectedNode();
19984 a.push(p); // push blank onto stack..
19985 p = this.getParentElement();
19989 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19993 a.push(this.doc.body);
19997 lastSelNode : false,
20000 getSelection : function()
20002 this.assignDocWin();
20003 return Roo.isIE ? this.doc.selection : this.win.getSelection();
20006 getSelectedNode: function()
20008 // this may only work on Gecko!!!
20010 // should we cache this!!!!
20015 var range = this.createRange(this.getSelection()).cloneRange();
20018 var parent = range.parentElement();
20020 var testRange = range.duplicate();
20021 testRange.moveToElementText(parent);
20022 if (testRange.inRange(range)) {
20025 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20028 parent = parent.parentElement;
20033 // is ancestor a text element.
20034 var ac = range.commonAncestorContainer;
20035 if (ac.nodeType == 3) {
20036 ac = ac.parentNode;
20039 var ar = ac.childNodes;
20042 var other_nodes = [];
20043 var has_other_nodes = false;
20044 for (var i=0;i<ar.length;i++) {
20045 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
20048 // fullly contained node.
20050 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20055 // probably selected..
20056 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20057 other_nodes.push(ar[i]);
20061 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
20066 has_other_nodes = true;
20068 if (!nodes.length && other_nodes.length) {
20069 nodes= other_nodes;
20071 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20077 createRange: function(sel)
20079 // this has strange effects when using with
20080 // top toolbar - not sure if it's a great idea.
20081 //this.editor.contentWindow.focus();
20082 if (typeof sel != "undefined") {
20084 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20086 return this.doc.createRange();
20089 return this.doc.createRange();
20092 getParentElement: function()
20095 this.assignDocWin();
20096 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20098 var range = this.createRange(sel);
20101 var p = range.commonAncestorContainer;
20102 while (p.nodeType == 3) { // text node
20113 * Range intersection.. the hard stuff...
20117 * [ -- selected range --- ]
20121 * if end is before start or hits it. fail.
20122 * if start is after end or hits it fail.
20124 * if either hits (but other is outside. - then it's not
20130 // @see http://www.thismuchiknow.co.uk/?p=64.
20131 rangeIntersectsNode : function(range, node)
20133 var nodeRange = node.ownerDocument.createRange();
20135 nodeRange.selectNode(node);
20137 nodeRange.selectNodeContents(node);
20140 var rangeStartRange = range.cloneRange();
20141 rangeStartRange.collapse(true);
20143 var rangeEndRange = range.cloneRange();
20144 rangeEndRange.collapse(false);
20146 var nodeStartRange = nodeRange.cloneRange();
20147 nodeStartRange.collapse(true);
20149 var nodeEndRange = nodeRange.cloneRange();
20150 nodeEndRange.collapse(false);
20152 return rangeStartRange.compareBoundaryPoints(
20153 Range.START_TO_START, nodeEndRange) == -1 &&
20154 rangeEndRange.compareBoundaryPoints(
20155 Range.START_TO_START, nodeStartRange) == 1;
20159 rangeCompareNode : function(range, node)
20161 var nodeRange = node.ownerDocument.createRange();
20163 nodeRange.selectNode(node);
20165 nodeRange.selectNodeContents(node);
20169 range.collapse(true);
20171 nodeRange.collapse(true);
20173 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20174 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
20176 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20178 var nodeIsBefore = ss == 1;
20179 var nodeIsAfter = ee == -1;
20181 if (nodeIsBefore && nodeIsAfter) {
20184 if (!nodeIsBefore && nodeIsAfter) {
20185 return 1; //right trailed.
20188 if (nodeIsBefore && !nodeIsAfter) {
20189 return 2; // left trailed.
20195 // private? - in a new class?
20196 cleanUpPaste : function()
20198 // cleans up the whole document..
20199 Roo.log('cleanuppaste');
20201 this.cleanUpChildren(this.doc.body);
20202 var clean = this.cleanWordChars(this.doc.body.innerHTML);
20203 if (clean != this.doc.body.innerHTML) {
20204 this.doc.body.innerHTML = clean;
20209 cleanWordChars : function(input) {// change the chars to hex code
20210 var he = Roo.HtmlEditorCore;
20212 var output = input;
20213 Roo.each(he.swapCodes, function(sw) {
20214 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20216 output = output.replace(swapper, sw[1]);
20223 cleanUpChildren : function (n)
20225 if (!n.childNodes.length) {
20228 for (var i = n.childNodes.length-1; i > -1 ; i--) {
20229 this.cleanUpChild(n.childNodes[i]);
20236 cleanUpChild : function (node)
20239 //console.log(node);
20240 if (node.nodeName == "#text") {
20241 // clean up silly Windows -- stuff?
20244 if (node.nodeName == "#comment") {
20245 node.parentNode.removeChild(node);
20246 // clean up silly Windows -- stuff?
20249 var lcname = node.tagName.toLowerCase();
20250 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20251 // whitelist of tags..
20253 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20255 node.parentNode.removeChild(node);
20260 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20262 // remove <a name=....> as rendering on yahoo mailer is borked with this.
20263 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20265 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20266 // remove_keep_children = true;
20269 if (remove_keep_children) {
20270 this.cleanUpChildren(node);
20271 // inserts everything just before this node...
20272 while (node.childNodes.length) {
20273 var cn = node.childNodes[0];
20274 node.removeChild(cn);
20275 node.parentNode.insertBefore(cn, node);
20277 node.parentNode.removeChild(node);
20281 if (!node.attributes || !node.attributes.length) {
20282 this.cleanUpChildren(node);
20286 function cleanAttr(n,v)
20289 if (v.match(/^\./) || v.match(/^\//)) {
20292 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20295 if (v.match(/^#/)) {
20298 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20299 node.removeAttribute(n);
20303 var cwhite = this.cwhite;
20304 var cblack = this.cblack;
20306 function cleanStyle(n,v)
20308 if (v.match(/expression/)) { //XSS?? should we even bother..
20309 node.removeAttribute(n);
20313 var parts = v.split(/;/);
20316 Roo.each(parts, function(p) {
20317 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20321 var l = p.split(':').shift().replace(/\s+/g,'');
20322 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20324 if ( cwhite.length && cblack.indexOf(l) > -1) {
20325 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20326 //node.removeAttribute(n);
20330 // only allow 'c whitelisted system attributes'
20331 if ( cwhite.length && cwhite.indexOf(l) < 0) {
20332 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20333 //node.removeAttribute(n);
20343 if (clean.length) {
20344 node.setAttribute(n, clean.join(';'));
20346 node.removeAttribute(n);
20352 for (var i = node.attributes.length-1; i > -1 ; i--) {
20353 var a = node.attributes[i];
20356 if (a.name.toLowerCase().substr(0,2)=='on') {
20357 node.removeAttribute(a.name);
20360 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20361 node.removeAttribute(a.name);
20364 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20365 cleanAttr(a.name,a.value); // fixme..
20368 if (a.name == 'style') {
20369 cleanStyle(a.name,a.value);
20372 /// clean up MS crap..
20373 // tecnically this should be a list of valid class'es..
20376 if (a.name == 'class') {
20377 if (a.value.match(/^Mso/)) {
20378 node.className = '';
20381 if (a.value.match(/body/)) {
20382 node.className = '';
20393 this.cleanUpChildren(node);
20399 * Clean up MS wordisms...
20401 cleanWord : function(node)
20406 this.cleanWord(this.doc.body);
20409 if (node.nodeName == "#text") {
20410 // clean up silly Windows -- stuff?
20413 if (node.nodeName == "#comment") {
20414 node.parentNode.removeChild(node);
20415 // clean up silly Windows -- stuff?
20419 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20420 node.parentNode.removeChild(node);
20424 // remove - but keep children..
20425 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20426 while (node.childNodes.length) {
20427 var cn = node.childNodes[0];
20428 node.removeChild(cn);
20429 node.parentNode.insertBefore(cn, node);
20431 node.parentNode.removeChild(node);
20432 this.iterateChildren(node, this.cleanWord);
20436 if (node.className.length) {
20438 var cn = node.className.split(/\W+/);
20440 Roo.each(cn, function(cls) {
20441 if (cls.match(/Mso[a-zA-Z]+/)) {
20446 node.className = cna.length ? cna.join(' ') : '';
20448 node.removeAttribute("class");
20452 if (node.hasAttribute("lang")) {
20453 node.removeAttribute("lang");
20456 if (node.hasAttribute("style")) {
20458 var styles = node.getAttribute("style").split(";");
20460 Roo.each(styles, function(s) {
20461 if (!s.match(/:/)) {
20464 var kv = s.split(":");
20465 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20468 // what ever is left... we allow.
20471 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20472 if (!nstyle.length) {
20473 node.removeAttribute('style');
20476 this.iterateChildren(node, this.cleanWord);
20482 * iterateChildren of a Node, calling fn each time, using this as the scole..
20483 * @param {DomNode} node node to iterate children of.
20484 * @param {Function} fn method of this class to call on each item.
20486 iterateChildren : function(node, fn)
20488 if (!node.childNodes.length) {
20491 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20492 fn.call(this, node.childNodes[i])
20498 * cleanTableWidths.
20500 * Quite often pasting from word etc.. results in tables with column and widths.
20501 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20504 cleanTableWidths : function(node)
20509 this.cleanTableWidths(this.doc.body);
20514 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20517 Roo.log(node.tagName);
20518 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20519 this.iterateChildren(node, this.cleanTableWidths);
20522 if (node.hasAttribute('width')) {
20523 node.removeAttribute('width');
20527 if (node.hasAttribute("style")) {
20530 var styles = node.getAttribute("style").split(";");
20532 Roo.each(styles, function(s) {
20533 if (!s.match(/:/)) {
20536 var kv = s.split(":");
20537 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20540 // what ever is left... we allow.
20543 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20544 if (!nstyle.length) {
20545 node.removeAttribute('style');
20549 this.iterateChildren(node, this.cleanTableWidths);
20557 domToHTML : function(currentElement, depth, nopadtext) {
20559 depth = depth || 0;
20560 nopadtext = nopadtext || false;
20562 if (!currentElement) {
20563 return this.domToHTML(this.doc.body);
20566 //Roo.log(currentElement);
20568 var allText = false;
20569 var nodeName = currentElement.nodeName;
20570 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20572 if (nodeName == '#text') {
20574 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20579 if (nodeName != 'BODY') {
20582 // Prints the node tagName, such as <A>, <IMG>, etc
20585 for(i = 0; i < currentElement.attributes.length;i++) {
20587 var aname = currentElement.attributes.item(i).name;
20588 if (!currentElement.attributes.item(i).value.length) {
20591 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20594 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20603 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20606 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20611 // Traverse the tree
20613 var currentElementChild = currentElement.childNodes.item(i);
20614 var allText = true;
20615 var innerHTML = '';
20617 while (currentElementChild) {
20618 // Formatting code (indent the tree so it looks nice on the screen)
20619 var nopad = nopadtext;
20620 if (lastnode == 'SPAN') {
20624 if (currentElementChild.nodeName == '#text') {
20625 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20626 toadd = nopadtext ? toadd : toadd.trim();
20627 if (!nopad && toadd.length > 80) {
20628 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20630 innerHTML += toadd;
20633 currentElementChild = currentElement.childNodes.item(i);
20639 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20641 // Recursively traverse the tree structure of the child node
20642 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20643 lastnode = currentElementChild.nodeName;
20645 currentElementChild=currentElement.childNodes.item(i);
20651 // The remaining code is mostly for formatting the tree
20652 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20657 ret+= "</"+tagName+">";
20663 applyBlacklists : function()
20665 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20666 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20670 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20671 if (b.indexOf(tag) > -1) {
20674 this.white.push(tag);
20678 Roo.each(w, function(tag) {
20679 if (b.indexOf(tag) > -1) {
20682 if (this.white.indexOf(tag) > -1) {
20685 this.white.push(tag);
20690 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20691 if (w.indexOf(tag) > -1) {
20694 this.black.push(tag);
20698 Roo.each(b, function(tag) {
20699 if (w.indexOf(tag) > -1) {
20702 if (this.black.indexOf(tag) > -1) {
20705 this.black.push(tag);
20710 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20711 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20715 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20716 if (b.indexOf(tag) > -1) {
20719 this.cwhite.push(tag);
20723 Roo.each(w, function(tag) {
20724 if (b.indexOf(tag) > -1) {
20727 if (this.cwhite.indexOf(tag) > -1) {
20730 this.cwhite.push(tag);
20735 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20736 if (w.indexOf(tag) > -1) {
20739 this.cblack.push(tag);
20743 Roo.each(b, function(tag) {
20744 if (w.indexOf(tag) > -1) {
20747 if (this.cblack.indexOf(tag) > -1) {
20750 this.cblack.push(tag);
20755 setStylesheets : function(stylesheets)
20757 if(typeof(stylesheets) == 'string'){
20758 Roo.get(this.iframe.contentDocument.head).createChild({
20760 rel : 'stylesheet',
20769 Roo.each(stylesheets, function(s) {
20774 Roo.get(_this.iframe.contentDocument.head).createChild({
20776 rel : 'stylesheet',
20785 removeStylesheets : function()
20789 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20794 // hide stuff that is not compatible
20808 * @event specialkey
20812 * @cfg {String} fieldClass @hide
20815 * @cfg {String} focusClass @hide
20818 * @cfg {String} autoCreate @hide
20821 * @cfg {String} inputType @hide
20824 * @cfg {String} invalidClass @hide
20827 * @cfg {String} invalidText @hide
20830 * @cfg {String} msgFx @hide
20833 * @cfg {String} validateOnBlur @hide
20837 Roo.HtmlEditorCore.white = [
20838 'area', 'br', 'img', 'input', 'hr', 'wbr',
20840 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
20841 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
20842 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
20843 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
20844 'table', 'ul', 'xmp',
20846 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
20849 'dir', 'menu', 'ol', 'ul', 'dl',
20855 Roo.HtmlEditorCore.black = [
20856 // 'embed', 'object', // enable - backend responsiblity to clean thiese
20858 'base', 'basefont', 'bgsound', 'blink', 'body',
20859 'frame', 'frameset', 'head', 'html', 'ilayer',
20860 'iframe', 'layer', 'link', 'meta', 'object',
20861 'script', 'style' ,'title', 'xml' // clean later..
20863 Roo.HtmlEditorCore.clean = [
20864 'script', 'style', 'title', 'xml'
20866 Roo.HtmlEditorCore.remove = [
20871 Roo.HtmlEditorCore.ablack = [
20875 Roo.HtmlEditorCore.aclean = [
20876 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
20880 Roo.HtmlEditorCore.pwhite= [
20881 'http', 'https', 'mailto'
20884 // white listed style attributes.
20885 Roo.HtmlEditorCore.cwhite= [
20886 // 'text-align', /// default is to allow most things..
20892 // black listed style attributes.
20893 Roo.HtmlEditorCore.cblack= [
20894 // 'font-size' -- this can be set by the project
20898 Roo.HtmlEditorCore.swapCodes =[
20917 * @class Roo.bootstrap.HtmlEditor
20918 * @extends Roo.bootstrap.TextArea
20919 * Bootstrap HtmlEditor class
20922 * Create a new HtmlEditor
20923 * @param {Object} config The config object
20926 Roo.bootstrap.HtmlEditor = function(config){
20927 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20928 if (!this.toolbars) {
20929 this.toolbars = [];
20931 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20934 * @event initialize
20935 * Fires when the editor is fully initialized (including the iframe)
20936 * @param {HtmlEditor} this
20941 * Fires when the editor is first receives the focus. Any insertion must wait
20942 * until after this event.
20943 * @param {HtmlEditor} this
20947 * @event beforesync
20948 * Fires before the textarea is updated with content from the editor iframe. Return false
20949 * to cancel the sync.
20950 * @param {HtmlEditor} this
20951 * @param {String} html
20955 * @event beforepush
20956 * Fires before the iframe editor is updated with content from the textarea. Return false
20957 * to cancel the push.
20958 * @param {HtmlEditor} this
20959 * @param {String} html
20964 * Fires when the textarea is updated with content from the editor iframe.
20965 * @param {HtmlEditor} this
20966 * @param {String} html
20971 * Fires when the iframe editor is updated with content from the textarea.
20972 * @param {HtmlEditor} this
20973 * @param {String} html
20977 * @event editmodechange
20978 * Fires when the editor switches edit modes
20979 * @param {HtmlEditor} this
20980 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20982 editmodechange: true,
20984 * @event editorevent
20985 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20986 * @param {HtmlEditor} this
20990 * @event firstfocus
20991 * Fires when on first focus - needed by toolbars..
20992 * @param {HtmlEditor} this
20997 * Auto save the htmlEditor value as a file into Events
20998 * @param {HtmlEditor} this
21002 * @event savedpreview
21003 * preview the saved version of htmlEditor
21004 * @param {HtmlEditor} this
21011 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
21015 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21020 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21025 * @cfg {Number} height (in pixels)
21029 * @cfg {Number} width (in pixels)
21034 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21037 stylesheets: false,
21042 // private properties
21043 validationEvent : false,
21045 initialized : false,
21048 onFocus : Roo.emptyFn,
21050 hideMode:'offsets',
21053 tbContainer : false,
21055 toolbarContainer :function() {
21056 return this.wrap.select('.x-html-editor-tb',true).first();
21060 * Protected method that will not generally be called directly. It
21061 * is called when the editor creates its toolbar. Override this method if you need to
21062 * add custom toolbar buttons.
21063 * @param {HtmlEditor} editor
21065 createToolbar : function(){
21067 Roo.log("create toolbars");
21069 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21070 this.toolbars[0].render(this.toolbarContainer());
21074 // if (!editor.toolbars || !editor.toolbars.length) {
21075 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21078 // for (var i =0 ; i < editor.toolbars.length;i++) {
21079 // editor.toolbars[i] = Roo.factory(
21080 // typeof(editor.toolbars[i]) == 'string' ?
21081 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
21082 // Roo.bootstrap.HtmlEditor);
21083 // editor.toolbars[i].init(editor);
21089 onRender : function(ct, position)
21091 // Roo.log("Call onRender: " + this.xtype);
21093 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21095 this.wrap = this.inputEl().wrap({
21096 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21099 this.editorcore.onRender(ct, position);
21101 if (this.resizable) {
21102 this.resizeEl = new Roo.Resizable(this.wrap, {
21106 minHeight : this.height,
21107 height: this.height,
21108 handles : this.resizable,
21111 resize : function(r, w, h) {
21112 _t.onResize(w,h); // -something
21118 this.createToolbar(this);
21121 if(!this.width && this.resizable){
21122 this.setSize(this.wrap.getSize());
21124 if (this.resizeEl) {
21125 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21126 // should trigger onReize..
21132 onResize : function(w, h)
21134 Roo.log('resize: ' +w + ',' + h );
21135 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21139 if(this.inputEl() ){
21140 if(typeof w == 'number'){
21141 var aw = w - this.wrap.getFrameWidth('lr');
21142 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21145 if(typeof h == 'number'){
21146 var tbh = -11; // fixme it needs to tool bar size!
21147 for (var i =0; i < this.toolbars.length;i++) {
21148 // fixme - ask toolbars for heights?
21149 tbh += this.toolbars[i].el.getHeight();
21150 //if (this.toolbars[i].footer) {
21151 // tbh += this.toolbars[i].footer.el.getHeight();
21159 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21160 ah -= 5; // knock a few pixes off for look..
21161 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21165 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21166 this.editorcore.onResize(ew,eh);
21171 * Toggles the editor between standard and source edit mode.
21172 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21174 toggleSourceEdit : function(sourceEditMode)
21176 this.editorcore.toggleSourceEdit(sourceEditMode);
21178 if(this.editorcore.sourceEditMode){
21179 Roo.log('editor - showing textarea');
21182 // Roo.log(this.syncValue());
21184 this.inputEl().removeClass(['hide', 'x-hidden']);
21185 this.inputEl().dom.removeAttribute('tabIndex');
21186 this.inputEl().focus();
21188 Roo.log('editor - hiding textarea');
21190 // Roo.log(this.pushValue());
21193 this.inputEl().addClass(['hide', 'x-hidden']);
21194 this.inputEl().dom.setAttribute('tabIndex', -1);
21195 //this.deferFocus();
21198 if(this.resizable){
21199 this.setSize(this.wrap.getSize());
21202 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21205 // private (for BoxComponent)
21206 adjustSize : Roo.BoxComponent.prototype.adjustSize,
21208 // private (for BoxComponent)
21209 getResizeEl : function(){
21213 // private (for BoxComponent)
21214 getPositionEl : function(){
21219 initEvents : function(){
21220 this.originalValue = this.getValue();
21224 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21227 // markInvalid : Roo.emptyFn,
21229 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21232 // clearInvalid : Roo.emptyFn,
21234 setValue : function(v){
21235 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21236 this.editorcore.pushValue();
21241 deferFocus : function(){
21242 this.focus.defer(10, this);
21246 focus : function(){
21247 this.editorcore.focus();
21253 onDestroy : function(){
21259 for (var i =0; i < this.toolbars.length;i++) {
21260 // fixme - ask toolbars for heights?
21261 this.toolbars[i].onDestroy();
21264 this.wrap.dom.innerHTML = '';
21265 this.wrap.remove();
21270 onFirstFocus : function(){
21271 //Roo.log("onFirstFocus");
21272 this.editorcore.onFirstFocus();
21273 for (var i =0; i < this.toolbars.length;i++) {
21274 this.toolbars[i].onFirstFocus();
21280 syncValue : function()
21282 this.editorcore.syncValue();
21285 pushValue : function()
21287 this.editorcore.pushValue();
21291 // hide stuff that is not compatible
21305 * @event specialkey
21309 * @cfg {String} fieldClass @hide
21312 * @cfg {String} focusClass @hide
21315 * @cfg {String} autoCreate @hide
21318 * @cfg {String} inputType @hide
21321 * @cfg {String} invalidClass @hide
21324 * @cfg {String} invalidText @hide
21327 * @cfg {String} msgFx @hide
21330 * @cfg {String} validateOnBlur @hide
21339 Roo.namespace('Roo.bootstrap.htmleditor');
21341 * @class Roo.bootstrap.HtmlEditorToolbar1
21346 new Roo.bootstrap.HtmlEditor({
21349 new Roo.bootstrap.HtmlEditorToolbar1({
21350 disable : { fonts: 1 , format: 1, ..., ... , ...],
21356 * @cfg {Object} disable List of elements to disable..
21357 * @cfg {Array} btns List of additional buttons.
21361 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21364 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21367 Roo.apply(this, config);
21369 // default disabled, based on 'good practice'..
21370 this.disable = this.disable || {};
21371 Roo.applyIf(this.disable, {
21374 specialElements : true
21376 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21378 this.editor = config.editor;
21379 this.editorcore = config.editor.editorcore;
21381 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21383 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21384 // dont call parent... till later.
21386 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
21391 editorcore : false,
21396 "h1","h2","h3","h4","h5","h6",
21398 "abbr", "acronym", "address", "cite", "samp", "var",
21402 onRender : function(ct, position)
21404 // Roo.log("Call onRender: " + this.xtype);
21406 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21408 this.el.dom.style.marginBottom = '0';
21410 var editorcore = this.editorcore;
21411 var editor= this.editor;
21414 var btn = function(id,cmd , toggle, handler){
21416 var event = toggle ? 'toggle' : 'click';
21421 xns: Roo.bootstrap,
21424 enableToggle:toggle !== false,
21426 pressed : toggle ? false : null,
21429 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21430 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21439 xns: Roo.bootstrap,
21440 glyphicon : 'font',
21444 xns: Roo.bootstrap,
21448 Roo.each(this.formats, function(f) {
21449 style.menu.items.push({
21451 xns: Roo.bootstrap,
21452 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21457 editorcore.insertTag(this.tagname);
21464 children.push(style);
21467 btn('bold',false,true);
21468 btn('italic',false,true);
21469 btn('align-left', 'justifyleft',true);
21470 btn('align-center', 'justifycenter',true);
21471 btn('align-right' , 'justifyright',true);
21472 btn('link', false, false, function(btn) {
21473 //Roo.log("create link?");
21474 var url = prompt(this.createLinkText, this.defaultLinkValue);
21475 if(url && url != 'http:/'+'/'){
21476 this.editorcore.relayCmd('createlink', url);
21479 btn('list','insertunorderedlist',true);
21480 btn('pencil', false,true, function(btn){
21483 this.toggleSourceEdit(btn.pressed);
21489 xns: Roo.bootstrap,
21494 xns: Roo.bootstrap,
21499 cog.menu.items.push({
21501 xns: Roo.bootstrap,
21502 html : Clean styles,
21507 editorcore.insertTag(this.tagname);
21516 this.xtype = 'NavSimplebar';
21518 for(var i=0;i< children.length;i++) {
21520 this.buttons.add(this.addxtypeChild(children[i]));
21524 editor.on('editorevent', this.updateToolbar, this);
21526 onBtnClick : function(id)
21528 this.editorcore.relayCmd(id);
21529 this.editorcore.focus();
21533 * Protected method that will not generally be called directly. It triggers
21534 * a toolbar update by reading the markup state of the current selection in the editor.
21536 updateToolbar: function(){
21538 if(!this.editorcore.activated){
21539 this.editor.onFirstFocus(); // is this neeed?
21543 var btns = this.buttons;
21544 var doc = this.editorcore.doc;
21545 btns.get('bold').setActive(doc.queryCommandState('bold'));
21546 btns.get('italic').setActive(doc.queryCommandState('italic'));
21547 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21549 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21550 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21551 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21553 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21554 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21557 var ans = this.editorcore.getAllAncestors();
21558 if (this.formatCombo) {
21561 var store = this.formatCombo.store;
21562 this.formatCombo.setValue("");
21563 for (var i =0; i < ans.length;i++) {
21564 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21566 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21574 // hides menus... - so this cant be on a menu...
21575 Roo.bootstrap.MenuMgr.hideAll();
21577 Roo.bootstrap.MenuMgr.hideAll();
21578 //this.editorsyncValue();
21580 onFirstFocus: function() {
21581 this.buttons.each(function(item){
21585 toggleSourceEdit : function(sourceEditMode){
21588 if(sourceEditMode){
21589 Roo.log("disabling buttons");
21590 this.buttons.each( function(item){
21591 if(item.cmd != 'pencil'){
21597 Roo.log("enabling buttons");
21598 if(this.editorcore.initialized){
21599 this.buttons.each( function(item){
21605 Roo.log("calling toggole on editor");
21606 // tell the editor that it's been pressed..
21607 this.editor.toggleSourceEdit(sourceEditMode);
21617 * @class Roo.bootstrap.Table.AbstractSelectionModel
21618 * @extends Roo.util.Observable
21619 * Abstract base class for grid SelectionModels. It provides the interface that should be
21620 * implemented by descendant classes. This class should not be directly instantiated.
21623 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21624 this.locked = false;
21625 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21629 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21630 /** @ignore Called by the grid automatically. Do not call directly. */
21631 init : function(grid){
21637 * Locks the selections.
21640 this.locked = true;
21644 * Unlocks the selections.
21646 unlock : function(){
21647 this.locked = false;
21651 * Returns true if the selections are locked.
21652 * @return {Boolean}
21654 isLocked : function(){
21655 return this.locked;
21659 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21660 * @class Roo.bootstrap.Table.RowSelectionModel
21661 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21662 * It supports multiple selections and keyboard selection/navigation.
21664 * @param {Object} config
21667 Roo.bootstrap.Table.RowSelectionModel = function(config){
21668 Roo.apply(this, config);
21669 this.selections = new Roo.util.MixedCollection(false, function(o){
21674 this.lastActive = false;
21678 * @event selectionchange
21679 * Fires when the selection changes
21680 * @param {SelectionModel} this
21682 "selectionchange" : true,
21684 * @event afterselectionchange
21685 * Fires after the selection changes (eg. by key press or clicking)
21686 * @param {SelectionModel} this
21688 "afterselectionchange" : true,
21690 * @event beforerowselect
21691 * Fires when a row is selected being selected, return false to cancel.
21692 * @param {SelectionModel} this
21693 * @param {Number} rowIndex The selected index
21694 * @param {Boolean} keepExisting False if other selections will be cleared
21696 "beforerowselect" : true,
21699 * Fires when a row is selected.
21700 * @param {SelectionModel} this
21701 * @param {Number} rowIndex The selected index
21702 * @param {Roo.data.Record} r The record
21704 "rowselect" : true,
21706 * @event rowdeselect
21707 * Fires when a row is deselected.
21708 * @param {SelectionModel} this
21709 * @param {Number} rowIndex The selected index
21711 "rowdeselect" : true
21713 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21714 this.locked = false;
21717 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21719 * @cfg {Boolean} singleSelect
21720 * True to allow selection of only one row at a time (defaults to false)
21722 singleSelect : false,
21725 initEvents : function(){
21727 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21728 this.grid.on("mousedown", this.handleMouseDown, this);
21729 }else{ // allow click to work like normal
21730 this.grid.on("rowclick", this.handleDragableRowClick, this);
21733 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21734 "up" : function(e){
21736 this.selectPrevious(e.shiftKey);
21737 }else if(this.last !== false && this.lastActive !== false){
21738 var last = this.last;
21739 this.selectRange(this.last, this.lastActive-1);
21740 this.grid.getView().focusRow(this.lastActive);
21741 if(last !== false){
21745 this.selectFirstRow();
21747 this.fireEvent("afterselectionchange", this);
21749 "down" : function(e){
21751 this.selectNext(e.shiftKey);
21752 }else if(this.last !== false && this.lastActive !== false){
21753 var last = this.last;
21754 this.selectRange(this.last, this.lastActive+1);
21755 this.grid.getView().focusRow(this.lastActive);
21756 if(last !== false){
21760 this.selectFirstRow();
21762 this.fireEvent("afterselectionchange", this);
21767 var view = this.grid.view;
21768 view.on("refresh", this.onRefresh, this);
21769 view.on("rowupdated", this.onRowUpdated, this);
21770 view.on("rowremoved", this.onRemove, this);
21774 onRefresh : function(){
21775 var ds = this.grid.dataSource, i, v = this.grid.view;
21776 var s = this.selections;
21777 s.each(function(r){
21778 if((i = ds.indexOfId(r.id)) != -1){
21787 onRemove : function(v, index, r){
21788 this.selections.remove(r);
21792 onRowUpdated : function(v, index, r){
21793 if(this.isSelected(r)){
21794 v.onRowSelect(index);
21800 * @param {Array} records The records to select
21801 * @param {Boolean} keepExisting (optional) True to keep existing selections
21803 selectRecords : function(records, keepExisting){
21805 this.clearSelections();
21807 var ds = this.grid.dataSource;
21808 for(var i = 0, len = records.length; i < len; i++){
21809 this.selectRow(ds.indexOf(records[i]), true);
21814 * Gets the number of selected rows.
21817 getCount : function(){
21818 return this.selections.length;
21822 * Selects the first row in the grid.
21824 selectFirstRow : function(){
21829 * Select the last row.
21830 * @param {Boolean} keepExisting (optional) True to keep existing selections
21832 selectLastRow : function(keepExisting){
21833 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21837 * Selects the row immediately following the last selected row.
21838 * @param {Boolean} keepExisting (optional) True to keep existing selections
21840 selectNext : function(keepExisting){
21841 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21842 this.selectRow(this.last+1, keepExisting);
21843 this.grid.getView().focusRow(this.last);
21848 * Selects the row that precedes the last selected row.
21849 * @param {Boolean} keepExisting (optional) True to keep existing selections
21851 selectPrevious : function(keepExisting){
21853 this.selectRow(this.last-1, keepExisting);
21854 this.grid.getView().focusRow(this.last);
21859 * Returns the selected records
21860 * @return {Array} Array of selected records
21862 getSelections : function(){
21863 return [].concat(this.selections.items);
21867 * Returns the first selected record.
21870 getSelected : function(){
21871 return this.selections.itemAt(0);
21876 * Clears all selections.
21878 clearSelections : function(fast){
21883 var ds = this.grid.dataSource;
21884 var s = this.selections;
21885 s.each(function(r){
21886 this.deselectRow(ds.indexOfId(r.id));
21890 this.selections.clear();
21897 * Selects all rows.
21899 selectAll : function(){
21903 this.selections.clear();
21904 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21905 this.selectRow(i, true);
21910 * Returns True if there is a selection.
21911 * @return {Boolean}
21913 hasSelection : function(){
21914 return this.selections.length > 0;
21918 * Returns True if the specified row is selected.
21919 * @param {Number/Record} record The record or index of the record to check
21920 * @return {Boolean}
21922 isSelected : function(index){
21923 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21924 return (r && this.selections.key(r.id) ? true : false);
21928 * Returns True if the specified record id is selected.
21929 * @param {String} id The id of record to check
21930 * @return {Boolean}
21932 isIdSelected : function(id){
21933 return (this.selections.key(id) ? true : false);
21937 handleMouseDown : function(e, t){
21938 var view = this.grid.getView(), rowIndex;
21939 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21942 if(e.shiftKey && this.last !== false){
21943 var last = this.last;
21944 this.selectRange(last, rowIndex, e.ctrlKey);
21945 this.last = last; // reset the last
21946 view.focusRow(rowIndex);
21948 var isSelected = this.isSelected(rowIndex);
21949 if(e.button !== 0 && isSelected){
21950 view.focusRow(rowIndex);
21951 }else if(e.ctrlKey && isSelected){
21952 this.deselectRow(rowIndex);
21953 }else if(!isSelected){
21954 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21955 view.focusRow(rowIndex);
21958 this.fireEvent("afterselectionchange", this);
21961 handleDragableRowClick : function(grid, rowIndex, e)
21963 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21964 this.selectRow(rowIndex, false);
21965 grid.view.focusRow(rowIndex);
21966 this.fireEvent("afterselectionchange", this);
21971 * Selects multiple rows.
21972 * @param {Array} rows Array of the indexes of the row to select
21973 * @param {Boolean} keepExisting (optional) True to keep existing selections
21975 selectRows : function(rows, keepExisting){
21977 this.clearSelections();
21979 for(var i = 0, len = rows.length; i < len; i++){
21980 this.selectRow(rows[i], true);
21985 * Selects a range of rows. All rows in between startRow and endRow are also selected.
21986 * @param {Number} startRow The index of the first row in the range
21987 * @param {Number} endRow The index of the last row in the range
21988 * @param {Boolean} keepExisting (optional) True to retain existing selections
21990 selectRange : function(startRow, endRow, keepExisting){
21995 this.clearSelections();
21997 if(startRow <= endRow){
21998 for(var i = startRow; i <= endRow; i++){
21999 this.selectRow(i, true);
22002 for(var i = startRow; i >= endRow; i--){
22003 this.selectRow(i, true);
22009 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22010 * @param {Number} startRow The index of the first row in the range
22011 * @param {Number} endRow The index of the last row in the range
22013 deselectRange : function(startRow, endRow, preventViewNotify){
22017 for(var i = startRow; i <= endRow; i++){
22018 this.deselectRow(i, preventViewNotify);
22024 * @param {Number} row The index of the row to select
22025 * @param {Boolean} keepExisting (optional) True to keep existing selections
22027 selectRow : function(index, keepExisting, preventViewNotify){
22028 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22031 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22032 if(!keepExisting || this.singleSelect){
22033 this.clearSelections();
22035 var r = this.grid.dataSource.getAt(index);
22036 this.selections.add(r);
22037 this.last = this.lastActive = index;
22038 if(!preventViewNotify){
22039 this.grid.getView().onRowSelect(index);
22041 this.fireEvent("rowselect", this, index, r);
22042 this.fireEvent("selectionchange", this);
22048 * @param {Number} row The index of the row to deselect
22050 deselectRow : function(index, preventViewNotify){
22054 if(this.last == index){
22057 if(this.lastActive == index){
22058 this.lastActive = false;
22060 var r = this.grid.dataSource.getAt(index);
22061 this.selections.remove(r);
22062 if(!preventViewNotify){
22063 this.grid.getView().onRowDeselect(index);
22065 this.fireEvent("rowdeselect", this, index);
22066 this.fireEvent("selectionchange", this);
22070 restoreLast : function(){
22072 this.last = this._last;
22077 acceptsNav : function(row, col, cm){
22078 return !cm.isHidden(col) && cm.isCellEditable(col, row);
22082 onEditorKey : function(field, e){
22083 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22088 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22090 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22092 }else if(k == e.ENTER && !e.ctrlKey){
22096 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22098 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22100 }else if(k == e.ESC){
22104 g.startEditing(newCell[0], newCell[1]);
22109 * Ext JS Library 1.1.1
22110 * Copyright(c) 2006-2007, Ext JS, LLC.
22112 * Originally Released Under LGPL - original licence link has changed is not relivant.
22115 * <script type="text/javascript">
22119 * @class Roo.bootstrap.PagingToolbar
22120 * @extends Roo.bootstrap.NavSimplebar
22121 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22123 * Create a new PagingToolbar
22124 * @param {Object} config The config object
22125 * @param {Roo.data.Store} store
22127 Roo.bootstrap.PagingToolbar = function(config)
22129 // old args format still supported... - xtype is prefered..
22130 // created from xtype...
22132 this.ds = config.dataSource;
22134 if (config.store && !this.ds) {
22135 this.store= Roo.factory(config.store, Roo.data);
22136 this.ds = this.store;
22137 this.ds.xmodule = this.xmodule || false;
22140 this.toolbarItems = [];
22141 if (config.items) {
22142 this.toolbarItems = config.items;
22145 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22150 this.bind(this.ds);
22153 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22157 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22159 * @cfg {Roo.data.Store} dataSource
22160 * The underlying data store providing the paged data
22163 * @cfg {String/HTMLElement/Element} container
22164 * container The id or element that will contain the toolbar
22167 * @cfg {Boolean} displayInfo
22168 * True to display the displayMsg (defaults to false)
22171 * @cfg {Number} pageSize
22172 * The number of records to display per page (defaults to 20)
22176 * @cfg {String} displayMsg
22177 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22179 displayMsg : 'Displaying {0} - {1} of {2}',
22181 * @cfg {String} emptyMsg
22182 * The message to display when no records are found (defaults to "No data to display")
22184 emptyMsg : 'No data to display',
22186 * Customizable piece of the default paging text (defaults to "Page")
22189 beforePageText : "Page",
22191 * Customizable piece of the default paging text (defaults to "of %0")
22194 afterPageText : "of {0}",
22196 * Customizable piece of the default paging text (defaults to "First Page")
22199 firstText : "First Page",
22201 * Customizable piece of the default paging text (defaults to "Previous Page")
22204 prevText : "Previous Page",
22206 * Customizable piece of the default paging text (defaults to "Next Page")
22209 nextText : "Next Page",
22211 * Customizable piece of the default paging text (defaults to "Last Page")
22214 lastText : "Last Page",
22216 * Customizable piece of the default paging text (defaults to "Refresh")
22219 refreshText : "Refresh",
22223 onRender : function(ct, position)
22225 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22226 this.navgroup.parentId = this.id;
22227 this.navgroup.onRender(this.el, null);
22228 // add the buttons to the navgroup
22230 if(this.displayInfo){
22231 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22232 this.displayEl = this.el.select('.x-paging-info', true).first();
22233 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22234 // this.displayEl = navel.el.select('span',true).first();
22240 Roo.each(_this.buttons, function(e){ // this might need to use render????
22241 Roo.factory(e).onRender(_this.el, null);
22245 Roo.each(_this.toolbarItems, function(e) {
22246 _this.navgroup.addItem(e);
22250 this.first = this.navgroup.addItem({
22251 tooltip: this.firstText,
22253 icon : 'fa fa-backward',
22255 preventDefault: true,
22256 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22259 this.prev = this.navgroup.addItem({
22260 tooltip: this.prevText,
22262 icon : 'fa fa-step-backward',
22264 preventDefault: true,
22265 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
22267 //this.addSeparator();
22270 var field = this.navgroup.addItem( {
22272 cls : 'x-paging-position',
22274 html : this.beforePageText +
22275 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22276 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
22279 this.field = field.el.select('input', true).first();
22280 this.field.on("keydown", this.onPagingKeydown, this);
22281 this.field.on("focus", function(){this.dom.select();});
22284 this.afterTextEl = field.el.select('.x-paging-after',true).first();
22285 //this.field.setHeight(18);
22286 //this.addSeparator();
22287 this.next = this.navgroup.addItem({
22288 tooltip: this.nextText,
22290 html : ' <i class="fa fa-step-forward">',
22292 preventDefault: true,
22293 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
22295 this.last = this.navgroup.addItem({
22296 tooltip: this.lastText,
22297 icon : 'fa fa-forward',
22300 preventDefault: true,
22301 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
22303 //this.addSeparator();
22304 this.loading = this.navgroup.addItem({
22305 tooltip: this.refreshText,
22306 icon: 'fa fa-refresh',
22307 preventDefault: true,
22308 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22314 updateInfo : function(){
22315 if(this.displayEl){
22316 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22317 var msg = count == 0 ?
22321 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
22323 this.displayEl.update(msg);
22328 onLoad : function(ds, r, o){
22329 this.cursor = o.params ? o.params.start : 0;
22330 var d = this.getPageData(),
22334 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22335 this.field.dom.value = ap;
22336 this.first.setDisabled(ap == 1);
22337 this.prev.setDisabled(ap == 1);
22338 this.next.setDisabled(ap == ps);
22339 this.last.setDisabled(ap == ps);
22340 this.loading.enable();
22345 getPageData : function(){
22346 var total = this.ds.getTotalCount();
22349 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22350 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22355 onLoadError : function(){
22356 this.loading.enable();
22360 onPagingKeydown : function(e){
22361 var k = e.getKey();
22362 var d = this.getPageData();
22364 var v = this.field.dom.value, pageNum;
22365 if(!v || isNaN(pageNum = parseInt(v, 10))){
22366 this.field.dom.value = d.activePage;
22369 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22370 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22373 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))
22375 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22376 this.field.dom.value = pageNum;
22377 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22380 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22382 var v = this.field.dom.value, pageNum;
22383 var increment = (e.shiftKey) ? 10 : 1;
22384 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22387 if(!v || isNaN(pageNum = parseInt(v, 10))) {
22388 this.field.dom.value = d.activePage;
22391 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22393 this.field.dom.value = parseInt(v, 10) + increment;
22394 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22395 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22402 beforeLoad : function(){
22404 this.loading.disable();
22409 onClick : function(which){
22418 ds.load({params:{start: 0, limit: this.pageSize}});
22421 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22424 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22427 var total = ds.getTotalCount();
22428 var extra = total % this.pageSize;
22429 var lastStart = extra ? (total - extra) : total-this.pageSize;
22430 ds.load({params:{start: lastStart, limit: this.pageSize}});
22433 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22439 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22440 * @param {Roo.data.Store} store The data store to unbind
22442 unbind : function(ds){
22443 ds.un("beforeload", this.beforeLoad, this);
22444 ds.un("load", this.onLoad, this);
22445 ds.un("loadexception", this.onLoadError, this);
22446 ds.un("remove", this.updateInfo, this);
22447 ds.un("add", this.updateInfo, this);
22448 this.ds = undefined;
22452 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22453 * @param {Roo.data.Store} store The data store to bind
22455 bind : function(ds){
22456 ds.on("beforeload", this.beforeLoad, this);
22457 ds.on("load", this.onLoad, this);
22458 ds.on("loadexception", this.onLoadError, this);
22459 ds.on("remove", this.updateInfo, this);
22460 ds.on("add", this.updateInfo, this);
22471 * @class Roo.bootstrap.MessageBar
22472 * @extends Roo.bootstrap.Component
22473 * Bootstrap MessageBar class
22474 * @cfg {String} html contents of the MessageBar
22475 * @cfg {String} weight (info | success | warning | danger) default info
22476 * @cfg {String} beforeClass insert the bar before the given class
22477 * @cfg {Boolean} closable (true | false) default false
22478 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22481 * Create a new Element
22482 * @param {Object} config The config object
22485 Roo.bootstrap.MessageBar = function(config){
22486 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22489 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22495 beforeClass: 'bootstrap-sticky-wrap',
22497 getAutoCreate : function(){
22501 cls: 'alert alert-dismissable alert-' + this.weight,
22506 html: this.html || ''
22512 cfg.cls += ' alert-messages-fixed';
22526 onRender : function(ct, position)
22528 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22531 var cfg = Roo.apply({}, this.getAutoCreate());
22535 cfg.cls += ' ' + this.cls;
22538 cfg.style = this.style;
22540 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22542 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22545 this.el.select('>button.close').on('click', this.hide, this);
22551 if (!this.rendered) {
22557 this.fireEvent('show', this);
22563 if (!this.rendered) {
22569 this.fireEvent('hide', this);
22572 update : function()
22574 // var e = this.el.dom.firstChild;
22576 // if(this.closable){
22577 // e = e.nextSibling;
22580 // e.data = this.html || '';
22582 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22598 * @class Roo.bootstrap.Graph
22599 * @extends Roo.bootstrap.Component
22600 * Bootstrap Graph class
22604 @cfg {String} graphtype bar | vbar | pie
22605 @cfg {number} g_x coodinator | centre x (pie)
22606 @cfg {number} g_y coodinator | centre y (pie)
22607 @cfg {number} g_r radius (pie)
22608 @cfg {number} g_height height of the chart (respected by all elements in the set)
22609 @cfg {number} g_width width of the chart (respected by all elements in the set)
22610 @cfg {Object} title The title of the chart
22613 -opts (object) options for the chart
22615 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22616 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22618 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.
22619 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22621 o stretch (boolean)
22623 -opts (object) options for the pie
22626 o startAngle (number)
22627 o endAngle (number)
22631 * Create a new Input
22632 * @param {Object} config The config object
22635 Roo.bootstrap.Graph = function(config){
22636 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22642 * The img click event for the img.
22643 * @param {Roo.EventObject} e
22649 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22660 //g_colors: this.colors,
22667 getAutoCreate : function(){
22678 onRender : function(ct,position){
22679 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22680 this.raphael = Raphael(this.el.dom);
22682 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22683 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22684 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22685 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22687 r.text(160, 10, "Single Series Chart").attr(txtattr);
22688 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22689 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22690 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22692 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22693 r.barchart(330, 10, 300, 220, data1);
22694 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22695 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22698 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22699 // r.barchart(30, 30, 560, 250, xdata, {
22700 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22701 // axis : "0 0 1 1",
22702 // axisxlabels : xdata
22703 // //yvalues : cols,
22706 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22708 // this.load(null,xdata,{
22709 // axis : "0 0 1 1",
22710 // axisxlabels : xdata
22715 load : function(graphtype,xdata,opts){
22716 this.raphael.clear();
22718 graphtype = this.graphtype;
22723 var r = this.raphael,
22724 fin = function () {
22725 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22727 fout = function () {
22728 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22730 pfin = function() {
22731 this.sector.stop();
22732 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22735 this.label[0].stop();
22736 this.label[0].attr({ r: 7.5 });
22737 this.label[1].attr({ "font-weight": 800 });
22740 pfout = function() {
22741 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22744 this.label[0].animate({ r: 5 }, 500, "bounce");
22745 this.label[1].attr({ "font-weight": 400 });
22751 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22754 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22757 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
22758 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22760 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22767 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22772 setTitle: function(o)
22777 initEvents: function() {
22780 this.el.on('click', this.onClick, this);
22784 onClick : function(e)
22786 Roo.log('img onclick');
22787 this.fireEvent('click', this, e);
22799 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22802 * @class Roo.bootstrap.dash.NumberBox
22803 * @extends Roo.bootstrap.Component
22804 * Bootstrap NumberBox class
22805 * @cfg {String} headline Box headline
22806 * @cfg {String} content Box content
22807 * @cfg {String} icon Box icon
22808 * @cfg {String} footer Footer text
22809 * @cfg {String} fhref Footer href
22812 * Create a new NumberBox
22813 * @param {Object} config The config object
22817 Roo.bootstrap.dash.NumberBox = function(config){
22818 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22822 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
22831 getAutoCreate : function(){
22835 cls : 'small-box ',
22843 cls : 'roo-headline',
22844 html : this.headline
22848 cls : 'roo-content',
22849 html : this.content
22863 cls : 'ion ' + this.icon
22872 cls : 'small-box-footer',
22873 href : this.fhref || '#',
22877 cfg.cn.push(footer);
22884 onRender : function(ct,position){
22885 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22892 setHeadline: function (value)
22894 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22897 setFooter: function (value, href)
22899 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22902 this.el.select('a.small-box-footer',true).first().attr('href', href);
22907 setContent: function (value)
22909 this.el.select('.roo-content',true).first().dom.innerHTML = value;
22912 initEvents: function()
22926 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22929 * @class Roo.bootstrap.dash.TabBox
22930 * @extends Roo.bootstrap.Component
22931 * Bootstrap TabBox class
22932 * @cfg {String} title Title of the TabBox
22933 * @cfg {String} icon Icon of the TabBox
22934 * @cfg {Boolean} showtabs (true|false) show the tabs default true
22935 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22938 * Create a new TabBox
22939 * @param {Object} config The config object
22943 Roo.bootstrap.dash.TabBox = function(config){
22944 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22949 * When a pane is added
22950 * @param {Roo.bootstrap.dash.TabPane} pane
22954 * @event activatepane
22955 * When a pane is activated
22956 * @param {Roo.bootstrap.dash.TabPane} pane
22958 "activatepane" : true
22966 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
22971 tabScrollable : false,
22973 getChildContainer : function()
22975 return this.el.select('.tab-content', true).first();
22978 getAutoCreate : function(){
22982 cls: 'pull-left header',
22990 cls: 'fa ' + this.icon
22996 cls: 'nav nav-tabs pull-right',
23002 if(this.tabScrollable){
23009 cls: 'nav nav-tabs pull-right',
23020 cls: 'nav-tabs-custom',
23025 cls: 'tab-content no-padding',
23033 initEvents : function()
23035 //Roo.log('add add pane handler');
23036 this.on('addpane', this.onAddPane, this);
23039 * Updates the box title
23040 * @param {String} html to set the title to.
23042 setTitle : function(value)
23044 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23046 onAddPane : function(pane)
23048 this.panes.push(pane);
23049 //Roo.log('addpane');
23051 // tabs are rendere left to right..
23052 if(!this.showtabs){
23056 var ctr = this.el.select('.nav-tabs', true).first();
23059 var existing = ctr.select('.nav-tab',true);
23060 var qty = existing.getCount();;
23063 var tab = ctr.createChild({
23065 cls : 'nav-tab' + (qty ? '' : ' active'),
23073 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23076 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23078 pane.el.addClass('active');
23083 onTabClick : function(ev,un,ob,pane)
23085 //Roo.log('tab - prev default');
23086 ev.preventDefault();
23089 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23090 pane.tab.addClass('active');
23091 //Roo.log(pane.title);
23092 this.getChildContainer().select('.tab-pane',true).removeClass('active');
23093 // technically we should have a deactivate event.. but maybe add later.
23094 // and it should not de-activate the selected tab...
23095 this.fireEvent('activatepane', pane);
23096 pane.el.addClass('active');
23097 pane.fireEvent('activate');
23102 getActivePane : function()
23105 Roo.each(this.panes, function(p) {
23106 if(p.el.hasClass('active')){
23127 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23129 * @class Roo.bootstrap.TabPane
23130 * @extends Roo.bootstrap.Component
23131 * Bootstrap TabPane class
23132 * @cfg {Boolean} active (false | true) Default false
23133 * @cfg {String} title title of panel
23137 * Create a new TabPane
23138 * @param {Object} config The config object
23141 Roo.bootstrap.dash.TabPane = function(config){
23142 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23148 * When a pane is activated
23149 * @param {Roo.bootstrap.dash.TabPane} pane
23156 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
23161 // the tabBox that this is attached to.
23164 getAutoCreate : function()
23172 cfg.cls += ' active';
23177 initEvents : function()
23179 //Roo.log('trigger add pane handler');
23180 this.parent().fireEvent('addpane', this)
23184 * Updates the tab title
23185 * @param {String} html to set the title to.
23187 setTitle: function(str)
23193 this.tab.select('a', true).first().dom.innerHTML = str;
23210 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23213 * @class Roo.bootstrap.menu.Menu
23214 * @extends Roo.bootstrap.Component
23215 * Bootstrap Menu class - container for Menu
23216 * @cfg {String} html Text of the menu
23217 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23218 * @cfg {String} icon Font awesome icon
23219 * @cfg {String} pos Menu align to (top | bottom) default bottom
23223 * Create a new Menu
23224 * @param {Object} config The config object
23228 Roo.bootstrap.menu.Menu = function(config){
23229 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23233 * @event beforeshow
23234 * Fires before this menu is displayed
23235 * @param {Roo.bootstrap.menu.Menu} this
23239 * @event beforehide
23240 * Fires before this menu is hidden
23241 * @param {Roo.bootstrap.menu.Menu} this
23246 * Fires after this menu is displayed
23247 * @param {Roo.bootstrap.menu.Menu} this
23252 * Fires after this menu is hidden
23253 * @param {Roo.bootstrap.menu.Menu} this
23258 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23259 * @param {Roo.bootstrap.menu.Menu} this
23260 * @param {Roo.EventObject} e
23267 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
23271 weight : 'default',
23276 getChildContainer : function() {
23277 if(this.isSubMenu){
23281 return this.el.select('ul.dropdown-menu', true).first();
23284 getAutoCreate : function()
23289 cls : 'roo-menu-text',
23297 cls : 'fa ' + this.icon
23308 cls : 'dropdown-button btn btn-' + this.weight,
23313 cls : 'dropdown-toggle btn btn-' + this.weight,
23323 cls : 'dropdown-menu'
23329 if(this.pos == 'top'){
23330 cfg.cls += ' dropup';
23333 if(this.isSubMenu){
23336 cls : 'dropdown-menu'
23343 onRender : function(ct, position)
23345 this.isSubMenu = ct.hasClass('dropdown-submenu');
23347 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23350 initEvents : function()
23352 if(this.isSubMenu){
23356 this.hidden = true;
23358 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23359 this.triggerEl.on('click', this.onTriggerPress, this);
23361 this.buttonEl = this.el.select('button.dropdown-button', true).first();
23362 this.buttonEl.on('click', this.onClick, this);
23368 if(this.isSubMenu){
23372 return this.el.select('ul.dropdown-menu', true).first();
23375 onClick : function(e)
23377 this.fireEvent("click", this, e);
23380 onTriggerPress : function(e)
23382 if (this.isVisible()) {
23389 isVisible : function(){
23390 return !this.hidden;
23395 this.fireEvent("beforeshow", this);
23397 this.hidden = false;
23398 this.el.addClass('open');
23400 Roo.get(document).on("mouseup", this.onMouseUp, this);
23402 this.fireEvent("show", this);
23409 this.fireEvent("beforehide", this);
23411 this.hidden = true;
23412 this.el.removeClass('open');
23414 Roo.get(document).un("mouseup", this.onMouseUp);
23416 this.fireEvent("hide", this);
23419 onMouseUp : function()
23433 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23436 * @class Roo.bootstrap.menu.Item
23437 * @extends Roo.bootstrap.Component
23438 * Bootstrap MenuItem class
23439 * @cfg {Boolean} submenu (true | false) default false
23440 * @cfg {String} html text of the item
23441 * @cfg {String} href the link
23442 * @cfg {Boolean} disable (true | false) default false
23443 * @cfg {Boolean} preventDefault (true | false) default true
23444 * @cfg {String} icon Font awesome icon
23445 * @cfg {String} pos Submenu align to (left | right) default right
23449 * Create a new Item
23450 * @param {Object} config The config object
23454 Roo.bootstrap.menu.Item = function(config){
23455 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23459 * Fires when the mouse is hovering over this menu
23460 * @param {Roo.bootstrap.menu.Item} this
23461 * @param {Roo.EventObject} e
23466 * Fires when the mouse exits this menu
23467 * @param {Roo.bootstrap.menu.Item} this
23468 * @param {Roo.EventObject} e
23474 * The raw click event for the entire grid.
23475 * @param {Roo.EventObject} e
23481 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23486 preventDefault: true,
23491 getAutoCreate : function()
23496 cls : 'roo-menu-item-text',
23504 cls : 'fa ' + this.icon
23513 href : this.href || '#',
23520 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23524 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23526 if(this.pos == 'left'){
23527 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23534 initEvents : function()
23536 this.el.on('mouseover', this.onMouseOver, this);
23537 this.el.on('mouseout', this.onMouseOut, this);
23539 this.el.select('a', true).first().on('click', this.onClick, this);
23543 onClick : function(e)
23545 if(this.preventDefault){
23546 e.preventDefault();
23549 this.fireEvent("click", this, e);
23552 onMouseOver : function(e)
23554 if(this.submenu && this.pos == 'left'){
23555 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23558 this.fireEvent("mouseover", this, e);
23561 onMouseOut : function(e)
23563 this.fireEvent("mouseout", this, e);
23575 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23578 * @class Roo.bootstrap.menu.Separator
23579 * @extends Roo.bootstrap.Component
23580 * Bootstrap Separator class
23583 * Create a new Separator
23584 * @param {Object} config The config object
23588 Roo.bootstrap.menu.Separator = function(config){
23589 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23592 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23594 getAutoCreate : function(){
23615 * @class Roo.bootstrap.Tooltip
23616 * Bootstrap Tooltip class
23617 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23618 * to determine which dom element triggers the tooltip.
23620 * It needs to add support for additional attributes like tooltip-position
23623 * Create a new Toolti
23624 * @param {Object} config The config object
23627 Roo.bootstrap.Tooltip = function(config){
23628 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23631 Roo.apply(Roo.bootstrap.Tooltip, {
23633 * @function init initialize tooltip monitoring.
23637 currentTip : false,
23638 currentRegion : false,
23644 Roo.get(document).on('mouseover', this.enter ,this);
23645 Roo.get(document).on('mouseout', this.leave, this);
23648 this.currentTip = new Roo.bootstrap.Tooltip();
23651 enter : function(ev)
23653 var dom = ev.getTarget();
23655 //Roo.log(['enter',dom]);
23656 var el = Roo.fly(dom);
23657 if (this.currentEl) {
23659 //Roo.log(this.currentEl);
23660 //Roo.log(this.currentEl.contains(dom));
23661 if (this.currentEl == el) {
23664 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23670 if (this.currentTip.el) {
23671 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23676 // you can not look for children, as if el is the body.. then everythign is the child..
23677 if (!el.attr('tooltip')) { //
23678 if (!el.select("[tooltip]").elements.length) {
23681 // is the mouse over this child...?
23682 bindEl = el.select("[tooltip]").first();
23683 var xy = ev.getXY();
23684 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23685 //Roo.log("not in region.");
23688 //Roo.log("child element over..");
23691 this.currentEl = bindEl;
23692 this.currentTip.bind(bindEl);
23693 this.currentRegion = Roo.lib.Region.getRegion(dom);
23694 this.currentTip.enter();
23697 leave : function(ev)
23699 var dom = ev.getTarget();
23700 //Roo.log(['leave',dom]);
23701 if (!this.currentEl) {
23706 if (dom != this.currentEl.dom) {
23709 var xy = ev.getXY();
23710 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23713 // only activate leave if mouse cursor is outside... bounding box..
23718 if (this.currentTip) {
23719 this.currentTip.leave();
23721 //Roo.log('clear currentEl');
23722 this.currentEl = false;
23727 'left' : ['r-l', [-2,0], 'right'],
23728 'right' : ['l-r', [2,0], 'left'],
23729 'bottom' : ['t-b', [0,2], 'top'],
23730 'top' : [ 'b-t', [0,-2], 'bottom']
23736 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
23741 delay : null, // can be { show : 300 , hide: 500}
23745 hoverState : null, //???
23747 placement : 'bottom',
23749 getAutoCreate : function(){
23756 cls : 'tooltip-arrow'
23759 cls : 'tooltip-inner'
23766 bind : function(el)
23772 enter : function () {
23774 if (this.timeout != null) {
23775 clearTimeout(this.timeout);
23778 this.hoverState = 'in';
23779 //Roo.log("enter - show");
23780 if (!this.delay || !this.delay.show) {
23785 this.timeout = setTimeout(function () {
23786 if (_t.hoverState == 'in') {
23789 }, this.delay.show);
23793 clearTimeout(this.timeout);
23795 this.hoverState = 'out';
23796 if (!this.delay || !this.delay.hide) {
23802 this.timeout = setTimeout(function () {
23803 //Roo.log("leave - timeout");
23805 if (_t.hoverState == 'out') {
23807 Roo.bootstrap.Tooltip.currentEl = false;
23815 this.render(document.body);
23818 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23820 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23822 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23824 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23826 var placement = typeof this.placement == 'function' ?
23827 this.placement.call(this, this.el, on_el) :
23830 var autoToken = /\s?auto?\s?/i;
23831 var autoPlace = autoToken.test(placement);
23833 placement = placement.replace(autoToken, '') || 'top';
23837 //this.el.setXY([0,0]);
23839 //this.el.dom.style.display='block';
23841 //this.el.appendTo(on_el);
23843 var p = this.getPosition();
23844 var box = this.el.getBox();
23850 var align = Roo.bootstrap.Tooltip.alignment[placement];
23852 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23854 if(placement == 'top' || placement == 'bottom'){
23856 placement = 'right';
23859 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23860 placement = 'left';
23864 align = Roo.bootstrap.Tooltip.alignment[placement];
23866 this.el.alignTo(this.bindEl, align[0],align[1]);
23867 //var arrow = this.el.select('.arrow',true).first();
23868 //arrow.set(align[2],
23870 this.el.addClass(placement);
23872 this.el.addClass('in fade');
23874 this.hoverState = null;
23876 if (this.el.hasClass('fade')) {
23887 //this.el.setXY([0,0]);
23888 this.el.removeClass('in');
23904 * @class Roo.bootstrap.LocationPicker
23905 * @extends Roo.bootstrap.Component
23906 * Bootstrap LocationPicker class
23907 * @cfg {Number} latitude Position when init default 0
23908 * @cfg {Number} longitude Position when init default 0
23909 * @cfg {Number} zoom default 15
23910 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23911 * @cfg {Boolean} mapTypeControl default false
23912 * @cfg {Boolean} disableDoubleClickZoom default false
23913 * @cfg {Boolean} scrollwheel default true
23914 * @cfg {Boolean} streetViewControl default false
23915 * @cfg {Number} radius default 0
23916 * @cfg {String} locationName
23917 * @cfg {Boolean} draggable default true
23918 * @cfg {Boolean} enableAutocomplete default false
23919 * @cfg {Boolean} enableReverseGeocode default true
23920 * @cfg {String} markerTitle
23923 * Create a new LocationPicker
23924 * @param {Object} config The config object
23928 Roo.bootstrap.LocationPicker = function(config){
23930 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23935 * Fires when the picker initialized.
23936 * @param {Roo.bootstrap.LocationPicker} this
23937 * @param {Google Location} location
23941 * @event positionchanged
23942 * Fires when the picker position changed.
23943 * @param {Roo.bootstrap.LocationPicker} this
23944 * @param {Google Location} location
23946 positionchanged : true,
23949 * Fires when the map resize.
23950 * @param {Roo.bootstrap.LocationPicker} this
23955 * Fires when the map show.
23956 * @param {Roo.bootstrap.LocationPicker} this
23961 * Fires when the map hide.
23962 * @param {Roo.bootstrap.LocationPicker} this
23967 * Fires when click the map.
23968 * @param {Roo.bootstrap.LocationPicker} this
23969 * @param {Map event} e
23973 * @event mapRightClick
23974 * Fires when right click the map.
23975 * @param {Roo.bootstrap.LocationPicker} this
23976 * @param {Map event} e
23978 mapRightClick : true,
23980 * @event markerClick
23981 * Fires when click the marker.
23982 * @param {Roo.bootstrap.LocationPicker} this
23983 * @param {Map event} e
23985 markerClick : true,
23987 * @event markerRightClick
23988 * Fires when right click the marker.
23989 * @param {Roo.bootstrap.LocationPicker} this
23990 * @param {Map event} e
23992 markerRightClick : true,
23994 * @event OverlayViewDraw
23995 * Fires when OverlayView Draw
23996 * @param {Roo.bootstrap.LocationPicker} this
23998 OverlayViewDraw : true,
24000 * @event OverlayViewOnAdd
24001 * Fires when OverlayView Draw
24002 * @param {Roo.bootstrap.LocationPicker} this
24004 OverlayViewOnAdd : true,
24006 * @event OverlayViewOnRemove
24007 * Fires when OverlayView Draw
24008 * @param {Roo.bootstrap.LocationPicker} this
24010 OverlayViewOnRemove : true,
24012 * @event OverlayViewShow
24013 * Fires when OverlayView Draw
24014 * @param {Roo.bootstrap.LocationPicker} this
24015 * @param {Pixel} cpx
24017 OverlayViewShow : true,
24019 * @event OverlayViewHide
24020 * Fires when OverlayView Draw
24021 * @param {Roo.bootstrap.LocationPicker} this
24023 OverlayViewHide : true,
24025 * @event loadexception
24026 * Fires when load google lib failed.
24027 * @param {Roo.bootstrap.LocationPicker} this
24029 loadexception : true
24034 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
24036 gMapContext: false,
24042 mapTypeControl: false,
24043 disableDoubleClickZoom: false,
24045 streetViewControl: false,
24049 enableAutocomplete: false,
24050 enableReverseGeocode: true,
24053 getAutoCreate: function()
24058 cls: 'roo-location-picker'
24064 initEvents: function(ct, position)
24066 if(!this.el.getWidth() || this.isApplied()){
24070 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24075 initial: function()
24077 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24078 this.fireEvent('loadexception', this);
24082 if(!this.mapTypeId){
24083 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24086 this.gMapContext = this.GMapContext();
24088 this.initOverlayView();
24090 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24094 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24095 _this.setPosition(_this.gMapContext.marker.position);
24098 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24099 _this.fireEvent('mapClick', this, event);
24103 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24104 _this.fireEvent('mapRightClick', this, event);
24108 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24109 _this.fireEvent('markerClick', this, event);
24113 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24114 _this.fireEvent('markerRightClick', this, event);
24118 this.setPosition(this.gMapContext.location);
24120 this.fireEvent('initial', this, this.gMapContext.location);
24123 initOverlayView: function()
24127 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24131 _this.fireEvent('OverlayViewDraw', _this);
24136 _this.fireEvent('OverlayViewOnAdd', _this);
24139 onRemove: function()
24141 _this.fireEvent('OverlayViewOnRemove', _this);
24144 show: function(cpx)
24146 _this.fireEvent('OverlayViewShow', _this, cpx);
24151 _this.fireEvent('OverlayViewHide', _this);
24157 fromLatLngToContainerPixel: function(event)
24159 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24162 isApplied: function()
24164 return this.getGmapContext() == false ? false : true;
24167 getGmapContext: function()
24169 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24172 GMapContext: function()
24174 var position = new google.maps.LatLng(this.latitude, this.longitude);
24176 var _map = new google.maps.Map(this.el.dom, {
24179 mapTypeId: this.mapTypeId,
24180 mapTypeControl: this.mapTypeControl,
24181 disableDoubleClickZoom: this.disableDoubleClickZoom,
24182 scrollwheel: this.scrollwheel,
24183 streetViewControl: this.streetViewControl,
24184 locationName: this.locationName,
24185 draggable: this.draggable,
24186 enableAutocomplete: this.enableAutocomplete,
24187 enableReverseGeocode: this.enableReverseGeocode
24190 var _marker = new google.maps.Marker({
24191 position: position,
24193 title: this.markerTitle,
24194 draggable: this.draggable
24201 location: position,
24202 radius: this.radius,
24203 locationName: this.locationName,
24204 addressComponents: {
24205 formatted_address: null,
24206 addressLine1: null,
24207 addressLine2: null,
24209 streetNumber: null,
24213 stateOrProvince: null
24216 domContainer: this.el.dom,
24217 geodecoder: new google.maps.Geocoder()
24221 drawCircle: function(center, radius, options)
24223 if (this.gMapContext.circle != null) {
24224 this.gMapContext.circle.setMap(null);
24228 options = Roo.apply({}, options, {
24229 strokeColor: "#0000FF",
24230 strokeOpacity: .35,
24232 fillColor: "#0000FF",
24236 options.map = this.gMapContext.map;
24237 options.radius = radius;
24238 options.center = center;
24239 this.gMapContext.circle = new google.maps.Circle(options);
24240 return this.gMapContext.circle;
24246 setPosition: function(location)
24248 this.gMapContext.location = location;
24249 this.gMapContext.marker.setPosition(location);
24250 this.gMapContext.map.panTo(location);
24251 this.drawCircle(location, this.gMapContext.radius, {});
24255 if (this.gMapContext.settings.enableReverseGeocode) {
24256 this.gMapContext.geodecoder.geocode({
24257 latLng: this.gMapContext.location
24258 }, function(results, status) {
24260 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24261 _this.gMapContext.locationName = results[0].formatted_address;
24262 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24264 _this.fireEvent('positionchanged', this, location);
24271 this.fireEvent('positionchanged', this, location);
24276 google.maps.event.trigger(this.gMapContext.map, "resize");
24278 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24280 this.fireEvent('resize', this);
24283 setPositionByLatLng: function(latitude, longitude)
24285 this.setPosition(new google.maps.LatLng(latitude, longitude));
24288 getCurrentPosition: function()
24291 latitude: this.gMapContext.location.lat(),
24292 longitude: this.gMapContext.location.lng()
24296 getAddressName: function()
24298 return this.gMapContext.locationName;
24301 getAddressComponents: function()
24303 return this.gMapContext.addressComponents;
24306 address_component_from_google_geocode: function(address_components)
24310 for (var i = 0; i < address_components.length; i++) {
24311 var component = address_components[i];
24312 if (component.types.indexOf("postal_code") >= 0) {
24313 result.postalCode = component.short_name;
24314 } else if (component.types.indexOf("street_number") >= 0) {
24315 result.streetNumber = component.short_name;
24316 } else if (component.types.indexOf("route") >= 0) {
24317 result.streetName = component.short_name;
24318 } else if (component.types.indexOf("neighborhood") >= 0) {
24319 result.city = component.short_name;
24320 } else if (component.types.indexOf("locality") >= 0) {
24321 result.city = component.short_name;
24322 } else if (component.types.indexOf("sublocality") >= 0) {
24323 result.district = component.short_name;
24324 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24325 result.stateOrProvince = component.short_name;
24326 } else if (component.types.indexOf("country") >= 0) {
24327 result.country = component.short_name;
24331 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24332 result.addressLine2 = "";
24336 setZoomLevel: function(zoom)
24338 this.gMapContext.map.setZoom(zoom);
24351 this.fireEvent('show', this);
24362 this.fireEvent('hide', this);
24367 Roo.apply(Roo.bootstrap.LocationPicker, {
24369 OverlayView : function(map, options)
24371 options = options || {};
24385 * @class Roo.bootstrap.Alert
24386 * @extends Roo.bootstrap.Component
24387 * Bootstrap Alert class
24388 * @cfg {String} title The title of alert
24389 * @cfg {String} html The content of alert
24390 * @cfg {String} weight ( success | info | warning | danger )
24391 * @cfg {String} faicon font-awesomeicon
24394 * Create a new alert
24395 * @param {Object} config The config object
24399 Roo.bootstrap.Alert = function(config){
24400 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24404 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
24411 getAutoCreate : function()
24420 cls : 'roo-alert-icon'
24425 cls : 'roo-alert-title',
24430 cls : 'roo-alert-text',
24437 cfg.cn[0].cls += ' fa ' + this.faicon;
24441 cfg.cls += ' alert-' + this.weight;
24447 initEvents: function()
24449 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24452 setTitle : function(str)
24454 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24457 setText : function(str)
24459 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24462 setWeight : function(weight)
24465 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24468 this.weight = weight;
24470 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24473 setIcon : function(icon)
24476 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24479 this.faicon = icon;
24481 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24502 * @class Roo.bootstrap.UploadCropbox
24503 * @extends Roo.bootstrap.Component
24504 * Bootstrap UploadCropbox class
24505 * @cfg {String} emptyText show when image has been loaded
24506 * @cfg {String} rotateNotify show when image too small to rotate
24507 * @cfg {Number} errorTimeout default 3000
24508 * @cfg {Number} minWidth default 300
24509 * @cfg {Number} minHeight default 300
24510 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24511 * @cfg {Boolean} isDocument (true|false) default false
24512 * @cfg {String} url action url
24513 * @cfg {String} paramName default 'imageUpload'
24514 * @cfg {String} method default POST
24515 * @cfg {Boolean} loadMask (true|false) default true
24516 * @cfg {Boolean} loadingText default 'Loading...'
24519 * Create a new UploadCropbox
24520 * @param {Object} config The config object
24523 Roo.bootstrap.UploadCropbox = function(config){
24524 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24528 * @event beforeselectfile
24529 * Fire before select file
24530 * @param {Roo.bootstrap.UploadCropbox} this
24532 "beforeselectfile" : true,
24535 * Fire after initEvent
24536 * @param {Roo.bootstrap.UploadCropbox} this
24541 * Fire after initEvent
24542 * @param {Roo.bootstrap.UploadCropbox} this
24543 * @param {String} data
24548 * Fire when preparing the file data
24549 * @param {Roo.bootstrap.UploadCropbox} this
24550 * @param {Object} file
24555 * Fire when get exception
24556 * @param {Roo.bootstrap.UploadCropbox} this
24557 * @param {XMLHttpRequest} xhr
24559 "exception" : true,
24561 * @event beforeloadcanvas
24562 * Fire before load the canvas
24563 * @param {Roo.bootstrap.UploadCropbox} this
24564 * @param {String} src
24566 "beforeloadcanvas" : true,
24569 * Fire when trash image
24570 * @param {Roo.bootstrap.UploadCropbox} this
24575 * Fire when download the image
24576 * @param {Roo.bootstrap.UploadCropbox} this
24580 * @event footerbuttonclick
24581 * Fire when footerbuttonclick
24582 * @param {Roo.bootstrap.UploadCropbox} this
24583 * @param {String} type
24585 "footerbuttonclick" : true,
24589 * @param {Roo.bootstrap.UploadCropbox} this
24594 * Fire when rotate the image
24595 * @param {Roo.bootstrap.UploadCropbox} this
24596 * @param {String} pos
24601 * Fire when inspect the file
24602 * @param {Roo.bootstrap.UploadCropbox} this
24603 * @param {Object} file
24608 * Fire when xhr upload the file
24609 * @param {Roo.bootstrap.UploadCropbox} this
24610 * @param {Object} data
24615 * Fire when arrange the file data
24616 * @param {Roo.bootstrap.UploadCropbox} this
24617 * @param {Object} formData
24622 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24625 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24627 emptyText : 'Click to upload image',
24628 rotateNotify : 'Image is too small to rotate',
24629 errorTimeout : 3000,
24643 cropType : 'image/jpeg',
24645 canvasLoaded : false,
24646 isDocument : false,
24648 paramName : 'imageUpload',
24650 loadingText : 'Loading...',
24653 getAutoCreate : function()
24657 cls : 'roo-upload-cropbox',
24661 cls : 'roo-upload-cropbox-selector',
24666 cls : 'roo-upload-cropbox-body',
24667 style : 'cursor:pointer',
24671 cls : 'roo-upload-cropbox-preview'
24675 cls : 'roo-upload-cropbox-thumb'
24679 cls : 'roo-upload-cropbox-empty-notify',
24680 html : this.emptyText
24684 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24685 html : this.rotateNotify
24691 cls : 'roo-upload-cropbox-footer',
24694 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24704 onRender : function(ct, position)
24706 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24708 if (this.buttons.length) {
24710 Roo.each(this.buttons, function(bb) {
24712 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24714 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24720 this.maskEl = this.el;
24724 initEvents : function()
24726 this.urlAPI = (window.createObjectURL && window) ||
24727 (window.URL && URL.revokeObjectURL && URL) ||
24728 (window.webkitURL && webkitURL);
24730 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24731 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24733 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24734 this.selectorEl.hide();
24736 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24737 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24739 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24740 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24741 this.thumbEl.hide();
24743 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24744 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24746 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24747 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24748 this.errorEl.hide();
24750 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24751 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24752 this.footerEl.hide();
24754 this.setThumbBoxSize();
24760 this.fireEvent('initial', this);
24767 window.addEventListener("resize", function() { _this.resize(); } );
24769 this.bodyEl.on('click', this.beforeSelectFile, this);
24772 this.bodyEl.on('touchstart', this.onTouchStart, this);
24773 this.bodyEl.on('touchmove', this.onTouchMove, this);
24774 this.bodyEl.on('touchend', this.onTouchEnd, this);
24778 this.bodyEl.on('mousedown', this.onMouseDown, this);
24779 this.bodyEl.on('mousemove', this.onMouseMove, this);
24780 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24781 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24782 Roo.get(document).on('mouseup', this.onMouseUp, this);
24785 this.selectorEl.on('change', this.onFileSelected, this);
24791 this.baseScale = 1;
24793 this.baseRotate = 1;
24794 this.dragable = false;
24795 this.pinching = false;
24798 this.cropData = false;
24799 this.notifyEl.dom.innerHTML = this.emptyText;
24801 this.selectorEl.dom.value = '';
24805 resize : function()
24807 if(this.fireEvent('resize', this) != false){
24808 this.setThumbBoxPosition();
24809 this.setCanvasPosition();
24813 onFooterButtonClick : function(e, el, o, type)
24816 case 'rotate-left' :
24817 this.onRotateLeft(e);
24819 case 'rotate-right' :
24820 this.onRotateRight(e);
24823 this.beforeSelectFile(e);
24838 this.fireEvent('footerbuttonclick', this, type);
24841 beforeSelectFile : function(e)
24843 e.preventDefault();
24845 if(this.fireEvent('beforeselectfile', this) != false){
24846 this.selectorEl.dom.click();
24850 onFileSelected : function(e)
24852 e.preventDefault();
24854 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24858 var file = this.selectorEl.dom.files[0];
24860 if(this.fireEvent('inspect', this, file) != false){
24861 this.prepare(file);
24866 trash : function(e)
24868 this.fireEvent('trash', this);
24871 download : function(e)
24873 this.fireEvent('download', this);
24876 loadCanvas : function(src)
24878 if(this.fireEvent('beforeloadcanvas', this, src) != false){
24882 this.imageEl = document.createElement('img');
24886 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24888 this.imageEl.src = src;
24892 onLoadCanvas : function()
24894 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24895 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24897 this.bodyEl.un('click', this.beforeSelectFile, this);
24899 this.notifyEl.hide();
24900 this.thumbEl.show();
24901 this.footerEl.show();
24903 this.baseRotateLevel();
24905 if(this.isDocument){
24906 this.setThumbBoxSize();
24909 this.setThumbBoxPosition();
24911 this.baseScaleLevel();
24917 this.canvasLoaded = true;
24920 this.maskEl.unmask();
24925 setCanvasPosition : function()
24927 if(!this.canvasEl){
24931 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24932 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24934 this.previewEl.setLeft(pw);
24935 this.previewEl.setTop(ph);
24939 onMouseDown : function(e)
24943 this.dragable = true;
24944 this.pinching = false;
24946 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24947 this.dragable = false;
24951 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24952 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24956 onMouseMove : function(e)
24960 if(!this.canvasLoaded){
24964 if (!this.dragable){
24968 var minX = Math.ceil(this.thumbEl.getLeft(true));
24969 var minY = Math.ceil(this.thumbEl.getTop(true));
24971 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24972 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24974 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24975 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24977 x = x - this.mouseX;
24978 y = y - this.mouseY;
24980 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24981 var bgY = Math.ceil(y + this.previewEl.getTop(true));
24983 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24984 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24986 this.previewEl.setLeft(bgX);
24987 this.previewEl.setTop(bgY);
24989 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24990 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24993 onMouseUp : function(e)
24997 this.dragable = false;
25000 onMouseWheel : function(e)
25004 this.startScale = this.scale;
25006 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25008 if(!this.zoomable()){
25009 this.scale = this.startScale;
25018 zoomable : function()
25020 var minScale = this.thumbEl.getWidth() / this.minWidth;
25022 if(this.minWidth < this.minHeight){
25023 minScale = this.thumbEl.getHeight() / this.minHeight;
25026 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25027 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25031 (this.rotate == 0 || this.rotate == 180) &&
25033 width > this.imageEl.OriginWidth ||
25034 height > this.imageEl.OriginHeight ||
25035 (width < this.minWidth && height < this.minHeight)
25043 (this.rotate == 90 || this.rotate == 270) &&
25045 width > this.imageEl.OriginWidth ||
25046 height > this.imageEl.OriginHeight ||
25047 (width < this.minHeight && height < this.minWidth)
25054 !this.isDocument &&
25055 (this.rotate == 0 || this.rotate == 180) &&
25057 width < this.minWidth ||
25058 width > this.imageEl.OriginWidth ||
25059 height < this.minHeight ||
25060 height > this.imageEl.OriginHeight
25067 !this.isDocument &&
25068 (this.rotate == 90 || this.rotate == 270) &&
25070 width < this.minHeight ||
25071 width > this.imageEl.OriginWidth ||
25072 height < this.minWidth ||
25073 height > this.imageEl.OriginHeight
25083 onRotateLeft : function(e)
25085 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25087 var minScale = this.thumbEl.getWidth() / this.minWidth;
25089 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25090 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25092 this.startScale = this.scale;
25094 while (this.getScaleLevel() < minScale){
25096 this.scale = this.scale + 1;
25098 if(!this.zoomable()){
25103 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25104 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25109 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25116 this.scale = this.startScale;
25118 this.onRotateFail();
25123 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25125 if(this.isDocument){
25126 this.setThumbBoxSize();
25127 this.setThumbBoxPosition();
25128 this.setCanvasPosition();
25133 this.fireEvent('rotate', this, 'left');
25137 onRotateRight : function(e)
25139 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25141 var minScale = this.thumbEl.getWidth() / this.minWidth;
25143 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25144 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25146 this.startScale = this.scale;
25148 while (this.getScaleLevel() < minScale){
25150 this.scale = this.scale + 1;
25152 if(!this.zoomable()){
25157 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25158 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25163 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25170 this.scale = this.startScale;
25172 this.onRotateFail();
25177 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25179 if(this.isDocument){
25180 this.setThumbBoxSize();
25181 this.setThumbBoxPosition();
25182 this.setCanvasPosition();
25187 this.fireEvent('rotate', this, 'right');
25190 onRotateFail : function()
25192 this.errorEl.show(true);
25196 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25201 this.previewEl.dom.innerHTML = '';
25203 var canvasEl = document.createElement("canvas");
25205 var contextEl = canvasEl.getContext("2d");
25207 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25208 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25209 var center = this.imageEl.OriginWidth / 2;
25211 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25212 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25213 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25214 center = this.imageEl.OriginHeight / 2;
25217 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25219 contextEl.translate(center, center);
25220 contextEl.rotate(this.rotate * Math.PI / 180);
25222 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25224 this.canvasEl = document.createElement("canvas");
25226 this.contextEl = this.canvasEl.getContext("2d");
25228 switch (this.rotate) {
25231 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25232 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25234 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25239 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25240 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25242 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25243 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);
25247 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25252 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25253 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25255 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25256 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);
25260 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);
25265 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25266 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25268 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25269 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25273 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);
25280 this.previewEl.appendChild(this.canvasEl);
25282 this.setCanvasPosition();
25287 if(!this.canvasLoaded){
25291 var imageCanvas = document.createElement("canvas");
25293 var imageContext = imageCanvas.getContext("2d");
25295 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25296 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25298 var center = imageCanvas.width / 2;
25300 imageContext.translate(center, center);
25302 imageContext.rotate(this.rotate * Math.PI / 180);
25304 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25306 var canvas = document.createElement("canvas");
25308 var context = canvas.getContext("2d");
25310 canvas.width = this.minWidth;
25311 canvas.height = this.minHeight;
25313 switch (this.rotate) {
25316 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25317 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25319 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25320 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25322 var targetWidth = this.minWidth - 2 * x;
25323 var targetHeight = this.minHeight - 2 * y;
25327 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25328 scale = targetWidth / width;
25331 if(x > 0 && y == 0){
25332 scale = targetHeight / height;
25335 if(x > 0 && y > 0){
25336 scale = targetWidth / width;
25338 if(width < height){
25339 scale = targetHeight / height;
25343 context.scale(scale, scale);
25345 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25346 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25348 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25349 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25351 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25356 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25357 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25359 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25360 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25362 var targetWidth = this.minWidth - 2 * x;
25363 var targetHeight = this.minHeight - 2 * y;
25367 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25368 scale = targetWidth / width;
25371 if(x > 0 && y == 0){
25372 scale = targetHeight / height;
25375 if(x > 0 && y > 0){
25376 scale = targetWidth / width;
25378 if(width < height){
25379 scale = targetHeight / height;
25383 context.scale(scale, scale);
25385 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25386 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25388 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25389 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25391 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25393 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25398 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25399 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25401 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25402 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25404 var targetWidth = this.minWidth - 2 * x;
25405 var targetHeight = this.minHeight - 2 * y;
25409 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25410 scale = targetWidth / width;
25413 if(x > 0 && y == 0){
25414 scale = targetHeight / height;
25417 if(x > 0 && y > 0){
25418 scale = targetWidth / width;
25420 if(width < height){
25421 scale = targetHeight / height;
25425 context.scale(scale, scale);
25427 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25428 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25430 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25431 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25433 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25434 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25436 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25441 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25442 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25444 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25445 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25447 var targetWidth = this.minWidth - 2 * x;
25448 var targetHeight = this.minHeight - 2 * y;
25452 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25453 scale = targetWidth / width;
25456 if(x > 0 && y == 0){
25457 scale = targetHeight / height;
25460 if(x > 0 && y > 0){
25461 scale = targetWidth / width;
25463 if(width < height){
25464 scale = targetHeight / height;
25468 context.scale(scale, scale);
25470 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25471 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25473 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25474 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25476 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25478 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25485 this.cropData = canvas.toDataURL(this.cropType);
25487 if(this.fireEvent('crop', this, this.cropData) !== false){
25488 this.process(this.file, this.cropData);
25495 setThumbBoxSize : function()
25499 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25500 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25501 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25503 this.minWidth = width;
25504 this.minHeight = height;
25506 if(this.rotate == 90 || this.rotate == 270){
25507 this.minWidth = height;
25508 this.minHeight = width;
25513 width = Math.ceil(this.minWidth * height / this.minHeight);
25515 if(this.minWidth > this.minHeight){
25517 height = Math.ceil(this.minHeight * width / this.minWidth);
25520 this.thumbEl.setStyle({
25521 width : width + 'px',
25522 height : height + 'px'
25529 setThumbBoxPosition : function()
25531 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25532 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25534 this.thumbEl.setLeft(x);
25535 this.thumbEl.setTop(y);
25539 baseRotateLevel : function()
25541 this.baseRotate = 1;
25544 typeof(this.exif) != 'undefined' &&
25545 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25546 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25548 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25551 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25555 baseScaleLevel : function()
25559 if(this.isDocument){
25561 if(this.baseRotate == 6 || this.baseRotate == 8){
25563 height = this.thumbEl.getHeight();
25564 this.baseScale = height / this.imageEl.OriginWidth;
25566 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25567 width = this.thumbEl.getWidth();
25568 this.baseScale = width / this.imageEl.OriginHeight;
25574 height = this.thumbEl.getHeight();
25575 this.baseScale = height / this.imageEl.OriginHeight;
25577 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25578 width = this.thumbEl.getWidth();
25579 this.baseScale = width / this.imageEl.OriginWidth;
25585 if(this.baseRotate == 6 || this.baseRotate == 8){
25587 width = this.thumbEl.getHeight();
25588 this.baseScale = width / this.imageEl.OriginHeight;
25590 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25591 height = this.thumbEl.getWidth();
25592 this.baseScale = height / this.imageEl.OriginHeight;
25595 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25596 height = this.thumbEl.getWidth();
25597 this.baseScale = height / this.imageEl.OriginHeight;
25599 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25600 width = this.thumbEl.getHeight();
25601 this.baseScale = width / this.imageEl.OriginWidth;
25608 width = this.thumbEl.getWidth();
25609 this.baseScale = width / this.imageEl.OriginWidth;
25611 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25612 height = this.thumbEl.getHeight();
25613 this.baseScale = height / this.imageEl.OriginHeight;
25616 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25618 height = this.thumbEl.getHeight();
25619 this.baseScale = height / this.imageEl.OriginHeight;
25621 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25622 width = this.thumbEl.getWidth();
25623 this.baseScale = width / this.imageEl.OriginWidth;
25631 getScaleLevel : function()
25633 return this.baseScale * Math.pow(1.1, this.scale);
25636 onTouchStart : function(e)
25638 if(!this.canvasLoaded){
25639 this.beforeSelectFile(e);
25643 var touches = e.browserEvent.touches;
25649 if(touches.length == 1){
25650 this.onMouseDown(e);
25654 if(touches.length != 2){
25660 for(var i = 0, finger; finger = touches[i]; i++){
25661 coords.push(finger.pageX, finger.pageY);
25664 var x = Math.pow(coords[0] - coords[2], 2);
25665 var y = Math.pow(coords[1] - coords[3], 2);
25667 this.startDistance = Math.sqrt(x + y);
25669 this.startScale = this.scale;
25671 this.pinching = true;
25672 this.dragable = false;
25676 onTouchMove : function(e)
25678 if(!this.pinching && !this.dragable){
25682 var touches = e.browserEvent.touches;
25689 this.onMouseMove(e);
25695 for(var i = 0, finger; finger = touches[i]; i++){
25696 coords.push(finger.pageX, finger.pageY);
25699 var x = Math.pow(coords[0] - coords[2], 2);
25700 var y = Math.pow(coords[1] - coords[3], 2);
25702 this.endDistance = Math.sqrt(x + y);
25704 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25706 if(!this.zoomable()){
25707 this.scale = this.startScale;
25715 onTouchEnd : function(e)
25717 this.pinching = false;
25718 this.dragable = false;
25722 process : function(file, crop)
25725 this.maskEl.mask(this.loadingText);
25728 this.xhr = new XMLHttpRequest();
25730 file.xhr = this.xhr;
25732 this.xhr.open(this.method, this.url, true);
25735 "Accept": "application/json",
25736 "Cache-Control": "no-cache",
25737 "X-Requested-With": "XMLHttpRequest"
25740 for (var headerName in headers) {
25741 var headerValue = headers[headerName];
25743 this.xhr.setRequestHeader(headerName, headerValue);
25749 this.xhr.onload = function()
25751 _this.xhrOnLoad(_this.xhr);
25754 this.xhr.onerror = function()
25756 _this.xhrOnError(_this.xhr);
25759 var formData = new FormData();
25761 formData.append('returnHTML', 'NO');
25764 formData.append('crop', crop);
25767 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25768 formData.append(this.paramName, file, file.name);
25771 if(typeof(file.filename) != 'undefined'){
25772 formData.append('filename', file.filename);
25775 if(typeof(file.mimetype) != 'undefined'){
25776 formData.append('mimetype', file.mimetype);
25779 if(this.fireEvent('arrange', this, formData) != false){
25780 this.xhr.send(formData);
25784 xhrOnLoad : function(xhr)
25787 this.maskEl.unmask();
25790 if (xhr.readyState !== 4) {
25791 this.fireEvent('exception', this, xhr);
25795 var response = Roo.decode(xhr.responseText);
25797 if(!response.success){
25798 this.fireEvent('exception', this, xhr);
25802 var response = Roo.decode(xhr.responseText);
25804 this.fireEvent('upload', this, response);
25808 xhrOnError : function()
25811 this.maskEl.unmask();
25814 Roo.log('xhr on error');
25816 var response = Roo.decode(xhr.responseText);
25822 prepare : function(file)
25825 this.maskEl.mask(this.loadingText);
25831 if(typeof(file) === 'string'){
25832 this.loadCanvas(file);
25836 if(!file || !this.urlAPI){
25841 this.cropType = file.type;
25845 if(this.fireEvent('prepare', this, this.file) != false){
25847 var reader = new FileReader();
25849 reader.onload = function (e) {
25850 if (e.target.error) {
25851 Roo.log(e.target.error);
25855 var buffer = e.target.result,
25856 dataView = new DataView(buffer),
25858 maxOffset = dataView.byteLength - 4,
25862 if (dataView.getUint16(0) === 0xffd8) {
25863 while (offset < maxOffset) {
25864 markerBytes = dataView.getUint16(offset);
25866 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25867 markerLength = dataView.getUint16(offset + 2) + 2;
25868 if (offset + markerLength > dataView.byteLength) {
25869 Roo.log('Invalid meta data: Invalid segment size.');
25873 if(markerBytes == 0xffe1){
25874 _this.parseExifData(
25881 offset += markerLength;
25891 var url = _this.urlAPI.createObjectURL(_this.file);
25893 _this.loadCanvas(url);
25898 reader.readAsArrayBuffer(this.file);
25904 parseExifData : function(dataView, offset, length)
25906 var tiffOffset = offset + 10,
25910 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25911 // No Exif data, might be XMP data instead
25915 // Check for the ASCII code for "Exif" (0x45786966):
25916 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25917 // No Exif data, might be XMP data instead
25920 if (tiffOffset + 8 > dataView.byteLength) {
25921 Roo.log('Invalid Exif data: Invalid segment size.');
25924 // Check for the two null bytes:
25925 if (dataView.getUint16(offset + 8) !== 0x0000) {
25926 Roo.log('Invalid Exif data: Missing byte alignment offset.');
25929 // Check the byte alignment:
25930 switch (dataView.getUint16(tiffOffset)) {
25932 littleEndian = true;
25935 littleEndian = false;
25938 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25941 // Check for the TIFF tag marker (0x002A):
25942 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25943 Roo.log('Invalid Exif data: Missing TIFF marker.');
25946 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25947 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25949 this.parseExifTags(
25952 tiffOffset + dirOffset,
25957 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25962 if (dirOffset + 6 > dataView.byteLength) {
25963 Roo.log('Invalid Exif data: Invalid directory offset.');
25966 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25967 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25968 if (dirEndOffset + 4 > dataView.byteLength) {
25969 Roo.log('Invalid Exif data: Invalid directory size.');
25972 for (i = 0; i < tagsNumber; i += 1) {
25976 dirOffset + 2 + 12 * i, // tag offset
25980 // Return the offset to the next directory:
25981 return dataView.getUint32(dirEndOffset, littleEndian);
25984 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
25986 var tag = dataView.getUint16(offset, littleEndian);
25988 this.exif[tag] = this.getExifValue(
25992 dataView.getUint16(offset + 2, littleEndian), // tag type
25993 dataView.getUint32(offset + 4, littleEndian), // tag length
25998 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26000 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26009 Roo.log('Invalid Exif data: Invalid tag type.');
26013 tagSize = tagType.size * length;
26014 // Determine if the value is contained in the dataOffset bytes,
26015 // or if the value at the dataOffset is a pointer to the actual data:
26016 dataOffset = tagSize > 4 ?
26017 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26018 if (dataOffset + tagSize > dataView.byteLength) {
26019 Roo.log('Invalid Exif data: Invalid data offset.');
26022 if (length === 1) {
26023 return tagType.getValue(dataView, dataOffset, littleEndian);
26026 for (i = 0; i < length; i += 1) {
26027 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26030 if (tagType.ascii) {
26032 // Concatenate the chars:
26033 for (i = 0; i < values.length; i += 1) {
26035 // Ignore the terminating NULL byte(s):
26036 if (c === '\u0000') {
26048 Roo.apply(Roo.bootstrap.UploadCropbox, {
26050 'Orientation': 0x0112
26054 1: 0, //'top-left',
26056 3: 180, //'bottom-right',
26057 // 4: 'bottom-left',
26059 6: 90, //'right-top',
26060 // 7: 'right-bottom',
26061 8: 270 //'left-bottom'
26065 // byte, 8-bit unsigned int:
26067 getValue: function (dataView, dataOffset) {
26068 return dataView.getUint8(dataOffset);
26072 // ascii, 8-bit byte:
26074 getValue: function (dataView, dataOffset) {
26075 return String.fromCharCode(dataView.getUint8(dataOffset));
26080 // short, 16 bit int:
26082 getValue: function (dataView, dataOffset, littleEndian) {
26083 return dataView.getUint16(dataOffset, littleEndian);
26087 // long, 32 bit int:
26089 getValue: function (dataView, dataOffset, littleEndian) {
26090 return dataView.getUint32(dataOffset, littleEndian);
26094 // rational = two long values, first is numerator, second is denominator:
26096 getValue: function (dataView, dataOffset, littleEndian) {
26097 return dataView.getUint32(dataOffset, littleEndian) /
26098 dataView.getUint32(dataOffset + 4, littleEndian);
26102 // slong, 32 bit signed int:
26104 getValue: function (dataView, dataOffset, littleEndian) {
26105 return dataView.getInt32(dataOffset, littleEndian);
26109 // srational, two slongs, first is numerator, second is denominator:
26111 getValue: function (dataView, dataOffset, littleEndian) {
26112 return dataView.getInt32(dataOffset, littleEndian) /
26113 dataView.getInt32(dataOffset + 4, littleEndian);
26123 cls : 'btn-group roo-upload-cropbox-rotate-left',
26124 action : 'rotate-left',
26128 cls : 'btn btn-default',
26129 html : '<i class="fa fa-undo"></i>'
26135 cls : 'btn-group roo-upload-cropbox-picture',
26136 action : 'picture',
26140 cls : 'btn btn-default',
26141 html : '<i class="fa fa-picture-o"></i>'
26147 cls : 'btn-group roo-upload-cropbox-rotate-right',
26148 action : 'rotate-right',
26152 cls : 'btn btn-default',
26153 html : '<i class="fa fa-repeat"></i>'
26161 cls : 'btn-group roo-upload-cropbox-rotate-left',
26162 action : 'rotate-left',
26166 cls : 'btn btn-default',
26167 html : '<i class="fa fa-undo"></i>'
26173 cls : 'btn-group roo-upload-cropbox-download',
26174 action : 'download',
26178 cls : 'btn btn-default',
26179 html : '<i class="fa fa-download"></i>'
26185 cls : 'btn-group roo-upload-cropbox-crop',
26190 cls : 'btn btn-default',
26191 html : '<i class="fa fa-crop"></i>'
26197 cls : 'btn-group roo-upload-cropbox-trash',
26202 cls : 'btn btn-default',
26203 html : '<i class="fa fa-trash"></i>'
26209 cls : 'btn-group roo-upload-cropbox-rotate-right',
26210 action : 'rotate-right',
26214 cls : 'btn btn-default',
26215 html : '<i class="fa fa-repeat"></i>'
26223 cls : 'btn-group roo-upload-cropbox-rotate-left',
26224 action : 'rotate-left',
26228 cls : 'btn btn-default',
26229 html : '<i class="fa fa-undo"></i>'
26235 cls : 'btn-group roo-upload-cropbox-rotate-right',
26236 action : 'rotate-right',
26240 cls : 'btn btn-default',
26241 html : '<i class="fa fa-repeat"></i>'
26254 * @class Roo.bootstrap.DocumentManager
26255 * @extends Roo.bootstrap.Component
26256 * Bootstrap DocumentManager class
26257 * @cfg {String} paramName default 'imageUpload'
26258 * @cfg {String} method default POST
26259 * @cfg {String} url action url
26260 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26261 * @cfg {Boolean} multiple multiple upload default true
26262 * @cfg {Number} thumbSize default 300
26263 * @cfg {String} fieldLabel
26264 * @cfg {Number} labelWidth default 4
26265 * @cfg {String} labelAlign (left|top) default left
26266 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26269 * Create a new DocumentManager
26270 * @param {Object} config The config object
26273 Roo.bootstrap.DocumentManager = function(config){
26274 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26279 * Fire when initial the DocumentManager
26280 * @param {Roo.bootstrap.DocumentManager} this
26285 * inspect selected file
26286 * @param {Roo.bootstrap.DocumentManager} this
26287 * @param {File} file
26292 * Fire when xhr load exception
26293 * @param {Roo.bootstrap.DocumentManager} this
26294 * @param {XMLHttpRequest} xhr
26296 "exception" : true,
26299 * prepare the form data
26300 * @param {Roo.bootstrap.DocumentManager} this
26301 * @param {Object} formData
26306 * Fire when remove the file
26307 * @param {Roo.bootstrap.DocumentManager} this
26308 * @param {Object} file
26313 * Fire after refresh the file
26314 * @param {Roo.bootstrap.DocumentManager} this
26319 * Fire after click the image
26320 * @param {Roo.bootstrap.DocumentManager} this
26321 * @param {Object} file
26326 * Fire when upload a image and editable set to true
26327 * @param {Roo.bootstrap.DocumentManager} this
26328 * @param {Object} file
26332 * @event beforeselectfile
26333 * Fire before select file
26334 * @param {Roo.bootstrap.DocumentManager} this
26336 "beforeselectfile" : true,
26339 * Fire before process file
26340 * @param {Roo.bootstrap.DocumentManager} this
26341 * @param {Object} file
26348 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
26357 paramName : 'imageUpload',
26360 labelAlign : 'left',
26367 getAutoCreate : function()
26369 var managerWidget = {
26371 cls : 'roo-document-manager',
26375 cls : 'roo-document-manager-selector',
26380 cls : 'roo-document-manager-uploader',
26384 cls : 'roo-document-manager-upload-btn',
26385 html : '<i class="fa fa-plus"></i>'
26396 cls : 'column col-md-12',
26401 if(this.fieldLabel.length){
26406 cls : 'column col-md-12',
26407 html : this.fieldLabel
26411 cls : 'column col-md-12',
26416 if(this.labelAlign == 'left'){
26420 cls : 'column col-md-' + this.labelWidth,
26421 html : this.fieldLabel
26425 cls : 'column col-md-' + (12 - this.labelWidth),
26435 cls : 'row clearfix',
26443 initEvents : function()
26445 this.managerEl = this.el.select('.roo-document-manager', true).first();
26446 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26448 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26449 this.selectorEl.hide();
26452 this.selectorEl.attr('multiple', 'multiple');
26455 this.selectorEl.on('change', this.onFileSelected, this);
26457 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26458 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26460 this.uploader.on('click', this.onUploaderClick, this);
26462 this.renderProgressDialog();
26466 window.addEventListener("resize", function() { _this.refresh(); } );
26468 this.fireEvent('initial', this);
26471 renderProgressDialog : function()
26475 this.progressDialog = new Roo.bootstrap.Modal({
26476 cls : 'roo-document-manager-progress-dialog',
26477 allow_close : false,
26487 btnclick : function() {
26488 _this.uploadCancel();
26494 this.progressDialog.render(Roo.get(document.body));
26496 this.progress = new Roo.bootstrap.Progress({
26497 cls : 'roo-document-manager-progress',
26502 this.progress.render(this.progressDialog.getChildContainer());
26504 this.progressBar = new Roo.bootstrap.ProgressBar({
26505 cls : 'roo-document-manager-progress-bar',
26508 aria_valuemax : 12,
26512 this.progressBar.render(this.progress.getChildContainer());
26515 onUploaderClick : function(e)
26517 e.preventDefault();
26519 if(this.fireEvent('beforeselectfile', this) != false){
26520 this.selectorEl.dom.click();
26525 onFileSelected : function(e)
26527 e.preventDefault();
26529 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26533 Roo.each(this.selectorEl.dom.files, function(file){
26534 if(this.fireEvent('inspect', this, file) != false){
26535 this.files.push(file);
26545 this.selectorEl.dom.value = '';
26547 if(!this.files.length){
26551 if(this.boxes > 0 && this.files.length > this.boxes){
26552 this.files = this.files.slice(0, this.boxes);
26555 this.uploader.show();
26557 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26558 this.uploader.hide();
26567 Roo.each(this.files, function(file){
26569 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26570 var f = this.renderPreview(file);
26575 if(file.type.indexOf('image') != -1){
26576 this.delegates.push(
26578 _this.process(file);
26579 }).createDelegate(this)
26587 _this.process(file);
26588 }).createDelegate(this)
26593 this.files = files;
26595 this.delegates = this.delegates.concat(docs);
26597 if(!this.delegates.length){
26602 this.progressBar.aria_valuemax = this.delegates.length;
26609 arrange : function()
26611 if(!this.delegates.length){
26612 this.progressDialog.hide();
26617 var delegate = this.delegates.shift();
26619 this.progressDialog.show();
26621 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26623 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26628 refresh : function()
26630 this.uploader.show();
26632 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26633 this.uploader.hide();
26636 Roo.isTouch ? this.closable(false) : this.closable(true);
26638 this.fireEvent('refresh', this);
26641 onRemove : function(e, el, o)
26643 e.preventDefault();
26645 this.fireEvent('remove', this, o);
26649 remove : function(o)
26653 Roo.each(this.files, function(file){
26654 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26663 this.files = files;
26670 Roo.each(this.files, function(file){
26675 file.target.remove();
26684 onClick : function(e, el, o)
26686 e.preventDefault();
26688 this.fireEvent('click', this, o);
26692 closable : function(closable)
26694 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26696 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26708 xhrOnLoad : function(xhr)
26710 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26714 if (xhr.readyState !== 4) {
26716 this.fireEvent('exception', this, xhr);
26720 var response = Roo.decode(xhr.responseText);
26722 if(!response.success){
26724 this.fireEvent('exception', this, xhr);
26728 var file = this.renderPreview(response.data);
26730 this.files.push(file);
26736 xhrOnError : function()
26738 Roo.log('xhr on error');
26740 var response = Roo.decode(xhr.responseText);
26747 process : function(file)
26749 if(this.fireEvent('process', this, file) !== false){
26750 if(this.editable && file.type.indexOf('image') != -1){
26751 this.fireEvent('edit', this, file);
26755 this.uploadStart(file, false);
26762 uploadStart : function(file, crop)
26764 this.xhr = new XMLHttpRequest();
26766 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26771 file.xhr = this.xhr;
26773 this.managerEl.createChild({
26775 cls : 'roo-document-manager-loading',
26779 tooltip : file.name,
26780 cls : 'roo-document-manager-thumb',
26781 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26787 this.xhr.open(this.method, this.url, true);
26790 "Accept": "application/json",
26791 "Cache-Control": "no-cache",
26792 "X-Requested-With": "XMLHttpRequest"
26795 for (var headerName in headers) {
26796 var headerValue = headers[headerName];
26798 this.xhr.setRequestHeader(headerName, headerValue);
26804 this.xhr.onload = function()
26806 _this.xhrOnLoad(_this.xhr);
26809 this.xhr.onerror = function()
26811 _this.xhrOnError(_this.xhr);
26814 var formData = new FormData();
26816 formData.append('returnHTML', 'NO');
26819 formData.append('crop', crop);
26822 formData.append(this.paramName, file, file.name);
26824 if(this.fireEvent('prepare', this, formData) != false){
26825 this.xhr.send(formData);
26829 uploadCancel : function()
26836 this.delegates = [];
26838 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26845 renderPreview : function(file)
26847 if(typeof(file.target) != 'undefined' && file.target){
26851 var previewEl = this.managerEl.createChild({
26853 cls : 'roo-document-manager-preview',
26857 tooltip : file.filename,
26858 cls : 'roo-document-manager-thumb',
26859 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26864 html : '<i class="fa fa-times-circle"></i>'
26869 var close = previewEl.select('button.close', true).first();
26871 close.on('click', this.onRemove, this, file);
26873 file.target = previewEl;
26875 var image = previewEl.select('img', true).first();
26879 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26881 image.on('click', this.onClick, this, file);
26887 onPreviewLoad : function(file, image)
26889 if(typeof(file.target) == 'undefined' || !file.target){
26893 var width = image.dom.naturalWidth || image.dom.width;
26894 var height = image.dom.naturalHeight || image.dom.height;
26896 if(width > height){
26897 file.target.addClass('wide');
26901 file.target.addClass('tall');
26906 uploadFromSource : function(file, crop)
26908 this.xhr = new XMLHttpRequest();
26910 this.managerEl.createChild({
26912 cls : 'roo-document-manager-loading',
26916 tooltip : file.name,
26917 cls : 'roo-document-manager-thumb',
26918 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26924 this.xhr.open(this.method, this.url, true);
26927 "Accept": "application/json",
26928 "Cache-Control": "no-cache",
26929 "X-Requested-With": "XMLHttpRequest"
26932 for (var headerName in headers) {
26933 var headerValue = headers[headerName];
26935 this.xhr.setRequestHeader(headerName, headerValue);
26941 this.xhr.onload = function()
26943 _this.xhrOnLoad(_this.xhr);
26946 this.xhr.onerror = function()
26948 _this.xhrOnError(_this.xhr);
26951 var formData = new FormData();
26953 formData.append('returnHTML', 'NO');
26955 formData.append('crop', crop);
26957 if(typeof(file.filename) != 'undefined'){
26958 formData.append('filename', file.filename);
26961 if(typeof(file.mimetype) != 'undefined'){
26962 formData.append('mimetype', file.mimetype);
26965 if(this.fireEvent('prepare', this, formData) != false){
26966 this.xhr.send(formData);
26976 * @class Roo.bootstrap.DocumentViewer
26977 * @extends Roo.bootstrap.Component
26978 * Bootstrap DocumentViewer class
26981 * Create a new DocumentViewer
26982 * @param {Object} config The config object
26985 Roo.bootstrap.DocumentViewer = function(config){
26986 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26991 * Fire after initEvent
26992 * @param {Roo.bootstrap.DocumentViewer} this
26998 * @param {Roo.bootstrap.DocumentViewer} this
27003 * Fire after trash button
27004 * @param {Roo.bootstrap.DocumentViewer} this
27011 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
27013 getAutoCreate : function()
27017 cls : 'roo-document-viewer',
27021 cls : 'roo-document-viewer-body',
27025 cls : 'roo-document-viewer-thumb',
27029 cls : 'roo-document-viewer-image'
27037 cls : 'roo-document-viewer-footer',
27040 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27048 cls : 'btn btn-default roo-document-viewer-trash',
27049 html : '<i class="fa fa-trash"></i>'
27062 initEvents : function()
27065 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27066 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27068 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27069 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27071 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27072 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27074 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27075 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27077 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27078 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27080 this.bodyEl.on('click', this.onClick, this);
27082 this.trashBtn.on('click', this.onTrash, this);
27086 initial : function()
27088 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27091 this.fireEvent('initial', this);
27095 onClick : function(e)
27097 e.preventDefault();
27099 this.fireEvent('click', this);
27102 onTrash : function(e)
27104 e.preventDefault();
27106 this.fireEvent('trash', this);
27118 * @class Roo.bootstrap.NavProgressBar
27119 * @extends Roo.bootstrap.Component
27120 * Bootstrap NavProgressBar class
27123 * Create a new nav progress bar
27124 * @param {Object} config The config object
27127 Roo.bootstrap.NavProgressBar = function(config){
27128 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27130 this.bullets = this.bullets || [];
27132 // Roo.bootstrap.NavProgressBar.register(this);
27136 * Fires when the active item changes
27137 * @param {Roo.bootstrap.NavProgressBar} this
27138 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27139 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
27146 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
27151 getAutoCreate : function()
27153 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27157 cls : 'roo-navigation-bar-group',
27161 cls : 'roo-navigation-top-bar'
27165 cls : 'roo-navigation-bullets-bar',
27169 cls : 'roo-navigation-bar'
27176 cls : 'roo-navigation-bottom-bar'
27186 initEvents: function()
27191 onRender : function(ct, position)
27193 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27195 if(this.bullets.length){
27196 Roo.each(this.bullets, function(b){
27205 addItem : function(cfg)
27207 var item = new Roo.bootstrap.NavProgressItem(cfg);
27209 item.parentId = this.id;
27210 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27213 var top = new Roo.bootstrap.Element({
27215 cls : 'roo-navigation-bar-text'
27218 var bottom = new Roo.bootstrap.Element({
27220 cls : 'roo-navigation-bar-text'
27223 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27224 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27226 var topText = new Roo.bootstrap.Element({
27228 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27231 var bottomText = new Roo.bootstrap.Element({
27233 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27236 topText.onRender(top.el, null);
27237 bottomText.onRender(bottom.el, null);
27240 item.bottomEl = bottom;
27243 this.barItems.push(item);
27248 getActive : function()
27250 var active = false;
27252 Roo.each(this.barItems, function(v){
27254 if (!v.isActive()) {
27266 setActiveItem : function(item)
27270 Roo.each(this.barItems, function(v){
27271 if (v.rid == item.rid) {
27275 if (v.isActive()) {
27276 v.setActive(false);
27281 item.setActive(true);
27283 this.fireEvent('changed', this, item, prev);
27286 getBarItem: function(rid)
27290 Roo.each(this.barItems, function(e) {
27291 if (e.rid != rid) {
27302 indexOfItem : function(item)
27306 Roo.each(this.barItems, function(v, i){
27308 if (v.rid != item.rid) {
27319 setActiveNext : function()
27321 var i = this.indexOfItem(this.getActive());
27323 if (i > this.barItems.length) {
27327 this.setActiveItem(this.barItems[i+1]);
27330 setActivePrev : function()
27332 var i = this.indexOfItem(this.getActive());
27338 this.setActiveItem(this.barItems[i-1]);
27341 format : function()
27343 if(!this.barItems.length){
27347 var width = 100 / this.barItems.length;
27349 Roo.each(this.barItems, function(i){
27350 i.el.setStyle('width', width + '%');
27351 i.topEl.el.setStyle('width', width + '%');
27352 i.bottomEl.el.setStyle('width', width + '%');
27361 * Nav Progress Item
27366 * @class Roo.bootstrap.NavProgressItem
27367 * @extends Roo.bootstrap.Component
27368 * Bootstrap NavProgressItem class
27369 * @cfg {String} rid the reference id
27370 * @cfg {Boolean} active (true|false) Is item active default false
27371 * @cfg {Boolean} disabled (true|false) Is item active default false
27372 * @cfg {String} html
27373 * @cfg {String} position (top|bottom) text position default bottom
27374 * @cfg {String} icon show icon instead of number
27377 * Create a new NavProgressItem
27378 * @param {Object} config The config object
27380 Roo.bootstrap.NavProgressItem = function(config){
27381 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27386 * The raw click event for the entire grid.
27387 * @param {Roo.bootstrap.NavProgressItem} this
27388 * @param {Roo.EventObject} e
27395 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
27401 position : 'bottom',
27404 getAutoCreate : function()
27406 var iconCls = 'roo-navigation-bar-item-icon';
27408 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27412 cls: 'roo-navigation-bar-item',
27422 cfg.cls += ' active';
27425 cfg.cls += ' disabled';
27431 disable : function()
27433 this.setDisabled(true);
27436 enable : function()
27438 this.setDisabled(false);
27441 initEvents: function()
27443 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27445 this.iconEl.on('click', this.onClick, this);
27448 onClick : function(e)
27450 e.preventDefault();
27456 if(this.fireEvent('click', this, e) === false){
27460 this.parent().setActiveItem(this);
27463 isActive: function ()
27465 return this.active;
27468 setActive : function(state)
27470 if(this.active == state){
27474 this.active = state;
27477 this.el.addClass('active');
27481 this.el.removeClass('active');
27486 setDisabled : function(state)
27488 if(this.disabled == state){
27492 this.disabled = state;
27495 this.el.addClass('disabled');
27499 this.el.removeClass('disabled');
27502 tooltipEl : function()
27504 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27517 * @class Roo.bootstrap.FieldLabel
27518 * @extends Roo.bootstrap.Component
27519 * Bootstrap FieldLabel class
27520 * @cfg {String} html contents of the element
27521 * @cfg {String} tag tag of the element default label
27522 * @cfg {String} cls class of the element
27523 * @cfg {String} target label target
27524 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27525 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27526 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27527 * @cfg {String} iconTooltip default "This field is required"
27530 * Create a new FieldLabel
27531 * @param {Object} config The config object
27534 Roo.bootstrap.FieldLabel = function(config){
27535 Roo.bootstrap.Element.superclass.constructor.call(this, config);
27540 * Fires after the field has been marked as invalid.
27541 * @param {Roo.form.FieldLabel} this
27542 * @param {String} msg The validation message
27547 * Fires after the field has been validated with no errors.
27548 * @param {Roo.form.FieldLabel} this
27554 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
27561 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27562 validClass : 'text-success fa fa-lg fa-check',
27563 iconTooltip : 'This field is required',
27565 getAutoCreate : function(){
27569 cls : 'roo-bootstrap-field-label ' + this.cls,
27575 tooltip : this.iconTooltip
27587 initEvents: function()
27589 Roo.bootstrap.Element.superclass.initEvents.call(this);
27591 this.iconEl = this.el.select('i', true).first();
27593 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27595 Roo.bootstrap.FieldLabel.register(this);
27599 * Mark this field as valid
27601 markValid : function()
27603 this.iconEl.show();
27605 this.iconEl.removeClass(this.invalidClass);
27607 this.iconEl.addClass(this.validClass);
27609 this.fireEvent('valid', this);
27613 * Mark this field as invalid
27614 * @param {String} msg The validation message
27616 markInvalid : function(msg)
27618 this.iconEl.show();
27620 this.iconEl.removeClass(this.validClass);
27622 this.iconEl.addClass(this.invalidClass);
27624 this.fireEvent('invalid', this, msg);
27630 Roo.apply(Roo.bootstrap.FieldLabel, {
27635 * register a FieldLabel Group
27636 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27638 register : function(label)
27640 if(this.groups.hasOwnProperty(label.target)){
27644 this.groups[label.target] = label;
27648 * fetch a FieldLabel Group based on the target
27649 * @param {string} target
27650 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27652 get: function(target) {
27653 if (typeof(this.groups[target]) == 'undefined') {
27657 return this.groups[target] ;
27666 * page DateSplitField.
27672 * @class Roo.bootstrap.DateSplitField
27673 * @extends Roo.bootstrap.Component
27674 * Bootstrap DateSplitField class
27675 * @cfg {string} fieldLabel - the label associated
27676 * @cfg {Number} labelWidth set the width of label (0-12)
27677 * @cfg {String} labelAlign (top|left)
27678 * @cfg {Boolean} dayAllowBlank (true|false) default false
27679 * @cfg {Boolean} monthAllowBlank (true|false) default false
27680 * @cfg {Boolean} yearAllowBlank (true|false) default false
27681 * @cfg {string} dayPlaceholder
27682 * @cfg {string} monthPlaceholder
27683 * @cfg {string} yearPlaceholder
27684 * @cfg {string} dayFormat default 'd'
27685 * @cfg {string} monthFormat default 'm'
27686 * @cfg {string} yearFormat default 'Y'
27690 * Create a new DateSplitField
27691 * @param {Object} config The config object
27694 Roo.bootstrap.DateSplitField = function(config){
27695 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27701 * getting the data of years
27702 * @param {Roo.bootstrap.DateSplitField} this
27703 * @param {Object} years
27708 * getting the data of days
27709 * @param {Roo.bootstrap.DateSplitField} this
27710 * @param {Object} days
27715 * Fires after the field has been marked as invalid.
27716 * @param {Roo.form.Field} this
27717 * @param {String} msg The validation message
27722 * Fires after the field has been validated with no errors.
27723 * @param {Roo.form.Field} this
27729 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
27732 labelAlign : 'top',
27734 dayAllowBlank : false,
27735 monthAllowBlank : false,
27736 yearAllowBlank : false,
27737 dayPlaceholder : '',
27738 monthPlaceholder : '',
27739 yearPlaceholder : '',
27743 isFormField : true,
27745 getAutoCreate : function()
27749 cls : 'row roo-date-split-field-group',
27754 cls : 'form-hidden-field roo-date-split-field-group-value',
27760 if(this.fieldLabel){
27763 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27767 html : this.fieldLabel
27773 Roo.each(['day', 'month', 'year'], function(t){
27776 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27783 inputEl: function ()
27785 return this.el.select('.roo-date-split-field-group-value', true).first();
27788 onRender : function(ct, position)
27792 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27794 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27796 this.dayField = new Roo.bootstrap.ComboBox({
27797 allowBlank : this.dayAllowBlank,
27798 alwaysQuery : true,
27799 displayField : 'value',
27802 forceSelection : true,
27804 placeholder : this.dayPlaceholder,
27805 selectOnFocus : true,
27806 tpl : '<div class="select2-result"><b>{value}</b></div>',
27807 triggerAction : 'all',
27809 valueField : 'value',
27810 store : new Roo.data.SimpleStore({
27811 data : (function() {
27813 _this.fireEvent('days', _this, days);
27816 fields : [ 'value' ]
27819 select : function (_self, record, index)
27821 _this.setValue(_this.getValue());
27826 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27828 this.monthField = new Roo.bootstrap.MonthField({
27829 after : '<i class=\"fa fa-calendar\"></i>',
27830 allowBlank : this.monthAllowBlank,
27831 placeholder : this.monthPlaceholder,
27834 render : function (_self)
27836 this.el.select('span.input-group-addon', true).first().on('click', function(e){
27837 e.preventDefault();
27841 select : function (_self, oldvalue, newvalue)
27843 _this.setValue(_this.getValue());
27848 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27850 this.yearField = new Roo.bootstrap.ComboBox({
27851 allowBlank : this.yearAllowBlank,
27852 alwaysQuery : true,
27853 displayField : 'value',
27856 forceSelection : true,
27858 placeholder : this.yearPlaceholder,
27859 selectOnFocus : true,
27860 tpl : '<div class="select2-result"><b>{value}</b></div>',
27861 triggerAction : 'all',
27863 valueField : 'value',
27864 store : new Roo.data.SimpleStore({
27865 data : (function() {
27867 _this.fireEvent('years', _this, years);
27870 fields : [ 'value' ]
27873 select : function (_self, record, index)
27875 _this.setValue(_this.getValue());
27880 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
27883 setValue : function(v, format)
27885 this.inputEl.dom.value = v;
27887 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
27889 var d = Date.parseDate(v, f);
27896 this.setDay(d.format(this.dayFormat));
27897 this.setMonth(d.format(this.monthFormat));
27898 this.setYear(d.format(this.yearFormat));
27905 setDay : function(v)
27907 this.dayField.setValue(v);
27908 this.inputEl.dom.value = this.getValue();
27913 setMonth : function(v)
27915 this.monthField.setValue(v, true);
27916 this.inputEl.dom.value = this.getValue();
27921 setYear : function(v)
27923 this.yearField.setValue(v);
27924 this.inputEl.dom.value = this.getValue();
27929 getDay : function()
27931 return this.dayField.getValue();
27934 getMonth : function()
27936 return this.monthField.getValue();
27939 getYear : function()
27941 return this.yearField.getValue();
27944 getValue : function()
27946 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
27948 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
27958 this.inputEl.dom.value = '';
27963 validate : function()
27965 var d = this.dayField.validate();
27966 var m = this.monthField.validate();
27967 var y = this.yearField.validate();
27972 (!this.dayAllowBlank && !d) ||
27973 (!this.monthAllowBlank && !m) ||
27974 (!this.yearAllowBlank && !y)
27979 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
27988 this.markInvalid();
27993 markValid : function()
27996 var label = this.el.select('label', true).first();
27997 var icon = this.el.select('i.fa-star', true).first();
28003 this.fireEvent('valid', this);
28007 * Mark this field as invalid
28008 * @param {String} msg The validation message
28010 markInvalid : function(msg)
28013 var label = this.el.select('label', true).first();
28014 var icon = this.el.select('i.fa-star', true).first();
28016 if(label && !icon){
28017 this.el.select('.roo-date-split-field-label', true).createChild({
28019 cls : 'text-danger fa fa-lg fa-star',
28020 tooltip : 'This field is required',
28021 style : 'margin-right:5px;'
28025 this.fireEvent('invalid', this, msg);
28028 clearInvalid : function()
28030 var label = this.el.select('label', true).first();
28031 var icon = this.el.select('i.fa-star', true).first();
28037 this.fireEvent('valid', this);
28040 getName: function()