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 panel - type - primary/success.....
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)){
5117 return this.config.length;
5121 * Returns the number of columns.
5124 getColumnCount : function(visibleOnly){
5125 if(visibleOnly === true){
5127 for(var i = 0, len = this.config.length; i < len; i++){
5128 if(!this.isHidden(i)){
5134 return this.config.length;
5138 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5139 * @param {Function} fn
5140 * @param {Object} scope (optional)
5141 * @return {Array} result
5143 getColumnsBy : function(fn, scope){
5145 for(var i = 0, len = this.config.length; i < len; i++){
5146 var c = this.config[i];
5147 if(fn.call(scope||this, c, i) === true){
5155 * Returns true if the specified column is sortable.
5156 * @param {Number} col The column index
5159 isSortable : function(col){
5160 if(typeof this.config[col].sortable == "undefined"){
5161 return this.defaultSortable;
5163 return this.config[col].sortable;
5167 * Returns the rendering (formatting) function defined for the column.
5168 * @param {Number} col The column index.
5169 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5171 getRenderer : function(col){
5172 if(!this.config[col].renderer){
5173 return Roo.grid.ColumnModel.defaultRenderer;
5175 return this.config[col].renderer;
5179 * Sets the rendering (formatting) function for a column.
5180 * @param {Number} col The column index
5181 * @param {Function} fn The function to use to process the cell's raw data
5182 * to return HTML markup for the grid view. The render function is called with
5183 * the following parameters:<ul>
5184 * <li>Data value.</li>
5185 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5186 * <li>css A CSS style string to apply to the table cell.</li>
5187 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5188 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5189 * <li>Row index</li>
5190 * <li>Column index</li>
5191 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5193 setRenderer : function(col, fn){
5194 this.config[col].renderer = fn;
5198 * Returns the width for the specified column.
5199 * @param {Number} col The column index
5202 getColumnWidth : function(col){
5203 return this.config[col].width * 1 || this.defaultWidth;
5207 * Sets the width for a column.
5208 * @param {Number} col The column index
5209 * @param {Number} width The new width
5211 setColumnWidth : function(col, width, suppressEvent){
5212 this.config[col].width = width;
5213 this.totalWidth = null;
5215 this.fireEvent("widthchange", this, col, width);
5220 * Returns the total width of all columns.
5221 * @param {Boolean} includeHidden True to include hidden column widths
5224 getTotalWidth : function(includeHidden){
5225 if(!this.totalWidth){
5226 this.totalWidth = 0;
5227 for(var i = 0, len = this.config.length; i < len; i++){
5228 if(includeHidden || !this.isHidden(i)){
5229 this.totalWidth += this.getColumnWidth(i);
5233 return this.totalWidth;
5237 * Returns the header for the specified column.
5238 * @param {Number} col The column index
5241 getColumnHeader : function(col){
5242 return this.config[col].header;
5246 * Sets the header for a column.
5247 * @param {Number} col The column index
5248 * @param {String} header The new header
5250 setColumnHeader : function(col, header){
5251 this.config[col].header = header;
5252 this.fireEvent("headerchange", this, col, header);
5256 * Returns the tooltip for the specified column.
5257 * @param {Number} col The column index
5260 getColumnTooltip : function(col){
5261 return this.config[col].tooltip;
5264 * Sets the tooltip for a column.
5265 * @param {Number} col The column index
5266 * @param {String} tooltip The new tooltip
5268 setColumnTooltip : function(col, tooltip){
5269 this.config[col].tooltip = tooltip;
5273 * Returns the dataIndex for the specified column.
5274 * @param {Number} col The column index
5277 getDataIndex : function(col){
5278 return this.config[col].dataIndex;
5282 * Sets the dataIndex for a column.
5283 * @param {Number} col The column index
5284 * @param {Number} dataIndex The new dataIndex
5286 setDataIndex : function(col, dataIndex){
5287 this.config[col].dataIndex = dataIndex;
5293 * Returns true if the cell is editable.
5294 * @param {Number} colIndex The column index
5295 * @param {Number} rowIndex The row index - this is nto actually used..?
5298 isCellEditable : function(colIndex, rowIndex){
5299 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5303 * Returns the editor defined for the cell/column.
5304 * return false or null to disable editing.
5305 * @param {Number} colIndex The column index
5306 * @param {Number} rowIndex The row index
5309 getCellEditor : function(colIndex, rowIndex){
5310 return this.config[colIndex].editor;
5314 * Sets if a column is editable.
5315 * @param {Number} col The column index
5316 * @param {Boolean} editable True if the column is editable
5318 setEditable : function(col, editable){
5319 this.config[col].editable = editable;
5324 * Returns true if the column is hidden.
5325 * @param {Number} colIndex The column index
5328 isHidden : function(colIndex){
5329 return this.config[colIndex].hidden;
5334 * Returns true if the column width cannot be changed
5336 isFixed : function(colIndex){
5337 return this.config[colIndex].fixed;
5341 * Returns true if the column can be resized
5344 isResizable : function(colIndex){
5345 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5348 * Sets if a column is hidden.
5349 * @param {Number} colIndex The column index
5350 * @param {Boolean} hidden True if the column is hidden
5352 setHidden : function(colIndex, hidden){
5353 this.config[colIndex].hidden = hidden;
5354 this.totalWidth = null;
5355 this.fireEvent("hiddenchange", this, colIndex, hidden);
5359 * Sets the editor for a column.
5360 * @param {Number} col The column index
5361 * @param {Object} editor The editor object
5363 setEditor : function(col, editor){
5364 this.config[col].editor = editor;
5368 Roo.grid.ColumnModel.defaultRenderer = function(value){
5369 if(typeof value == "string" && value.length < 1){
5375 // Alias for backwards compatibility
5376 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5379 * Ext JS Library 1.1.1
5380 * Copyright(c) 2006-2007, Ext JS, LLC.
5382 * Originally Released Under LGPL - original licence link has changed is not relivant.
5385 * <script type="text/javascript">
5389 * @class Roo.LoadMask
5390 * A simple utility class for generically masking elements while loading data. If the element being masked has
5391 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5392 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5393 * element's UpdateManager load indicator and will be destroyed after the initial load.
5395 * Create a new LoadMask
5396 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5397 * @param {Object} config The config object
5399 Roo.LoadMask = function(el, config){
5400 this.el = Roo.get(el);
5401 Roo.apply(this, config);
5403 this.store.on('beforeload', this.onBeforeLoad, this);
5404 this.store.on('load', this.onLoad, this);
5405 this.store.on('loadexception', this.onLoadException, this);
5406 this.removeMask = false;
5408 var um = this.el.getUpdateManager();
5409 um.showLoadIndicator = false; // disable the default indicator
5410 um.on('beforeupdate', this.onBeforeLoad, this);
5411 um.on('update', this.onLoad, this);
5412 um.on('failure', this.onLoad, this);
5413 this.removeMask = true;
5417 Roo.LoadMask.prototype = {
5419 * @cfg {Boolean} removeMask
5420 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5421 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5425 * The text to display in a centered loading message box (defaults to 'Loading...')
5429 * @cfg {String} msgCls
5430 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5432 msgCls : 'x-mask-loading',
5435 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5441 * Disables the mask to prevent it from being displayed
5443 disable : function(){
5444 this.disabled = true;
5448 * Enables the mask so that it can be displayed
5450 enable : function(){
5451 this.disabled = false;
5454 onLoadException : function()
5458 if (typeof(arguments[3]) != 'undefined') {
5459 Roo.MessageBox.alert("Error loading",arguments[3]);
5463 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5464 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5473 this.el.unmask(this.removeMask);
5478 this.el.unmask(this.removeMask);
5482 onBeforeLoad : function(){
5484 this.el.mask(this.msg, this.msgCls);
5489 destroy : function(){
5491 this.store.un('beforeload', this.onBeforeLoad, this);
5492 this.store.un('load', this.onLoad, this);
5493 this.store.un('loadexception', this.onLoadException, this);
5495 var um = this.el.getUpdateManager();
5496 um.un('beforeupdate', this.onBeforeLoad, this);
5497 um.un('update', this.onLoad, this);
5498 um.un('failure', this.onLoad, this);
5509 * @class Roo.bootstrap.Table
5510 * @extends Roo.bootstrap.Component
5511 * Bootstrap Table class
5512 * @cfg {String} cls table class
5513 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5514 * @cfg {String} bgcolor Specifies the background color for a table
5515 * @cfg {Number} border Specifies whether the table cells should have borders or not
5516 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5517 * @cfg {Number} cellspacing Specifies the space between cells
5518 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5519 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5520 * @cfg {String} sortable Specifies that the table should be sortable
5521 * @cfg {String} summary Specifies a summary of the content of a table
5522 * @cfg {Number} width Specifies the width of a table
5523 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5525 * @cfg {boolean} striped Should the rows be alternative striped
5526 * @cfg {boolean} bordered Add borders to the table
5527 * @cfg {boolean} hover Add hover highlighting
5528 * @cfg {boolean} condensed Format condensed
5529 * @cfg {boolean} responsive Format condensed
5530 * @cfg {Boolean} loadMask (true|false) default false
5531 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5532 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5533 * @cfg {Boolean} rowSelection (true|false) default false
5534 * @cfg {Boolean} cellSelection (true|false) default false
5535 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5539 * Create a new Table
5540 * @param {Object} config The config object
5543 Roo.bootstrap.Table = function(config){
5544 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5547 this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5548 this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5549 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5550 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5554 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5555 this.sm = this.selModel;
5556 this.sm.xmodule = this.xmodule || false;
5558 if (this.cm && typeof(this.cm.config) == 'undefined') {
5559 this.colModel = new Roo.grid.ColumnModel(this.cm);
5560 this.cm = this.colModel;
5561 this.cm.xmodule = this.xmodule || false;
5564 this.store= Roo.factory(this.store, Roo.data);
5565 this.ds = this.store;
5566 this.ds.xmodule = this.xmodule || false;
5569 if (this.footer && this.store) {
5570 this.footer.dataSource = this.ds;
5571 this.footer = Roo.factory(this.footer);
5578 * Fires when a cell is clicked
5579 * @param {Roo.bootstrap.Table} this
5580 * @param {Roo.Element} el
5581 * @param {Number} rowIndex
5582 * @param {Number} columnIndex
5583 * @param {Roo.EventObject} e
5587 * @event celldblclick
5588 * Fires when a cell is double clicked
5589 * @param {Roo.bootstrap.Table} this
5590 * @param {Roo.Element} el
5591 * @param {Number} rowIndex
5592 * @param {Number} columnIndex
5593 * @param {Roo.EventObject} e
5595 "celldblclick" : true,
5598 * Fires when a row is clicked
5599 * @param {Roo.bootstrap.Table} this
5600 * @param {Roo.Element} el
5601 * @param {Number} rowIndex
5602 * @param {Roo.EventObject} e
5606 * @event rowdblclick
5607 * Fires when a row is double clicked
5608 * @param {Roo.bootstrap.Table} this
5609 * @param {Roo.Element} el
5610 * @param {Number} rowIndex
5611 * @param {Roo.EventObject} e
5613 "rowdblclick" : true,
5616 * Fires when a mouseover occur
5617 * @param {Roo.bootstrap.Table} this
5618 * @param {Roo.Element} el
5619 * @param {Number} rowIndex
5620 * @param {Number} columnIndex
5621 * @param {Roo.EventObject} e
5626 * Fires when a mouseout occur
5627 * @param {Roo.bootstrap.Table} this
5628 * @param {Roo.Element} el
5629 * @param {Number} rowIndex
5630 * @param {Number} columnIndex
5631 * @param {Roo.EventObject} e
5636 * Fires when a row is rendered, so you can change add a style to it.
5637 * @param {Roo.bootstrap.Table} this
5638 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5642 * @event rowsrendered
5643 * Fires when all the rows have been rendered
5644 * @param {Roo.bootstrap.Table} this
5646 'rowsrendered' : true
5651 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5676 rowSelection : false,
5677 cellSelection : false,
5680 // Roo.Element - the tbody
5683 getAutoCreate : function(){
5684 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5693 cfg.cls += ' table-striped';
5697 cfg.cls += ' table-hover';
5699 if (this.bordered) {
5700 cfg.cls += ' table-bordered';
5702 if (this.condensed) {
5703 cfg.cls += ' table-condensed';
5705 if (this.responsive) {
5706 cfg.cls += ' table-responsive';
5710 cfg.cls+= ' ' +this.cls;
5713 // this lot should be simplifed...
5716 cfg.align=this.align;
5719 cfg.bgcolor=this.bgcolor;
5722 cfg.border=this.border;
5724 if (this.cellpadding) {
5725 cfg.cellpadding=this.cellpadding;
5727 if (this.cellspacing) {
5728 cfg.cellspacing=this.cellspacing;
5731 cfg.frame=this.frame;
5734 cfg.rules=this.rules;
5736 if (this.sortable) {
5737 cfg.sortable=this.sortable;
5740 cfg.summary=this.summary;
5743 cfg.width=this.width;
5746 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5749 if(this.store || this.cm){
5750 if(this.headerShow){
5751 cfg.cn.push(this.renderHeader());
5754 cfg.cn.push(this.renderBody());
5756 if(this.footerShow){
5757 cfg.cn.push(this.renderFooter());
5760 cfg.cls+= ' TableGrid';
5763 return { cn : [ cfg ] };
5766 initEvents : function()
5768 if(!this.store || !this.cm){
5772 //Roo.log('initEvents with ds!!!!');
5774 this.mainBody = this.el.select('tbody', true).first();
5779 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5780 e.on('click', _this.sort, _this);
5783 this.el.on("click", this.onClick, this);
5784 this.el.on("dblclick", this.onDblClick, this);
5786 // why is this done????? = it breaks dialogs??
5787 //this.parent().el.setStyle('position', 'relative');
5791 this.footer.parentId = this.id;
5792 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5795 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5797 this.store.on('load', this.onLoad, this);
5798 this.store.on('beforeload', this.onBeforeLoad, this);
5799 this.store.on('update', this.onUpdate, this);
5800 this.store.on('add', this.onAdd, this);
5804 onMouseover : function(e, el)
5806 var cell = Roo.get(el);
5812 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5813 cell = cell.findParent('td', false, true);
5816 var row = cell.findParent('tr', false, true);
5817 var cellIndex = cell.dom.cellIndex;
5818 var rowIndex = row.dom.rowIndex - 1; // start from 0
5820 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5824 onMouseout : function(e, el)
5826 var cell = Roo.get(el);
5832 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5833 cell = cell.findParent('td', false, true);
5836 var row = cell.findParent('tr', false, true);
5837 var cellIndex = cell.dom.cellIndex;
5838 var rowIndex = row.dom.rowIndex - 1; // start from 0
5840 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5844 onClick : function(e, el)
5846 var cell = Roo.get(el);
5848 if(!cell || (!this.cellSelection && !this.rowSelection)){
5852 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5853 cell = cell.findParent('td', false, true);
5856 if(!cell || typeof(cell) == 'undefined'){
5860 var row = cell.findParent('tr', false, true);
5862 if(!row || typeof(row) == 'undefined'){
5866 var cellIndex = cell.dom.cellIndex;
5867 var rowIndex = this.getRowIndex(row);
5869 // why??? - should these not be based on SelectionModel?
5870 if(this.cellSelection){
5871 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5874 if(this.rowSelection){
5875 this.fireEvent('rowclick', this, row, rowIndex, e);
5881 onDblClick : function(e,el)
5883 var cell = Roo.get(el);
5885 if(!cell || (!this.CellSelection && !this.RowSelection)){
5889 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5890 cell = cell.findParent('td', false, true);
5893 if(!cell || typeof(cell) == 'undefined'){
5897 var row = cell.findParent('tr', false, true);
5899 if(!row || typeof(row) == 'undefined'){
5903 var cellIndex = cell.dom.cellIndex;
5904 var rowIndex = this.getRowIndex(row);
5906 if(this.CellSelection){
5907 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5910 if(this.RowSelection){
5911 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5915 sort : function(e,el)
5917 var col = Roo.get(el);
5919 if(!col.hasClass('sortable')){
5923 var sort = col.attr('sort');
5926 if(col.hasClass('glyphicon-arrow-up')){
5930 this.store.sortInfo = {field : sort, direction : dir};
5933 Roo.log("calling footer first");
5934 this.footer.onClick('first');
5937 this.store.load({ params : { start : 0 } });
5941 renderHeader : function()
5950 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5952 var config = cm.config[i];
5957 html: cm.getColumnHeader(i)
5962 if(typeof(config.lgHeader) != 'undefined'){
5963 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5966 if(typeof(config.mdHeader) != 'undefined'){
5967 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5970 if(typeof(config.smHeader) != 'undefined'){
5971 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5974 if(typeof(config.xsHeader) != 'undefined'){
5975 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5982 if(typeof(config.tooltip) != 'undefined'){
5983 c.tooltip = config.tooltip;
5986 if(typeof(config.colspan) != 'undefined'){
5987 c.colspan = config.colspan;
5990 if(typeof(config.hidden) != 'undefined' && config.hidden){
5991 c.style += ' display:none;';
5994 if(typeof(config.dataIndex) != 'undefined'){
5995 c.sort = config.dataIndex;
5998 if(typeof(config.sortable) != 'undefined' && config.sortable){
6002 if(typeof(config.align) != 'undefined' && config.align.length){
6003 c.style += ' text-align:' + config.align + ';';
6006 if(typeof(config.width) != 'undefined'){
6007 c.style += ' width:' + config.width + 'px;';
6010 if(typeof(config.cls) != 'undefined'){
6011 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6014 ['xs','sm','md','lg'].map(function(size){
6016 if(typeof(config[size]) == 'undefined'){
6020 if (!config[size]) { // 0 = hidden
6021 c.cls += ' hidden-' + size;
6025 c.cls += ' col-' + size + '-' + config[size];
6035 renderBody : function()
6045 colspan : this.cm.getColumnCount()
6055 renderFooter : function()
6065 colspan : this.cm.getColumnCount()
6079 // Roo.log('ds onload');
6084 var ds = this.store;
6086 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6087 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6089 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6090 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6093 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6094 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6098 var tbody = this.mainBody;
6100 if(ds.getCount() > 0){
6101 ds.data.each(function(d,rowIndex){
6102 var row = this.renderRow(cm, ds, rowIndex);
6104 tbody.createChild(row);
6108 if(row.cellObjects.length){
6109 Roo.each(row.cellObjects, function(r){
6110 _this.renderCellObject(r);
6117 Roo.each(this.el.select('tbody td', true).elements, function(e){
6118 e.on('mouseover', _this.onMouseover, _this);
6121 Roo.each(this.el.select('tbody td', true).elements, function(e){
6122 e.on('mouseout', _this.onMouseout, _this);
6124 this.fireEvent('rowsrendered', this);
6125 //if(this.loadMask){
6126 // this.maskEl.hide();
6131 onUpdate : function(ds,record)
6133 this.refreshRow(record);
6136 onRemove : function(ds, record, index, isUpdate){
6137 if(isUpdate !== true){
6138 this.fireEvent("beforerowremoved", this, index, record);
6140 var bt = this.mainBody.dom;
6142 var rows = this.el.select('tbody > tr', true).elements;
6144 if(typeof(rows[index]) != 'undefined'){
6145 bt.removeChild(rows[index].dom);
6148 // if(bt.rows[index]){
6149 // bt.removeChild(bt.rows[index]);
6152 if(isUpdate !== true){
6153 //this.stripeRows(index);
6154 //this.syncRowHeights(index, index);
6156 this.fireEvent("rowremoved", this, index, record);
6160 onAdd : function(ds, records, rowIndex)
6162 //Roo.log('on Add called');
6163 // - note this does not handle multiple adding very well..
6164 var bt = this.mainBody.dom;
6165 for (var i =0 ; i < records.length;i++) {
6166 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6167 //Roo.log(records[i]);
6168 //Roo.log(this.store.getAt(rowIndex+i));
6169 this.insertRow(this.store, rowIndex + i, false);
6176 refreshRow : function(record){
6177 var ds = this.store, index;
6178 if(typeof record == 'number'){
6180 record = ds.getAt(index);
6182 index = ds.indexOf(record);
6184 this.insertRow(ds, index, true);
6185 this.onRemove(ds, record, index+1, true);
6186 //this.syncRowHeights(index, index);
6188 this.fireEvent("rowupdated", this, index, record);
6191 insertRow : function(dm, rowIndex, isUpdate){
6194 this.fireEvent("beforerowsinserted", this, rowIndex);
6196 //var s = this.getScrollState();
6197 var row = this.renderRow(this.cm, this.store, rowIndex);
6198 // insert before rowIndex..
6199 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6203 if(row.cellObjects.length){
6204 Roo.each(row.cellObjects, function(r){
6205 _this.renderCellObject(r);
6210 this.fireEvent("rowsinserted", this, rowIndex);
6211 //this.syncRowHeights(firstRow, lastRow);
6212 //this.stripeRows(firstRow);
6219 getRowDom : function(rowIndex)
6221 var rows = this.el.select('tbody > tr', true).elements;
6223 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6226 // returns the object tree for a tr..
6229 renderRow : function(cm, ds, rowIndex)
6232 var d = ds.getAt(rowIndex);
6239 var cellObjects = [];
6241 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6242 var config = cm.config[i];
6244 var renderer = cm.getRenderer(i);
6248 if(typeof(renderer) !== 'undefined'){
6249 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6251 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6252 // and are rendered into the cells after the row is rendered - using the id for the element.
6254 if(typeof(value) === 'object'){
6264 rowIndex : rowIndex,
6269 this.fireEvent('rowclass', this, rowcfg);
6273 cls : rowcfg.rowClass,
6275 html: (typeof(value) === 'object') ? '' : value
6282 if(typeof(config.colspan) != 'undefined'){
6283 td.colspan = config.colspan;
6286 if(typeof(config.hidden) != 'undefined' && config.hidden){
6287 td.style += ' display:none;';
6290 if(typeof(config.align) != 'undefined' && config.align.length){
6291 td.style += ' text-align:' + config.align + ';';
6294 if(typeof(config.width) != 'undefined'){
6295 td.style += ' width:' + config.width + 'px;';
6298 if(typeof(config.cursor) != 'undefined'){
6299 td.style += ' cursor:' + config.cursor + ';';
6302 if(typeof(config.cls) != 'undefined'){
6303 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6306 ['xs','sm','md','lg'].map(function(size){
6308 if(typeof(config[size]) == 'undefined'){
6312 if (!config[size]) { // 0 = hidden
6313 td.cls += ' hidden-' + size;
6317 td.cls += ' col-' + size + '-' + config[size];
6325 row.cellObjects = cellObjects;
6333 onBeforeLoad : function()
6335 //Roo.log('ds onBeforeLoad');
6339 //if(this.loadMask){
6340 // this.maskEl.show();
6348 this.el.select('tbody', true).first().dom.innerHTML = '';
6351 * Show or hide a row.
6352 * @param {Number} rowIndex to show or hide
6353 * @param {Boolean} state hide
6355 setRowVisibility : function(rowIndex, state)
6357 var bt = this.mainBody.dom;
6359 var rows = this.el.select('tbody > tr', true).elements;
6361 if(typeof(rows[rowIndex]) == 'undefined'){
6364 rows[rowIndex].dom.style.display = state ? '' : 'none';
6368 getSelectionModel : function(){
6370 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6372 return this.selModel;
6375 * Render the Roo.bootstrap object from renderder
6377 renderCellObject : function(r)
6381 var t = r.cfg.render(r.container);
6384 Roo.each(r.cfg.cn, function(c){
6386 container: t.getChildContainer(),
6389 _this.renderCellObject(child);
6394 getRowIndex : function(row)
6398 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6421 * @class Roo.bootstrap.TableCell
6422 * @extends Roo.bootstrap.Component
6423 * Bootstrap TableCell class
6424 * @cfg {String} html cell contain text
6425 * @cfg {String} cls cell class
6426 * @cfg {String} tag cell tag (td|th) default td
6427 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6428 * @cfg {String} align Aligns the content in a cell
6429 * @cfg {String} axis Categorizes cells
6430 * @cfg {String} bgcolor Specifies the background color of a cell
6431 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6432 * @cfg {Number} colspan Specifies the number of columns a cell should span
6433 * @cfg {String} headers Specifies one or more header cells a cell is related to
6434 * @cfg {Number} height Sets the height of a cell
6435 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6436 * @cfg {Number} rowspan Sets the number of rows a cell should span
6437 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6438 * @cfg {String} valign Vertical aligns the content in a cell
6439 * @cfg {Number} width Specifies the width of a cell
6442 * Create a new TableCell
6443 * @param {Object} config The config object
6446 Roo.bootstrap.TableCell = function(config){
6447 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6450 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6470 getAutoCreate : function(){
6471 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6491 cfg.align=this.align
6497 cfg.bgcolor=this.bgcolor
6500 cfg.charoff=this.charoff
6503 cfg.colspan=this.colspan
6506 cfg.headers=this.headers
6509 cfg.height=this.height
6512 cfg.nowrap=this.nowrap
6515 cfg.rowspan=this.rowspan
6518 cfg.scope=this.scope
6521 cfg.valign=this.valign
6524 cfg.width=this.width
6543 * @class Roo.bootstrap.TableRow
6544 * @extends Roo.bootstrap.Component
6545 * Bootstrap TableRow class
6546 * @cfg {String} cls row class
6547 * @cfg {String} align Aligns the content in a table row
6548 * @cfg {String} bgcolor Specifies a background color for a table row
6549 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6550 * @cfg {String} valign Vertical aligns the content in a table row
6553 * Create a new TableRow
6554 * @param {Object} config The config object
6557 Roo.bootstrap.TableRow = function(config){
6558 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6561 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6569 getAutoCreate : function(){
6570 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6580 cfg.align = this.align;
6583 cfg.bgcolor = this.bgcolor;
6586 cfg.charoff = this.charoff;
6589 cfg.valign = this.valign;
6607 * @class Roo.bootstrap.TableBody
6608 * @extends Roo.bootstrap.Component
6609 * Bootstrap TableBody class
6610 * @cfg {String} cls element class
6611 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6612 * @cfg {String} align Aligns the content inside the element
6613 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6614 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6617 * Create a new TableBody
6618 * @param {Object} config The config object
6621 Roo.bootstrap.TableBody = function(config){
6622 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6625 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6633 getAutoCreate : function(){
6634 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6648 cfg.align = this.align;
6651 cfg.charoff = this.charoff;
6654 cfg.valign = this.valign;
6661 // initEvents : function()
6668 // this.store = Roo.factory(this.store, Roo.data);
6669 // this.store.on('load', this.onLoad, this);
6671 // this.store.load();
6675 // onLoad: function ()
6677 // this.fireEvent('load', this);
6687 * Ext JS Library 1.1.1
6688 * Copyright(c) 2006-2007, Ext JS, LLC.
6690 * Originally Released Under LGPL - original licence link has changed is not relivant.
6693 * <script type="text/javascript">
6696 // as we use this in bootstrap.
6697 Roo.namespace('Roo.form');
6699 * @class Roo.form.Action
6700 * Internal Class used to handle form actions
6702 * @param {Roo.form.BasicForm} el The form element or its id
6703 * @param {Object} config Configuration options
6708 // define the action interface
6709 Roo.form.Action = function(form, options){
6711 this.options = options || {};
6714 * Client Validation Failed
6717 Roo.form.Action.CLIENT_INVALID = 'client';
6719 * Server Validation Failed
6722 Roo.form.Action.SERVER_INVALID = 'server';
6724 * Connect to Server Failed
6727 Roo.form.Action.CONNECT_FAILURE = 'connect';
6729 * Reading Data from Server Failed
6732 Roo.form.Action.LOAD_FAILURE = 'load';
6734 Roo.form.Action.prototype = {
6736 failureType : undefined,
6737 response : undefined,
6741 run : function(options){
6746 success : function(response){
6751 handleResponse : function(response){
6755 // default connection failure
6756 failure : function(response){
6758 this.response = response;
6759 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6760 this.form.afterAction(this, false);
6763 processResponse : function(response){
6764 this.response = response;
6765 if(!response.responseText){
6768 this.result = this.handleResponse(response);
6772 // utility functions used internally
6773 getUrl : function(appendParams){
6774 var url = this.options.url || this.form.url || this.form.el.dom.action;
6776 var p = this.getParams();
6778 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6784 getMethod : function(){
6785 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6788 getParams : function(){
6789 var bp = this.form.baseParams;
6790 var p = this.options.params;
6792 if(typeof p == "object"){
6793 p = Roo.urlEncode(Roo.applyIf(p, bp));
6794 }else if(typeof p == 'string' && bp){
6795 p += '&' + Roo.urlEncode(bp);
6798 p = Roo.urlEncode(bp);
6803 createCallback : function(){
6805 success: this.success,
6806 failure: this.failure,
6808 timeout: (this.form.timeout*1000),
6809 upload: this.form.fileUpload ? this.success : undefined
6814 Roo.form.Action.Submit = function(form, options){
6815 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6818 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6821 haveProgress : false,
6822 uploadComplete : false,
6824 // uploadProgress indicator.
6825 uploadProgress : function()
6827 if (!this.form.progressUrl) {
6831 if (!this.haveProgress) {
6832 Roo.MessageBox.progress("Uploading", "Uploading");
6834 if (this.uploadComplete) {
6835 Roo.MessageBox.hide();
6839 this.haveProgress = true;
6841 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6843 var c = new Roo.data.Connection();
6845 url : this.form.progressUrl,
6850 success : function(req){
6851 //console.log(data);
6855 rdata = Roo.decode(req.responseText)
6857 Roo.log("Invalid data from server..");
6861 if (!rdata || !rdata.success) {
6863 Roo.MessageBox.alert(Roo.encode(rdata));
6866 var data = rdata.data;
6868 if (this.uploadComplete) {
6869 Roo.MessageBox.hide();
6874 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6875 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6878 this.uploadProgress.defer(2000,this);
6881 failure: function(data) {
6882 Roo.log('progress url failed ');
6893 // run get Values on the form, so it syncs any secondary forms.
6894 this.form.getValues();
6896 var o = this.options;
6897 var method = this.getMethod();
6898 var isPost = method == 'POST';
6899 if(o.clientValidation === false || this.form.isValid()){
6901 if (this.form.progressUrl) {
6902 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6903 (new Date() * 1) + '' + Math.random());
6908 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6909 form:this.form.el.dom,
6910 url:this.getUrl(!isPost),
6912 params:isPost ? this.getParams() : null,
6913 isUpload: this.form.fileUpload
6916 this.uploadProgress();
6918 }else if (o.clientValidation !== false){ // client validation failed
6919 this.failureType = Roo.form.Action.CLIENT_INVALID;
6920 this.form.afterAction(this, false);
6924 success : function(response)
6926 this.uploadComplete= true;
6927 if (this.haveProgress) {
6928 Roo.MessageBox.hide();
6932 var result = this.processResponse(response);
6933 if(result === true || result.success){
6934 this.form.afterAction(this, true);
6938 this.form.markInvalid(result.errors);
6939 this.failureType = Roo.form.Action.SERVER_INVALID;
6941 this.form.afterAction(this, false);
6943 failure : function(response)
6945 this.uploadComplete= true;
6946 if (this.haveProgress) {
6947 Roo.MessageBox.hide();
6950 this.response = response;
6951 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6952 this.form.afterAction(this, false);
6955 handleResponse : function(response){
6956 if(this.form.errorReader){
6957 var rs = this.form.errorReader.read(response);
6960 for(var i = 0, len = rs.records.length; i < len; i++) {
6961 var r = rs.records[i];
6965 if(errors.length < 1){
6969 success : rs.success,
6975 ret = Roo.decode(response.responseText);
6979 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6989 Roo.form.Action.Load = function(form, options){
6990 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6991 this.reader = this.form.reader;
6994 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6999 Roo.Ajax.request(Roo.apply(
7000 this.createCallback(), {
7001 method:this.getMethod(),
7002 url:this.getUrl(false),
7003 params:this.getParams()
7007 success : function(response){
7009 var result = this.processResponse(response);
7010 if(result === true || !result.success || !result.data){
7011 this.failureType = Roo.form.Action.LOAD_FAILURE;
7012 this.form.afterAction(this, false);
7015 this.form.clearInvalid();
7016 this.form.setValues(result.data);
7017 this.form.afterAction(this, true);
7020 handleResponse : function(response){
7021 if(this.form.reader){
7022 var rs = this.form.reader.read(response);
7023 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7025 success : rs.success,
7029 return Roo.decode(response.responseText);
7033 Roo.form.Action.ACTION_TYPES = {
7034 'load' : Roo.form.Action.Load,
7035 'submit' : Roo.form.Action.Submit
7044 * @class Roo.bootstrap.Form
7045 * @extends Roo.bootstrap.Component
7046 * Bootstrap Form class
7047 * @cfg {String} method GET | POST (default POST)
7048 * @cfg {String} labelAlign top | left (default top)
7049 * @cfg {String} align left | right - for navbars
7050 * @cfg {Boolean} loadMask load mask when submit (default true)
7055 * @param {Object} config The config object
7059 Roo.bootstrap.Form = function(config){
7060 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7063 * @event clientvalidation
7064 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7065 * @param {Form} this
7066 * @param {Boolean} valid true if the form has passed client-side validation
7068 clientvalidation: true,
7070 * @event beforeaction
7071 * Fires before any action is performed. Return false to cancel the action.
7072 * @param {Form} this
7073 * @param {Action} action The action to be performed
7077 * @event actionfailed
7078 * Fires when an action fails.
7079 * @param {Form} this
7080 * @param {Action} action The action that failed
7082 actionfailed : true,
7084 * @event actioncomplete
7085 * Fires when an action is completed.
7086 * @param {Form} this
7087 * @param {Action} action The action that completed
7089 actioncomplete : true
7094 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7097 * @cfg {String} method
7098 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7103 * The URL to use for form actions if one isn't supplied in the action options.
7106 * @cfg {Boolean} fileUpload
7107 * Set to true if this form is a file upload.
7111 * @cfg {Object} baseParams
7112 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7116 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7120 * @cfg {Sting} align (left|right) for navbar forms
7125 activeAction : null,
7128 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7129 * element by passing it or its id or mask the form itself by passing in true.
7132 waitMsgTarget : false,
7136 getAutoCreate : function(){
7140 method : this.method || 'POST',
7141 id : this.id || Roo.id(),
7144 if (this.parent().xtype.match(/^Nav/)) {
7145 cfg.cls = 'navbar-form navbar-' + this.align;
7149 if (this.labelAlign == 'left' ) {
7150 cfg.cls += ' form-horizontal';
7156 initEvents : function()
7158 this.el.on('submit', this.onSubmit, this);
7159 // this was added as random key presses on the form where triggering form submit.
7160 this.el.on('keypress', function(e) {
7161 if (e.getCharCode() != 13) {
7164 // we might need to allow it for textareas.. and some other items.
7165 // check e.getTarget().
7167 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7171 Roo.log("keypress blocked");
7179 onSubmit : function(e){
7184 * Returns true if client-side validation on the form is successful.
7187 isValid : function(){
7188 var items = this.getItems();
7190 items.each(function(f){
7199 * Returns true if any fields in this form have changed since their original load.
7202 isDirty : function(){
7204 var items = this.getItems();
7205 items.each(function(f){
7215 * Performs a predefined action (submit or load) or custom actions you define on this form.
7216 * @param {String} actionName The name of the action type
7217 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7218 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7219 * accept other config options):
7221 Property Type Description
7222 ---------------- --------------- ----------------------------------------------------------------------------------
7223 url String The url for the action (defaults to the form's url)
7224 method String The form method to use (defaults to the form's method, or POST if not defined)
7225 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7226 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7227 validate the form on the client (defaults to false)
7229 * @return {BasicForm} this
7231 doAction : function(action, options){
7232 if(typeof action == 'string'){
7233 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7235 if(this.fireEvent('beforeaction', this, action) !== false){
7236 this.beforeAction(action);
7237 action.run.defer(100, action);
7243 beforeAction : function(action){
7244 var o = action.options;
7247 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7249 // not really supported yet.. ??
7251 //if(this.waitMsgTarget === true){
7252 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7253 //}else if(this.waitMsgTarget){
7254 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7255 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7257 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7263 afterAction : function(action, success){
7264 this.activeAction = null;
7265 var o = action.options;
7267 //if(this.waitMsgTarget === true){
7269 //}else if(this.waitMsgTarget){
7270 // this.waitMsgTarget.unmask();
7272 // Roo.MessageBox.updateProgress(1);
7273 // Roo.MessageBox.hide();
7280 Roo.callback(o.success, o.scope, [this, action]);
7281 this.fireEvent('actioncomplete', this, action);
7285 // failure condition..
7286 // we have a scenario where updates need confirming.
7287 // eg. if a locking scenario exists..
7288 // we look for { errors : { needs_confirm : true }} in the response.
7290 (typeof(action.result) != 'undefined') &&
7291 (typeof(action.result.errors) != 'undefined') &&
7292 (typeof(action.result.errors.needs_confirm) != 'undefined')
7295 Roo.log("not supported yet");
7298 Roo.MessageBox.confirm(
7299 "Change requires confirmation",
7300 action.result.errorMsg,
7305 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7315 Roo.callback(o.failure, o.scope, [this, action]);
7316 // show an error message if no failed handler is set..
7317 if (!this.hasListener('actionfailed')) {
7318 Roo.log("need to add dialog support");
7320 Roo.MessageBox.alert("Error",
7321 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7322 action.result.errorMsg :
7323 "Saving Failed, please check your entries or try again"
7328 this.fireEvent('actionfailed', this, action);
7333 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7334 * @param {String} id The value to search for
7337 findField : function(id){
7338 var items = this.getItems();
7339 var field = items.get(id);
7341 items.each(function(f){
7342 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7349 return field || null;
7352 * Mark fields in this form invalid in bulk.
7353 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7354 * @return {BasicForm} this
7356 markInvalid : function(errors){
7357 if(errors instanceof Array){
7358 for(var i = 0, len = errors.length; i < len; i++){
7359 var fieldError = errors[i];
7360 var f = this.findField(fieldError.id);
7362 f.markInvalid(fieldError.msg);
7368 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7369 field.markInvalid(errors[id]);
7373 //Roo.each(this.childForms || [], function (f) {
7374 // f.markInvalid(errors);
7381 * Set values for fields in this form in bulk.
7382 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7383 * @return {BasicForm} this
7385 setValues : function(values){
7386 if(values instanceof Array){ // array of objects
7387 for(var i = 0, len = values.length; i < len; i++){
7389 var f = this.findField(v.id);
7391 f.setValue(v.value);
7392 if(this.trackResetOnLoad){
7393 f.originalValue = f.getValue();
7397 }else{ // object hash
7400 if(typeof values[id] != 'function' && (field = this.findField(id))){
7402 if (field.setFromData &&
7404 field.displayField &&
7405 // combos' with local stores can
7406 // be queried via setValue()
7407 // to set their value..
7408 (field.store && !field.store.isLocal)
7412 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7413 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7414 field.setFromData(sd);
7417 field.setValue(values[id]);
7421 if(this.trackResetOnLoad){
7422 field.originalValue = field.getValue();
7428 //Roo.each(this.childForms || [], function (f) {
7429 // f.setValues(values);
7436 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7437 * they are returned as an array.
7438 * @param {Boolean} asString
7441 getValues : function(asString){
7442 //if (this.childForms) {
7443 // copy values from the child forms
7444 // Roo.each(this.childForms, function (f) {
7445 // this.setValues(f.getValues());
7451 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7452 if(asString === true){
7455 return Roo.urlDecode(fs);
7459 * Returns the fields in this form as an object with key/value pairs.
7460 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7463 getFieldValues : function(with_hidden)
7465 var items = this.getItems();
7467 items.each(function(f){
7471 var v = f.getValue();
7472 if (f.inputType =='radio') {
7473 if (typeof(ret[f.getName()]) == 'undefined') {
7474 ret[f.getName()] = ''; // empty..
7477 if (!f.el.dom.checked) {
7485 // not sure if this supported any more..
7486 if ((typeof(v) == 'object') && f.getRawValue) {
7487 v = f.getRawValue() ; // dates..
7489 // combo boxes where name != hiddenName...
7490 if (f.name != f.getName()) {
7491 ret[f.name] = f.getRawValue();
7493 ret[f.getName()] = v;
7500 * Clears all invalid messages in this form.
7501 * @return {BasicForm} this
7503 clearInvalid : function(){
7504 var items = this.getItems();
7506 items.each(function(f){
7517 * @return {BasicForm} this
7520 var items = this.getItems();
7521 items.each(function(f){
7525 Roo.each(this.childForms || [], function (f) {
7532 getItems : function()
7534 var r=new Roo.util.MixedCollection(false, function(o){
7535 return o.id || (o.id = Roo.id());
7537 var iter = function(el) {
7544 Roo.each(el.items,function(e) {
7564 * Ext JS Library 1.1.1
7565 * Copyright(c) 2006-2007, Ext JS, LLC.
7567 * Originally Released Under LGPL - original licence link has changed is not relivant.
7570 * <script type="text/javascript">
7573 * @class Roo.form.VTypes
7574 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7577 Roo.form.VTypes = function(){
7578 // closure these in so they are only created once.
7579 var alpha = /^[a-zA-Z_]+$/;
7580 var alphanum = /^[a-zA-Z0-9_]+$/;
7581 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7582 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7584 // All these messages and functions are configurable
7587 * The function used to validate email addresses
7588 * @param {String} value The email address
7590 'email' : function(v){
7591 return email.test(v);
7594 * The error text to display when the email validation function returns false
7597 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7599 * The keystroke filter mask to be applied on email input
7602 'emailMask' : /[a-z0-9_\.\-@]/i,
7605 * The function used to validate URLs
7606 * @param {String} value The URL
7608 'url' : function(v){
7612 * The error text to display when the url validation function returns false
7615 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7618 * The function used to validate alpha values
7619 * @param {String} value The value
7621 'alpha' : function(v){
7622 return alpha.test(v);
7625 * The error text to display when the alpha validation function returns false
7628 'alphaText' : 'This field should only contain letters and _',
7630 * The keystroke filter mask to be applied on alpha input
7633 'alphaMask' : /[a-z_]/i,
7636 * The function used to validate alphanumeric values
7637 * @param {String} value The value
7639 'alphanum' : function(v){
7640 return alphanum.test(v);
7643 * The error text to display when the alphanumeric validation function returns false
7646 'alphanumText' : 'This field should only contain letters, numbers and _',
7648 * The keystroke filter mask to be applied on alphanumeric input
7651 'alphanumMask' : /[a-z0-9_]/i
7661 * @class Roo.bootstrap.Input
7662 * @extends Roo.bootstrap.Component
7663 * Bootstrap Input class
7664 * @cfg {Boolean} disabled is it disabled
7665 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7666 * @cfg {String} name name of the input
7667 * @cfg {string} fieldLabel - the label associated
7668 * @cfg {string} placeholder - placeholder to put in text.
7669 * @cfg {string} before - input group add on before
7670 * @cfg {string} after - input group add on after
7671 * @cfg {string} size - (lg|sm) or leave empty..
7672 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7673 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7674 * @cfg {Number} md colspan out of 12 for computer-sized screens
7675 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7676 * @cfg {string} value default value of the input
7677 * @cfg {Number} labelWidth set the width of label (0-12)
7678 * @cfg {String} labelAlign (top|left)
7679 * @cfg {Boolean} readOnly Specifies that the field should be read-only
7680 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7682 * @cfg {String} align (left|center|right) Default left
7683 * @cfg {Boolean} forceFeedback (true|false) Default false
7689 * Create a new Input
7690 * @param {Object} config The config object
7693 Roo.bootstrap.Input = function(config){
7694 Roo.bootstrap.Input.superclass.constructor.call(this, config);
7699 * Fires when this field receives input focus.
7700 * @param {Roo.form.Field} this
7705 * Fires when this field loses input focus.
7706 * @param {Roo.form.Field} this
7711 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
7712 * {@link Roo.EventObject#getKey} to determine which key was pressed.
7713 * @param {Roo.form.Field} this
7714 * @param {Roo.EventObject} e The event object
7719 * Fires just before the field blurs if the field value has changed.
7720 * @param {Roo.form.Field} this
7721 * @param {Mixed} newValue The new value
7722 * @param {Mixed} oldValue The original value
7727 * Fires after the field has been marked as invalid.
7728 * @param {Roo.form.Field} this
7729 * @param {String} msg The validation message
7734 * Fires after the field has been validated with no errors.
7735 * @param {Roo.form.Field} this
7740 * Fires after the key up
7741 * @param {Roo.form.Field} this
7742 * @param {Roo.EventObject} e The event Object
7748 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
7750 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7751 automatic validation (defaults to "keyup").
7753 validationEvent : "keyup",
7755 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7757 validateOnBlur : true,
7759 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7761 validationDelay : 250,
7763 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7765 focusClass : "x-form-focus", // not needed???
7769 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7771 invalidClass : "has-warning",
7774 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7776 validClass : "has-success",
7779 * @cfg {Boolean} hasFeedback (true|false) default true
7784 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7786 invalidFeedbackClass : "glyphicon-warning-sign",
7789 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7791 validFeedbackClass : "glyphicon-ok",
7794 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7796 selectOnFocus : false,
7799 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7803 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7808 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7810 disableKeyFilter : false,
7813 * @cfg {Boolean} disabled True to disable the field (defaults to false).
7817 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7821 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7823 blankText : "This field is required",
7826 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7830 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7832 maxLength : Number.MAX_VALUE,
7834 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7836 minLengthText : "The minimum length for this field is {0}",
7838 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7840 maxLengthText : "The maximum length for this field is {0}",
7844 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7845 * If available, this function will be called only after the basic validators all return true, and will be passed the
7846 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7850 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7851 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7852 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
7856 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7860 autocomplete: false,
7879 formatedValue : false,
7880 forceFeedback : false,
7882 parentLabelAlign : function()
7885 while (parent.parent()) {
7886 parent = parent.parent();
7887 if (typeof(parent.labelAlign) !='undefined') {
7888 return parent.labelAlign;
7895 getAutoCreate : function(){
7897 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7903 if(this.inputType != 'hidden'){
7904 cfg.cls = 'form-group' //input-group
7910 type : this.inputType,
7912 cls : 'form-control',
7913 placeholder : this.placeholder || '',
7914 autocomplete : this.autocomplete || 'new-password'
7919 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7922 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7923 input.maxLength = this.maxLength;
7926 if (this.disabled) {
7927 input.disabled=true;
7930 if (this.readOnly) {
7931 input.readonly=true;
7935 input.name = this.name;
7938 input.cls += ' input-' + this.size;
7941 ['xs','sm','md','lg'].map(function(size){
7942 if (settings[size]) {
7943 cfg.cls += ' col-' + size + '-' + settings[size];
7947 var inputblock = input;
7951 cls: 'glyphicon form-control-feedback'
7954 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7957 cls : 'has-feedback',
7965 if (this.before || this.after) {
7968 cls : 'input-group',
7972 if (this.before && typeof(this.before) == 'string') {
7974 inputblock.cn.push({
7976 cls : 'roo-input-before input-group-addon',
7980 if (this.before && typeof(this.before) == 'object') {
7981 this.before = Roo.factory(this.before);
7983 inputblock.cn.push({
7985 cls : 'roo-input-before input-group-' +
7986 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7990 inputblock.cn.push(input);
7992 if (this.after && typeof(this.after) == 'string') {
7993 inputblock.cn.push({
7995 cls : 'roo-input-after input-group-addon',
7999 if (this.after && typeof(this.after) == 'object') {
8000 this.after = Roo.factory(this.after);
8002 inputblock.cn.push({
8004 cls : 'roo-input-after input-group-' +
8005 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8009 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8010 inputblock.cls += ' has-feedback';
8011 inputblock.cn.push(feedback);
8015 if (align ==='left' && this.fieldLabel.length) {
8022 cls : 'control-label col-sm-' + this.labelWidth,
8023 html : this.fieldLabel
8027 cls : "col-sm-" + (12 - this.labelWidth),
8034 } else if ( this.fieldLabel.length) {
8040 //cls : 'input-group-addon',
8041 html : this.fieldLabel
8060 if (this.parentType === 'Navbar' && this.parent().bar) {
8061 cfg.cls += ' navbar-form';
8068 * return the real input element.
8070 inputEl: function ()
8072 return this.el.select('input.form-control',true).first();
8075 tooltipEl : function()
8077 return this.inputEl();
8080 setDisabled : function(v)
8082 var i = this.inputEl().dom;
8084 i.removeAttribute('disabled');
8088 i.setAttribute('disabled','true');
8090 initEvents : function()
8093 this.inputEl().on("keydown" , this.fireKey, this);
8094 this.inputEl().on("focus", this.onFocus, this);
8095 this.inputEl().on("blur", this.onBlur, this);
8097 this.inputEl().relayEvent('keyup', this);
8099 // reference to original value for reset
8100 this.originalValue = this.getValue();
8101 //Roo.form.TextField.superclass.initEvents.call(this);
8102 if(this.validationEvent == 'keyup'){
8103 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8104 this.inputEl().on('keyup', this.filterValidation, this);
8106 else if(this.validationEvent !== false){
8107 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8110 if(this.selectOnFocus){
8111 this.on("focus", this.preFocus, this);
8114 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8115 this.inputEl().on("keypress", this.filterKeys, this);
8118 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8119 this.el.on("click", this.autoSize, this);
8122 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8123 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8126 if (typeof(this.before) == 'object') {
8127 this.before.render(this.el.select('.roo-input-before',true).first());
8129 if (typeof(this.after) == 'object') {
8130 this.after.render(this.el.select('.roo-input-after',true).first());
8135 filterValidation : function(e){
8136 if(!e.isNavKeyPress()){
8137 this.validationTask.delay(this.validationDelay);
8141 * Validates the field value
8142 * @return {Boolean} True if the value is valid, else false
8144 validate : function(){
8145 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8146 if(this.disabled || this.validateValue(this.getRawValue())){
8157 * Validates a value according to the field's validation rules and marks the field as invalid
8158 * if the validation fails
8159 * @param {Mixed} value The value to validate
8160 * @return {Boolean} True if the value is valid, else false
8162 validateValue : function(value){
8163 if(value.length < 1) { // if it's blank
8164 if(this.allowBlank){
8170 if(value.length < this.minLength){
8173 if(value.length > this.maxLength){
8177 var vt = Roo.form.VTypes;
8178 if(!vt[this.vtype](value, this)){
8182 if(typeof this.validator == "function"){
8183 var msg = this.validator(value);
8189 if(this.regex && !this.regex.test(value)){
8199 fireKey : function(e){
8200 //Roo.log('field ' + e.getKey());
8201 if(e.isNavKeyPress()){
8202 this.fireEvent("specialkey", this, e);
8205 focus : function (selectText){
8207 this.inputEl().focus();
8208 if(selectText === true){
8209 this.inputEl().dom.select();
8215 onFocus : function(){
8216 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8217 // this.el.addClass(this.focusClass);
8220 this.hasFocus = true;
8221 this.startValue = this.getValue();
8222 this.fireEvent("focus", this);
8226 beforeBlur : Roo.emptyFn,
8230 onBlur : function(){
8232 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8233 //this.el.removeClass(this.focusClass);
8235 this.hasFocus = false;
8236 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8239 var v = this.getValue();
8240 if(String(v) !== String(this.startValue)){
8241 this.fireEvent('change', this, v, this.startValue);
8243 this.fireEvent("blur", this);
8247 * Resets the current field value to the originally loaded value and clears any validation messages
8250 this.setValue(this.originalValue);
8254 * Returns the name of the field
8255 * @return {Mixed} name The name field
8257 getName: function(){
8261 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8262 * @return {Mixed} value The field value
8264 getValue : function(){
8266 var v = this.inputEl().getValue();
8271 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8272 * @return {Mixed} value The field value
8274 getRawValue : function(){
8275 var v = this.inputEl().getValue();
8281 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8282 * @param {Mixed} value The value to set
8284 setRawValue : function(v){
8285 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8288 selectText : function(start, end){
8289 var v = this.getRawValue();
8291 start = start === undefined ? 0 : start;
8292 end = end === undefined ? v.length : end;
8293 var d = this.inputEl().dom;
8294 if(d.setSelectionRange){
8295 d.setSelectionRange(start, end);
8296 }else if(d.createTextRange){
8297 var range = d.createTextRange();
8298 range.moveStart("character", start);
8299 range.moveEnd("character", v.length-end);
8306 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8307 * @param {Mixed} value The value to set
8309 setValue : function(v){
8312 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8318 processValue : function(value){
8319 if(this.stripCharsRe){
8320 var newValue = value.replace(this.stripCharsRe, '');
8321 if(newValue !== value){
8322 this.setRawValue(newValue);
8329 preFocus : function(){
8331 if(this.selectOnFocus){
8332 this.inputEl().dom.select();
8335 filterKeys : function(e){
8337 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8340 var c = e.getCharCode(), cc = String.fromCharCode(c);
8341 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8344 if(!this.maskRe.test(cc)){
8349 * Clear any invalid styles/messages for this field
8351 clearInvalid : function(){
8353 if(!this.el || this.preventMark){ // not rendered
8356 this.el.removeClass(this.invalidClass);
8358 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8360 var feedback = this.el.select('.form-control-feedback', true).first();
8363 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8368 this.fireEvent('valid', this);
8372 * Mark this field as valid
8374 markValid : function()
8376 if(!this.el || this.preventMark){ // not rendered
8380 this.el.removeClass([this.invalidClass, this.validClass]);
8382 var feedback = this.el.select('.form-control-feedback', true).first();
8385 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8388 if(this.disabled || this.allowBlank){
8392 var formGroup = this.el.findParent('.form-group', false, true);
8396 var label = formGroup.select('label', true).first();
8397 var icon = formGroup.select('i.fa-star', true).first();
8404 this.el.addClass(this.validClass);
8406 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8408 var feedback = this.el.select('.form-control-feedback', true).first();
8411 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8412 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8417 this.fireEvent('valid', this);
8421 * Mark this field as invalid
8422 * @param {String} msg The validation message
8424 markInvalid : function(msg)
8426 if(!this.el || this.preventMark){ // not rendered
8430 this.el.removeClass([this.invalidClass, this.validClass]);
8432 var feedback = this.el.select('.form-control-feedback', true).first();
8435 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8438 if(this.disabled || this.allowBlank){
8442 var formGroup = this.el.findParent('.form-group', false, true);
8445 var label = formGroup.select('label', true).first();
8446 var icon = formGroup.select('i.fa-star', true).first();
8448 if(!this.getValue().length && label && !icon){
8449 this.el.findParent('.form-group', false, true).createChild({
8451 cls : 'text-danger fa fa-lg fa-star',
8452 tooltip : 'This field is required',
8453 style : 'margin-right:5px;'
8459 this.el.addClass(this.invalidClass);
8461 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8463 var feedback = this.el.select('.form-control-feedback', true).first();
8466 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8468 if(this.getValue().length || this.forceFeedback){
8469 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8476 this.fireEvent('invalid', this, msg);
8479 SafariOnKeyDown : function(event)
8481 // this is a workaround for a password hang bug on chrome/ webkit.
8483 var isSelectAll = false;
8485 if(this.inputEl().dom.selectionEnd > 0){
8486 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8488 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8489 event.preventDefault();
8494 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8496 event.preventDefault();
8497 // this is very hacky as keydown always get's upper case.
8499 var cc = String.fromCharCode(event.getCharCode());
8500 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8504 adjustWidth : function(tag, w){
8505 tag = tag.toLowerCase();
8506 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8507 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8511 if(tag == 'textarea'){
8514 }else if(Roo.isOpera){
8518 if(tag == 'textarea'){
8537 * @class Roo.bootstrap.TextArea
8538 * @extends Roo.bootstrap.Input
8539 * Bootstrap TextArea class
8540 * @cfg {Number} cols Specifies the visible width of a text area
8541 * @cfg {Number} rows Specifies the visible number of lines in a text area
8542 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8543 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8544 * @cfg {string} html text
8547 * Create a new TextArea
8548 * @param {Object} config The config object
8551 Roo.bootstrap.TextArea = function(config){
8552 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8556 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8566 getAutoCreate : function(){
8568 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8579 value : this.value || '',
8580 html: this.html || '',
8581 cls : 'form-control',
8582 placeholder : this.placeholder || ''
8586 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8587 input.maxLength = this.maxLength;
8591 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8595 input.cols = this.cols;
8598 if (this.readOnly) {
8599 input.readonly = true;
8603 input.name = this.name;
8607 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8611 ['xs','sm','md','lg'].map(function(size){
8612 if (settings[size]) {
8613 cfg.cls += ' col-' + size + '-' + settings[size];
8617 var inputblock = input;
8619 if(this.hasFeedback && !this.allowBlank){
8623 cls: 'glyphicon form-control-feedback'
8627 cls : 'has-feedback',
8636 if (this.before || this.after) {
8639 cls : 'input-group',
8643 inputblock.cn.push({
8645 cls : 'input-group-addon',
8650 inputblock.cn.push(input);
8652 if(this.hasFeedback && !this.allowBlank){
8653 inputblock.cls += ' has-feedback';
8654 inputblock.cn.push(feedback);
8658 inputblock.cn.push({
8660 cls : 'input-group-addon',
8667 if (align ==='left' && this.fieldLabel.length) {
8668 // Roo.log("left and has label");
8674 cls : 'control-label col-sm-' + this.labelWidth,
8675 html : this.fieldLabel
8679 cls : "col-sm-" + (12 - this.labelWidth),
8686 } else if ( this.fieldLabel.length) {
8687 // Roo.log(" label");
8692 //cls : 'input-group-addon',
8693 html : this.fieldLabel
8703 // Roo.log(" no label && no align");
8713 if (this.disabled) {
8714 input.disabled=true;
8721 * return the real textarea element.
8723 inputEl: function ()
8725 return this.el.select('textarea.form-control',true).first();
8729 * Clear any invalid styles/messages for this field
8731 clearInvalid : function()
8734 if(!this.el || this.preventMark){ // not rendered
8738 var label = this.el.select('label', true).first();
8739 var icon = this.el.select('i.fa-star', true).first();
8745 this.el.removeClass(this.invalidClass);
8747 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8749 var feedback = this.el.select('.form-control-feedback', true).first();
8752 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8757 this.fireEvent('valid', this);
8761 * Mark this field as valid
8763 markValid : function()
8765 if(!this.el || this.preventMark){ // not rendered
8769 this.el.removeClass([this.invalidClass, this.validClass]);
8771 var feedback = this.el.select('.form-control-feedback', true).first();
8774 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8777 if(this.disabled || this.allowBlank){
8781 var label = this.el.select('label', true).first();
8782 var icon = this.el.select('i.fa-star', true).first();
8788 this.el.addClass(this.validClass);
8790 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8792 var feedback = this.el.select('.form-control-feedback', true).first();
8795 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8796 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8801 this.fireEvent('valid', this);
8805 * Mark this field as invalid
8806 * @param {String} msg The validation message
8808 markInvalid : function(msg)
8810 if(!this.el || this.preventMark){ // not rendered
8814 this.el.removeClass([this.invalidClass, this.validClass]);
8816 var feedback = this.el.select('.form-control-feedback', true).first();
8819 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8822 if(this.disabled || this.allowBlank){
8826 var label = this.el.select('label', true).first();
8827 var icon = this.el.select('i.fa-star', true).first();
8829 if(!this.getValue().length && label && !icon){
8830 this.el.createChild({
8832 cls : 'text-danger fa fa-lg fa-star',
8833 tooltip : 'This field is required',
8834 style : 'margin-right:5px;'
8838 this.el.addClass(this.invalidClass);
8840 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8842 var feedback = this.el.select('.form-control-feedback', true).first();
8845 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8847 if(this.getValue().length || this.forceFeedback){
8848 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8855 this.fireEvent('invalid', this, msg);
8863 * trigger field - base class for combo..
8868 * @class Roo.bootstrap.TriggerField
8869 * @extends Roo.bootstrap.Input
8870 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8871 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8872 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8873 * for which you can provide a custom implementation. For example:
8875 var trigger = new Roo.bootstrap.TriggerField();
8876 trigger.onTriggerClick = myTriggerFn;
8877 trigger.applyTo('my-field');
8880 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8881 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8882 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
8883 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8884 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8887 * Create a new TriggerField.
8888 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8889 * to the base TextField)
8891 Roo.bootstrap.TriggerField = function(config){
8892 this.mimicing = false;
8893 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8896 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
8898 * @cfg {String} triggerClass A CSS class to apply to the trigger
8901 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8906 * @cfg {Boolean} removable (true|false) special filter default false
8910 /** @cfg {Boolean} grow @hide */
8911 /** @cfg {Number} growMin @hide */
8912 /** @cfg {Number} growMax @hide */
8918 autoSize: Roo.emptyFn,
8925 actionMode : 'wrap',
8930 getAutoCreate : function(){
8932 var align = this.labelAlign || this.parentLabelAlign();
8937 cls: 'form-group' //input-group
8944 type : this.inputType,
8945 cls : 'form-control',
8946 autocomplete: 'new-password',
8947 placeholder : this.placeholder || ''
8951 input.name = this.name;
8954 input.cls += ' input-' + this.size;
8957 if (this.disabled) {
8958 input.disabled=true;
8961 var inputblock = input;
8963 if(this.hasFeedback && !this.allowBlank){
8967 cls: 'glyphicon form-control-feedback'
8970 if(this.removable && !this.editable && !this.tickable){
8972 cls : 'has-feedback',
8978 cls : 'roo-combo-removable-btn close'
8985 cls : 'has-feedback',
8994 if(this.removable && !this.editable && !this.tickable){
8996 cls : 'roo-removable',
9002 cls : 'roo-combo-removable-btn close'
9009 if (this.before || this.after) {
9012 cls : 'input-group',
9016 inputblock.cn.push({
9018 cls : 'input-group-addon',
9023 inputblock.cn.push(input);
9025 if(this.hasFeedback && !this.allowBlank){
9026 inputblock.cls += ' has-feedback';
9027 inputblock.cn.push(feedback);
9031 inputblock.cn.push({
9033 cls : 'input-group-addon',
9046 cls: 'form-hidden-field'
9060 cls: 'form-hidden-field'
9064 cls: 'select2-choices',
9068 cls: 'select2-search-field',
9081 cls: 'select2-container input-group',
9086 // cls: 'typeahead typeahead-long dropdown-menu',
9087 // style: 'display:none'
9092 if(!this.multiple && this.showToggleBtn){
9098 if (this.caret != false) {
9101 cls: 'fa fa-' + this.caret
9108 cls : 'input-group-addon btn dropdown-toggle',
9113 cls: 'combobox-clear',
9127 combobox.cls += ' select2-container-multi';
9130 if (align ==='left' && this.fieldLabel.length) {
9132 // Roo.log("left and has label");
9138 cls : 'control-label col-sm-' + this.labelWidth,
9139 html : this.fieldLabel
9143 cls : "col-sm-" + (12 - this.labelWidth),
9150 } else if ( this.fieldLabel.length) {
9151 // Roo.log(" label");
9156 //cls : 'input-group-addon',
9157 html : this.fieldLabel
9167 // Roo.log(" no label && no align");
9174 ['xs','sm','md','lg'].map(function(size){
9175 if (settings[size]) {
9176 cfg.cls += ' col-' + size + '-' + settings[size];
9187 onResize : function(w, h){
9188 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9189 // if(typeof w == 'number'){
9190 // var x = w - this.trigger.getWidth();
9191 // this.inputEl().setWidth(this.adjustWidth('input', x));
9192 // this.trigger.setStyle('left', x+'px');
9197 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9200 getResizeEl : function(){
9201 return this.inputEl();
9205 getPositionEl : function(){
9206 return this.inputEl();
9210 alignErrorIcon : function(){
9211 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9215 initEvents : function(){
9219 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9220 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9221 if(!this.multiple && this.showToggleBtn){
9222 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9223 if(this.hideTrigger){
9224 this.trigger.setDisplayed(false);
9226 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9230 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9233 if(this.removable && !this.editable && !this.tickable){
9234 var close = this.closeTriggerEl();
9237 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9238 close.on('click', this.removeBtnClick, this, close);
9242 //this.trigger.addClassOnOver('x-form-trigger-over');
9243 //this.trigger.addClassOnClick('x-form-trigger-click');
9246 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9250 closeTriggerEl : function()
9252 var close = this.el.select('.roo-combo-removable-btn', true).first();
9253 return close ? close : false;
9256 removeBtnClick : function(e, h, el)
9260 if(this.fireEvent("remove", this) !== false){
9265 createList : function()
9267 this.list = Roo.get(document.body).createChild({
9269 cls: 'typeahead typeahead-long dropdown-menu',
9270 style: 'display:none'
9273 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9278 initTrigger : function(){
9283 onDestroy : function(){
9285 this.trigger.removeAllListeners();
9286 // this.trigger.remove();
9289 // this.wrap.remove();
9291 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9295 onFocus : function(){
9296 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9299 this.wrap.addClass('x-trigger-wrap-focus');
9300 this.mimicing = true;
9301 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9302 if(this.monitorTab){
9303 this.el.on("keydown", this.checkTab, this);
9310 checkTab : function(e){
9311 if(e.getKey() == e.TAB){
9317 onBlur : function(){
9322 mimicBlur : function(e, t){
9324 if(!this.wrap.contains(t) && this.validateBlur()){
9331 triggerBlur : function(){
9332 this.mimicing = false;
9333 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9334 if(this.monitorTab){
9335 this.el.un("keydown", this.checkTab, this);
9337 //this.wrap.removeClass('x-trigger-wrap-focus');
9338 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9342 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9343 validateBlur : function(e, t){
9348 onDisable : function(){
9349 this.inputEl().dom.disabled = true;
9350 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9352 // this.wrap.addClass('x-item-disabled');
9357 onEnable : function(){
9358 this.inputEl().dom.disabled = false;
9359 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9361 // this.el.removeClass('x-item-disabled');
9366 onShow : function(){
9367 var ae = this.getActionEl();
9370 ae.dom.style.display = '';
9371 ae.dom.style.visibility = 'visible';
9377 onHide : function(){
9378 var ae = this.getActionEl();
9379 ae.dom.style.display = 'none';
9383 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9384 * by an implementing function.
9386 * @param {EventObject} e
9388 onTriggerClick : Roo.emptyFn
9392 * Ext JS Library 1.1.1
9393 * Copyright(c) 2006-2007, Ext JS, LLC.
9395 * Originally Released Under LGPL - original licence link has changed is not relivant.
9398 * <script type="text/javascript">
9403 * @class Roo.data.SortTypes
9405 * Defines the default sorting (casting?) comparison functions used when sorting data.
9407 Roo.data.SortTypes = {
9409 * Default sort that does nothing
9410 * @param {Mixed} s The value being converted
9411 * @return {Mixed} The comparison value
9418 * The regular expression used to strip tags
9422 stripTagsRE : /<\/?[^>]+>/gi,
9425 * Strips all HTML tags to sort on text only
9426 * @param {Mixed} s The value being converted
9427 * @return {String} The comparison value
9429 asText : function(s){
9430 return String(s).replace(this.stripTagsRE, "");
9434 * Strips all HTML tags to sort on text only - Case insensitive
9435 * @param {Mixed} s The value being converted
9436 * @return {String} The comparison value
9438 asUCText : function(s){
9439 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9443 * Case insensitive string
9444 * @param {Mixed} s The value being converted
9445 * @return {String} The comparison value
9447 asUCString : function(s) {
9448 return String(s).toUpperCase();
9453 * @param {Mixed} s The value being converted
9454 * @return {Number} The comparison value
9456 asDate : function(s) {
9460 if(s instanceof Date){
9463 return Date.parse(String(s));
9468 * @param {Mixed} s The value being converted
9469 * @return {Float} The comparison value
9471 asFloat : function(s) {
9472 var val = parseFloat(String(s).replace(/,/g, ""));
9481 * @param {Mixed} s The value being converted
9482 * @return {Number} The comparison value
9484 asInt : function(s) {
9485 var val = parseInt(String(s).replace(/,/g, ""));
9493 * Ext JS Library 1.1.1
9494 * Copyright(c) 2006-2007, Ext JS, LLC.
9496 * Originally Released Under LGPL - original licence link has changed is not relivant.
9499 * <script type="text/javascript">
9503 * @class Roo.data.Record
9504 * Instances of this class encapsulate both record <em>definition</em> information, and record
9505 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9506 * to access Records cached in an {@link Roo.data.Store} object.<br>
9508 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9509 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9512 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9514 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9515 * {@link #create}. The parameters are the same.
9516 * @param {Array} data An associative Array of data values keyed by the field name.
9517 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9518 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9519 * not specified an integer id is generated.
9521 Roo.data.Record = function(data, id){
9522 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9527 * Generate a constructor for a specific record layout.
9528 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9529 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9530 * Each field definition object may contain the following properties: <ul>
9531 * <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,
9532 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9533 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9534 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9535 * is being used, then this is a string containing the javascript expression to reference the data relative to
9536 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9537 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9538 * this may be omitted.</p></li>
9539 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9540 * <ul><li>auto (Default, implies no conversion)</li>
9545 * <li>date</li></ul></p></li>
9546 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9547 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9548 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9549 * by the Reader into an object that will be stored in the Record. It is passed the
9550 * following parameters:<ul>
9551 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9553 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9555 * <br>usage:<br><pre><code>
9556 var TopicRecord = Roo.data.Record.create(
9557 {name: 'title', mapping: 'topic_title'},
9558 {name: 'author', mapping: 'username'},
9559 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9560 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9561 {name: 'lastPoster', mapping: 'user2'},
9562 {name: 'excerpt', mapping: 'post_text'}
9565 var myNewRecord = new TopicRecord({
9566 title: 'Do my job please',
9569 lastPost: new Date(),
9570 lastPoster: 'Animal',
9571 excerpt: 'No way dude!'
9573 myStore.add(myNewRecord);
9578 Roo.data.Record.create = function(o){
9580 f.superclass.constructor.apply(this, arguments);
9582 Roo.extend(f, Roo.data.Record);
9583 var p = f.prototype;
9584 p.fields = new Roo.util.MixedCollection(false, function(field){
9587 for(var i = 0, len = o.length; i < len; i++){
9588 p.fields.add(new Roo.data.Field(o[i]));
9590 f.getField = function(name){
9591 return p.fields.get(name);
9596 Roo.data.Record.AUTO_ID = 1000;
9597 Roo.data.Record.EDIT = 'edit';
9598 Roo.data.Record.REJECT = 'reject';
9599 Roo.data.Record.COMMIT = 'commit';
9601 Roo.data.Record.prototype = {
9603 * Readonly flag - true if this record has been modified.
9612 join : function(store){
9617 * Set the named field to the specified value.
9618 * @param {String} name The name of the field to set.
9619 * @param {Object} value The value to set the field to.
9621 set : function(name, value){
9622 if(this.data[name] == value){
9629 if(typeof this.modified[name] == 'undefined'){
9630 this.modified[name] = this.data[name];
9632 this.data[name] = value;
9633 if(!this.editing && this.store){
9634 this.store.afterEdit(this);
9639 * Get the value of the named field.
9640 * @param {String} name The name of the field to get the value of.
9641 * @return {Object} The value of the field.
9643 get : function(name){
9644 return this.data[name];
9648 beginEdit : function(){
9649 this.editing = true;
9654 cancelEdit : function(){
9655 this.editing = false;
9656 delete this.modified;
9660 endEdit : function(){
9661 this.editing = false;
9662 if(this.dirty && this.store){
9663 this.store.afterEdit(this);
9668 * Usually called by the {@link Roo.data.Store} which owns the Record.
9669 * Rejects all changes made to the Record since either creation, or the last commit operation.
9670 * Modified fields are reverted to their original values.
9672 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9673 * of reject operations.
9675 reject : function(){
9676 var m = this.modified;
9678 if(typeof m[n] != "function"){
9679 this.data[n] = m[n];
9683 delete this.modified;
9684 this.editing = false;
9686 this.store.afterReject(this);
9691 * Usually called by the {@link Roo.data.Store} which owns the Record.
9692 * Commits all changes made to the Record since either creation, or the last commit operation.
9694 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9695 * of commit operations.
9697 commit : function(){
9699 delete this.modified;
9700 this.editing = false;
9702 this.store.afterCommit(this);
9707 hasError : function(){
9708 return this.error != null;
9712 clearError : function(){
9717 * Creates a copy of this record.
9718 * @param {String} id (optional) A new record id if you don't want to use this record's id
9721 copy : function(newId) {
9722 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9726 * Ext JS Library 1.1.1
9727 * Copyright(c) 2006-2007, Ext JS, LLC.
9729 * Originally Released Under LGPL - original licence link has changed is not relivant.
9732 * <script type="text/javascript">
9738 * @class Roo.data.Store
9739 * @extends Roo.util.Observable
9740 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9741 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9743 * 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
9744 * has no knowledge of the format of the data returned by the Proxy.<br>
9746 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9747 * instances from the data object. These records are cached and made available through accessor functions.
9749 * Creates a new Store.
9750 * @param {Object} config A config object containing the objects needed for the Store to access data,
9751 * and read the data into Records.
9753 Roo.data.Store = function(config){
9754 this.data = new Roo.util.MixedCollection(false);
9755 this.data.getKey = function(o){
9758 this.baseParams = {};
9765 "multisort" : "_multisort"
9768 if(config && config.data){
9769 this.inlineData = config.data;
9773 Roo.apply(this, config);
9775 if(this.reader){ // reader passed
9776 this.reader = Roo.factory(this.reader, Roo.data);
9777 this.reader.xmodule = this.xmodule || false;
9778 if(!this.recordType){
9779 this.recordType = this.reader.recordType;
9781 if(this.reader.onMetaChange){
9782 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9786 if(this.recordType){
9787 this.fields = this.recordType.prototype.fields;
9793 * @event datachanged
9794 * Fires when the data cache has changed, and a widget which is using this Store
9795 * as a Record cache should refresh its view.
9796 * @param {Store} this
9801 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9802 * @param {Store} this
9803 * @param {Object} meta The JSON metadata
9808 * Fires when Records have been added to the Store
9809 * @param {Store} this
9810 * @param {Roo.data.Record[]} records The array of Records added
9811 * @param {Number} index The index at which the record(s) were added
9816 * Fires when a Record has been removed from the Store
9817 * @param {Store} this
9818 * @param {Roo.data.Record} record The Record that was removed
9819 * @param {Number} index The index at which the record was removed
9824 * Fires when a Record has been updated
9825 * @param {Store} this
9826 * @param {Roo.data.Record} record The Record that was updated
9827 * @param {String} operation The update operation being performed. Value may be one of:
9829 Roo.data.Record.EDIT
9830 Roo.data.Record.REJECT
9831 Roo.data.Record.COMMIT
9837 * Fires when the data cache has been cleared.
9838 * @param {Store} this
9843 * Fires before a request is made for a new data object. If the beforeload handler returns false
9844 * the load action will be canceled.
9845 * @param {Store} this
9846 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9850 * @event beforeloadadd
9851 * Fires after a new set of Records has been loaded.
9852 * @param {Store} this
9853 * @param {Roo.data.Record[]} records The Records that were loaded
9854 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9856 beforeloadadd : true,
9859 * Fires after a new set of Records has been loaded, before they are added to the store.
9860 * @param {Store} this
9861 * @param {Roo.data.Record[]} records The Records that were loaded
9862 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9863 * @params {Object} return from reader
9867 * @event loadexception
9868 * Fires if an exception occurs in the Proxy during loading.
9869 * Called with the signature of the Proxy's "loadexception" event.
9870 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9873 * @param {Object} return from JsonData.reader() - success, totalRecords, records
9874 * @param {Object} load options
9875 * @param {Object} jsonData from your request (normally this contains the Exception)
9877 loadexception : true
9881 this.proxy = Roo.factory(this.proxy, Roo.data);
9882 this.proxy.xmodule = this.xmodule || false;
9883 this.relayEvents(this.proxy, ["loadexception"]);
9885 this.sortToggle = {};
9886 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9888 Roo.data.Store.superclass.constructor.call(this);
9890 if(this.inlineData){
9891 this.loadData(this.inlineData);
9892 delete this.inlineData;
9896 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9898 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
9899 * without a remote query - used by combo/forms at present.
9903 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9906 * @cfg {Array} data Inline data to be loaded when the store is initialized.
9909 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9910 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9913 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9914 * on any HTTP request
9917 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9920 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9924 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9925 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9930 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9931 * loaded or when a record is removed. (defaults to false).
9933 pruneModifiedRecords : false,
9939 * Add Records to the Store and fires the add event.
9940 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9942 add : function(records){
9943 records = [].concat(records);
9944 for(var i = 0, len = records.length; i < len; i++){
9945 records[i].join(this);
9947 var index = this.data.length;
9948 this.data.addAll(records);
9949 this.fireEvent("add", this, records, index);
9953 * Remove a Record from the Store and fires the remove event.
9954 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9956 remove : function(record){
9957 var index = this.data.indexOf(record);
9958 this.data.removeAt(index);
9959 if(this.pruneModifiedRecords){
9960 this.modified.remove(record);
9962 this.fireEvent("remove", this, record, index);
9966 * Remove all Records from the Store and fires the clear event.
9968 removeAll : function(){
9970 if(this.pruneModifiedRecords){
9973 this.fireEvent("clear", this);
9977 * Inserts Records to the Store at the given index and fires the add event.
9978 * @param {Number} index The start index at which to insert the passed Records.
9979 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9981 insert : function(index, records){
9982 records = [].concat(records);
9983 for(var i = 0, len = records.length; i < len; i++){
9984 this.data.insert(index, records[i]);
9985 records[i].join(this);
9987 this.fireEvent("add", this, records, index);
9991 * Get the index within the cache of the passed Record.
9992 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9993 * @return {Number} The index of the passed Record. Returns -1 if not found.
9995 indexOf : function(record){
9996 return this.data.indexOf(record);
10000 * Get the index within the cache of the Record with the passed id.
10001 * @param {String} id The id of the Record to find.
10002 * @return {Number} The index of the Record. Returns -1 if not found.
10004 indexOfId : function(id){
10005 return this.data.indexOfKey(id);
10009 * Get the Record with the specified id.
10010 * @param {String} id The id of the Record to find.
10011 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10013 getById : function(id){
10014 return this.data.key(id);
10018 * Get the Record at the specified index.
10019 * @param {Number} index The index of the Record to find.
10020 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10022 getAt : function(index){
10023 return this.data.itemAt(index);
10027 * Returns a range of Records between specified indices.
10028 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10029 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10030 * @return {Roo.data.Record[]} An array of Records
10032 getRange : function(start, end){
10033 return this.data.getRange(start, end);
10037 storeOptions : function(o){
10038 o = Roo.apply({}, o);
10041 this.lastOptions = o;
10045 * Loads the Record cache from the configured Proxy using the configured Reader.
10047 * If using remote paging, then the first load call must specify the <em>start</em>
10048 * and <em>limit</em> properties in the options.params property to establish the initial
10049 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10051 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10052 * and this call will return before the new data has been loaded. Perform any post-processing
10053 * in a callback function, or in a "load" event handler.</strong>
10055 * @param {Object} options An object containing properties which control loading options:<ul>
10056 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10057 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10058 * passed the following arguments:<ul>
10059 * <li>r : Roo.data.Record[]</li>
10060 * <li>options: Options object from the load call</li>
10061 * <li>success: Boolean success indicator</li></ul></li>
10062 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10063 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10066 load : function(options){
10067 options = options || {};
10068 if(this.fireEvent("beforeload", this, options) !== false){
10069 this.storeOptions(options);
10070 var p = Roo.apply(options.params || {}, this.baseParams);
10071 // if meta was not loaded from remote source.. try requesting it.
10072 if (!this.reader.metaFromRemote) {
10073 p._requestMeta = 1;
10075 if(this.sortInfo && this.remoteSort){
10076 var pn = this.paramNames;
10077 p[pn["sort"]] = this.sortInfo.field;
10078 p[pn["dir"]] = this.sortInfo.direction;
10080 if (this.multiSort) {
10081 var pn = this.paramNames;
10082 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10085 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10090 * Reloads the Record cache from the configured Proxy using the configured Reader and
10091 * the options from the last load operation performed.
10092 * @param {Object} options (optional) An object containing properties which may override the options
10093 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10094 * the most recently used options are reused).
10096 reload : function(options){
10097 this.load(Roo.applyIf(options||{}, this.lastOptions));
10101 // Called as a callback by the Reader during a load operation.
10102 loadRecords : function(o, options, success){
10103 if(!o || success === false){
10104 if(success !== false){
10105 this.fireEvent("load", this, [], options, o);
10107 if(options.callback){
10108 options.callback.call(options.scope || this, [], options, false);
10112 // if data returned failure - throw an exception.
10113 if (o.success === false) {
10114 // show a message if no listener is registered.
10115 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10116 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10118 // loadmask wil be hooked into this..
10119 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10122 var r = o.records, t = o.totalRecords || r.length;
10124 this.fireEvent("beforeloadadd", this, r, options, o);
10126 if(!options || options.add !== true){
10127 if(this.pruneModifiedRecords){
10128 this.modified = [];
10130 for(var i = 0, len = r.length; i < len; i++){
10134 this.data = this.snapshot;
10135 delete this.snapshot;
10138 this.data.addAll(r);
10139 this.totalLength = t;
10141 this.fireEvent("datachanged", this);
10143 this.totalLength = Math.max(t, this.data.length+r.length);
10146 this.fireEvent("load", this, r, options, o);
10147 if(options.callback){
10148 options.callback.call(options.scope || this, r, options, true);
10154 * Loads data from a passed data block. A Reader which understands the format of the data
10155 * must have been configured in the constructor.
10156 * @param {Object} data The data block from which to read the Records. The format of the data expected
10157 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10158 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10160 loadData : function(o, append){
10161 var r = this.reader.readRecords(o);
10162 this.loadRecords(r, {add: append}, true);
10166 * Gets the number of cached records.
10168 * <em>If using paging, this may not be the total size of the dataset. If the data object
10169 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10170 * the data set size</em>
10172 getCount : function(){
10173 return this.data.length || 0;
10177 * Gets the total number of records in the dataset as returned by the server.
10179 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10180 * the dataset size</em>
10182 getTotalCount : function(){
10183 return this.totalLength || 0;
10187 * Returns the sort state of the Store as an object with two properties:
10189 field {String} The name of the field by which the Records are sorted
10190 direction {String} The sort order, "ASC" or "DESC"
10193 getSortState : function(){
10194 return this.sortInfo;
10198 applySort : function(){
10199 if(this.sortInfo && !this.remoteSort){
10200 var s = this.sortInfo, f = s.field;
10201 var st = this.fields.get(f).sortType;
10202 var fn = function(r1, r2){
10203 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10204 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10206 this.data.sort(s.direction, fn);
10207 if(this.snapshot && this.snapshot != this.data){
10208 this.snapshot.sort(s.direction, fn);
10214 * Sets the default sort column and order to be used by the next load operation.
10215 * @param {String} fieldName The name of the field to sort by.
10216 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10218 setDefaultSort : function(field, dir){
10219 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10223 * Sort the Records.
10224 * If remote sorting is used, the sort is performed on the server, and the cache is
10225 * reloaded. If local sorting is used, the cache is sorted internally.
10226 * @param {String} fieldName The name of the field to sort by.
10227 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10229 sort : function(fieldName, dir){
10230 var f = this.fields.get(fieldName);
10232 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10234 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10235 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10240 this.sortToggle[f.name] = dir;
10241 this.sortInfo = {field: f.name, direction: dir};
10242 if(!this.remoteSort){
10244 this.fireEvent("datachanged", this);
10246 this.load(this.lastOptions);
10251 * Calls the specified function for each of the Records in the cache.
10252 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10253 * Returning <em>false</em> aborts and exits the iteration.
10254 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10256 each : function(fn, scope){
10257 this.data.each(fn, scope);
10261 * Gets all records modified since the last commit. Modified records are persisted across load operations
10262 * (e.g., during paging).
10263 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10265 getModifiedRecords : function(){
10266 return this.modified;
10270 createFilterFn : function(property, value, anyMatch){
10271 if(!value.exec){ // not a regex
10272 value = String(value);
10273 if(value.length == 0){
10276 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10278 return function(r){
10279 return value.test(r.data[property]);
10284 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10285 * @param {String} property A field on your records
10286 * @param {Number} start The record index to start at (defaults to 0)
10287 * @param {Number} end The last record index to include (defaults to length - 1)
10288 * @return {Number} The sum
10290 sum : function(property, start, end){
10291 var rs = this.data.items, v = 0;
10292 start = start || 0;
10293 end = (end || end === 0) ? end : rs.length-1;
10295 for(var i = start; i <= end; i++){
10296 v += (rs[i].data[property] || 0);
10302 * Filter the records by a specified property.
10303 * @param {String} field A field on your records
10304 * @param {String/RegExp} value Either a string that the field
10305 * should start with or a RegExp to test against the field
10306 * @param {Boolean} anyMatch True to match any part not just the beginning
10308 filter : function(property, value, anyMatch){
10309 var fn = this.createFilterFn(property, value, anyMatch);
10310 return fn ? this.filterBy(fn) : this.clearFilter();
10314 * Filter by a function. The specified function will be called with each
10315 * record in this data source. If the function returns true the record is included,
10316 * otherwise it is filtered.
10317 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10318 * @param {Object} scope (optional) The scope of the function (defaults to this)
10320 filterBy : function(fn, scope){
10321 this.snapshot = this.snapshot || this.data;
10322 this.data = this.queryBy(fn, scope||this);
10323 this.fireEvent("datachanged", this);
10327 * Query the records by a specified property.
10328 * @param {String} field A field on your records
10329 * @param {String/RegExp} value Either a string that the field
10330 * should start with or a RegExp to test against the field
10331 * @param {Boolean} anyMatch True to match any part not just the beginning
10332 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10334 query : function(property, value, anyMatch){
10335 var fn = this.createFilterFn(property, value, anyMatch);
10336 return fn ? this.queryBy(fn) : this.data.clone();
10340 * Query by a function. The specified function will be called with each
10341 * record in this data source. If the function returns true the record is included
10343 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10344 * @param {Object} scope (optional) The scope of the function (defaults to this)
10345 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10347 queryBy : function(fn, scope){
10348 var data = this.snapshot || this.data;
10349 return data.filterBy(fn, scope||this);
10353 * Collects unique values for a particular dataIndex from this store.
10354 * @param {String} dataIndex The property to collect
10355 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10356 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10357 * @return {Array} An array of the unique values
10359 collect : function(dataIndex, allowNull, bypassFilter){
10360 var d = (bypassFilter === true && this.snapshot) ?
10361 this.snapshot.items : this.data.items;
10362 var v, sv, r = [], l = {};
10363 for(var i = 0, len = d.length; i < len; i++){
10364 v = d[i].data[dataIndex];
10366 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10375 * Revert to a view of the Record cache with no filtering applied.
10376 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10378 clearFilter : function(suppressEvent){
10379 if(this.snapshot && this.snapshot != this.data){
10380 this.data = this.snapshot;
10381 delete this.snapshot;
10382 if(suppressEvent !== true){
10383 this.fireEvent("datachanged", this);
10389 afterEdit : function(record){
10390 if(this.modified.indexOf(record) == -1){
10391 this.modified.push(record);
10393 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10397 afterReject : function(record){
10398 this.modified.remove(record);
10399 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10403 afterCommit : function(record){
10404 this.modified.remove(record);
10405 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10409 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10410 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10412 commitChanges : function(){
10413 var m = this.modified.slice(0);
10414 this.modified = [];
10415 for(var i = 0, len = m.length; i < len; i++){
10421 * Cancel outstanding changes on all changed records.
10423 rejectChanges : function(){
10424 var m = this.modified.slice(0);
10425 this.modified = [];
10426 for(var i = 0, len = m.length; i < len; i++){
10431 onMetaChange : function(meta, rtype, o){
10432 this.recordType = rtype;
10433 this.fields = rtype.prototype.fields;
10434 delete this.snapshot;
10435 this.sortInfo = meta.sortInfo || this.sortInfo;
10436 this.modified = [];
10437 this.fireEvent('metachange', this, this.reader.meta);
10440 moveIndex : function(data, type)
10442 var index = this.indexOf(data);
10444 var newIndex = index + type;
10448 this.insert(newIndex, data);
10453 * Ext JS Library 1.1.1
10454 * Copyright(c) 2006-2007, Ext JS, LLC.
10456 * Originally Released Under LGPL - original licence link has changed is not relivant.
10459 * <script type="text/javascript">
10463 * @class Roo.data.SimpleStore
10464 * @extends Roo.data.Store
10465 * Small helper class to make creating Stores from Array data easier.
10466 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10467 * @cfg {Array} fields An array of field definition objects, or field name strings.
10468 * @cfg {Array} data The multi-dimensional array of data
10470 * @param {Object} config
10472 Roo.data.SimpleStore = function(config){
10473 Roo.data.SimpleStore.superclass.constructor.call(this, {
10475 reader: new Roo.data.ArrayReader({
10478 Roo.data.Record.create(config.fields)
10480 proxy : new Roo.data.MemoryProxy(config.data)
10484 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10486 * Ext JS Library 1.1.1
10487 * Copyright(c) 2006-2007, Ext JS, LLC.
10489 * Originally Released Under LGPL - original licence link has changed is not relivant.
10492 * <script type="text/javascript">
10497 * @extends Roo.data.Store
10498 * @class Roo.data.JsonStore
10499 * Small helper class to make creating Stores for JSON data easier. <br/>
10501 var store = new Roo.data.JsonStore({
10502 url: 'get-images.php',
10504 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10507 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10508 * JsonReader and HttpProxy (unless inline data is provided).</b>
10509 * @cfg {Array} fields An array of field definition objects, or field name strings.
10511 * @param {Object} config
10513 Roo.data.JsonStore = function(c){
10514 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10515 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10516 reader: new Roo.data.JsonReader(c, c.fields)
10519 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10521 * Ext JS Library 1.1.1
10522 * Copyright(c) 2006-2007, Ext JS, LLC.
10524 * Originally Released Under LGPL - original licence link has changed is not relivant.
10527 * <script type="text/javascript">
10531 Roo.data.Field = function(config){
10532 if(typeof config == "string"){
10533 config = {name: config};
10535 Roo.apply(this, config);
10538 this.type = "auto";
10541 var st = Roo.data.SortTypes;
10542 // named sortTypes are supported, here we look them up
10543 if(typeof this.sortType == "string"){
10544 this.sortType = st[this.sortType];
10547 // set default sortType for strings and dates
10548 if(!this.sortType){
10551 this.sortType = st.asUCString;
10554 this.sortType = st.asDate;
10557 this.sortType = st.none;
10562 var stripRe = /[\$,%]/g;
10564 // prebuilt conversion function for this field, instead of
10565 // switching every time we're reading a value
10567 var cv, dateFormat = this.dateFormat;
10572 cv = function(v){ return v; };
10575 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10579 return v !== undefined && v !== null && v !== '' ?
10580 parseInt(String(v).replace(stripRe, ""), 10) : '';
10585 return v !== undefined && v !== null && v !== '' ?
10586 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10591 cv = function(v){ return v === true || v === "true" || v == 1; };
10598 if(v instanceof Date){
10602 if(dateFormat == "timestamp"){
10603 return new Date(v*1000);
10605 return Date.parseDate(v, dateFormat);
10607 var parsed = Date.parse(v);
10608 return parsed ? new Date(parsed) : null;
10617 Roo.data.Field.prototype = {
10625 * Ext JS Library 1.1.1
10626 * Copyright(c) 2006-2007, Ext JS, LLC.
10628 * Originally Released Under LGPL - original licence link has changed is not relivant.
10631 * <script type="text/javascript">
10634 // Base class for reading structured data from a data source. This class is intended to be
10635 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10638 * @class Roo.data.DataReader
10639 * Base class for reading structured data from a data source. This class is intended to be
10640 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10643 Roo.data.DataReader = function(meta, recordType){
10647 this.recordType = recordType instanceof Array ?
10648 Roo.data.Record.create(recordType) : recordType;
10651 Roo.data.DataReader.prototype = {
10653 * Create an empty record
10654 * @param {Object} data (optional) - overlay some values
10655 * @return {Roo.data.Record} record created.
10657 newRow : function(d) {
10659 this.recordType.prototype.fields.each(function(c) {
10661 case 'int' : da[c.name] = 0; break;
10662 case 'date' : da[c.name] = new Date(); break;
10663 case 'float' : da[c.name] = 0.0; break;
10664 case 'boolean' : da[c.name] = false; break;
10665 default : da[c.name] = ""; break;
10669 return new this.recordType(Roo.apply(da, d));
10674 * Ext JS Library 1.1.1
10675 * Copyright(c) 2006-2007, Ext JS, LLC.
10677 * Originally Released Under LGPL - original licence link has changed is not relivant.
10680 * <script type="text/javascript">
10684 * @class Roo.data.DataProxy
10685 * @extends Roo.data.Observable
10686 * This class is an abstract base class for implementations which provide retrieval of
10687 * unformatted data objects.<br>
10689 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10690 * (of the appropriate type which knows how to parse the data object) to provide a block of
10691 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10693 * Custom implementations must implement the load method as described in
10694 * {@link Roo.data.HttpProxy#load}.
10696 Roo.data.DataProxy = function(){
10699 * @event beforeload
10700 * Fires before a network request is made to retrieve a data object.
10701 * @param {Object} This DataProxy object.
10702 * @param {Object} params The params parameter to the load function.
10707 * Fires before the load method's callback is called.
10708 * @param {Object} This DataProxy object.
10709 * @param {Object} o The data object.
10710 * @param {Object} arg The callback argument object passed to the load function.
10714 * @event loadexception
10715 * Fires if an Exception occurs during data retrieval.
10716 * @param {Object} This DataProxy object.
10717 * @param {Object} o The data object.
10718 * @param {Object} arg The callback argument object passed to the load function.
10719 * @param {Object} e The Exception.
10721 loadexception : true
10723 Roo.data.DataProxy.superclass.constructor.call(this);
10726 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10729 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10733 * Ext JS Library 1.1.1
10734 * Copyright(c) 2006-2007, Ext JS, LLC.
10736 * Originally Released Under LGPL - original licence link has changed is not relivant.
10739 * <script type="text/javascript">
10742 * @class Roo.data.MemoryProxy
10743 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10744 * to the Reader when its load method is called.
10746 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10748 Roo.data.MemoryProxy = function(data){
10752 Roo.data.MemoryProxy.superclass.constructor.call(this);
10756 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10758 * Load data from the requested source (in this case an in-memory
10759 * data object passed to the constructor), read the data object into
10760 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10761 * process that block using the passed callback.
10762 * @param {Object} params This parameter is not used by the MemoryProxy class.
10763 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10764 * object into a block of Roo.data.Records.
10765 * @param {Function} callback The function into which to pass the block of Roo.data.records.
10766 * The function must be passed <ul>
10767 * <li>The Record block object</li>
10768 * <li>The "arg" argument from the load function</li>
10769 * <li>A boolean success indicator</li>
10771 * @param {Object} scope The scope in which to call the callback
10772 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10774 load : function(params, reader, callback, scope, arg){
10775 params = params || {};
10778 result = reader.readRecords(this.data);
10780 this.fireEvent("loadexception", this, arg, null, e);
10781 callback.call(scope, null, arg, false);
10784 callback.call(scope, result, arg, true);
10788 update : function(params, records){
10793 * Ext JS Library 1.1.1
10794 * Copyright(c) 2006-2007, Ext JS, LLC.
10796 * Originally Released Under LGPL - original licence link has changed is not relivant.
10799 * <script type="text/javascript">
10802 * @class Roo.data.HttpProxy
10803 * @extends Roo.data.DataProxy
10804 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10805 * configured to reference a certain URL.<br><br>
10807 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10808 * from which the running page was served.<br><br>
10810 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10812 * Be aware that to enable the browser to parse an XML document, the server must set
10813 * the Content-Type header in the HTTP response to "text/xml".
10815 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10816 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
10817 * will be used to make the request.
10819 Roo.data.HttpProxy = function(conn){
10820 Roo.data.HttpProxy.superclass.constructor.call(this);
10821 // is conn a conn config or a real conn?
10823 this.useAjax = !conn || !conn.events;
10827 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10828 // thse are take from connection...
10831 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10834 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10835 * extra parameters to each request made by this object. (defaults to undefined)
10838 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10839 * to each request made by this object. (defaults to undefined)
10842 * @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)
10845 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10848 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10854 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10858 * Return the {@link Roo.data.Connection} object being used by this Proxy.
10859 * @return {Connection} The Connection object. This object may be used to subscribe to events on
10860 * a finer-grained basis than the DataProxy events.
10862 getConnection : function(){
10863 return this.useAjax ? Roo.Ajax : this.conn;
10867 * Load data from the configured {@link Roo.data.Connection}, read the data object into
10868 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10869 * process that block using the passed callback.
10870 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10871 * for the request to the remote server.
10872 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10873 * object into a block of Roo.data.Records.
10874 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10875 * The function must be passed <ul>
10876 * <li>The Record block object</li>
10877 * <li>The "arg" argument from the load function</li>
10878 * <li>A boolean success indicator</li>
10880 * @param {Object} scope The scope in which to call the callback
10881 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10883 load : function(params, reader, callback, scope, arg){
10884 if(this.fireEvent("beforeload", this, params) !== false){
10886 params : params || {},
10888 callback : callback,
10893 callback : this.loadResponse,
10897 Roo.applyIf(o, this.conn);
10898 if(this.activeRequest){
10899 Roo.Ajax.abort(this.activeRequest);
10901 this.activeRequest = Roo.Ajax.request(o);
10903 this.conn.request(o);
10906 callback.call(scope||this, null, arg, false);
10911 loadResponse : function(o, success, response){
10912 delete this.activeRequest;
10914 this.fireEvent("loadexception", this, o, response);
10915 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10920 result = o.reader.read(response);
10922 this.fireEvent("loadexception", this, o, response, e);
10923 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10927 this.fireEvent("load", this, o, o.request.arg);
10928 o.request.callback.call(o.request.scope, result, o.request.arg, true);
10932 update : function(dataSet){
10937 updateResponse : function(dataSet){
10942 * Ext JS Library 1.1.1
10943 * Copyright(c) 2006-2007, Ext JS, LLC.
10945 * Originally Released Under LGPL - original licence link has changed is not relivant.
10948 * <script type="text/javascript">
10952 * @class Roo.data.ScriptTagProxy
10953 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10954 * other than the originating domain of the running page.<br><br>
10956 * <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
10957 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10959 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10960 * source code that is used as the source inside a <script> tag.<br><br>
10962 * In order for the browser to process the returned data, the server must wrap the data object
10963 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10964 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10965 * depending on whether the callback name was passed:
10968 boolean scriptTag = false;
10969 String cb = request.getParameter("callback");
10972 response.setContentType("text/javascript");
10974 response.setContentType("application/x-json");
10976 Writer out = response.getWriter();
10978 out.write(cb + "(");
10980 out.print(dataBlock.toJsonString());
10987 * @param {Object} config A configuration object.
10989 Roo.data.ScriptTagProxy = function(config){
10990 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10991 Roo.apply(this, config);
10992 this.head = document.getElementsByTagName("head")[0];
10995 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10997 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10999 * @cfg {String} url The URL from which to request the data object.
11002 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11006 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11007 * the server the name of the callback function set up by the load call to process the returned data object.
11008 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11009 * javascript output which calls this named function passing the data object as its only parameter.
11011 callbackParam : "callback",
11013 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11014 * name to the request.
11019 * Load data from the configured URL, read the data object into
11020 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11021 * process that block using the passed callback.
11022 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11023 * for the request to the remote server.
11024 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11025 * object into a block of Roo.data.Records.
11026 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11027 * The function must be passed <ul>
11028 * <li>The Record block object</li>
11029 * <li>The "arg" argument from the load function</li>
11030 * <li>A boolean success indicator</li>
11032 * @param {Object} scope The scope in which to call the callback
11033 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11035 load : function(params, reader, callback, scope, arg){
11036 if(this.fireEvent("beforeload", this, params) !== false){
11038 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11040 var url = this.url;
11041 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11043 url += "&_dc=" + (new Date().getTime());
11045 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11048 cb : "stcCallback"+transId,
11049 scriptId : "stcScript"+transId,
11053 callback : callback,
11059 window[trans.cb] = function(o){
11060 conn.handleResponse(o, trans);
11063 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11065 if(this.autoAbort !== false){
11069 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11071 var script = document.createElement("script");
11072 script.setAttribute("src", url);
11073 script.setAttribute("type", "text/javascript");
11074 script.setAttribute("id", trans.scriptId);
11075 this.head.appendChild(script);
11077 this.trans = trans;
11079 callback.call(scope||this, null, arg, false);
11084 isLoading : function(){
11085 return this.trans ? true : false;
11089 * Abort the current server request.
11091 abort : function(){
11092 if(this.isLoading()){
11093 this.destroyTrans(this.trans);
11098 destroyTrans : function(trans, isLoaded){
11099 this.head.removeChild(document.getElementById(trans.scriptId));
11100 clearTimeout(trans.timeoutId);
11102 window[trans.cb] = undefined;
11104 delete window[trans.cb];
11107 // if hasn't been loaded, wait for load to remove it to prevent script error
11108 window[trans.cb] = function(){
11109 window[trans.cb] = undefined;
11111 delete window[trans.cb];
11118 handleResponse : function(o, trans){
11119 this.trans = false;
11120 this.destroyTrans(trans, true);
11123 result = trans.reader.readRecords(o);
11125 this.fireEvent("loadexception", this, o, trans.arg, e);
11126 trans.callback.call(trans.scope||window, null, trans.arg, false);
11129 this.fireEvent("load", this, o, trans.arg);
11130 trans.callback.call(trans.scope||window, result, trans.arg, true);
11134 handleFailure : function(trans){
11135 this.trans = false;
11136 this.destroyTrans(trans, false);
11137 this.fireEvent("loadexception", this, null, trans.arg);
11138 trans.callback.call(trans.scope||window, null, trans.arg, false);
11142 * Ext JS Library 1.1.1
11143 * Copyright(c) 2006-2007, Ext JS, LLC.
11145 * Originally Released Under LGPL - original licence link has changed is not relivant.
11148 * <script type="text/javascript">
11152 * @class Roo.data.JsonReader
11153 * @extends Roo.data.DataReader
11154 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11155 * based on mappings in a provided Roo.data.Record constructor.
11157 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11158 * in the reply previously.
11163 var RecordDef = Roo.data.Record.create([
11164 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11165 {name: 'occupation'} // This field will use "occupation" as the mapping.
11167 var myReader = new Roo.data.JsonReader({
11168 totalProperty: "results", // The property which contains the total dataset size (optional)
11169 root: "rows", // The property which contains an Array of row objects
11170 id: "id" // The property within each row object that provides an ID for the record (optional)
11174 * This would consume a JSON file like this:
11176 { 'results': 2, 'rows': [
11177 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11178 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11181 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11182 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11183 * paged from the remote server.
11184 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11185 * @cfg {String} root name of the property which contains the Array of row objects.
11186 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11187 * @cfg {Array} fields Array of field definition objects
11189 * Create a new JsonReader
11190 * @param {Object} meta Metadata configuration options
11191 * @param {Object} recordType Either an Array of field definition objects,
11192 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11194 Roo.data.JsonReader = function(meta, recordType){
11197 // set some defaults:
11198 Roo.applyIf(meta, {
11199 totalProperty: 'total',
11200 successProperty : 'success',
11205 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11207 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11210 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11211 * Used by Store query builder to append _requestMeta to params.
11214 metaFromRemote : false,
11216 * This method is only used by a DataProxy which has retrieved data from a remote server.
11217 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11218 * @return {Object} data A data block which is used by an Roo.data.Store object as
11219 * a cache of Roo.data.Records.
11221 read : function(response){
11222 var json = response.responseText;
11224 var o = /* eval:var:o */ eval("("+json+")");
11226 throw {message: "JsonReader.read: Json object not found"};
11232 this.metaFromRemote = true;
11233 this.meta = o.metaData;
11234 this.recordType = Roo.data.Record.create(o.metaData.fields);
11235 this.onMetaChange(this.meta, this.recordType, o);
11237 return this.readRecords(o);
11240 // private function a store will implement
11241 onMetaChange : function(meta, recordType, o){
11248 simpleAccess: function(obj, subsc) {
11255 getJsonAccessor: function(){
11257 return function(expr) {
11259 return(re.test(expr))
11260 ? new Function("obj", "return obj." + expr)
11265 return Roo.emptyFn;
11270 * Create a data block containing Roo.data.Records from an XML document.
11271 * @param {Object} o An object which contains an Array of row objects in the property specified
11272 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11273 * which contains the total size of the dataset.
11274 * @return {Object} data A data block which is used by an Roo.data.Store object as
11275 * a cache of Roo.data.Records.
11277 readRecords : function(o){
11279 * After any data loads, the raw JSON data is available for further custom processing.
11283 var s = this.meta, Record = this.recordType,
11284 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11286 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11288 if(s.totalProperty) {
11289 this.getTotal = this.getJsonAccessor(s.totalProperty);
11291 if(s.successProperty) {
11292 this.getSuccess = this.getJsonAccessor(s.successProperty);
11294 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11296 var g = this.getJsonAccessor(s.id);
11297 this.getId = function(rec) {
11299 return (r === undefined || r === "") ? null : r;
11302 this.getId = function(){return null;};
11305 for(var jj = 0; jj < fl; jj++){
11307 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11308 this.ef[jj] = this.getJsonAccessor(map);
11312 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11313 if(s.totalProperty){
11314 var vt = parseInt(this.getTotal(o), 10);
11319 if(s.successProperty){
11320 var vs = this.getSuccess(o);
11321 if(vs === false || vs === 'false'){
11326 for(var i = 0; i < c; i++){
11329 var id = this.getId(n);
11330 for(var j = 0; j < fl; j++){
11332 var v = this.ef[j](n);
11334 Roo.log('missing convert for ' + f.name);
11338 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11340 var record = new Record(values, id);
11342 records[i] = record;
11348 totalRecords : totalRecords
11353 * Ext JS Library 1.1.1
11354 * Copyright(c) 2006-2007, Ext JS, LLC.
11356 * Originally Released Under LGPL - original licence link has changed is not relivant.
11359 * <script type="text/javascript">
11363 * @class Roo.data.ArrayReader
11364 * @extends Roo.data.DataReader
11365 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11366 * Each element of that Array represents a row of data fields. The
11367 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11368 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11372 var RecordDef = Roo.data.Record.create([
11373 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11374 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11376 var myReader = new Roo.data.ArrayReader({
11377 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11381 * This would consume an Array like this:
11383 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11385 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11387 * Create a new JsonReader
11388 * @param {Object} meta Metadata configuration options.
11389 * @param {Object} recordType Either an Array of field definition objects
11390 * as specified to {@link Roo.data.Record#create},
11391 * or an {@link Roo.data.Record} object
11392 * created using {@link Roo.data.Record#create}.
11394 Roo.data.ArrayReader = function(meta, recordType){
11395 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11398 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11400 * Create a data block containing Roo.data.Records from an XML document.
11401 * @param {Object} o An Array of row objects which represents the dataset.
11402 * @return {Object} data A data block which is used by an Roo.data.Store object as
11403 * a cache of Roo.data.Records.
11405 readRecords : function(o){
11406 var sid = this.meta ? this.meta.id : null;
11407 var recordType = this.recordType, fields = recordType.prototype.fields;
11410 for(var i = 0; i < root.length; i++){
11413 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11414 for(var j = 0, jlen = fields.length; j < jlen; j++){
11415 var f = fields.items[j];
11416 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11417 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11419 values[f.name] = v;
11421 var record = new recordType(values, id);
11423 records[records.length] = record;
11427 totalRecords : records.length
11436 * @class Roo.bootstrap.ComboBox
11437 * @extends Roo.bootstrap.TriggerField
11438 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11439 * @cfg {Boolean} append (true|false) default false
11440 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11441 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11442 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11443 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11444 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11445 * @cfg {Boolean} animate default true
11446 * @cfg {Boolean} emptyResultText only for touch device
11447 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11449 * Create a new ComboBox.
11450 * @param {Object} config Configuration options
11452 Roo.bootstrap.ComboBox = function(config){
11453 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11457 * Fires when the dropdown list is expanded
11458 * @param {Roo.bootstrap.ComboBox} combo This combo box
11463 * Fires when the dropdown list is collapsed
11464 * @param {Roo.bootstrap.ComboBox} combo This combo box
11468 * @event beforeselect
11469 * Fires before a list item is selected. Return false to cancel the selection.
11470 * @param {Roo.bootstrap.ComboBox} combo This combo box
11471 * @param {Roo.data.Record} record The data record returned from the underlying store
11472 * @param {Number} index The index of the selected item in the dropdown list
11474 'beforeselect' : true,
11477 * Fires when a list item is selected
11478 * @param {Roo.bootstrap.ComboBox} combo This combo box
11479 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11480 * @param {Number} index The index of the selected item in the dropdown list
11484 * @event beforequery
11485 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11486 * The event object passed has these properties:
11487 * @param {Roo.bootstrap.ComboBox} combo This combo box
11488 * @param {String} query The query
11489 * @param {Boolean} forceAll true to force "all" query
11490 * @param {Boolean} cancel true to cancel the query
11491 * @param {Object} e The query event object
11493 'beforequery': true,
11496 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11497 * @param {Roo.bootstrap.ComboBox} combo This combo box
11502 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11503 * @param {Roo.bootstrap.ComboBox} combo This combo box
11504 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11509 * Fires when the remove value from the combobox array
11510 * @param {Roo.bootstrap.ComboBox} combo This combo box
11514 * @event specialfilter
11515 * Fires when specialfilter
11516 * @param {Roo.bootstrap.ComboBox} combo This combo box
11518 'specialfilter' : true,
11521 * Fires when tick the element
11522 * @param {Roo.bootstrap.ComboBox} combo This combo box
11526 * @event touchviewdisplay
11527 * Fires when touch view require special display (default is using displayField)
11528 * @param {Roo.bootstrap.ComboBox} combo This combo box
11529 * @param {Object} cfg set html .
11531 'touchviewdisplay' : true
11536 this.tickItems = [];
11538 this.selectedIndex = -1;
11539 if(this.mode == 'local'){
11540 if(config.queryDelay === undefined){
11541 this.queryDelay = 10;
11543 if(config.minChars === undefined){
11549 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11552 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11553 * rendering into an Roo.Editor, defaults to false)
11556 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11557 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11560 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11563 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11564 * the dropdown list (defaults to undefined, with no header element)
11568 * @cfg {String/Roo.Template} tpl The template to use to render the output
11572 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11574 listWidth: undefined,
11576 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11577 * mode = 'remote' or 'text' if mode = 'local')
11579 displayField: undefined,
11582 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11583 * mode = 'remote' or 'value' if mode = 'local').
11584 * Note: use of a valueField requires the user make a selection
11585 * in order for a value to be mapped.
11587 valueField: undefined,
11591 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11592 * field's data value (defaults to the underlying DOM element's name)
11594 hiddenName: undefined,
11596 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11600 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11602 selectedClass: 'active',
11605 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11609 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11610 * anchor positions (defaults to 'tl-bl')
11612 listAlign: 'tl-bl?',
11614 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11618 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11619 * query specified by the allQuery config option (defaults to 'query')
11621 triggerAction: 'query',
11623 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11624 * (defaults to 4, does not apply if editable = false)
11628 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11629 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11633 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11634 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11638 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11639 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11643 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11644 * when editable = true (defaults to false)
11646 selectOnFocus:false,
11648 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11650 queryParam: 'query',
11652 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
11653 * when mode = 'remote' (defaults to 'Loading...')
11655 loadingText: 'Loading...',
11657 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11661 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11665 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11666 * traditional select (defaults to true)
11670 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11674 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11678 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11679 * listWidth has a higher value)
11683 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11684 * allow the user to set arbitrary text into the field (defaults to false)
11686 forceSelection:false,
11688 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11689 * if typeAhead = true (defaults to 250)
11691 typeAheadDelay : 250,
11693 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11694 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11696 valueNotFoundText : undefined,
11698 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11700 blockFocus : false,
11703 * @cfg {Boolean} disableClear Disable showing of clear button.
11705 disableClear : false,
11707 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
11709 alwaysQuery : false,
11712 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
11717 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11719 invalidClass : "has-warning",
11722 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11724 validClass : "has-success",
11727 * @cfg {Boolean} specialFilter (true|false) special filter default false
11729 specialFilter : false,
11732 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11734 mobileTouchView : true,
11746 btnPosition : 'right',
11747 triggerList : true,
11748 showToggleBtn : true,
11750 emptyResultText: 'Empty',
11751 triggerText : 'Select',
11753 // element that contains real text value.. (when hidden is used..)
11755 getAutoCreate : function()
11763 if(Roo.isTouch && this.mobileTouchView){
11764 cfg = this.getAutoCreateTouchView();
11771 if(!this.tickable){
11772 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11777 * ComboBox with tickable selections
11780 var align = this.labelAlign || this.parentLabelAlign();
11783 cls : 'form-group roo-combobox-tickable' //input-group
11788 cls : 'tickable-buttons',
11793 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11794 html : this.triggerText
11800 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11807 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11814 buttons.cn.unshift({
11816 cls: 'select2-search-field-input'
11822 Roo.each(buttons.cn, function(c){
11824 c.cls += ' btn-' + _this.size;
11827 if (_this.disabled) {
11838 cls: 'form-hidden-field'
11842 cls: 'select2-choices',
11846 cls: 'select2-search-field',
11858 cls: 'select2-container input-group select2-container-multi',
11863 // cls: 'typeahead typeahead-long dropdown-menu',
11864 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
11869 if(this.hasFeedback && !this.allowBlank){
11873 cls: 'glyphicon form-control-feedback'
11876 combobox.cn.push(feedback);
11879 if (align ==='left' && this.fieldLabel.length) {
11881 // Roo.log("left and has label");
11887 cls : 'control-label col-sm-' + this.labelWidth,
11888 html : this.fieldLabel
11892 cls : "col-sm-" + (12 - this.labelWidth),
11899 } else if ( this.fieldLabel.length) {
11900 // Roo.log(" label");
11905 //cls : 'input-group-addon',
11906 html : this.fieldLabel
11916 // Roo.log(" no label && no align");
11923 ['xs','sm','md','lg'].map(function(size){
11924 if (settings[size]) {
11925 cfg.cls += ' col-' + size + '-' + settings[size];
11933 _initEventsCalled : false,
11936 initEvents: function()
11939 if (this._initEventsCalled) { // as we call render... prevent looping...
11942 this._initEventsCalled = true;
11945 throw "can not find store for combo";
11948 this.store = Roo.factory(this.store, Roo.data);
11950 // if we are building from html. then this element is so complex, that we can not really
11951 // use the rendered HTML.
11952 // so we have to trash and replace the previous code.
11953 if (Roo.XComponent.build_from_html) {
11955 // remove this element....
11956 var e = this.el.dom, k=0;
11957 while (e ) { e = e.previousSibling; ++k;}
11962 this.rendered = false;
11964 this.render(this.parent().getChildContainer(true), k);
11975 if(Roo.isTouch && this.mobileTouchView){
11976 this.initTouchView();
11981 this.initTickableEvents();
11985 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11987 if(this.hiddenName){
11989 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11991 this.hiddenField.dom.value =
11992 this.hiddenValue !== undefined ? this.hiddenValue :
11993 this.value !== undefined ? this.value : '';
11995 // prevent input submission
11996 this.el.dom.removeAttribute('name');
11997 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12002 // this.el.dom.setAttribute('autocomplete', 'off');
12005 var cls = 'x-combo-list';
12007 //this.list = new Roo.Layer({
12008 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12014 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12015 _this.list.setWidth(lw);
12018 this.list.on('mouseover', this.onViewOver, this);
12019 this.list.on('mousemove', this.onViewMove, this);
12021 this.list.on('scroll', this.onViewScroll, this);
12024 this.list.swallowEvent('mousewheel');
12025 this.assetHeight = 0;
12028 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12029 this.assetHeight += this.header.getHeight();
12032 this.innerList = this.list.createChild({cls:cls+'-inner'});
12033 this.innerList.on('mouseover', this.onViewOver, this);
12034 this.innerList.on('mousemove', this.onViewMove, this);
12035 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12037 if(this.allowBlank && !this.pageSize && !this.disableClear){
12038 this.footer = this.list.createChild({cls:cls+'-ft'});
12039 this.pageTb = new Roo.Toolbar(this.footer);
12043 this.footer = this.list.createChild({cls:cls+'-ft'});
12044 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12045 {pageSize: this.pageSize});
12049 if (this.pageTb && this.allowBlank && !this.disableClear) {
12051 this.pageTb.add(new Roo.Toolbar.Fill(), {
12052 cls: 'x-btn-icon x-btn-clear',
12054 handler: function()
12057 _this.clearValue();
12058 _this.onSelect(false, -1);
12063 this.assetHeight += this.footer.getHeight();
12068 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12071 this.view = new Roo.View(this.list, this.tpl, {
12072 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12074 //this.view.wrapEl.setDisplayed(false);
12075 this.view.on('click', this.onViewClick, this);
12079 this.store.on('beforeload', this.onBeforeLoad, this);
12080 this.store.on('load', this.onLoad, this);
12081 this.store.on('loadexception', this.onLoadException, this);
12083 if(this.resizable){
12084 this.resizer = new Roo.Resizable(this.list, {
12085 pinned:true, handles:'se'
12087 this.resizer.on('resize', function(r, w, h){
12088 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12089 this.listWidth = w;
12090 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12091 this.restrictHeight();
12093 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12096 if(!this.editable){
12097 this.editable = true;
12098 this.setEditable(false);
12103 if (typeof(this.events.add.listeners) != 'undefined') {
12105 this.addicon = this.wrap.createChild(
12106 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12108 this.addicon.on('click', function(e) {
12109 this.fireEvent('add', this);
12112 if (typeof(this.events.edit.listeners) != 'undefined') {
12114 this.editicon = this.wrap.createChild(
12115 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12116 if (this.addicon) {
12117 this.editicon.setStyle('margin-left', '40px');
12119 this.editicon.on('click', function(e) {
12121 // we fire even if inothing is selected..
12122 this.fireEvent('edit', this, this.lastData );
12128 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12129 "up" : function(e){
12130 this.inKeyMode = true;
12134 "down" : function(e){
12135 if(!this.isExpanded()){
12136 this.onTriggerClick();
12138 this.inKeyMode = true;
12143 "enter" : function(e){
12144 // this.onViewClick();
12148 if(this.fireEvent("specialkey", this, e)){
12149 this.onViewClick(false);
12155 "esc" : function(e){
12159 "tab" : function(e){
12162 if(this.fireEvent("specialkey", this, e)){
12163 this.onViewClick(false);
12171 doRelay : function(foo, bar, hname){
12172 if(hname == 'down' || this.scope.isExpanded()){
12173 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12182 this.queryDelay = Math.max(this.queryDelay || 10,
12183 this.mode == 'local' ? 10 : 250);
12186 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12188 if(this.typeAhead){
12189 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12191 if(this.editable !== false){
12192 this.inputEl().on("keyup", this.onKeyUp, this);
12194 if(this.forceSelection){
12195 this.inputEl().on('blur', this.doForce, this);
12199 this.choices = this.el.select('ul.select2-choices', true).first();
12200 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12204 initTickableEvents: function()
12208 if(this.hiddenName){
12210 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12212 this.hiddenField.dom.value =
12213 this.hiddenValue !== undefined ? this.hiddenValue :
12214 this.value !== undefined ? this.value : '';
12216 // prevent input submission
12217 this.el.dom.removeAttribute('name');
12218 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12223 // this.list = this.el.select('ul.dropdown-menu',true).first();
12225 this.choices = this.el.select('ul.select2-choices', true).first();
12226 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12227 if(this.triggerList){
12228 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12231 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12232 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12234 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12235 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12237 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12238 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12240 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12241 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12242 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12245 this.cancelBtn.hide();
12250 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12251 _this.list.setWidth(lw);
12254 this.list.on('mouseover', this.onViewOver, this);
12255 this.list.on('mousemove', this.onViewMove, this);
12257 this.list.on('scroll', this.onViewScroll, this);
12260 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>';
12263 this.view = new Roo.View(this.list, this.tpl, {
12264 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12267 //this.view.wrapEl.setDisplayed(false);
12268 this.view.on('click', this.onViewClick, this);
12272 this.store.on('beforeload', this.onBeforeLoad, this);
12273 this.store.on('load', this.onLoad, this);
12274 this.store.on('loadexception', this.onLoadException, this);
12277 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12278 "up" : function(e){
12279 this.inKeyMode = true;
12283 "down" : function(e){
12284 this.inKeyMode = true;
12288 "enter" : function(e){
12289 if(this.fireEvent("specialkey", this, e)){
12290 this.onViewClick(false);
12296 "esc" : function(e){
12297 this.onTickableFooterButtonClick(e, false, false);
12300 "tab" : function(e){
12301 this.fireEvent("specialkey", this, e);
12303 this.onTickableFooterButtonClick(e, false, false);
12310 doRelay : function(e, fn, key){
12311 if(this.scope.isExpanded()){
12312 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12321 this.queryDelay = Math.max(this.queryDelay || 10,
12322 this.mode == 'local' ? 10 : 250);
12325 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12327 if(this.typeAhead){
12328 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12331 if(this.editable !== false){
12332 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12337 onDestroy : function(){
12339 this.view.setStore(null);
12340 this.view.el.removeAllListeners();
12341 this.view.el.remove();
12342 this.view.purgeListeners();
12345 this.list.dom.innerHTML = '';
12349 this.store.un('beforeload', this.onBeforeLoad, this);
12350 this.store.un('load', this.onLoad, this);
12351 this.store.un('loadexception', this.onLoadException, this);
12353 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12357 fireKey : function(e){
12358 if(e.isNavKeyPress() && !this.list.isVisible()){
12359 this.fireEvent("specialkey", this, e);
12364 onResize: function(w, h){
12365 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12367 // if(typeof w != 'number'){
12368 // // we do not handle it!?!?
12371 // var tw = this.trigger.getWidth();
12372 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12373 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12375 // this.inputEl().setWidth( this.adjustWidth('input', x));
12377 // //this.trigger.setStyle('left', x+'px');
12379 // if(this.list && this.listWidth === undefined){
12380 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12381 // this.list.setWidth(lw);
12382 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12390 * Allow or prevent the user from directly editing the field text. If false is passed,
12391 * the user will only be able to select from the items defined in the dropdown list. This method
12392 * is the runtime equivalent of setting the 'editable' config option at config time.
12393 * @param {Boolean} value True to allow the user to directly edit the field text
12395 setEditable : function(value){
12396 if(value == this.editable){
12399 this.editable = value;
12401 this.inputEl().dom.setAttribute('readOnly', true);
12402 this.inputEl().on('mousedown', this.onTriggerClick, this);
12403 this.inputEl().addClass('x-combo-noedit');
12405 this.inputEl().dom.setAttribute('readOnly', false);
12406 this.inputEl().un('mousedown', this.onTriggerClick, this);
12407 this.inputEl().removeClass('x-combo-noedit');
12413 onBeforeLoad : function(combo,opts){
12414 if(!this.hasFocus){
12418 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12420 this.restrictHeight();
12421 this.selectedIndex = -1;
12425 onLoad : function(){
12427 this.hasQuery = false;
12429 if(!this.hasFocus){
12433 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12434 this.loading.hide();
12437 if(this.store.getCount() > 0){
12439 this.restrictHeight();
12440 if(this.lastQuery == this.allQuery){
12441 if(this.editable && !this.tickable){
12442 this.inputEl().dom.select();
12446 !this.selectByValue(this.value, true) &&
12449 !this.store.lastOptions ||
12450 typeof(this.store.lastOptions.add) == 'undefined' ||
12451 this.store.lastOptions.add != true
12454 this.select(0, true);
12457 if(this.autoFocus){
12460 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12461 this.taTask.delay(this.typeAheadDelay);
12465 this.onEmptyResults();
12471 onLoadException : function()
12473 this.hasQuery = false;
12475 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12476 this.loading.hide();
12479 if(this.tickable && this.editable){
12484 // only causes errors at present
12485 //Roo.log(this.store.reader.jsonData);
12486 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12488 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12494 onTypeAhead : function(){
12495 if(this.store.getCount() > 0){
12496 var r = this.store.getAt(0);
12497 var newValue = r.data[this.displayField];
12498 var len = newValue.length;
12499 var selStart = this.getRawValue().length;
12501 if(selStart != len){
12502 this.setRawValue(newValue);
12503 this.selectText(selStart, newValue.length);
12509 onSelect : function(record, index){
12511 if(this.fireEvent('beforeselect', this, record, index) !== false){
12513 this.setFromData(index > -1 ? record.data : false);
12516 this.fireEvent('select', this, record, index);
12521 * Returns the currently selected field value or empty string if no value is set.
12522 * @return {String} value The selected value
12524 getValue : function(){
12527 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12530 if(this.valueField){
12531 return typeof this.value != 'undefined' ? this.value : '';
12533 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12538 * Clears any text/value currently set in the field
12540 clearValue : function(){
12541 if(this.hiddenField){
12542 this.hiddenField.dom.value = '';
12545 this.setRawValue('');
12546 this.lastSelectionText = '';
12547 this.lastData = false;
12549 var close = this.closeTriggerEl();
12558 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12559 * will be displayed in the field. If the value does not match the data value of an existing item,
12560 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12561 * Otherwise the field will be blank (although the value will still be set).
12562 * @param {String} value The value to match
12564 setValue : function(v){
12571 if(this.valueField){
12572 var r = this.findRecord(this.valueField, v);
12574 text = r.data[this.displayField];
12575 }else if(this.valueNotFoundText !== undefined){
12576 text = this.valueNotFoundText;
12579 this.lastSelectionText = text;
12580 if(this.hiddenField){
12581 this.hiddenField.dom.value = v;
12583 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12586 var close = this.closeTriggerEl();
12589 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12593 * @property {Object} the last set data for the element
12598 * Sets the value of the field based on a object which is related to the record format for the store.
12599 * @param {Object} value the value to set as. or false on reset?
12601 setFromData : function(o){
12608 var dv = ''; // display value
12609 var vv = ''; // value value..
12611 if (this.displayField) {
12612 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12614 // this is an error condition!!!
12615 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12618 if(this.valueField){
12619 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12622 var close = this.closeTriggerEl();
12625 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12628 if(this.hiddenField){
12629 this.hiddenField.dom.value = vv;
12631 this.lastSelectionText = dv;
12632 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12636 // no hidden field.. - we store the value in 'value', but still display
12637 // display field!!!!
12638 this.lastSelectionText = dv;
12639 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12646 reset : function(){
12647 // overridden so that last data is reset..
12654 this.setValue(this.originalValue);
12655 this.clearInvalid();
12656 this.lastData = false;
12658 this.view.clearSelections();
12662 findRecord : function(prop, value){
12664 if(this.store.getCount() > 0){
12665 this.store.each(function(r){
12666 if(r.data[prop] == value){
12676 getName: function()
12678 // returns hidden if it's set..
12679 if (!this.rendered) {return ''};
12680 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
12684 onViewMove : function(e, t){
12685 this.inKeyMode = false;
12689 onViewOver : function(e, t){
12690 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12693 var item = this.view.findItemFromChild(t);
12696 var index = this.view.indexOf(item);
12697 this.select(index, false);
12702 onViewClick : function(view, doFocus, el, e)
12704 var index = this.view.getSelectedIndexes()[0];
12706 var r = this.store.getAt(index);
12710 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12717 Roo.each(this.tickItems, function(v,k){
12719 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12721 _this.tickItems.splice(k, 1);
12723 if(typeof(e) == 'undefined' && view == false){
12724 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12736 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
12737 this.tickItems.push(r.data);
12740 if(typeof(e) == 'undefined' && view == false){
12741 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12748 this.onSelect(r, index);
12750 if(doFocus !== false && !this.blockFocus){
12751 this.inputEl().focus();
12756 restrictHeight : function(){
12757 //this.innerList.dom.style.height = '';
12758 //var inner = this.innerList.dom;
12759 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12760 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12761 //this.list.beginUpdate();
12762 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12763 this.list.alignTo(this.inputEl(), this.listAlign);
12764 this.list.alignTo(this.inputEl(), this.listAlign);
12765 //this.list.endUpdate();
12769 onEmptyResults : function(){
12771 if(this.tickable && this.editable){
12772 this.restrictHeight();
12780 * Returns true if the dropdown list is expanded, else false.
12782 isExpanded : function(){
12783 return this.list.isVisible();
12787 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12788 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12789 * @param {String} value The data value of the item to select
12790 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12791 * selected item if it is not currently in view (defaults to true)
12792 * @return {Boolean} True if the value matched an item in the list, else false
12794 selectByValue : function(v, scrollIntoView){
12795 if(v !== undefined && v !== null){
12796 var r = this.findRecord(this.valueField || this.displayField, v);
12798 this.select(this.store.indexOf(r), scrollIntoView);
12806 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12807 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12808 * @param {Number} index The zero-based index of the list item to select
12809 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12810 * selected item if it is not currently in view (defaults to true)
12812 select : function(index, scrollIntoView){
12813 this.selectedIndex = index;
12814 this.view.select(index);
12815 if(scrollIntoView !== false){
12816 var el = this.view.getNode(index);
12818 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12821 this.list.scrollChildIntoView(el, false);
12827 selectNext : function(){
12828 var ct = this.store.getCount();
12830 if(this.selectedIndex == -1){
12832 }else if(this.selectedIndex < ct-1){
12833 this.select(this.selectedIndex+1);
12839 selectPrev : function(){
12840 var ct = this.store.getCount();
12842 if(this.selectedIndex == -1){
12844 }else if(this.selectedIndex != 0){
12845 this.select(this.selectedIndex-1);
12851 onKeyUp : function(e){
12852 if(this.editable !== false && !e.isSpecialKey()){
12853 this.lastKey = e.getKey();
12854 this.dqTask.delay(this.queryDelay);
12859 validateBlur : function(){
12860 return !this.list || !this.list.isVisible();
12864 initQuery : function(){
12866 var v = this.getRawValue();
12868 if(this.tickable && this.editable){
12869 v = this.tickableInputEl().getValue();
12876 doForce : function(){
12877 if(this.inputEl().dom.value.length > 0){
12878 this.inputEl().dom.value =
12879 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12885 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
12886 * query allowing the query action to be canceled if needed.
12887 * @param {String} query The SQL query to execute
12888 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12889 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
12890 * saved in the current store (defaults to false)
12892 doQuery : function(q, forceAll){
12894 if(q === undefined || q === null){
12899 forceAll: forceAll,
12903 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12908 forceAll = qe.forceAll;
12909 if(forceAll === true || (q.length >= this.minChars)){
12911 this.hasQuery = true;
12913 if(this.lastQuery != q || this.alwaysQuery){
12914 this.lastQuery = q;
12915 if(this.mode == 'local'){
12916 this.selectedIndex = -1;
12918 this.store.clearFilter();
12921 if(this.specialFilter){
12922 this.fireEvent('specialfilter', this);
12927 this.store.filter(this.displayField, q);
12930 this.store.fireEvent("datachanged", this.store);
12937 this.store.baseParams[this.queryParam] = q;
12939 var options = {params : this.getParams(q)};
12942 options.add = true;
12943 options.params.start = this.page * this.pageSize;
12946 this.store.load(options);
12949 * this code will make the page width larger, at the beginning, the list not align correctly,
12950 * we should expand the list on onLoad
12951 * so command out it
12956 this.selectedIndex = -1;
12961 this.loadNext = false;
12965 getParams : function(q){
12967 //p[this.queryParam] = q;
12971 p.limit = this.pageSize;
12977 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12979 collapse : function(){
12980 if(!this.isExpanded()){
12987 this.hasFocus = false;
12989 this.cancelBtn.hide();
12990 this.trigger.show();
12993 this.tickableInputEl().dom.value = '';
12994 this.tickableInputEl().blur();
12999 Roo.get(document).un('mousedown', this.collapseIf, this);
13000 Roo.get(document).un('mousewheel', this.collapseIf, this);
13001 if (!this.editable) {
13002 Roo.get(document).un('keydown', this.listKeyPress, this);
13004 this.fireEvent('collapse', this);
13008 collapseIf : function(e){
13009 var in_combo = e.within(this.el);
13010 var in_list = e.within(this.list);
13011 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13013 if (in_combo || in_list || is_list) {
13014 //e.stopPropagation();
13019 this.onTickableFooterButtonClick(e, false, false);
13027 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13029 expand : function(){
13031 if(this.isExpanded() || !this.hasFocus){
13035 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13036 this.list.setWidth(lw);
13043 this.restrictHeight();
13047 this.tickItems = Roo.apply([], this.item);
13050 this.cancelBtn.show();
13051 this.trigger.hide();
13054 this.tickableInputEl().focus();
13059 Roo.get(document).on('mousedown', this.collapseIf, this);
13060 Roo.get(document).on('mousewheel', this.collapseIf, this);
13061 if (!this.editable) {
13062 Roo.get(document).on('keydown', this.listKeyPress, this);
13065 this.fireEvent('expand', this);
13069 // Implements the default empty TriggerField.onTriggerClick function
13070 onTriggerClick : function(e)
13072 Roo.log('trigger click');
13074 if(this.disabled || !this.triggerList){
13079 this.loadNext = false;
13081 if(this.isExpanded()){
13083 if (!this.blockFocus) {
13084 this.inputEl().focus();
13088 this.hasFocus = true;
13089 if(this.triggerAction == 'all') {
13090 this.doQuery(this.allQuery, true);
13092 this.doQuery(this.getRawValue());
13094 if (!this.blockFocus) {
13095 this.inputEl().focus();
13100 onTickableTriggerClick : function(e)
13107 this.loadNext = false;
13108 this.hasFocus = true;
13110 if(this.triggerAction == 'all') {
13111 this.doQuery(this.allQuery, true);
13113 this.doQuery(this.getRawValue());
13117 onSearchFieldClick : function(e)
13119 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13120 this.onTickableFooterButtonClick(e, false, false);
13124 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13129 this.loadNext = false;
13130 this.hasFocus = true;
13132 if(this.triggerAction == 'all') {
13133 this.doQuery(this.allQuery, true);
13135 this.doQuery(this.getRawValue());
13139 listKeyPress : function(e)
13141 //Roo.log('listkeypress');
13142 // scroll to first matching element based on key pres..
13143 if (e.isSpecialKey()) {
13146 var k = String.fromCharCode(e.getKey()).toUpperCase();
13149 var csel = this.view.getSelectedNodes();
13150 var cselitem = false;
13152 var ix = this.view.indexOf(csel[0]);
13153 cselitem = this.store.getAt(ix);
13154 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13160 this.store.each(function(v) {
13162 // start at existing selection.
13163 if (cselitem.id == v.id) {
13169 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13170 match = this.store.indexOf(v);
13176 if (match === false) {
13177 return true; // no more action?
13180 this.view.select(match);
13181 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13182 sn.scrollIntoView(sn.dom.parentNode, false);
13185 onViewScroll : function(e, t){
13187 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){
13191 this.hasQuery = true;
13193 this.loading = this.list.select('.loading', true).first();
13195 if(this.loading === null){
13196 this.list.createChild({
13198 cls: 'loading select2-more-results select2-active',
13199 html: 'Loading more results...'
13202 this.loading = this.list.select('.loading', true).first();
13204 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13206 this.loading.hide();
13209 this.loading.show();
13214 this.loadNext = true;
13216 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13221 addItem : function(o)
13223 var dv = ''; // display value
13225 if (this.displayField) {
13226 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13228 // this is an error condition!!!
13229 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13236 var choice = this.choices.createChild({
13238 cls: 'select2-search-choice',
13247 cls: 'select2-search-choice-close',
13252 }, this.searchField);
13254 var close = choice.select('a.select2-search-choice-close', true).first();
13256 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13264 this.inputEl().dom.value = '';
13269 onRemoveItem : function(e, _self, o)
13271 e.preventDefault();
13273 this.lastItem = Roo.apply([], this.item);
13275 var index = this.item.indexOf(o.data) * 1;
13278 Roo.log('not this item?!');
13282 this.item.splice(index, 1);
13287 this.fireEvent('remove', this, e);
13293 syncValue : function()
13295 if(!this.item.length){
13302 Roo.each(this.item, function(i){
13303 if(_this.valueField){
13304 value.push(i[_this.valueField]);
13311 this.value = value.join(',');
13313 if(this.hiddenField){
13314 this.hiddenField.dom.value = this.value;
13317 this.store.fireEvent("datachanged", this.store);
13320 clearItem : function()
13322 if(!this.multiple){
13328 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13336 if(this.tickable && !Roo.isTouch){
13337 this.view.refresh();
13341 inputEl: function ()
13343 if(Roo.isTouch && this.mobileTouchView){
13344 return this.el.select('input.form-control',true).first();
13348 return this.searchField;
13351 return this.el.select('input.form-control',true).first();
13355 onTickableFooterButtonClick : function(e, btn, el)
13357 e.preventDefault();
13359 this.lastItem = Roo.apply([], this.item);
13361 if(btn && btn.name == 'cancel'){
13362 this.tickItems = Roo.apply([], this.item);
13371 Roo.each(this.tickItems, function(o){
13379 validate : function()
13381 var v = this.getRawValue();
13384 v = this.getValue();
13387 if(this.disabled || this.allowBlank || v.length){
13392 this.markInvalid();
13396 tickableInputEl : function()
13398 if(!this.tickable || !this.editable){
13399 return this.inputEl();
13402 return this.inputEl().select('.select2-search-field-input', true).first();
13406 getAutoCreateTouchView : function()
13411 cls: 'form-group' //input-group
13417 type : this.inputType,
13418 cls : 'form-control x-combo-noedit',
13419 autocomplete: 'new-password',
13420 placeholder : this.placeholder || '',
13425 input.name = this.name;
13429 input.cls += ' input-' + this.size;
13432 if (this.disabled) {
13433 input.disabled = true;
13444 inputblock.cls += ' input-group';
13446 inputblock.cn.unshift({
13448 cls : 'input-group-addon',
13453 if(this.removable && !this.multiple){
13454 inputblock.cls += ' roo-removable';
13456 inputblock.cn.push({
13459 cls : 'roo-combo-removable-btn close'
13463 if(this.hasFeedback && !this.allowBlank){
13465 inputblock.cls += ' has-feedback';
13467 inputblock.cn.push({
13469 cls: 'glyphicon form-control-feedback'
13476 inputblock.cls += (this.before) ? '' : ' input-group';
13478 inputblock.cn.push({
13480 cls : 'input-group-addon',
13491 cls: 'form-hidden-field'
13505 cls: 'form-hidden-field'
13509 cls: 'select2-choices',
13513 cls: 'select2-search-field',
13526 cls: 'select2-container input-group',
13533 combobox.cls += ' select2-container-multi';
13536 var align = this.labelAlign || this.parentLabelAlign();
13540 if(this.fieldLabel.length){
13542 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13543 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13548 cls : 'control-label ' + lw,
13549 html : this.fieldLabel
13561 var settings = this;
13563 ['xs','sm','md','lg'].map(function(size){
13564 if (settings[size]) {
13565 cfg.cls += ' col-' + size + '-' + settings[size];
13572 initTouchView : function()
13574 this.renderTouchView();
13576 this.touchViewEl.on('scroll', function(){
13577 this.el.dom.scrollTop = 0;
13580 this.originalValue = this.getValue();
13582 this.inputEl().on("click", this.showTouchView, this);
13584 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13585 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13587 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13589 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13590 this.store.on('load', this.onTouchViewLoad, this);
13591 this.store.on('loadexception', this.onTouchViewLoadException, this);
13593 if(this.hiddenName){
13595 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13597 this.hiddenField.dom.value =
13598 this.hiddenValue !== undefined ? this.hiddenValue :
13599 this.value !== undefined ? this.value : '';
13601 this.el.dom.removeAttribute('name');
13602 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13606 this.choices = this.el.select('ul.select2-choices', true).first();
13607 this.searchField = this.el.select('ul li.select2-search-field', true).first();
13610 if(this.removable && !this.multiple){
13611 var close = this.closeTriggerEl();
13613 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13614 close.on('click', this.removeBtnClick, this, close);
13618 * fix the bug in Safari iOS8
13620 this.inputEl().on("focus", function(e){
13621 document.activeElement.blur();
13629 renderTouchView : function()
13631 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13632 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13634 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13635 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13637 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13638 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13639 this.touchViewBodyEl.setStyle('overflow', 'auto');
13641 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13642 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13644 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13645 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13649 showTouchView : function()
13655 this.touchViewHeaderEl.hide();
13657 if(this.fieldLabel.length){
13658 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13659 this.touchViewHeaderEl.show();
13662 this.touchViewEl.show();
13664 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13665 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13667 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13669 if(this.fieldLabel.length){
13670 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13673 this.touchViewBodyEl.setHeight(bodyHeight);
13677 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13679 this.touchViewEl.addClass('in');
13682 this.doTouchViewQuery();
13686 hideTouchView : function()
13688 this.touchViewEl.removeClass('in');
13692 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13694 this.touchViewEl.setStyle('display', 'none');
13699 setTouchViewValue : function()
13706 Roo.each(this.tickItems, function(o){
13711 this.hideTouchView();
13714 doTouchViewQuery : function()
13723 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13727 if(!this.alwaysQuery || this.mode == 'local'){
13728 this.onTouchViewLoad();
13735 onTouchViewBeforeLoad : function(combo,opts)
13741 onTouchViewLoad : function()
13743 if(this.store.getCount() < 1){
13744 this.onTouchViewEmptyResults();
13748 this.clearTouchView();
13750 var rawValue = this.getRawValue();
13752 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13754 this.tickItems = [];
13756 this.store.data.each(function(d, rowIndex){
13757 var row = this.touchViewListGroup.createChild(template);
13759 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13762 html : d.data[this.displayField]
13765 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
13766 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
13770 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13771 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13774 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13775 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13776 this.tickItems.push(d.data);
13779 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13783 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13785 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13787 if(this.fieldLabel.length){
13788 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13791 var listHeight = this.touchViewListGroup.getHeight();
13795 if(firstChecked && listHeight > bodyHeight){
13796 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13801 onTouchViewLoadException : function()
13803 this.hideTouchView();
13806 onTouchViewEmptyResults : function()
13808 this.clearTouchView();
13810 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13812 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13816 clearTouchView : function()
13818 this.touchViewListGroup.dom.innerHTML = '';
13821 onTouchViewClick : function(e, el, o)
13823 e.preventDefault();
13826 var rowIndex = o.rowIndex;
13828 var r = this.store.getAt(rowIndex);
13830 if(!this.multiple){
13831 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13832 c.dom.removeAttribute('checked');
13835 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13837 this.setFromData(r.data);
13839 var close = this.closeTriggerEl();
13845 this.hideTouchView();
13847 this.fireEvent('select', this, r, rowIndex);
13852 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13853 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13854 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13858 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13859 this.addItem(r.data);
13860 this.tickItems.push(r.data);
13866 * @cfg {Boolean} grow
13870 * @cfg {Number} growMin
13874 * @cfg {Number} growMax
13883 Roo.apply(Roo.bootstrap.ComboBox, {
13887 cls: 'modal-header',
13909 cls: 'list-group-item',
13913 cls: 'roo-combobox-list-group-item-value'
13917 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13931 listItemCheckbox : {
13933 cls: 'list-group-item',
13937 cls: 'roo-combobox-list-group-item-value'
13941 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13957 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13962 cls: 'modal-footer',
13970 cls: 'col-xs-6 text-left',
13973 cls: 'btn btn-danger roo-touch-view-cancel',
13979 cls: 'col-xs-6 text-right',
13982 cls: 'btn btn-success roo-touch-view-ok',
13993 Roo.apply(Roo.bootstrap.ComboBox, {
13995 touchViewTemplate : {
13997 cls: 'modal fade roo-combobox-touch-view',
14001 cls: 'modal-dialog',
14002 style : 'position:fixed', // we have to fix position....
14006 cls: 'modal-content',
14008 Roo.bootstrap.ComboBox.header,
14009 Roo.bootstrap.ComboBox.body,
14010 Roo.bootstrap.ComboBox.footer
14019 * Ext JS Library 1.1.1
14020 * Copyright(c) 2006-2007, Ext JS, LLC.
14022 * Originally Released Under LGPL - original licence link has changed is not relivant.
14025 * <script type="text/javascript">
14030 * @extends Roo.util.Observable
14031 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14032 * This class also supports single and multi selection modes. <br>
14033 * Create a data model bound view:
14035 var store = new Roo.data.Store(...);
14037 var view = new Roo.View({
14039 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14041 singleSelect: true,
14042 selectedClass: "ydataview-selected",
14046 // listen for node click?
14047 view.on("click", function(vw, index, node, e){
14048 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14052 dataModel.load("foobar.xml");
14054 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14056 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14057 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14059 * Note: old style constructor is still suported (container, template, config)
14062 * Create a new View
14063 * @param {Object} config The config object
14066 Roo.View = function(config, depreciated_tpl, depreciated_config){
14068 this.parent = false;
14070 if (typeof(depreciated_tpl) == 'undefined') {
14071 // new way.. - universal constructor.
14072 Roo.apply(this, config);
14073 this.el = Roo.get(this.el);
14076 this.el = Roo.get(config);
14077 this.tpl = depreciated_tpl;
14078 Roo.apply(this, depreciated_config);
14080 this.wrapEl = this.el.wrap().wrap();
14081 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14084 if(typeof(this.tpl) == "string"){
14085 this.tpl = new Roo.Template(this.tpl);
14087 // support xtype ctors..
14088 this.tpl = new Roo.factory(this.tpl, Roo);
14092 this.tpl.compile();
14097 * @event beforeclick
14098 * Fires before a click is processed. Returns false to cancel the default action.
14099 * @param {Roo.View} this
14100 * @param {Number} index The index of the target node
14101 * @param {HTMLElement} node The target node
14102 * @param {Roo.EventObject} e The raw event object
14104 "beforeclick" : true,
14107 * Fires when a template node is clicked.
14108 * @param {Roo.View} this
14109 * @param {Number} index The index of the target node
14110 * @param {HTMLElement} node The target node
14111 * @param {Roo.EventObject} e The raw event object
14116 * Fires when a template node is double clicked.
14117 * @param {Roo.View} this
14118 * @param {Number} index The index of the target node
14119 * @param {HTMLElement} node The target node
14120 * @param {Roo.EventObject} e The raw event object
14124 * @event contextmenu
14125 * Fires when a template node is right clicked.
14126 * @param {Roo.View} this
14127 * @param {Number} index The index of the target node
14128 * @param {HTMLElement} node The target node
14129 * @param {Roo.EventObject} e The raw event object
14131 "contextmenu" : true,
14133 * @event selectionchange
14134 * Fires when the selected nodes change.
14135 * @param {Roo.View} this
14136 * @param {Array} selections Array of the selected nodes
14138 "selectionchange" : true,
14141 * @event beforeselect
14142 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14143 * @param {Roo.View} this
14144 * @param {HTMLElement} node The node to be selected
14145 * @param {Array} selections Array of currently selected nodes
14147 "beforeselect" : true,
14149 * @event preparedata
14150 * Fires on every row to render, to allow you to change the data.
14151 * @param {Roo.View} this
14152 * @param {Object} data to be rendered (change this)
14154 "preparedata" : true
14162 "click": this.onClick,
14163 "dblclick": this.onDblClick,
14164 "contextmenu": this.onContextMenu,
14168 this.selections = [];
14170 this.cmp = new Roo.CompositeElementLite([]);
14172 this.store = Roo.factory(this.store, Roo.data);
14173 this.setStore(this.store, true);
14176 if ( this.footer && this.footer.xtype) {
14178 var fctr = this.wrapEl.appendChild(document.createElement("div"));
14180 this.footer.dataSource = this.store;
14181 this.footer.container = fctr;
14182 this.footer = Roo.factory(this.footer, Roo);
14183 fctr.insertFirst(this.el);
14185 // this is a bit insane - as the paging toolbar seems to detach the el..
14186 // dom.parentNode.parentNode.parentNode
14187 // they get detached?
14191 Roo.View.superclass.constructor.call(this);
14196 Roo.extend(Roo.View, Roo.util.Observable, {
14199 * @cfg {Roo.data.Store} store Data store to load data from.
14204 * @cfg {String|Roo.Element} el The container element.
14209 * @cfg {String|Roo.Template} tpl The template used by this View
14213 * @cfg {String} dataName the named area of the template to use as the data area
14214 * Works with domtemplates roo-name="name"
14218 * @cfg {String} selectedClass The css class to add to selected nodes
14220 selectedClass : "x-view-selected",
14222 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14227 * @cfg {String} text to display on mask (default Loading)
14231 * @cfg {Boolean} multiSelect Allow multiple selection
14233 multiSelect : false,
14235 * @cfg {Boolean} singleSelect Allow single selection
14237 singleSelect: false,
14240 * @cfg {Boolean} toggleSelect - selecting
14242 toggleSelect : false,
14245 * @cfg {Boolean} tickable - selecting
14250 * Returns the element this view is bound to.
14251 * @return {Roo.Element}
14253 getEl : function(){
14254 return this.wrapEl;
14260 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14262 refresh : function(){
14263 //Roo.log('refresh');
14266 // if we are using something like 'domtemplate', then
14267 // the what gets used is:
14268 // t.applySubtemplate(NAME, data, wrapping data..)
14269 // the outer template then get' applied with
14270 // the store 'extra data'
14271 // and the body get's added to the
14272 // roo-name="data" node?
14273 // <span class='roo-tpl-{name}'></span> ?????
14277 this.clearSelections();
14278 this.el.update("");
14280 var records = this.store.getRange();
14281 if(records.length < 1) {
14283 // is this valid?? = should it render a template??
14285 this.el.update(this.emptyText);
14289 if (this.dataName) {
14290 this.el.update(t.apply(this.store.meta)); //????
14291 el = this.el.child('.roo-tpl-' + this.dataName);
14294 for(var i = 0, len = records.length; i < len; i++){
14295 var data = this.prepareData(records[i].data, i, records[i]);
14296 this.fireEvent("preparedata", this, data, i, records[i]);
14298 var d = Roo.apply({}, data);
14301 Roo.apply(d, {'roo-id' : Roo.id()});
14305 Roo.each(this.parent.item, function(item){
14306 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14309 Roo.apply(d, {'roo-data-checked' : 'checked'});
14313 html[html.length] = Roo.util.Format.trim(
14315 t.applySubtemplate(this.dataName, d, this.store.meta) :
14322 el.update(html.join(""));
14323 this.nodes = el.dom.childNodes;
14324 this.updateIndexes(0);
14329 * Function to override to reformat the data that is sent to
14330 * the template for each node.
14331 * DEPRICATED - use the preparedata event handler.
14332 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14333 * a JSON object for an UpdateManager bound view).
14335 prepareData : function(data, index, record)
14337 this.fireEvent("preparedata", this, data, index, record);
14341 onUpdate : function(ds, record){
14342 // Roo.log('on update');
14343 this.clearSelections();
14344 var index = this.store.indexOf(record);
14345 var n = this.nodes[index];
14346 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14347 n.parentNode.removeChild(n);
14348 this.updateIndexes(index, index);
14354 onAdd : function(ds, records, index)
14356 //Roo.log(['on Add', ds, records, index] );
14357 this.clearSelections();
14358 if(this.nodes.length == 0){
14362 var n = this.nodes[index];
14363 for(var i = 0, len = records.length; i < len; i++){
14364 var d = this.prepareData(records[i].data, i, records[i]);
14366 this.tpl.insertBefore(n, d);
14369 this.tpl.append(this.el, d);
14372 this.updateIndexes(index);
14375 onRemove : function(ds, record, index){
14376 // Roo.log('onRemove');
14377 this.clearSelections();
14378 var el = this.dataName ?
14379 this.el.child('.roo-tpl-' + this.dataName) :
14382 el.dom.removeChild(this.nodes[index]);
14383 this.updateIndexes(index);
14387 * Refresh an individual node.
14388 * @param {Number} index
14390 refreshNode : function(index){
14391 this.onUpdate(this.store, this.store.getAt(index));
14394 updateIndexes : function(startIndex, endIndex){
14395 var ns = this.nodes;
14396 startIndex = startIndex || 0;
14397 endIndex = endIndex || ns.length - 1;
14398 for(var i = startIndex; i <= endIndex; i++){
14399 ns[i].nodeIndex = i;
14404 * Changes the data store this view uses and refresh the view.
14405 * @param {Store} store
14407 setStore : function(store, initial){
14408 if(!initial && this.store){
14409 this.store.un("datachanged", this.refresh);
14410 this.store.un("add", this.onAdd);
14411 this.store.un("remove", this.onRemove);
14412 this.store.un("update", this.onUpdate);
14413 this.store.un("clear", this.refresh);
14414 this.store.un("beforeload", this.onBeforeLoad);
14415 this.store.un("load", this.onLoad);
14416 this.store.un("loadexception", this.onLoad);
14420 store.on("datachanged", this.refresh, this);
14421 store.on("add", this.onAdd, this);
14422 store.on("remove", this.onRemove, this);
14423 store.on("update", this.onUpdate, this);
14424 store.on("clear", this.refresh, this);
14425 store.on("beforeload", this.onBeforeLoad, this);
14426 store.on("load", this.onLoad, this);
14427 store.on("loadexception", this.onLoad, this);
14435 * onbeforeLoad - masks the loading area.
14438 onBeforeLoad : function(store,opts)
14440 //Roo.log('onBeforeLoad');
14442 this.el.update("");
14444 this.el.mask(this.mask ? this.mask : "Loading" );
14446 onLoad : function ()
14453 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14454 * @param {HTMLElement} node
14455 * @return {HTMLElement} The template node
14457 findItemFromChild : function(node){
14458 var el = this.dataName ?
14459 this.el.child('.roo-tpl-' + this.dataName,true) :
14462 if(!node || node.parentNode == el){
14465 var p = node.parentNode;
14466 while(p && p != el){
14467 if(p.parentNode == el){
14476 onClick : function(e){
14477 var item = this.findItemFromChild(e.getTarget());
14479 var index = this.indexOf(item);
14480 if(this.onItemClick(item, index, e) !== false){
14481 this.fireEvent("click", this, index, item, e);
14484 this.clearSelections();
14489 onContextMenu : function(e){
14490 var item = this.findItemFromChild(e.getTarget());
14492 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14497 onDblClick : function(e){
14498 var item = this.findItemFromChild(e.getTarget());
14500 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14504 onItemClick : function(item, index, e)
14506 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14509 if (this.toggleSelect) {
14510 var m = this.isSelected(item) ? 'unselect' : 'select';
14513 _t[m](item, true, false);
14516 if(this.multiSelect || this.singleSelect){
14517 if(this.multiSelect && e.shiftKey && this.lastSelection){
14518 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14520 this.select(item, this.multiSelect && e.ctrlKey);
14521 this.lastSelection = item;
14524 if(!this.tickable){
14525 e.preventDefault();
14533 * Get the number of selected nodes.
14536 getSelectionCount : function(){
14537 return this.selections.length;
14541 * Get the currently selected nodes.
14542 * @return {Array} An array of HTMLElements
14544 getSelectedNodes : function(){
14545 return this.selections;
14549 * Get the indexes of the selected nodes.
14552 getSelectedIndexes : function(){
14553 var indexes = [], s = this.selections;
14554 for(var i = 0, len = s.length; i < len; i++){
14555 indexes.push(s[i].nodeIndex);
14561 * Clear all selections
14562 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14564 clearSelections : function(suppressEvent){
14565 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14566 this.cmp.elements = this.selections;
14567 this.cmp.removeClass(this.selectedClass);
14568 this.selections = [];
14569 if(!suppressEvent){
14570 this.fireEvent("selectionchange", this, this.selections);
14576 * Returns true if the passed node is selected
14577 * @param {HTMLElement/Number} node The node or node index
14578 * @return {Boolean}
14580 isSelected : function(node){
14581 var s = this.selections;
14585 node = this.getNode(node);
14586 return s.indexOf(node) !== -1;
14591 * @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
14592 * @param {Boolean} keepExisting (optional) true to keep existing selections
14593 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14595 select : function(nodeInfo, keepExisting, suppressEvent){
14596 if(nodeInfo instanceof Array){
14598 this.clearSelections(true);
14600 for(var i = 0, len = nodeInfo.length; i < len; i++){
14601 this.select(nodeInfo[i], true, true);
14605 var node = this.getNode(nodeInfo);
14606 if(!node || this.isSelected(node)){
14607 return; // already selected.
14610 this.clearSelections(true);
14613 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14614 Roo.fly(node).addClass(this.selectedClass);
14615 this.selections.push(node);
14616 if(!suppressEvent){
14617 this.fireEvent("selectionchange", this, this.selections);
14625 * @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
14626 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14627 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14629 unselect : function(nodeInfo, keepExisting, suppressEvent)
14631 if(nodeInfo instanceof Array){
14632 Roo.each(this.selections, function(s) {
14633 this.unselect(s, nodeInfo);
14637 var node = this.getNode(nodeInfo);
14638 if(!node || !this.isSelected(node)){
14639 //Roo.log("not selected");
14640 return; // not selected.
14644 Roo.each(this.selections, function(s) {
14646 Roo.fly(node).removeClass(this.selectedClass);
14653 this.selections= ns;
14654 this.fireEvent("selectionchange", this, this.selections);
14658 * Gets a template node.
14659 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14660 * @return {HTMLElement} The node or null if it wasn't found
14662 getNode : function(nodeInfo){
14663 if(typeof nodeInfo == "string"){
14664 return document.getElementById(nodeInfo);
14665 }else if(typeof nodeInfo == "number"){
14666 return this.nodes[nodeInfo];
14672 * Gets a range template nodes.
14673 * @param {Number} startIndex
14674 * @param {Number} endIndex
14675 * @return {Array} An array of nodes
14677 getNodes : function(start, end){
14678 var ns = this.nodes;
14679 start = start || 0;
14680 end = typeof end == "undefined" ? ns.length - 1 : end;
14683 for(var i = start; i <= end; i++){
14687 for(var i = start; i >= end; i--){
14695 * Finds the index of the passed node
14696 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14697 * @return {Number} The index of the node or -1
14699 indexOf : function(node){
14700 node = this.getNode(node);
14701 if(typeof node.nodeIndex == "number"){
14702 return node.nodeIndex;
14704 var ns = this.nodes;
14705 for(var i = 0, len = ns.length; i < len; i++){
14716 * based on jquery fullcalendar
14720 Roo.bootstrap = Roo.bootstrap || {};
14722 * @class Roo.bootstrap.Calendar
14723 * @extends Roo.bootstrap.Component
14724 * Bootstrap Calendar class
14725 * @cfg {Boolean} loadMask (true|false) default false
14726 * @cfg {Object} header generate the user specific header of the calendar, default false
14729 * Create a new Container
14730 * @param {Object} config The config object
14735 Roo.bootstrap.Calendar = function(config){
14736 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14740 * Fires when a date is selected
14741 * @param {DatePicker} this
14742 * @param {Date} date The selected date
14746 * @event monthchange
14747 * Fires when the displayed month changes
14748 * @param {DatePicker} this
14749 * @param {Date} date The selected month
14751 'monthchange': true,
14753 * @event evententer
14754 * Fires when mouse over an event
14755 * @param {Calendar} this
14756 * @param {event} Event
14758 'evententer': true,
14760 * @event eventleave
14761 * Fires when the mouse leaves an
14762 * @param {Calendar} this
14765 'eventleave': true,
14767 * @event eventclick
14768 * Fires when the mouse click an
14769 * @param {Calendar} this
14778 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
14781 * @cfg {Number} startDay
14782 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14790 getAutoCreate : function(){
14793 var fc_button = function(name, corner, style, content ) {
14794 return Roo.apply({},{
14796 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
14798 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14801 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14812 style : 'width:100%',
14819 cls : 'fc-header-left',
14821 fc_button('prev', 'left', 'arrow', '‹' ),
14822 fc_button('next', 'right', 'arrow', '›' ),
14823 { tag: 'span', cls: 'fc-header-space' },
14824 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
14832 cls : 'fc-header-center',
14836 cls: 'fc-header-title',
14839 html : 'month / year'
14847 cls : 'fc-header-right',
14849 /* fc_button('month', 'left', '', 'month' ),
14850 fc_button('week', '', '', 'week' ),
14851 fc_button('day', 'right', '', 'day' )
14863 header = this.header;
14866 var cal_heads = function() {
14868 // fixme - handle this.
14870 for (var i =0; i < Date.dayNames.length; i++) {
14871 var d = Date.dayNames[i];
14874 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14875 html : d.substring(0,3)
14879 ret[0].cls += ' fc-first';
14880 ret[6].cls += ' fc-last';
14883 var cal_cell = function(n) {
14886 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14891 cls: 'fc-day-number',
14895 cls: 'fc-day-content',
14899 style: 'position: relative;' // height: 17px;
14911 var cal_rows = function() {
14914 for (var r = 0; r < 6; r++) {
14921 for (var i =0; i < Date.dayNames.length; i++) {
14922 var d = Date.dayNames[i];
14923 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14926 row.cn[0].cls+=' fc-first';
14927 row.cn[0].cn[0].style = 'min-height:90px';
14928 row.cn[6].cls+=' fc-last';
14932 ret[0].cls += ' fc-first';
14933 ret[4].cls += ' fc-prev-last';
14934 ret[5].cls += ' fc-last';
14941 cls: 'fc-border-separate',
14942 style : 'width:100%',
14950 cls : 'fc-first fc-last',
14968 cls : 'fc-content',
14969 style : "position: relative;",
14972 cls : 'fc-view fc-view-month fc-grid',
14973 style : 'position: relative',
14974 unselectable : 'on',
14977 cls : 'fc-event-container',
14978 style : 'position:absolute;z-index:8;top:0;left:0;'
14996 initEvents : function()
14999 throw "can not find store for calendar";
15005 style: "text-align:center",
15009 style: "background-color:white;width:50%;margin:250 auto",
15013 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15024 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15026 var size = this.el.select('.fc-content', true).first().getSize();
15027 this.maskEl.setSize(size.width, size.height);
15028 this.maskEl.enableDisplayMode("block");
15029 if(!this.loadMask){
15030 this.maskEl.hide();
15033 this.store = Roo.factory(this.store, Roo.data);
15034 this.store.on('load', this.onLoad, this);
15035 this.store.on('beforeload', this.onBeforeLoad, this);
15039 this.cells = this.el.select('.fc-day',true);
15040 //Roo.log(this.cells);
15041 this.textNodes = this.el.query('.fc-day-number');
15042 this.cells.addClassOnOver('fc-state-hover');
15044 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15045 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15046 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15047 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15049 this.on('monthchange', this.onMonthChange, this);
15051 this.update(new Date().clearTime());
15054 resize : function() {
15055 var sz = this.el.getSize();
15057 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15058 this.el.select('.fc-day-content div',true).setHeight(34);
15063 showPrevMonth : function(e){
15064 this.update(this.activeDate.add("mo", -1));
15066 showToday : function(e){
15067 this.update(new Date().clearTime());
15070 showNextMonth : function(e){
15071 this.update(this.activeDate.add("mo", 1));
15075 showPrevYear : function(){
15076 this.update(this.activeDate.add("y", -1));
15080 showNextYear : function(){
15081 this.update(this.activeDate.add("y", 1));
15086 update : function(date)
15088 var vd = this.activeDate;
15089 this.activeDate = date;
15090 // if(vd && this.el){
15091 // var t = date.getTime();
15092 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15093 // Roo.log('using add remove');
15095 // this.fireEvent('monthchange', this, date);
15097 // this.cells.removeClass("fc-state-highlight");
15098 // this.cells.each(function(c){
15099 // if(c.dateValue == t){
15100 // c.addClass("fc-state-highlight");
15101 // setTimeout(function(){
15102 // try{c.dom.firstChild.focus();}catch(e){}
15112 var days = date.getDaysInMonth();
15114 var firstOfMonth = date.getFirstDateOfMonth();
15115 var startingPos = firstOfMonth.getDay()-this.startDay;
15117 if(startingPos < this.startDay){
15121 var pm = date.add(Date.MONTH, -1);
15122 var prevStart = pm.getDaysInMonth()-startingPos;
15124 this.cells = this.el.select('.fc-day',true);
15125 this.textNodes = this.el.query('.fc-day-number');
15126 this.cells.addClassOnOver('fc-state-hover');
15128 var cells = this.cells.elements;
15129 var textEls = this.textNodes;
15131 Roo.each(cells, function(cell){
15132 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15135 days += startingPos;
15137 // convert everything to numbers so it's fast
15138 var day = 86400000;
15139 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15142 //Roo.log(prevStart);
15144 var today = new Date().clearTime().getTime();
15145 var sel = date.clearTime().getTime();
15146 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15147 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15148 var ddMatch = this.disabledDatesRE;
15149 var ddText = this.disabledDatesText;
15150 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15151 var ddaysText = this.disabledDaysText;
15152 var format = this.format;
15154 var setCellClass = function(cal, cell){
15158 //Roo.log('set Cell Class');
15160 var t = d.getTime();
15164 cell.dateValue = t;
15166 cell.className += " fc-today";
15167 cell.className += " fc-state-highlight";
15168 cell.title = cal.todayText;
15171 // disable highlight in other month..
15172 //cell.className += " fc-state-highlight";
15177 cell.className = " fc-state-disabled";
15178 cell.title = cal.minText;
15182 cell.className = " fc-state-disabled";
15183 cell.title = cal.maxText;
15187 if(ddays.indexOf(d.getDay()) != -1){
15188 cell.title = ddaysText;
15189 cell.className = " fc-state-disabled";
15192 if(ddMatch && format){
15193 var fvalue = d.dateFormat(format);
15194 if(ddMatch.test(fvalue)){
15195 cell.title = ddText.replace("%0", fvalue);
15196 cell.className = " fc-state-disabled";
15200 if (!cell.initialClassName) {
15201 cell.initialClassName = cell.dom.className;
15204 cell.dom.className = cell.initialClassName + ' ' + cell.className;
15209 for(; i < startingPos; i++) {
15210 textEls[i].innerHTML = (++prevStart);
15211 d.setDate(d.getDate()+1);
15213 cells[i].className = "fc-past fc-other-month";
15214 setCellClass(this, cells[i]);
15219 for(; i < days; i++){
15220 intDay = i - startingPos + 1;
15221 textEls[i].innerHTML = (intDay);
15222 d.setDate(d.getDate()+1);
15224 cells[i].className = ''; // "x-date-active";
15225 setCellClass(this, cells[i]);
15229 for(; i < 42; i++) {
15230 textEls[i].innerHTML = (++extraDays);
15231 d.setDate(d.getDate()+1);
15233 cells[i].className = "fc-future fc-other-month";
15234 setCellClass(this, cells[i]);
15237 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15239 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15241 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15242 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15244 if(totalRows != 6){
15245 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15246 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15249 this.fireEvent('monthchange', this, date);
15253 if(!this.internalRender){
15254 var main = this.el.dom.firstChild;
15255 var w = main.offsetWidth;
15256 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15257 Roo.fly(main).setWidth(w);
15258 this.internalRender = true;
15259 // opera does not respect the auto grow header center column
15260 // then, after it gets a width opera refuses to recalculate
15261 // without a second pass
15262 if(Roo.isOpera && !this.secondPass){
15263 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15264 this.secondPass = true;
15265 this.update.defer(10, this, [date]);
15272 findCell : function(dt) {
15273 dt = dt.clearTime().getTime();
15275 this.cells.each(function(c){
15276 //Roo.log("check " +c.dateValue + '?=' + dt);
15277 if(c.dateValue == dt){
15287 findCells : function(ev) {
15288 var s = ev.start.clone().clearTime().getTime();
15290 var e= ev.end.clone().clearTime().getTime();
15293 this.cells.each(function(c){
15294 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15296 if(c.dateValue > e){
15299 if(c.dateValue < s){
15308 // findBestRow: function(cells)
15312 // for (var i =0 ; i < cells.length;i++) {
15313 // ret = Math.max(cells[i].rows || 0,ret);
15320 addItem : function(ev)
15322 // look for vertical location slot in
15323 var cells = this.findCells(ev);
15325 // ev.row = this.findBestRow(cells);
15327 // work out the location.
15331 for(var i =0; i < cells.length; i++) {
15333 cells[i].row = cells[0].row;
15336 cells[i].row = cells[i].row + 1;
15346 if (crow.start.getY() == cells[i].getY()) {
15348 crow.end = cells[i];
15365 cells[0].events.push(ev);
15367 this.calevents.push(ev);
15370 clearEvents: function() {
15372 if(!this.calevents){
15376 Roo.each(this.cells.elements, function(c){
15382 Roo.each(this.calevents, function(e) {
15383 Roo.each(e.els, function(el) {
15384 el.un('mouseenter' ,this.onEventEnter, this);
15385 el.un('mouseleave' ,this.onEventLeave, this);
15390 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15396 renderEvents: function()
15400 this.cells.each(function(c) {
15409 if(c.row != c.events.length){
15410 r = 4 - (4 - (c.row - c.events.length));
15413 c.events = ev.slice(0, r);
15414 c.more = ev.slice(r);
15416 if(c.more.length && c.more.length == 1){
15417 c.events.push(c.more.pop());
15420 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15424 this.cells.each(function(c) {
15426 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15429 for (var e = 0; e < c.events.length; e++){
15430 var ev = c.events[e];
15431 var rows = ev.rows;
15433 for(var i = 0; i < rows.length; i++) {
15435 // how many rows should it span..
15438 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15439 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15441 unselectable : "on",
15444 cls: 'fc-event-inner',
15448 // cls: 'fc-event-time',
15449 // html : cells.length > 1 ? '' : ev.time
15453 cls: 'fc-event-title',
15454 html : String.format('{0}', ev.title)
15461 cls: 'ui-resizable-handle ui-resizable-e',
15462 html : '  '
15469 cfg.cls += ' fc-event-start';
15471 if ((i+1) == rows.length) {
15472 cfg.cls += ' fc-event-end';
15475 var ctr = _this.el.select('.fc-event-container',true).first();
15476 var cg = ctr.createChild(cfg);
15478 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15479 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15481 var r = (c.more.length) ? 1 : 0;
15482 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15483 cg.setWidth(ebox.right - sbox.x -2);
15485 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15486 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15487 cg.on('click', _this.onEventClick, _this, ev);
15498 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15499 style : 'position: absolute',
15500 unselectable : "on",
15503 cls: 'fc-event-inner',
15507 cls: 'fc-event-title',
15515 cls: 'ui-resizable-handle ui-resizable-e',
15516 html : '  '
15522 var ctr = _this.el.select('.fc-event-container',true).first();
15523 var cg = ctr.createChild(cfg);
15525 var sbox = c.select('.fc-day-content',true).first().getBox();
15526 var ebox = c.select('.fc-day-content',true).first().getBox();
15528 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15529 cg.setWidth(ebox.right - sbox.x -2);
15531 cg.on('click', _this.onMoreEventClick, _this, c.more);
15541 onEventEnter: function (e, el,event,d) {
15542 this.fireEvent('evententer', this, el, event);
15545 onEventLeave: function (e, el,event,d) {
15546 this.fireEvent('eventleave', this, el, event);
15549 onEventClick: function (e, el,event,d) {
15550 this.fireEvent('eventclick', this, el, event);
15553 onMonthChange: function () {
15557 onMoreEventClick: function(e, el, more)
15561 this.calpopover.placement = 'right';
15562 this.calpopover.setTitle('More');
15564 this.calpopover.setContent('');
15566 var ctr = this.calpopover.el.select('.popover-content', true).first();
15568 Roo.each(more, function(m){
15570 cls : 'fc-event-hori fc-event-draggable',
15573 var cg = ctr.createChild(cfg);
15575 cg.on('click', _this.onEventClick, _this, m);
15578 this.calpopover.show(el);
15583 onLoad: function ()
15585 this.calevents = [];
15588 if(this.store.getCount() > 0){
15589 this.store.data.each(function(d){
15592 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15593 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15594 time : d.data.start_time,
15595 title : d.data.title,
15596 description : d.data.description,
15597 venue : d.data.venue
15602 this.renderEvents();
15604 if(this.calevents.length && this.loadMask){
15605 this.maskEl.hide();
15609 onBeforeLoad: function()
15611 this.clearEvents();
15613 this.maskEl.show();
15627 * @class Roo.bootstrap.Popover
15628 * @extends Roo.bootstrap.Component
15629 * Bootstrap Popover class
15630 * @cfg {String} html contents of the popover (or false to use children..)
15631 * @cfg {String} title of popover (or false to hide)
15632 * @cfg {String} placement how it is placed
15633 * @cfg {String} trigger click || hover (or false to trigger manually)
15634 * @cfg {String} over what (parent or false to trigger manually.)
15635 * @cfg {Number} delay - delay before showing
15638 * Create a new Popover
15639 * @param {Object} config The config object
15642 Roo.bootstrap.Popover = function(config){
15643 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15646 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
15648 title: 'Fill in a title',
15651 placement : 'right',
15652 trigger : 'hover', // hover
15658 can_build_overlaid : false,
15660 getChildContainer : function()
15662 return this.el.select('.popover-content',true).first();
15665 getAutoCreate : function(){
15668 cls : 'popover roo-dynamic',
15669 style: 'display:block',
15675 cls : 'popover-inner',
15679 cls: 'popover-title',
15683 cls : 'popover-content',
15694 setTitle: function(str)
15697 this.el.select('.popover-title',true).first().dom.innerHTML = str;
15699 setContent: function(str)
15702 this.el.select('.popover-content',true).first().dom.innerHTML = str;
15704 // as it get's added to the bottom of the page.
15705 onRender : function(ct, position)
15707 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15709 var cfg = Roo.apply({}, this.getAutoCreate());
15713 cfg.cls += ' ' + this.cls;
15716 cfg.style = this.style;
15718 //Roo.log("adding to ");
15719 this.el = Roo.get(document.body).createChild(cfg, position);
15720 // Roo.log(this.el);
15725 initEvents : function()
15727 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15728 this.el.enableDisplayMode('block');
15730 if (this.over === false) {
15733 if (this.triggers === false) {
15736 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15737 var triggers = this.trigger ? this.trigger.split(' ') : [];
15738 Roo.each(triggers, function(trigger) {
15740 if (trigger == 'click') {
15741 on_el.on('click', this.toggle, this);
15742 } else if (trigger != 'manual') {
15743 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
15744 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15746 on_el.on(eventIn ,this.enter, this);
15747 on_el.on(eventOut, this.leave, this);
15758 toggle : function () {
15759 this.hoverState == 'in' ? this.leave() : this.enter();
15762 enter : function () {
15765 clearTimeout(this.timeout);
15767 this.hoverState = 'in';
15769 if (!this.delay || !this.delay.show) {
15774 this.timeout = setTimeout(function () {
15775 if (_t.hoverState == 'in') {
15778 }, this.delay.show)
15780 leave : function() {
15781 clearTimeout(this.timeout);
15783 this.hoverState = 'out';
15785 if (!this.delay || !this.delay.hide) {
15790 this.timeout = setTimeout(function () {
15791 if (_t.hoverState == 'out') {
15794 }, this.delay.hide)
15797 show : function (on_el)
15800 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15803 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15804 if (this.html !== false) {
15805 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15807 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15808 if (!this.title.length) {
15809 this.el.select('.popover-title',true).hide();
15812 var placement = typeof this.placement == 'function' ?
15813 this.placement.call(this, this.el, on_el) :
15816 var autoToken = /\s?auto?\s?/i;
15817 var autoPlace = autoToken.test(placement);
15819 placement = placement.replace(autoToken, '') || 'top';
15823 //this.el.setXY([0,0]);
15825 this.el.dom.style.display='block';
15826 this.el.addClass(placement);
15828 //this.el.appendTo(on_el);
15830 var p = this.getPosition();
15831 var box = this.el.getBox();
15836 var align = Roo.bootstrap.Popover.alignment[placement];
15837 this.el.alignTo(on_el, align[0],align[1]);
15838 //var arrow = this.el.select('.arrow',true).first();
15839 //arrow.set(align[2],
15841 this.el.addClass('in');
15844 if (this.el.hasClass('fade')) {
15851 this.el.setXY([0,0]);
15852 this.el.removeClass('in');
15854 this.hoverState = null;
15860 Roo.bootstrap.Popover.alignment = {
15861 'left' : ['r-l', [-10,0], 'right'],
15862 'right' : ['l-r', [10,0], 'left'],
15863 'bottom' : ['t-b', [0,10], 'top'],
15864 'top' : [ 'b-t', [0,-10], 'bottom']
15875 * @class Roo.bootstrap.Progress
15876 * @extends Roo.bootstrap.Component
15877 * Bootstrap Progress class
15878 * @cfg {Boolean} striped striped of the progress bar
15879 * @cfg {Boolean} active animated of the progress bar
15883 * Create a new Progress
15884 * @param {Object} config The config object
15887 Roo.bootstrap.Progress = function(config){
15888 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15891 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
15896 getAutoCreate : function(){
15904 cfg.cls += ' progress-striped';
15908 cfg.cls += ' active';
15927 * @class Roo.bootstrap.ProgressBar
15928 * @extends Roo.bootstrap.Component
15929 * Bootstrap ProgressBar class
15930 * @cfg {Number} aria_valuenow aria-value now
15931 * @cfg {Number} aria_valuemin aria-value min
15932 * @cfg {Number} aria_valuemax aria-value max
15933 * @cfg {String} label label for the progress bar
15934 * @cfg {String} panel (success | info | warning | danger )
15935 * @cfg {String} role role of the progress bar
15936 * @cfg {String} sr_only text
15940 * Create a new ProgressBar
15941 * @param {Object} config The config object
15944 Roo.bootstrap.ProgressBar = function(config){
15945 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15948 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
15952 aria_valuemax : 100,
15958 getAutoCreate : function()
15963 cls: 'progress-bar',
15964 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15976 cfg.role = this.role;
15979 if(this.aria_valuenow){
15980 cfg['aria-valuenow'] = this.aria_valuenow;
15983 if(this.aria_valuemin){
15984 cfg['aria-valuemin'] = this.aria_valuemin;
15987 if(this.aria_valuemax){
15988 cfg['aria-valuemax'] = this.aria_valuemax;
15991 if(this.label && !this.sr_only){
15992 cfg.html = this.label;
15996 cfg.cls += ' progress-bar-' + this.panel;
16002 update : function(aria_valuenow)
16004 this.aria_valuenow = aria_valuenow;
16006 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16021 * @class Roo.bootstrap.TabGroup
16022 * @extends Roo.bootstrap.Column
16023 * Bootstrap Column class
16024 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16025 * @cfg {Boolean} carousel true to make the group behave like a carousel
16026 * @cfg {Boolean} bullets show bullets for the panels
16027 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16028 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16029 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16032 * Create a new TabGroup
16033 * @param {Object} config The config object
16036 Roo.bootstrap.TabGroup = function(config){
16037 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16039 this.navId = Roo.id();
16042 Roo.bootstrap.TabGroup.register(this);
16046 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16049 transition : false,
16054 slideOnTouch : false,
16056 getAutoCreate : function()
16058 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16060 cfg.cls += ' tab-content';
16062 if (this.carousel) {
16063 cfg.cls += ' carousel slide';
16066 cls : 'carousel-inner'
16069 if(this.bullets && !Roo.isTouch){
16072 cls : 'carousel-bullets',
16076 if(this.bullets_cls){
16077 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16080 for (var i = 0; i < this.bullets; i++){
16082 cls : 'bullet bullet-' + i
16090 cfg.cn[0].cn = bullets;
16097 initEvents: function()
16099 if(Roo.isTouch && this.slideOnTouch){
16100 this.el.on("touchstart", this.onTouchStart, this);
16103 if(this.autoslide){
16106 this.slideFn = window.setInterval(function() {
16107 _this.showPanelNext();
16113 onTouchStart : function(e, el, o)
16115 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16119 this.showPanelNext();
16122 getChildContainer : function()
16124 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16128 * register a Navigation item
16129 * @param {Roo.bootstrap.NavItem} the navitem to add
16131 register : function(item)
16133 this.tabs.push( item);
16134 item.navId = this.navId; // not really needed..
16139 getActivePanel : function()
16142 Roo.each(this.tabs, function(t) {
16152 getPanelByName : function(n)
16155 Roo.each(this.tabs, function(t) {
16156 if (t.tabId == n) {
16164 indexOfPanel : function(p)
16167 Roo.each(this.tabs, function(t,i) {
16168 if (t.tabId == p.tabId) {
16177 * show a specific panel
16178 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16179 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16181 showPanel : function (pan)
16183 if(this.transition || typeof(pan) == 'undefined'){
16184 Roo.log("waiting for the transitionend");
16188 if (typeof(pan) == 'number') {
16189 pan = this.tabs[pan];
16192 if (typeof(pan) == 'string') {
16193 pan = this.getPanelByName(pan);
16196 var cur = this.getActivePanel();
16199 Roo.log('pan or acitve pan is undefined');
16203 if (pan.tabId == this.getActivePanel().tabId) {
16207 if (false === cur.fireEvent('beforedeactivate')) {
16211 if(this.bullets > 0 && !Roo.isTouch){
16212 this.setActiveBullet(this.indexOfPanel(pan));
16215 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16217 this.transition = true;
16218 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16219 var lr = dir == 'next' ? 'left' : 'right';
16220 pan.el.addClass(dir); // or prev
16221 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16222 cur.el.addClass(lr); // or right
16223 pan.el.addClass(lr);
16226 cur.el.on('transitionend', function() {
16227 Roo.log("trans end?");
16229 pan.el.removeClass([lr,dir]);
16230 pan.setActive(true);
16232 cur.el.removeClass([lr]);
16233 cur.setActive(false);
16235 _this.transition = false;
16237 }, this, { single: true } );
16242 cur.setActive(false);
16243 pan.setActive(true);
16248 showPanelNext : function()
16250 var i = this.indexOfPanel(this.getActivePanel());
16252 if (i >= this.tabs.length - 1 && !this.autoslide) {
16256 if (i >= this.tabs.length - 1 && this.autoslide) {
16260 this.showPanel(this.tabs[i+1]);
16263 showPanelPrev : function()
16265 var i = this.indexOfPanel(this.getActivePanel());
16267 if (i < 1 && !this.autoslide) {
16271 if (i < 1 && this.autoslide) {
16272 i = this.tabs.length;
16275 this.showPanel(this.tabs[i-1]);
16279 addBullet: function()
16281 if(!this.bullets || Roo.isTouch){
16284 var ctr = this.el.select('.carousel-bullets',true).first();
16285 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16286 var bullet = ctr.createChild({
16287 cls : 'bullet bullet-' + i
16288 },ctr.dom.lastChild);
16293 bullet.on('click', (function(e, el, o, ii, t){
16295 e.preventDefault();
16297 this.showPanel(ii);
16299 if(this.autoslide && this.slideFn){
16300 clearInterval(this.slideFn);
16301 this.slideFn = window.setInterval(function() {
16302 _this.showPanelNext();
16306 }).createDelegate(this, [i, bullet], true));
16311 setActiveBullet : function(i)
16317 Roo.each(this.el.select('.bullet', true).elements, function(el){
16318 el.removeClass('selected');
16321 var bullet = this.el.select('.bullet-' + i, true).first();
16327 bullet.addClass('selected');
16338 Roo.apply(Roo.bootstrap.TabGroup, {
16342 * register a Navigation Group
16343 * @param {Roo.bootstrap.NavGroup} the navgroup to add
16345 register : function(navgrp)
16347 this.groups[navgrp.navId] = navgrp;
16351 * fetch a Navigation Group based on the navigation ID
16352 * if one does not exist , it will get created.
16353 * @param {string} the navgroup to add
16354 * @returns {Roo.bootstrap.NavGroup} the navgroup
16356 get: function(navId) {
16357 if (typeof(this.groups[navId]) == 'undefined') {
16358 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16360 return this.groups[navId] ;
16375 * @class Roo.bootstrap.TabPanel
16376 * @extends Roo.bootstrap.Component
16377 * Bootstrap TabPanel class
16378 * @cfg {Boolean} active panel active
16379 * @cfg {String} html panel content
16380 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16381 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16385 * Create a new TabPanel
16386 * @param {Object} config The config object
16389 Roo.bootstrap.TabPanel = function(config){
16390 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16394 * Fires when the active status changes
16395 * @param {Roo.bootstrap.TabPanel} this
16396 * @param {Boolean} state the new state
16401 * @event beforedeactivate
16402 * Fires before a tab is de-activated - can be used to do validation on a form.
16403 * @param {Roo.bootstrap.TabPanel} this
16404 * @return {Boolean} false if there is an error
16407 'beforedeactivate': true
16410 this.tabId = this.tabId || Roo.id();
16414 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16421 getAutoCreate : function(){
16424 // item is needed for carousel - not sure if it has any effect otherwise
16425 cls: 'tab-pane item',
16426 html: this.html || ''
16430 cfg.cls += ' active';
16434 cfg.tabId = this.tabId;
16441 initEvents: function()
16443 var p = this.parent();
16444 this.navId = this.navId || p.navId;
16446 if (typeof(this.navId) != 'undefined') {
16447 // not really needed.. but just in case.. parent should be a NavGroup.
16448 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16452 var i = tg.tabs.length - 1;
16454 if(this.active && tg.bullets > 0 && i < tg.bullets){
16455 tg.setActiveBullet(i);
16462 onRender : function(ct, position)
16464 // Roo.log("Call onRender: " + this.xtype);
16466 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16474 setActive: function(state)
16476 Roo.log("panel - set active " + this.tabId + "=" + state);
16478 this.active = state;
16480 this.el.removeClass('active');
16482 } else if (!this.el.hasClass('active')) {
16483 this.el.addClass('active');
16486 this.fireEvent('changed', this, state);
16503 * @class Roo.bootstrap.DateField
16504 * @extends Roo.bootstrap.Input
16505 * Bootstrap DateField class
16506 * @cfg {Number} weekStart default 0
16507 * @cfg {String} viewMode default empty, (months|years)
16508 * @cfg {String} minViewMode default empty, (months|years)
16509 * @cfg {Number} startDate default -Infinity
16510 * @cfg {Number} endDate default Infinity
16511 * @cfg {Boolean} todayHighlight default false
16512 * @cfg {Boolean} todayBtn default false
16513 * @cfg {Boolean} calendarWeeks default false
16514 * @cfg {Object} daysOfWeekDisabled default empty
16515 * @cfg {Boolean} singleMode default false (true | false)
16517 * @cfg {Boolean} keyboardNavigation default true
16518 * @cfg {String} language default en
16521 * Create a new DateField
16522 * @param {Object} config The config object
16525 Roo.bootstrap.DateField = function(config){
16526 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16530 * Fires when this field show.
16531 * @param {Roo.bootstrap.DateField} this
16532 * @param {Mixed} date The date value
16537 * Fires when this field hide.
16538 * @param {Roo.bootstrap.DateField} this
16539 * @param {Mixed} date The date value
16544 * Fires when select a date.
16545 * @param {Roo.bootstrap.DateField} this
16546 * @param {Mixed} date The date value
16550 * @event beforeselect
16551 * Fires when before select a date.
16552 * @param {Roo.bootstrap.DateField} this
16553 * @param {Mixed} date The date value
16555 beforeselect : true
16559 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16562 * @cfg {String} format
16563 * The default date format string which can be overriden for localization support. The format must be
16564 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16568 * @cfg {String} altFormats
16569 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16570 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16572 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16580 todayHighlight : false,
16586 keyboardNavigation: true,
16588 calendarWeeks: false,
16590 startDate: -Infinity,
16594 daysOfWeekDisabled: [],
16598 singleMode : false,
16600 UTCDate: function()
16602 return new Date(Date.UTC.apply(Date, arguments));
16605 UTCToday: function()
16607 var today = new Date();
16608 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16611 getDate: function() {
16612 var d = this.getUTCDate();
16613 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16616 getUTCDate: function() {
16620 setDate: function(d) {
16621 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16624 setUTCDate: function(d) {
16626 this.setValue(this.formatDate(this.date));
16629 onRender: function(ct, position)
16632 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16634 this.language = this.language || 'en';
16635 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16636 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16638 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16639 this.format = this.format || 'm/d/y';
16640 this.isInline = false;
16641 this.isInput = true;
16642 this.component = this.el.select('.add-on', true).first() || false;
16643 this.component = (this.component && this.component.length === 0) ? false : this.component;
16644 this.hasInput = this.component && this.inputEL().length;
16646 if (typeof(this.minViewMode === 'string')) {
16647 switch (this.minViewMode) {
16649 this.minViewMode = 1;
16652 this.minViewMode = 2;
16655 this.minViewMode = 0;
16660 if (typeof(this.viewMode === 'string')) {
16661 switch (this.viewMode) {
16674 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16676 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16678 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16680 this.picker().on('mousedown', this.onMousedown, this);
16681 this.picker().on('click', this.onClick, this);
16683 this.picker().addClass('datepicker-dropdown');
16685 this.startViewMode = this.viewMode;
16687 if(this.singleMode){
16688 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16689 v.setVisibilityMode(Roo.Element.DISPLAY);
16693 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16694 v.setStyle('width', '189px');
16698 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16699 if(!this.calendarWeeks){
16704 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16705 v.attr('colspan', function(i, val){
16706 return parseInt(val) + 1;
16711 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16713 this.setStartDate(this.startDate);
16714 this.setEndDate(this.endDate);
16716 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16723 if(this.isInline) {
16728 picker : function()
16730 return this.pickerEl;
16731 // return this.el.select('.datepicker', true).first();
16734 fillDow: function()
16736 var dowCnt = this.weekStart;
16745 if(this.calendarWeeks){
16753 while (dowCnt < this.weekStart + 7) {
16757 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16761 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16764 fillMonths: function()
16767 var months = this.picker().select('>.datepicker-months td', true).first();
16769 months.dom.innerHTML = '';
16775 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16778 months.createChild(month);
16785 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;
16787 if (this.date < this.startDate) {
16788 this.viewDate = new Date(this.startDate);
16789 } else if (this.date > this.endDate) {
16790 this.viewDate = new Date(this.endDate);
16792 this.viewDate = new Date(this.date);
16800 var d = new Date(this.viewDate),
16801 year = d.getUTCFullYear(),
16802 month = d.getUTCMonth(),
16803 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16804 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16805 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16806 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16807 currentDate = this.date && this.date.valueOf(),
16808 today = this.UTCToday();
16810 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16812 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16814 // this.picker.select('>tfoot th.today').
16815 // .text(dates[this.language].today)
16816 // .toggle(this.todayBtn !== false);
16818 this.updateNavArrows();
16821 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16823 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16825 prevMonth.setUTCDate(day);
16827 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16829 var nextMonth = new Date(prevMonth);
16831 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16833 nextMonth = nextMonth.valueOf();
16835 var fillMonths = false;
16837 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16839 while(prevMonth.valueOf() < nextMonth) {
16842 if (prevMonth.getUTCDay() === this.weekStart) {
16844 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16852 if(this.calendarWeeks){
16853 // ISO 8601: First week contains first thursday.
16854 // ISO also states week starts on Monday, but we can be more abstract here.
16856 // Start of current week: based on weekstart/current date
16857 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16858 // Thursday of this week
16859 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16860 // First Thursday of year, year from thursday
16861 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16862 // Calendar week: ms between thursdays, div ms per day, div 7 days
16863 calWeek = (th - yth) / 864e5 / 7 + 1;
16865 fillMonths.cn.push({
16873 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16875 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16878 if (this.todayHighlight &&
16879 prevMonth.getUTCFullYear() == today.getFullYear() &&
16880 prevMonth.getUTCMonth() == today.getMonth() &&
16881 prevMonth.getUTCDate() == today.getDate()) {
16882 clsName += ' today';
16885 if (currentDate && prevMonth.valueOf() === currentDate) {
16886 clsName += ' active';
16889 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16890 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16891 clsName += ' disabled';
16894 fillMonths.cn.push({
16896 cls: 'day ' + clsName,
16897 html: prevMonth.getDate()
16900 prevMonth.setDate(prevMonth.getDate()+1);
16903 var currentYear = this.date && this.date.getUTCFullYear();
16904 var currentMonth = this.date && this.date.getUTCMonth();
16906 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16908 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16909 v.removeClass('active');
16911 if(currentYear === year && k === currentMonth){
16912 v.addClass('active');
16915 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16916 v.addClass('disabled');
16922 year = parseInt(year/10, 10) * 10;
16924 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16926 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16929 for (var i = -1; i < 11; i++) {
16930 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16932 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16940 showMode: function(dir)
16943 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16946 Roo.each(this.picker().select('>div',true).elements, function(v){
16947 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16950 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16955 if(this.isInline) {
16959 this.picker().removeClass(['bottom', 'top']);
16961 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16963 * place to the top of element!
16967 this.picker().addClass('top');
16968 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16973 this.picker().addClass('bottom');
16975 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16978 parseDate : function(value)
16980 if(!value || value instanceof Date){
16983 var v = Date.parseDate(value, this.format);
16984 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16985 v = Date.parseDate(value, 'Y-m-d');
16987 if(!v && this.altFormats){
16988 if(!this.altFormatsArray){
16989 this.altFormatsArray = this.altFormats.split("|");
16991 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16992 v = Date.parseDate(value, this.altFormatsArray[i]);
16998 formatDate : function(date, fmt)
17000 return (!date || !(date instanceof Date)) ?
17001 date : date.dateFormat(fmt || this.format);
17004 onFocus : function()
17006 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17010 onBlur : function()
17012 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17014 var d = this.inputEl().getValue();
17023 this.picker().show();
17027 this.fireEvent('show', this, this.date);
17032 if(this.isInline) {
17035 this.picker().hide();
17036 this.viewMode = this.startViewMode;
17039 this.fireEvent('hide', this, this.date);
17043 onMousedown: function(e)
17045 e.stopPropagation();
17046 e.preventDefault();
17051 Roo.bootstrap.DateField.superclass.keyup.call(this);
17055 setValue: function(v)
17057 if(this.fireEvent('beforeselect', this, v) !== false){
17058 var d = new Date(this.parseDate(v) ).clearTime();
17060 if(isNaN(d.getTime())){
17061 this.date = this.viewDate = '';
17062 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17066 v = this.formatDate(d);
17068 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17070 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17074 this.fireEvent('select', this, this.date);
17078 getValue: function()
17080 return this.formatDate(this.date);
17083 fireKey: function(e)
17085 if (!this.picker().isVisible()){
17086 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17092 var dateChanged = false,
17094 newDate, newViewDate;
17099 e.preventDefault();
17103 if (!this.keyboardNavigation) {
17106 dir = e.keyCode == 37 ? -1 : 1;
17109 newDate = this.moveYear(this.date, dir);
17110 newViewDate = this.moveYear(this.viewDate, dir);
17111 } else if (e.shiftKey){
17112 newDate = this.moveMonth(this.date, dir);
17113 newViewDate = this.moveMonth(this.viewDate, dir);
17115 newDate = new Date(this.date);
17116 newDate.setUTCDate(this.date.getUTCDate() + dir);
17117 newViewDate = new Date(this.viewDate);
17118 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17120 if (this.dateWithinRange(newDate)){
17121 this.date = newDate;
17122 this.viewDate = newViewDate;
17123 this.setValue(this.formatDate(this.date));
17125 e.preventDefault();
17126 dateChanged = true;
17131 if (!this.keyboardNavigation) {
17134 dir = e.keyCode == 38 ? -1 : 1;
17136 newDate = this.moveYear(this.date, dir);
17137 newViewDate = this.moveYear(this.viewDate, dir);
17138 } else if (e.shiftKey){
17139 newDate = this.moveMonth(this.date, dir);
17140 newViewDate = this.moveMonth(this.viewDate, dir);
17142 newDate = new Date(this.date);
17143 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17144 newViewDate = new Date(this.viewDate);
17145 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17147 if (this.dateWithinRange(newDate)){
17148 this.date = newDate;
17149 this.viewDate = newViewDate;
17150 this.setValue(this.formatDate(this.date));
17152 e.preventDefault();
17153 dateChanged = true;
17157 this.setValue(this.formatDate(this.date));
17159 e.preventDefault();
17162 this.setValue(this.formatDate(this.date));
17176 onClick: function(e)
17178 e.stopPropagation();
17179 e.preventDefault();
17181 var target = e.getTarget();
17183 if(target.nodeName.toLowerCase() === 'i'){
17184 target = Roo.get(target).dom.parentNode;
17187 var nodeName = target.nodeName;
17188 var className = target.className;
17189 var html = target.innerHTML;
17190 //Roo.log(nodeName);
17192 switch(nodeName.toLowerCase()) {
17194 switch(className) {
17200 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17201 switch(this.viewMode){
17203 this.viewDate = this.moveMonth(this.viewDate, dir);
17207 this.viewDate = this.moveYear(this.viewDate, dir);
17213 var date = new Date();
17214 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17216 this.setValue(this.formatDate(this.date));
17223 if (className.indexOf('disabled') < 0) {
17224 this.viewDate.setUTCDate(1);
17225 if (className.indexOf('month') > -1) {
17226 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17228 var year = parseInt(html, 10) || 0;
17229 this.viewDate.setUTCFullYear(year);
17233 if(this.singleMode){
17234 this.setValue(this.formatDate(this.viewDate));
17245 //Roo.log(className);
17246 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17247 var day = parseInt(html, 10) || 1;
17248 var year = this.viewDate.getUTCFullYear(),
17249 month = this.viewDate.getUTCMonth();
17251 if (className.indexOf('old') > -1) {
17258 } else if (className.indexOf('new') > -1) {
17266 //Roo.log([year,month,day]);
17267 this.date = this.UTCDate(year, month, day,0,0,0,0);
17268 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17270 //Roo.log(this.formatDate(this.date));
17271 this.setValue(this.formatDate(this.date));
17278 setStartDate: function(startDate)
17280 this.startDate = startDate || -Infinity;
17281 if (this.startDate !== -Infinity) {
17282 this.startDate = this.parseDate(this.startDate);
17285 this.updateNavArrows();
17288 setEndDate: function(endDate)
17290 this.endDate = endDate || Infinity;
17291 if (this.endDate !== Infinity) {
17292 this.endDate = this.parseDate(this.endDate);
17295 this.updateNavArrows();
17298 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17300 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17301 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17302 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17304 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17305 return parseInt(d, 10);
17308 this.updateNavArrows();
17311 updateNavArrows: function()
17313 if(this.singleMode){
17317 var d = new Date(this.viewDate),
17318 year = d.getUTCFullYear(),
17319 month = d.getUTCMonth();
17321 Roo.each(this.picker().select('.prev', true).elements, function(v){
17323 switch (this.viewMode) {
17326 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17332 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17339 Roo.each(this.picker().select('.next', true).elements, function(v){
17341 switch (this.viewMode) {
17344 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17350 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17358 moveMonth: function(date, dir)
17363 var new_date = new Date(date.valueOf()),
17364 day = new_date.getUTCDate(),
17365 month = new_date.getUTCMonth(),
17366 mag = Math.abs(dir),
17368 dir = dir > 0 ? 1 : -1;
17371 // If going back one month, make sure month is not current month
17372 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17374 return new_date.getUTCMonth() == month;
17376 // If going forward one month, make sure month is as expected
17377 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17379 return new_date.getUTCMonth() != new_month;
17381 new_month = month + dir;
17382 new_date.setUTCMonth(new_month);
17383 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17384 if (new_month < 0 || new_month > 11) {
17385 new_month = (new_month + 12) % 12;
17388 // For magnitudes >1, move one month at a time...
17389 for (var i=0; i<mag; i++) {
17390 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17391 new_date = this.moveMonth(new_date, dir);
17393 // ...then reset the day, keeping it in the new month
17394 new_month = new_date.getUTCMonth();
17395 new_date.setUTCDate(day);
17397 return new_month != new_date.getUTCMonth();
17400 // Common date-resetting loop -- if date is beyond end of month, make it
17403 new_date.setUTCDate(--day);
17404 new_date.setUTCMonth(new_month);
17409 moveYear: function(date, dir)
17411 return this.moveMonth(date, dir*12);
17414 dateWithinRange: function(date)
17416 return date >= this.startDate && date <= this.endDate;
17422 this.picker().remove();
17427 Roo.apply(Roo.bootstrap.DateField, {
17438 html: '<i class="fa fa-arrow-left"/>'
17448 html: '<i class="fa fa-arrow-right"/>'
17490 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17491 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17492 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17493 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17494 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17507 navFnc: 'FullYear',
17512 navFnc: 'FullYear',
17517 Roo.apply(Roo.bootstrap.DateField, {
17521 cls: 'datepicker dropdown-menu roo-dynamic',
17525 cls: 'datepicker-days',
17529 cls: 'table-condensed',
17531 Roo.bootstrap.DateField.head,
17535 Roo.bootstrap.DateField.footer
17542 cls: 'datepicker-months',
17546 cls: 'table-condensed',
17548 Roo.bootstrap.DateField.head,
17549 Roo.bootstrap.DateField.content,
17550 Roo.bootstrap.DateField.footer
17557 cls: 'datepicker-years',
17561 cls: 'table-condensed',
17563 Roo.bootstrap.DateField.head,
17564 Roo.bootstrap.DateField.content,
17565 Roo.bootstrap.DateField.footer
17584 * @class Roo.bootstrap.TimeField
17585 * @extends Roo.bootstrap.Input
17586 * Bootstrap DateField class
17590 * Create a new TimeField
17591 * @param {Object} config The config object
17594 Roo.bootstrap.TimeField = function(config){
17595 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17599 * Fires when this field show.
17600 * @param {Roo.bootstrap.DateField} thisthis
17601 * @param {Mixed} date The date value
17606 * Fires when this field hide.
17607 * @param {Roo.bootstrap.DateField} this
17608 * @param {Mixed} date The date value
17613 * Fires when select a date.
17614 * @param {Roo.bootstrap.DateField} this
17615 * @param {Mixed} date The date value
17621 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17624 * @cfg {String} format
17625 * The default time format string which can be overriden for localization support. The format must be
17626 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17630 onRender: function(ct, position)
17633 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17635 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17637 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17639 this.pop = this.picker().select('>.datepicker-time',true).first();
17640 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17642 this.picker().on('mousedown', this.onMousedown, this);
17643 this.picker().on('click', this.onClick, this);
17645 this.picker().addClass('datepicker-dropdown');
17650 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17651 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17652 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17653 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17654 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17655 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17659 fireKey: function(e){
17660 if (!this.picker().isVisible()){
17661 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17667 e.preventDefault();
17675 this.onTogglePeriod();
17678 this.onIncrementMinutes();
17681 this.onDecrementMinutes();
17690 onClick: function(e) {
17691 e.stopPropagation();
17692 e.preventDefault();
17695 picker : function()
17697 return this.el.select('.datepicker', true).first();
17700 fillTime: function()
17702 var time = this.pop.select('tbody', true).first();
17704 time.dom.innerHTML = '';
17719 cls: 'hours-up glyphicon glyphicon-chevron-up'
17739 cls: 'minutes-up glyphicon glyphicon-chevron-up'
17760 cls: 'timepicker-hour',
17775 cls: 'timepicker-minute',
17790 cls: 'btn btn-primary period',
17812 cls: 'hours-down glyphicon glyphicon-chevron-down'
17832 cls: 'minutes-down glyphicon glyphicon-chevron-down'
17850 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17857 var hours = this.time.getHours();
17858 var minutes = this.time.getMinutes();
17871 hours = hours - 12;
17875 hours = '0' + hours;
17879 minutes = '0' + minutes;
17882 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17883 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17884 this.pop.select('button', true).first().dom.innerHTML = period;
17890 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17892 var cls = ['bottom'];
17894 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17901 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17906 this.picker().addClass(cls.join('-'));
17910 Roo.each(cls, function(c){
17912 _this.picker().setTop(_this.inputEl().getHeight());
17916 _this.picker().setTop(0 - _this.picker().getHeight());
17921 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17925 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17932 onFocus : function()
17934 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17938 onBlur : function()
17940 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17946 this.picker().show();
17951 this.fireEvent('show', this, this.date);
17956 this.picker().hide();
17959 this.fireEvent('hide', this, this.date);
17962 setTime : function()
17965 this.setValue(this.time.format(this.format));
17967 this.fireEvent('select', this, this.date);
17972 onMousedown: function(e){
17973 e.stopPropagation();
17974 e.preventDefault();
17977 onIncrementHours: function()
17979 Roo.log('onIncrementHours');
17980 this.time = this.time.add(Date.HOUR, 1);
17985 onDecrementHours: function()
17987 Roo.log('onDecrementHours');
17988 this.time = this.time.add(Date.HOUR, -1);
17992 onIncrementMinutes: function()
17994 Roo.log('onIncrementMinutes');
17995 this.time = this.time.add(Date.MINUTE, 1);
17999 onDecrementMinutes: function()
18001 Roo.log('onDecrementMinutes');
18002 this.time = this.time.add(Date.MINUTE, -1);
18006 onTogglePeriod: function()
18008 Roo.log('onTogglePeriod');
18009 this.time = this.time.add(Date.HOUR, 12);
18016 Roo.apply(Roo.bootstrap.TimeField, {
18046 cls: 'btn btn-info ok',
18058 Roo.apply(Roo.bootstrap.TimeField, {
18062 cls: 'datepicker dropdown-menu',
18066 cls: 'datepicker-time',
18070 cls: 'table-condensed',
18072 Roo.bootstrap.TimeField.content,
18073 Roo.bootstrap.TimeField.footer
18092 * @class Roo.bootstrap.MonthField
18093 * @extends Roo.bootstrap.Input
18094 * Bootstrap MonthField class
18096 * @cfg {String} language default en
18099 * Create a new MonthField
18100 * @param {Object} config The config object
18103 Roo.bootstrap.MonthField = function(config){
18104 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18109 * Fires when this field show.
18110 * @param {Roo.bootstrap.MonthField} this
18111 * @param {Mixed} date The date value
18116 * Fires when this field hide.
18117 * @param {Roo.bootstrap.MonthField} this
18118 * @param {Mixed} date The date value
18123 * Fires when select a date.
18124 * @param {Roo.bootstrap.MonthField} this
18125 * @param {String} oldvalue The old value
18126 * @param {String} newvalue The new value
18132 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
18134 onRender: function(ct, position)
18137 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18139 this.language = this.language || 'en';
18140 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18141 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18143 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18144 this.isInline = false;
18145 this.isInput = true;
18146 this.component = this.el.select('.add-on', true).first() || false;
18147 this.component = (this.component && this.component.length === 0) ? false : this.component;
18148 this.hasInput = this.component && this.inputEL().length;
18150 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18152 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18154 this.picker().on('mousedown', this.onMousedown, this);
18155 this.picker().on('click', this.onClick, this);
18157 this.picker().addClass('datepicker-dropdown');
18159 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18160 v.setStyle('width', '189px');
18167 if(this.isInline) {
18173 setValue: function(v, suppressEvent)
18175 var o = this.getValue();
18177 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18181 if(suppressEvent !== true){
18182 this.fireEvent('select', this, o, v);
18187 getValue: function()
18192 onClick: function(e)
18194 e.stopPropagation();
18195 e.preventDefault();
18197 var target = e.getTarget();
18199 if(target.nodeName.toLowerCase() === 'i'){
18200 target = Roo.get(target).dom.parentNode;
18203 var nodeName = target.nodeName;
18204 var className = target.className;
18205 var html = target.innerHTML;
18207 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18211 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18213 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18219 picker : function()
18221 return this.pickerEl;
18224 fillMonths: function()
18227 var months = this.picker().select('>.datepicker-months td', true).first();
18229 months.dom.innerHTML = '';
18235 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18238 months.createChild(month);
18247 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18248 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18251 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18252 e.removeClass('active');
18254 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18255 e.addClass('active');
18262 if(this.isInline) {
18266 this.picker().removeClass(['bottom', 'top']);
18268 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18270 * place to the top of element!
18274 this.picker().addClass('top');
18275 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18280 this.picker().addClass('bottom');
18282 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18285 onFocus : function()
18287 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18291 onBlur : function()
18293 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18295 var d = this.inputEl().getValue();
18304 this.picker().show();
18305 this.picker().select('>.datepicker-months', true).first().show();
18309 this.fireEvent('show', this, this.date);
18314 if(this.isInline) {
18317 this.picker().hide();
18318 this.fireEvent('hide', this, this.date);
18322 onMousedown: function(e)
18324 e.stopPropagation();
18325 e.preventDefault();
18330 Roo.bootstrap.MonthField.superclass.keyup.call(this);
18334 fireKey: function(e)
18336 if (!this.picker().isVisible()){
18337 if (e.keyCode == 27) {// allow escape to hide and re-show picker
18348 e.preventDefault();
18352 dir = e.keyCode == 37 ? -1 : 1;
18354 this.vIndex = this.vIndex + dir;
18356 if(this.vIndex < 0){
18360 if(this.vIndex > 11){
18364 if(isNaN(this.vIndex)){
18368 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18374 dir = e.keyCode == 38 ? -1 : 1;
18376 this.vIndex = this.vIndex + dir * 4;
18378 if(this.vIndex < 0){
18382 if(this.vIndex > 11){
18386 if(isNaN(this.vIndex)){
18390 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18395 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18396 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18400 e.preventDefault();
18403 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18404 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18420 this.picker().remove();
18425 Roo.apply(Roo.bootstrap.MonthField, {
18444 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18445 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18450 Roo.apply(Roo.bootstrap.MonthField, {
18454 cls: 'datepicker dropdown-menu roo-dynamic',
18458 cls: 'datepicker-months',
18462 cls: 'table-condensed',
18464 Roo.bootstrap.DateField.content
18484 * @class Roo.bootstrap.CheckBox
18485 * @extends Roo.bootstrap.Input
18486 * Bootstrap CheckBox class
18488 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18489 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18490 * @cfg {String} boxLabel The text that appears beside the checkbox
18491 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18492 * @cfg {Boolean} checked initnal the element
18493 * @cfg {Boolean} inline inline the element (default false)
18494 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18497 * Create a new CheckBox
18498 * @param {Object} config The config object
18501 Roo.bootstrap.CheckBox = function(config){
18502 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18507 * Fires when the element is checked or unchecked.
18508 * @param {Roo.bootstrap.CheckBox} this This input
18509 * @param {Boolean} checked The new checked value
18516 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18518 inputType: 'checkbox',
18526 getAutoCreate : function()
18528 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18534 cfg.cls = 'form-group ' + this.inputType; //input-group
18537 cfg.cls += ' ' + this.inputType + '-inline';
18543 type : this.inputType,
18544 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18545 cls : 'roo-' + this.inputType, //'form-box',
18546 placeholder : this.placeholder || ''
18550 if (this.weight) { // Validity check?
18551 cfg.cls += " " + this.inputType + "-" + this.weight;
18554 if (this.disabled) {
18555 input.disabled=true;
18559 input.checked = this.checked;
18563 input.name = this.name;
18567 input.cls += ' input-' + this.size;
18572 ['xs','sm','md','lg'].map(function(size){
18573 if (settings[size]) {
18574 cfg.cls += ' col-' + size + '-' + settings[size];
18578 var inputblock = input;
18580 if (this.before || this.after) {
18583 cls : 'input-group',
18588 inputblock.cn.push({
18590 cls : 'input-group-addon',
18595 inputblock.cn.push(input);
18598 inputblock.cn.push({
18600 cls : 'input-group-addon',
18607 if (align ==='left' && this.fieldLabel.length) {
18608 // Roo.log("left and has label");
18614 cls : 'control-label col-md-' + this.labelWidth,
18615 html : this.fieldLabel
18619 cls : "col-md-" + (12 - this.labelWidth),
18626 } else if ( this.fieldLabel.length) {
18627 // Roo.log(" label");
18631 tag: this.boxLabel ? 'span' : 'label',
18633 cls: 'control-label box-input-label',
18634 //cls : 'input-group-addon',
18635 html : this.fieldLabel
18645 // Roo.log(" no label && no align");
18646 cfg.cn = [ inputblock ] ;
18651 var boxLabelCfg = {
18653 //'for': id, // box label is handled by onclick - so no for...
18655 html: this.boxLabel
18659 boxLabelCfg.tooltip = this.tooltip;
18662 cfg.cn.push(boxLabelCfg);
18672 * return the real input element.
18674 inputEl: function ()
18676 return this.el.select('input.roo-' + this.inputType,true).first();
18679 labelEl: function()
18681 return this.el.select('label.control-label',true).first();
18683 /* depricated... */
18687 return this.labelEl();
18690 initEvents : function()
18692 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18694 this.inputEl().on('click', this.onClick, this);
18696 if (this.boxLabel) {
18697 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18700 this.startValue = this.getValue();
18703 Roo.bootstrap.CheckBox.register(this);
18707 onClick : function()
18709 this.setChecked(!this.checked);
18712 setChecked : function(state,suppressEvent)
18714 this.startValue = this.getValue();
18716 if(this.inputType == 'radio'){
18718 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18719 e.dom.checked = false;
18722 this.inputEl().dom.checked = true;
18724 this.inputEl().dom.value = this.inputValue;
18726 if(suppressEvent !== true){
18727 this.fireEvent('check', this, true);
18735 this.checked = state;
18737 this.inputEl().dom.checked = state;
18739 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18741 if(suppressEvent !== true){
18742 this.fireEvent('check', this, state);
18748 getValue : function()
18750 if(this.inputType == 'radio'){
18751 return this.getGroupValue();
18754 return this.inputEl().getValue();
18758 getGroupValue : function()
18760 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18764 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18767 setValue : function(v,suppressEvent)
18769 if(this.inputType == 'radio'){
18770 this.setGroupValue(v, suppressEvent);
18774 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18779 setGroupValue : function(v, suppressEvent)
18781 this.startValue = this.getValue();
18783 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18784 e.dom.checked = false;
18786 if(e.dom.value == v){
18787 e.dom.checked = true;
18791 if(suppressEvent !== true){
18792 this.fireEvent('check', this, true);
18800 validate : function()
18804 (this.inputType == 'radio' && this.validateRadio()) ||
18805 (this.inputType == 'checkbox' && this.validateCheckbox())
18811 this.markInvalid();
18815 validateRadio : function()
18819 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18820 if(!e.dom.checked){
18832 validateCheckbox : function()
18835 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18838 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18846 for(var i in group){
18851 r = (group[i].getValue() == group[i].inputValue) ? true : false;
18858 * Mark this field as valid
18860 markValid : function()
18862 if(this.allowBlank){
18868 this.fireEvent('valid', this);
18870 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18873 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18880 if(this.inputType == 'radio'){
18881 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18882 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18883 e.findParent('.form-group', false, true).addClass(_this.validClass);
18890 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18891 this.el.findParent('.form-group', false, true).addClass(this.validClass);
18895 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18901 for(var i in group){
18902 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18903 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18908 * Mark this field as invalid
18909 * @param {String} msg The validation message
18911 markInvalid : function(msg)
18913 if(this.allowBlank){
18919 this.fireEvent('invalid', this, msg);
18921 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18924 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18928 label.markInvalid();
18931 if(this.inputType == 'radio'){
18932 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18933 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18934 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18941 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18942 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18946 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18952 for(var i in group){
18953 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18954 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18961 Roo.apply(Roo.bootstrap.CheckBox, {
18966 * register a CheckBox Group
18967 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18969 register : function(checkbox)
18971 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18972 this.groups[checkbox.groupId] = {};
18975 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18979 this.groups[checkbox.groupId][checkbox.name] = checkbox;
18983 * fetch a CheckBox Group based on the group ID
18984 * @param {string} the group ID
18985 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18987 get: function(groupId) {
18988 if (typeof(this.groups[groupId]) == 'undefined') {
18992 return this.groups[groupId] ;
19004 *<div class="radio">
19006 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19007 Option one is this and that—be sure to include why it's great
19014 *<label class="radio-inline">fieldLabel</label>
19015 *<label class="radio-inline">
19016 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19024 * @class Roo.bootstrap.Radio
19025 * @extends Roo.bootstrap.CheckBox
19026 * Bootstrap Radio class
19029 * Create a new Radio
19030 * @param {Object} config The config object
19033 Roo.bootstrap.Radio = function(config){
19034 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19038 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
19040 inputType: 'radio',
19044 getAutoCreate : function()
19046 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19047 align = align || 'left'; // default...
19054 tag : this.inline ? 'span' : 'div',
19059 var inline = this.inline ? ' radio-inline' : '';
19063 // does not need for, as we wrap the input with it..
19065 cls : 'control-label box-label' + inline,
19068 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19072 //cls : 'control-label' + inline,
19073 html : this.fieldLabel,
19074 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19083 type : this.inputType,
19084 //value : (!this.checked) ? this.valueOff : this.inputValue,
19085 value : this.inputValue,
19087 placeholder : this.placeholder || '' // ?? needed????
19090 if (this.weight) { // Validity check?
19091 input.cls += " radio-" + this.weight;
19093 if (this.disabled) {
19094 input.disabled=true;
19098 input.checked = this.checked;
19102 input.name = this.name;
19106 input.cls += ' input-' + this.size;
19109 //?? can span's inline have a width??
19112 ['xs','sm','md','lg'].map(function(size){
19113 if (settings[size]) {
19114 cfg.cls += ' col-' + size + '-' + settings[size];
19118 var inputblock = input;
19120 if (this.before || this.after) {
19123 cls : 'input-group',
19128 inputblock.cn.push({
19130 cls : 'input-group-addon',
19134 inputblock.cn.push(input);
19136 inputblock.cn.push({
19138 cls : 'input-group-addon',
19146 if (this.fieldLabel && this.fieldLabel.length) {
19147 cfg.cn.push(fieldLabel);
19150 // normal bootstrap puts the input inside the label.
19151 // however with our styled version - it has to go after the input.
19153 //lbl.cn.push(inputblock);
19157 cls: 'radio' + inline,
19164 cfg.cn.push( lblwrap);
19169 html: this.boxLabel
19178 initEvents : function()
19180 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19182 this.inputEl().on('click', this.onClick, this);
19183 if (this.boxLabel) {
19184 //Roo.log('find label');
19185 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
19190 inputEl: function ()
19192 return this.el.select('input.roo-radio',true).first();
19194 onClick : function()
19197 this.setChecked(true);
19200 setChecked : function(state,suppressEvent)
19203 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19204 v.dom.checked = false;
19207 Roo.log(this.inputEl().dom);
19208 this.checked = state;
19209 this.inputEl().dom.checked = state;
19211 if(suppressEvent !== true){
19212 this.fireEvent('check', this, state);
19215 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19219 getGroupValue : function()
19222 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19223 if(v.dom.checked == true){
19224 value = v.dom.value;
19232 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
19233 * @return {Mixed} value The field value
19235 getValue : function(){
19236 return this.getGroupValue();
19242 //<script type="text/javascript">
19245 * Based Ext JS Library 1.1.1
19246 * Copyright(c) 2006-2007, Ext JS, LLC.
19252 * @class Roo.HtmlEditorCore
19253 * @extends Roo.Component
19254 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19256 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19259 Roo.HtmlEditorCore = function(config){
19262 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19267 * @event initialize
19268 * Fires when the editor is fully initialized (including the iframe)
19269 * @param {Roo.HtmlEditorCore} this
19274 * Fires when the editor is first receives the focus. Any insertion must wait
19275 * until after this event.
19276 * @param {Roo.HtmlEditorCore} this
19280 * @event beforesync
19281 * Fires before the textarea is updated with content from the editor iframe. Return false
19282 * to cancel the sync.
19283 * @param {Roo.HtmlEditorCore} this
19284 * @param {String} html
19288 * @event beforepush
19289 * Fires before the iframe editor is updated with content from the textarea. Return false
19290 * to cancel the push.
19291 * @param {Roo.HtmlEditorCore} this
19292 * @param {String} html
19297 * Fires when the textarea is updated with content from the editor iframe.
19298 * @param {Roo.HtmlEditorCore} this
19299 * @param {String} html
19304 * Fires when the iframe editor is updated with content from the textarea.
19305 * @param {Roo.HtmlEditorCore} this
19306 * @param {String} html
19311 * @event editorevent
19312 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19313 * @param {Roo.HtmlEditorCore} this
19319 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19321 // defaults : white / black...
19322 this.applyBlacklists();
19329 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
19333 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
19339 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
19344 * @cfg {Number} height (in pixels)
19348 * @cfg {Number} width (in pixels)
19353 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19356 stylesheets: false,
19361 // private properties
19362 validationEvent : false,
19364 initialized : false,
19366 sourceEditMode : false,
19367 onFocus : Roo.emptyFn,
19369 hideMode:'offsets',
19373 // blacklist + whitelisted elements..
19380 * Protected method that will not generally be called directly. It
19381 * is called when the editor initializes the iframe with HTML contents. Override this method if you
19382 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19384 getDocMarkup : function(){
19388 // inherit styels from page...??
19389 if (this.stylesheets === false) {
19391 Roo.get(document.head).select('style').each(function(node) {
19392 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19395 Roo.get(document.head).select('link').each(function(node) {
19396 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19399 } else if (!this.stylesheets.length) {
19401 st = '<style type="text/css">' +
19402 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19408 st += '<style type="text/css">' +
19409 'IMG { cursor: pointer } ' +
19413 return '<html><head>' + st +
19414 //<style type="text/css">' +
19415 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19417 ' </head><body class="roo-htmleditor-body"></body></html>';
19421 onRender : function(ct, position)
19424 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19425 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19428 this.el.dom.style.border = '0 none';
19429 this.el.dom.setAttribute('tabIndex', -1);
19430 this.el.addClass('x-hidden hide');
19434 if(Roo.isIE){ // fix IE 1px bogus margin
19435 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19439 this.frameId = Roo.id();
19443 var iframe = this.owner.wrap.createChild({
19445 cls: 'form-control', // bootstrap..
19447 name: this.frameId,
19448 frameBorder : 'no',
19449 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19454 this.iframe = iframe.dom;
19456 this.assignDocWin();
19458 this.doc.designMode = 'on';
19461 this.doc.write(this.getDocMarkup());
19465 var task = { // must defer to wait for browser to be ready
19467 //console.log("run task?" + this.doc.readyState);
19468 this.assignDocWin();
19469 if(this.doc.body || this.doc.readyState == 'complete'){
19471 this.doc.designMode="on";
19475 Roo.TaskMgr.stop(task);
19476 this.initEditor.defer(10, this);
19483 Roo.TaskMgr.start(task);
19488 onResize : function(w, h)
19490 Roo.log('resize: ' +w + ',' + h );
19491 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19495 if(typeof w == 'number'){
19497 this.iframe.style.width = w + 'px';
19499 if(typeof h == 'number'){
19501 this.iframe.style.height = h + 'px';
19503 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19510 * Toggles the editor between standard and source edit mode.
19511 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19513 toggleSourceEdit : function(sourceEditMode){
19515 this.sourceEditMode = sourceEditMode === true;
19517 if(this.sourceEditMode){
19519 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19522 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19523 //this.iframe.className = '';
19526 //this.setSize(this.owner.wrap.getSize());
19527 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19534 * Protected method that will not generally be called directly. If you need/want
19535 * custom HTML cleanup, this is the method you should override.
19536 * @param {String} html The HTML to be cleaned
19537 * return {String} The cleaned HTML
19539 cleanHtml : function(html){
19540 html = String(html);
19541 if(html.length > 5){
19542 if(Roo.isSafari){ // strip safari nonsense
19543 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19546 if(html == ' '){
19553 * HTML Editor -> Textarea
19554 * Protected method that will not generally be called directly. Syncs the contents
19555 * of the editor iframe with the textarea.
19557 syncValue : function(){
19558 if(this.initialized){
19559 var bd = (this.doc.body || this.doc.documentElement);
19560 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19561 var html = bd.innerHTML;
19563 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19564 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19566 html = '<div style="'+m[0]+'">' + html + '</div>';
19569 html = this.cleanHtml(html);
19570 // fix up the special chars.. normaly like back quotes in word...
19571 // however we do not want to do this with chinese..
19572 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19573 var cc = b.charCodeAt();
19575 (cc >= 0x4E00 && cc < 0xA000 ) ||
19576 (cc >= 0x3400 && cc < 0x4E00 ) ||
19577 (cc >= 0xf900 && cc < 0xfb00 )
19583 if(this.owner.fireEvent('beforesync', this, html) !== false){
19584 this.el.dom.value = html;
19585 this.owner.fireEvent('sync', this, html);
19591 * Protected method that will not generally be called directly. Pushes the value of the textarea
19592 * into the iframe editor.
19594 pushValue : function(){
19595 if(this.initialized){
19596 var v = this.el.dom.value.trim();
19598 // if(v.length < 1){
19602 if(this.owner.fireEvent('beforepush', this, v) !== false){
19603 var d = (this.doc.body || this.doc.documentElement);
19605 this.cleanUpPaste();
19606 this.el.dom.value = d.innerHTML;
19607 this.owner.fireEvent('push', this, v);
19613 deferFocus : function(){
19614 this.focus.defer(10, this);
19618 focus : function(){
19619 if(this.win && !this.sourceEditMode){
19626 assignDocWin: function()
19628 var iframe = this.iframe;
19631 this.doc = iframe.contentWindow.document;
19632 this.win = iframe.contentWindow;
19634 // if (!Roo.get(this.frameId)) {
19637 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19638 // this.win = Roo.get(this.frameId).dom.contentWindow;
19640 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19644 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19645 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19650 initEditor : function(){
19651 //console.log("INIT EDITOR");
19652 this.assignDocWin();
19656 this.doc.designMode="on";
19658 this.doc.write(this.getDocMarkup());
19661 var dbody = (this.doc.body || this.doc.documentElement);
19662 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19663 // this copies styles from the containing element into thsi one..
19664 // not sure why we need all of this..
19665 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19667 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19668 //ss['background-attachment'] = 'fixed'; // w3c
19669 dbody.bgProperties = 'fixed'; // ie
19670 //Roo.DomHelper.applyStyles(dbody, ss);
19671 Roo.EventManager.on(this.doc, {
19672 //'mousedown': this.onEditorEvent,
19673 'mouseup': this.onEditorEvent,
19674 'dblclick': this.onEditorEvent,
19675 'click': this.onEditorEvent,
19676 'keyup': this.onEditorEvent,
19681 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19683 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19684 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19686 this.initialized = true;
19688 this.owner.fireEvent('initialize', this);
19693 onDestroy : function(){
19699 //for (var i =0; i < this.toolbars.length;i++) {
19700 // // fixme - ask toolbars for heights?
19701 // this.toolbars[i].onDestroy();
19704 //this.wrap.dom.innerHTML = '';
19705 //this.wrap.remove();
19710 onFirstFocus : function(){
19712 this.assignDocWin();
19715 this.activated = true;
19718 if(Roo.isGecko){ // prevent silly gecko errors
19720 var s = this.win.getSelection();
19721 if(!s.focusNode || s.focusNode.nodeType != 3){
19722 var r = s.getRangeAt(0);
19723 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19728 this.execCmd('useCSS', true);
19729 this.execCmd('styleWithCSS', false);
19732 this.owner.fireEvent('activate', this);
19736 adjustFont: function(btn){
19737 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19738 //if(Roo.isSafari){ // safari
19741 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19742 if(Roo.isSafari){ // safari
19743 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19744 v = (v < 10) ? 10 : v;
19745 v = (v > 48) ? 48 : v;
19746 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19751 v = Math.max(1, v+adjust);
19753 this.execCmd('FontSize', v );
19756 onEditorEvent : function(e)
19758 this.owner.fireEvent('editorevent', this, e);
19759 // this.updateToolbar();
19760 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19763 insertTag : function(tg)
19765 // could be a bit smarter... -> wrap the current selected tRoo..
19766 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19768 range = this.createRange(this.getSelection());
19769 var wrappingNode = this.doc.createElement(tg.toLowerCase());
19770 wrappingNode.appendChild(range.extractContents());
19771 range.insertNode(wrappingNode);
19778 this.execCmd("formatblock", tg);
19782 insertText : function(txt)
19786 var range = this.createRange();
19787 range.deleteContents();
19788 //alert(Sender.getAttribute('label'));
19790 range.insertNode(this.doc.createTextNode(txt));
19796 * Executes a Midas editor command on the editor document and performs necessary focus and
19797 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19798 * @param {String} cmd The Midas command
19799 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19801 relayCmd : function(cmd, value){
19803 this.execCmd(cmd, value);
19804 this.owner.fireEvent('editorevent', this);
19805 //this.updateToolbar();
19806 this.owner.deferFocus();
19810 * Executes a Midas editor command directly on the editor document.
19811 * For visual commands, you should use {@link #relayCmd} instead.
19812 * <b>This should only be called after the editor is initialized.</b>
19813 * @param {String} cmd The Midas command
19814 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19816 execCmd : function(cmd, value){
19817 this.doc.execCommand(cmd, false, value === undefined ? null : value);
19824 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19826 * @param {String} text | dom node..
19828 insertAtCursor : function(text)
19833 if(!this.activated){
19839 var r = this.doc.selection.createRange();
19850 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19854 // from jquery ui (MIT licenced)
19856 var win = this.win;
19858 if (win.getSelection && win.getSelection().getRangeAt) {
19859 range = win.getSelection().getRangeAt(0);
19860 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19861 range.insertNode(node);
19862 } else if (win.document.selection && win.document.selection.createRange) {
19863 // no firefox support
19864 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19865 win.document.selection.createRange().pasteHTML(txt);
19867 // no firefox support
19868 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19869 this.execCmd('InsertHTML', txt);
19878 mozKeyPress : function(e){
19880 var c = e.getCharCode(), cmd;
19883 c = String.fromCharCode(c).toLowerCase();
19897 this.cleanUpPaste.defer(100, this);
19905 e.preventDefault();
19913 fixKeys : function(){ // load time branching for fastest keydown performance
19915 return function(e){
19916 var k = e.getKey(), r;
19919 r = this.doc.selection.createRange();
19922 r.pasteHTML('    ');
19929 r = this.doc.selection.createRange();
19931 var target = r.parentElement();
19932 if(!target || target.tagName.toLowerCase() != 'li'){
19934 r.pasteHTML('<br />');
19940 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19941 this.cleanUpPaste.defer(100, this);
19947 }else if(Roo.isOpera){
19948 return function(e){
19949 var k = e.getKey();
19953 this.execCmd('InsertHTML','    ');
19956 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19957 this.cleanUpPaste.defer(100, this);
19962 }else if(Roo.isSafari){
19963 return function(e){
19964 var k = e.getKey();
19968 this.execCmd('InsertText','\t');
19972 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19973 this.cleanUpPaste.defer(100, this);
19981 getAllAncestors: function()
19983 var p = this.getSelectedNode();
19986 a.push(p); // push blank onto stack..
19987 p = this.getParentElement();
19991 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19995 a.push(this.doc.body);
19999 lastSelNode : false,
20002 getSelection : function()
20004 this.assignDocWin();
20005 return Roo.isIE ? this.doc.selection : this.win.getSelection();
20008 getSelectedNode: function()
20010 // this may only work on Gecko!!!
20012 // should we cache this!!!!
20017 var range = this.createRange(this.getSelection()).cloneRange();
20020 var parent = range.parentElement();
20022 var testRange = range.duplicate();
20023 testRange.moveToElementText(parent);
20024 if (testRange.inRange(range)) {
20027 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20030 parent = parent.parentElement;
20035 // is ancestor a text element.
20036 var ac = range.commonAncestorContainer;
20037 if (ac.nodeType == 3) {
20038 ac = ac.parentNode;
20041 var ar = ac.childNodes;
20044 var other_nodes = [];
20045 var has_other_nodes = false;
20046 for (var i=0;i<ar.length;i++) {
20047 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
20050 // fullly contained node.
20052 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20057 // probably selected..
20058 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20059 other_nodes.push(ar[i]);
20063 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
20068 has_other_nodes = true;
20070 if (!nodes.length && other_nodes.length) {
20071 nodes= other_nodes;
20073 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20079 createRange: function(sel)
20081 // this has strange effects when using with
20082 // top toolbar - not sure if it's a great idea.
20083 //this.editor.contentWindow.focus();
20084 if (typeof sel != "undefined") {
20086 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20088 return this.doc.createRange();
20091 return this.doc.createRange();
20094 getParentElement: function()
20097 this.assignDocWin();
20098 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20100 var range = this.createRange(sel);
20103 var p = range.commonAncestorContainer;
20104 while (p.nodeType == 3) { // text node
20115 * Range intersection.. the hard stuff...
20119 * [ -- selected range --- ]
20123 * if end is before start or hits it. fail.
20124 * if start is after end or hits it fail.
20126 * if either hits (but other is outside. - then it's not
20132 // @see http://www.thismuchiknow.co.uk/?p=64.
20133 rangeIntersectsNode : function(range, node)
20135 var nodeRange = node.ownerDocument.createRange();
20137 nodeRange.selectNode(node);
20139 nodeRange.selectNodeContents(node);
20142 var rangeStartRange = range.cloneRange();
20143 rangeStartRange.collapse(true);
20145 var rangeEndRange = range.cloneRange();
20146 rangeEndRange.collapse(false);
20148 var nodeStartRange = nodeRange.cloneRange();
20149 nodeStartRange.collapse(true);
20151 var nodeEndRange = nodeRange.cloneRange();
20152 nodeEndRange.collapse(false);
20154 return rangeStartRange.compareBoundaryPoints(
20155 Range.START_TO_START, nodeEndRange) == -1 &&
20156 rangeEndRange.compareBoundaryPoints(
20157 Range.START_TO_START, nodeStartRange) == 1;
20161 rangeCompareNode : function(range, node)
20163 var nodeRange = node.ownerDocument.createRange();
20165 nodeRange.selectNode(node);
20167 nodeRange.selectNodeContents(node);
20171 range.collapse(true);
20173 nodeRange.collapse(true);
20175 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20176 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
20178 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20180 var nodeIsBefore = ss == 1;
20181 var nodeIsAfter = ee == -1;
20183 if (nodeIsBefore && nodeIsAfter) {
20186 if (!nodeIsBefore && nodeIsAfter) {
20187 return 1; //right trailed.
20190 if (nodeIsBefore && !nodeIsAfter) {
20191 return 2; // left trailed.
20197 // private? - in a new class?
20198 cleanUpPaste : function()
20200 // cleans up the whole document..
20201 Roo.log('cleanuppaste');
20203 this.cleanUpChildren(this.doc.body);
20204 var clean = this.cleanWordChars(this.doc.body.innerHTML);
20205 if (clean != this.doc.body.innerHTML) {
20206 this.doc.body.innerHTML = clean;
20211 cleanWordChars : function(input) {// change the chars to hex code
20212 var he = Roo.HtmlEditorCore;
20214 var output = input;
20215 Roo.each(he.swapCodes, function(sw) {
20216 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20218 output = output.replace(swapper, sw[1]);
20225 cleanUpChildren : function (n)
20227 if (!n.childNodes.length) {
20230 for (var i = n.childNodes.length-1; i > -1 ; i--) {
20231 this.cleanUpChild(n.childNodes[i]);
20238 cleanUpChild : function (node)
20241 //console.log(node);
20242 if (node.nodeName == "#text") {
20243 // clean up silly Windows -- stuff?
20246 if (node.nodeName == "#comment") {
20247 node.parentNode.removeChild(node);
20248 // clean up silly Windows -- stuff?
20251 var lcname = node.tagName.toLowerCase();
20252 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20253 // whitelist of tags..
20255 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20257 node.parentNode.removeChild(node);
20262 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20264 // remove <a name=....> as rendering on yahoo mailer is borked with this.
20265 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20267 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20268 // remove_keep_children = true;
20271 if (remove_keep_children) {
20272 this.cleanUpChildren(node);
20273 // inserts everything just before this node...
20274 while (node.childNodes.length) {
20275 var cn = node.childNodes[0];
20276 node.removeChild(cn);
20277 node.parentNode.insertBefore(cn, node);
20279 node.parentNode.removeChild(node);
20283 if (!node.attributes || !node.attributes.length) {
20284 this.cleanUpChildren(node);
20288 function cleanAttr(n,v)
20291 if (v.match(/^\./) || v.match(/^\//)) {
20294 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20297 if (v.match(/^#/)) {
20300 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20301 node.removeAttribute(n);
20305 var cwhite = this.cwhite;
20306 var cblack = this.cblack;
20308 function cleanStyle(n,v)
20310 if (v.match(/expression/)) { //XSS?? should we even bother..
20311 node.removeAttribute(n);
20315 var parts = v.split(/;/);
20318 Roo.each(parts, function(p) {
20319 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20323 var l = p.split(':').shift().replace(/\s+/g,'');
20324 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20326 if ( cwhite.length && cblack.indexOf(l) > -1) {
20327 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20328 //node.removeAttribute(n);
20332 // only allow 'c whitelisted system attributes'
20333 if ( cwhite.length && cwhite.indexOf(l) < 0) {
20334 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20335 //node.removeAttribute(n);
20345 if (clean.length) {
20346 node.setAttribute(n, clean.join(';'));
20348 node.removeAttribute(n);
20354 for (var i = node.attributes.length-1; i > -1 ; i--) {
20355 var a = node.attributes[i];
20358 if (a.name.toLowerCase().substr(0,2)=='on') {
20359 node.removeAttribute(a.name);
20362 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20363 node.removeAttribute(a.name);
20366 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20367 cleanAttr(a.name,a.value); // fixme..
20370 if (a.name == 'style') {
20371 cleanStyle(a.name,a.value);
20374 /// clean up MS crap..
20375 // tecnically this should be a list of valid class'es..
20378 if (a.name == 'class') {
20379 if (a.value.match(/^Mso/)) {
20380 node.className = '';
20383 if (a.value.match(/body/)) {
20384 node.className = '';
20395 this.cleanUpChildren(node);
20401 * Clean up MS wordisms...
20403 cleanWord : function(node)
20408 this.cleanWord(this.doc.body);
20411 if (node.nodeName == "#text") {
20412 // clean up silly Windows -- stuff?
20415 if (node.nodeName == "#comment") {
20416 node.parentNode.removeChild(node);
20417 // clean up silly Windows -- stuff?
20421 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20422 node.parentNode.removeChild(node);
20426 // remove - but keep children..
20427 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20428 while (node.childNodes.length) {
20429 var cn = node.childNodes[0];
20430 node.removeChild(cn);
20431 node.parentNode.insertBefore(cn, node);
20433 node.parentNode.removeChild(node);
20434 this.iterateChildren(node, this.cleanWord);
20438 if (node.className.length) {
20440 var cn = node.className.split(/\W+/);
20442 Roo.each(cn, function(cls) {
20443 if (cls.match(/Mso[a-zA-Z]+/)) {
20448 node.className = cna.length ? cna.join(' ') : '';
20450 node.removeAttribute("class");
20454 if (node.hasAttribute("lang")) {
20455 node.removeAttribute("lang");
20458 if (node.hasAttribute("style")) {
20460 var styles = node.getAttribute("style").split(";");
20462 Roo.each(styles, function(s) {
20463 if (!s.match(/:/)) {
20466 var kv = s.split(":");
20467 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20470 // what ever is left... we allow.
20473 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20474 if (!nstyle.length) {
20475 node.removeAttribute('style');
20478 this.iterateChildren(node, this.cleanWord);
20484 * iterateChildren of a Node, calling fn each time, using this as the scole..
20485 * @param {DomNode} node node to iterate children of.
20486 * @param {Function} fn method of this class to call on each item.
20488 iterateChildren : function(node, fn)
20490 if (!node.childNodes.length) {
20493 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20494 fn.call(this, node.childNodes[i])
20500 * cleanTableWidths.
20502 * Quite often pasting from word etc.. results in tables with column and widths.
20503 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20506 cleanTableWidths : function(node)
20511 this.cleanTableWidths(this.doc.body);
20516 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20519 Roo.log(node.tagName);
20520 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20521 this.iterateChildren(node, this.cleanTableWidths);
20524 if (node.hasAttribute('width')) {
20525 node.removeAttribute('width');
20529 if (node.hasAttribute("style")) {
20532 var styles = node.getAttribute("style").split(";");
20534 Roo.each(styles, function(s) {
20535 if (!s.match(/:/)) {
20538 var kv = s.split(":");
20539 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20542 // what ever is left... we allow.
20545 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20546 if (!nstyle.length) {
20547 node.removeAttribute('style');
20551 this.iterateChildren(node, this.cleanTableWidths);
20559 domToHTML : function(currentElement, depth, nopadtext) {
20561 depth = depth || 0;
20562 nopadtext = nopadtext || false;
20564 if (!currentElement) {
20565 return this.domToHTML(this.doc.body);
20568 //Roo.log(currentElement);
20570 var allText = false;
20571 var nodeName = currentElement.nodeName;
20572 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20574 if (nodeName == '#text') {
20576 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20581 if (nodeName != 'BODY') {
20584 // Prints the node tagName, such as <A>, <IMG>, etc
20587 for(i = 0; i < currentElement.attributes.length;i++) {
20589 var aname = currentElement.attributes.item(i).name;
20590 if (!currentElement.attributes.item(i).value.length) {
20593 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20596 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20605 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20608 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20613 // Traverse the tree
20615 var currentElementChild = currentElement.childNodes.item(i);
20616 var allText = true;
20617 var innerHTML = '';
20619 while (currentElementChild) {
20620 // Formatting code (indent the tree so it looks nice on the screen)
20621 var nopad = nopadtext;
20622 if (lastnode == 'SPAN') {
20626 if (currentElementChild.nodeName == '#text') {
20627 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20628 toadd = nopadtext ? toadd : toadd.trim();
20629 if (!nopad && toadd.length > 80) {
20630 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20632 innerHTML += toadd;
20635 currentElementChild = currentElement.childNodes.item(i);
20641 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20643 // Recursively traverse the tree structure of the child node
20644 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20645 lastnode = currentElementChild.nodeName;
20647 currentElementChild=currentElement.childNodes.item(i);
20653 // The remaining code is mostly for formatting the tree
20654 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20659 ret+= "</"+tagName+">";
20665 applyBlacklists : function()
20667 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20668 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20672 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20673 if (b.indexOf(tag) > -1) {
20676 this.white.push(tag);
20680 Roo.each(w, function(tag) {
20681 if (b.indexOf(tag) > -1) {
20684 if (this.white.indexOf(tag) > -1) {
20687 this.white.push(tag);
20692 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20693 if (w.indexOf(tag) > -1) {
20696 this.black.push(tag);
20700 Roo.each(b, function(tag) {
20701 if (w.indexOf(tag) > -1) {
20704 if (this.black.indexOf(tag) > -1) {
20707 this.black.push(tag);
20712 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20713 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20717 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20718 if (b.indexOf(tag) > -1) {
20721 this.cwhite.push(tag);
20725 Roo.each(w, function(tag) {
20726 if (b.indexOf(tag) > -1) {
20729 if (this.cwhite.indexOf(tag) > -1) {
20732 this.cwhite.push(tag);
20737 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20738 if (w.indexOf(tag) > -1) {
20741 this.cblack.push(tag);
20745 Roo.each(b, function(tag) {
20746 if (w.indexOf(tag) > -1) {
20749 if (this.cblack.indexOf(tag) > -1) {
20752 this.cblack.push(tag);
20757 setStylesheets : function(stylesheets)
20759 if(typeof(stylesheets) == 'string'){
20760 Roo.get(this.iframe.contentDocument.head).createChild({
20762 rel : 'stylesheet',
20771 Roo.each(stylesheets, function(s) {
20776 Roo.get(_this.iframe.contentDocument.head).createChild({
20778 rel : 'stylesheet',
20787 removeStylesheets : function()
20791 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20796 // hide stuff that is not compatible
20810 * @event specialkey
20814 * @cfg {String} fieldClass @hide
20817 * @cfg {String} focusClass @hide
20820 * @cfg {String} autoCreate @hide
20823 * @cfg {String} inputType @hide
20826 * @cfg {String} invalidClass @hide
20829 * @cfg {String} invalidText @hide
20832 * @cfg {String} msgFx @hide
20835 * @cfg {String} validateOnBlur @hide
20839 Roo.HtmlEditorCore.white = [
20840 'area', 'br', 'img', 'input', 'hr', 'wbr',
20842 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
20843 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
20844 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
20845 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
20846 'table', 'ul', 'xmp',
20848 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
20851 'dir', 'menu', 'ol', 'ul', 'dl',
20857 Roo.HtmlEditorCore.black = [
20858 // 'embed', 'object', // enable - backend responsiblity to clean thiese
20860 'base', 'basefont', 'bgsound', 'blink', 'body',
20861 'frame', 'frameset', 'head', 'html', 'ilayer',
20862 'iframe', 'layer', 'link', 'meta', 'object',
20863 'script', 'style' ,'title', 'xml' // clean later..
20865 Roo.HtmlEditorCore.clean = [
20866 'script', 'style', 'title', 'xml'
20868 Roo.HtmlEditorCore.remove = [
20873 Roo.HtmlEditorCore.ablack = [
20877 Roo.HtmlEditorCore.aclean = [
20878 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
20882 Roo.HtmlEditorCore.pwhite= [
20883 'http', 'https', 'mailto'
20886 // white listed style attributes.
20887 Roo.HtmlEditorCore.cwhite= [
20888 // 'text-align', /// default is to allow most things..
20894 // black listed style attributes.
20895 Roo.HtmlEditorCore.cblack= [
20896 // 'font-size' -- this can be set by the project
20900 Roo.HtmlEditorCore.swapCodes =[
20919 * @class Roo.bootstrap.HtmlEditor
20920 * @extends Roo.bootstrap.TextArea
20921 * Bootstrap HtmlEditor class
20924 * Create a new HtmlEditor
20925 * @param {Object} config The config object
20928 Roo.bootstrap.HtmlEditor = function(config){
20929 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20930 if (!this.toolbars) {
20931 this.toolbars = [];
20933 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20936 * @event initialize
20937 * Fires when the editor is fully initialized (including the iframe)
20938 * @param {HtmlEditor} this
20943 * Fires when the editor is first receives the focus. Any insertion must wait
20944 * until after this event.
20945 * @param {HtmlEditor} this
20949 * @event beforesync
20950 * Fires before the textarea is updated with content from the editor iframe. Return false
20951 * to cancel the sync.
20952 * @param {HtmlEditor} this
20953 * @param {String} html
20957 * @event beforepush
20958 * Fires before the iframe editor is updated with content from the textarea. Return false
20959 * to cancel the push.
20960 * @param {HtmlEditor} this
20961 * @param {String} html
20966 * Fires when the textarea is updated with content from the editor iframe.
20967 * @param {HtmlEditor} this
20968 * @param {String} html
20973 * Fires when the iframe editor is updated with content from the textarea.
20974 * @param {HtmlEditor} this
20975 * @param {String} html
20979 * @event editmodechange
20980 * Fires when the editor switches edit modes
20981 * @param {HtmlEditor} this
20982 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20984 editmodechange: true,
20986 * @event editorevent
20987 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20988 * @param {HtmlEditor} this
20992 * @event firstfocus
20993 * Fires when on first focus - needed by toolbars..
20994 * @param {HtmlEditor} this
20999 * Auto save the htmlEditor value as a file into Events
21000 * @param {HtmlEditor} this
21004 * @event savedpreview
21005 * preview the saved version of htmlEditor
21006 * @param {HtmlEditor} this
21013 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
21017 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21022 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21027 * @cfg {Number} height (in pixels)
21031 * @cfg {Number} width (in pixels)
21036 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21039 stylesheets: false,
21044 // private properties
21045 validationEvent : false,
21047 initialized : false,
21050 onFocus : Roo.emptyFn,
21052 hideMode:'offsets',
21055 tbContainer : false,
21057 toolbarContainer :function() {
21058 return this.wrap.select('.x-html-editor-tb',true).first();
21062 * Protected method that will not generally be called directly. It
21063 * is called when the editor creates its toolbar. Override this method if you need to
21064 * add custom toolbar buttons.
21065 * @param {HtmlEditor} editor
21067 createToolbar : function(){
21069 Roo.log("create toolbars");
21071 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21072 this.toolbars[0].render(this.toolbarContainer());
21076 // if (!editor.toolbars || !editor.toolbars.length) {
21077 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21080 // for (var i =0 ; i < editor.toolbars.length;i++) {
21081 // editor.toolbars[i] = Roo.factory(
21082 // typeof(editor.toolbars[i]) == 'string' ?
21083 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
21084 // Roo.bootstrap.HtmlEditor);
21085 // editor.toolbars[i].init(editor);
21091 onRender : function(ct, position)
21093 // Roo.log("Call onRender: " + this.xtype);
21095 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21097 this.wrap = this.inputEl().wrap({
21098 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21101 this.editorcore.onRender(ct, position);
21103 if (this.resizable) {
21104 this.resizeEl = new Roo.Resizable(this.wrap, {
21108 minHeight : this.height,
21109 height: this.height,
21110 handles : this.resizable,
21113 resize : function(r, w, h) {
21114 _t.onResize(w,h); // -something
21120 this.createToolbar(this);
21123 if(!this.width && this.resizable){
21124 this.setSize(this.wrap.getSize());
21126 if (this.resizeEl) {
21127 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21128 // should trigger onReize..
21134 onResize : function(w, h)
21136 Roo.log('resize: ' +w + ',' + h );
21137 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21141 if(this.inputEl() ){
21142 if(typeof w == 'number'){
21143 var aw = w - this.wrap.getFrameWidth('lr');
21144 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21147 if(typeof h == 'number'){
21148 var tbh = -11; // fixme it needs to tool bar size!
21149 for (var i =0; i < this.toolbars.length;i++) {
21150 // fixme - ask toolbars for heights?
21151 tbh += this.toolbars[i].el.getHeight();
21152 //if (this.toolbars[i].footer) {
21153 // tbh += this.toolbars[i].footer.el.getHeight();
21161 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21162 ah -= 5; // knock a few pixes off for look..
21163 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21167 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21168 this.editorcore.onResize(ew,eh);
21173 * Toggles the editor between standard and source edit mode.
21174 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21176 toggleSourceEdit : function(sourceEditMode)
21178 this.editorcore.toggleSourceEdit(sourceEditMode);
21180 if(this.editorcore.sourceEditMode){
21181 Roo.log('editor - showing textarea');
21184 // Roo.log(this.syncValue());
21186 this.inputEl().removeClass(['hide', 'x-hidden']);
21187 this.inputEl().dom.removeAttribute('tabIndex');
21188 this.inputEl().focus();
21190 Roo.log('editor - hiding textarea');
21192 // Roo.log(this.pushValue());
21195 this.inputEl().addClass(['hide', 'x-hidden']);
21196 this.inputEl().dom.setAttribute('tabIndex', -1);
21197 //this.deferFocus();
21200 if(this.resizable){
21201 this.setSize(this.wrap.getSize());
21204 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21207 // private (for BoxComponent)
21208 adjustSize : Roo.BoxComponent.prototype.adjustSize,
21210 // private (for BoxComponent)
21211 getResizeEl : function(){
21215 // private (for BoxComponent)
21216 getPositionEl : function(){
21221 initEvents : function(){
21222 this.originalValue = this.getValue();
21226 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21229 // markInvalid : Roo.emptyFn,
21231 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21234 // clearInvalid : Roo.emptyFn,
21236 setValue : function(v){
21237 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21238 this.editorcore.pushValue();
21243 deferFocus : function(){
21244 this.focus.defer(10, this);
21248 focus : function(){
21249 this.editorcore.focus();
21255 onDestroy : function(){
21261 for (var i =0; i < this.toolbars.length;i++) {
21262 // fixme - ask toolbars for heights?
21263 this.toolbars[i].onDestroy();
21266 this.wrap.dom.innerHTML = '';
21267 this.wrap.remove();
21272 onFirstFocus : function(){
21273 //Roo.log("onFirstFocus");
21274 this.editorcore.onFirstFocus();
21275 for (var i =0; i < this.toolbars.length;i++) {
21276 this.toolbars[i].onFirstFocus();
21282 syncValue : function()
21284 this.editorcore.syncValue();
21287 pushValue : function()
21289 this.editorcore.pushValue();
21293 // hide stuff that is not compatible
21307 * @event specialkey
21311 * @cfg {String} fieldClass @hide
21314 * @cfg {String} focusClass @hide
21317 * @cfg {String} autoCreate @hide
21320 * @cfg {String} inputType @hide
21323 * @cfg {String} invalidClass @hide
21326 * @cfg {String} invalidText @hide
21329 * @cfg {String} msgFx @hide
21332 * @cfg {String} validateOnBlur @hide
21341 Roo.namespace('Roo.bootstrap.htmleditor');
21343 * @class Roo.bootstrap.HtmlEditorToolbar1
21348 new Roo.bootstrap.HtmlEditor({
21351 new Roo.bootstrap.HtmlEditorToolbar1({
21352 disable : { fonts: 1 , format: 1, ..., ... , ...],
21358 * @cfg {Object} disable List of elements to disable..
21359 * @cfg {Array} btns List of additional buttons.
21363 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21366 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21369 Roo.apply(this, config);
21371 // default disabled, based on 'good practice'..
21372 this.disable = this.disable || {};
21373 Roo.applyIf(this.disable, {
21376 specialElements : true
21378 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21380 this.editor = config.editor;
21381 this.editorcore = config.editor.editorcore;
21383 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21385 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21386 // dont call parent... till later.
21388 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
21393 editorcore : false,
21398 "h1","h2","h3","h4","h5","h6",
21400 "abbr", "acronym", "address", "cite", "samp", "var",
21404 onRender : function(ct, position)
21406 // Roo.log("Call onRender: " + this.xtype);
21408 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21410 this.el.dom.style.marginBottom = '0';
21412 var editorcore = this.editorcore;
21413 var editor= this.editor;
21416 var btn = function(id,cmd , toggle, handler){
21418 var event = toggle ? 'toggle' : 'click';
21423 xns: Roo.bootstrap,
21426 enableToggle:toggle !== false,
21428 pressed : toggle ? false : null,
21431 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21432 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21441 xns: Roo.bootstrap,
21442 glyphicon : 'font',
21446 xns: Roo.bootstrap,
21450 Roo.each(this.formats, function(f) {
21451 style.menu.items.push({
21453 xns: Roo.bootstrap,
21454 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21459 editorcore.insertTag(this.tagname);
21466 children.push(style);
21469 btn('bold',false,true);
21470 btn('italic',false,true);
21471 btn('align-left', 'justifyleft',true);
21472 btn('align-center', 'justifycenter',true);
21473 btn('align-right' , 'justifyright',true);
21474 btn('link', false, false, function(btn) {
21475 //Roo.log("create link?");
21476 var url = prompt(this.createLinkText, this.defaultLinkValue);
21477 if(url && url != 'http:/'+'/'){
21478 this.editorcore.relayCmd('createlink', url);
21481 btn('list','insertunorderedlist',true);
21482 btn('pencil', false,true, function(btn){
21485 this.toggleSourceEdit(btn.pressed);
21491 xns: Roo.bootstrap,
21496 xns: Roo.bootstrap,
21501 cog.menu.items.push({
21503 xns: Roo.bootstrap,
21504 html : Clean styles,
21509 editorcore.insertTag(this.tagname);
21518 this.xtype = 'NavSimplebar';
21520 for(var i=0;i< children.length;i++) {
21522 this.buttons.add(this.addxtypeChild(children[i]));
21526 editor.on('editorevent', this.updateToolbar, this);
21528 onBtnClick : function(id)
21530 this.editorcore.relayCmd(id);
21531 this.editorcore.focus();
21535 * Protected method that will not generally be called directly. It triggers
21536 * a toolbar update by reading the markup state of the current selection in the editor.
21538 updateToolbar: function(){
21540 if(!this.editorcore.activated){
21541 this.editor.onFirstFocus(); // is this neeed?
21545 var btns = this.buttons;
21546 var doc = this.editorcore.doc;
21547 btns.get('bold').setActive(doc.queryCommandState('bold'));
21548 btns.get('italic').setActive(doc.queryCommandState('italic'));
21549 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21551 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21552 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21553 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21555 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21556 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21559 var ans = this.editorcore.getAllAncestors();
21560 if (this.formatCombo) {
21563 var store = this.formatCombo.store;
21564 this.formatCombo.setValue("");
21565 for (var i =0; i < ans.length;i++) {
21566 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21568 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21576 // hides menus... - so this cant be on a menu...
21577 Roo.bootstrap.MenuMgr.hideAll();
21579 Roo.bootstrap.MenuMgr.hideAll();
21580 //this.editorsyncValue();
21582 onFirstFocus: function() {
21583 this.buttons.each(function(item){
21587 toggleSourceEdit : function(sourceEditMode){
21590 if(sourceEditMode){
21591 Roo.log("disabling buttons");
21592 this.buttons.each( function(item){
21593 if(item.cmd != 'pencil'){
21599 Roo.log("enabling buttons");
21600 if(this.editorcore.initialized){
21601 this.buttons.each( function(item){
21607 Roo.log("calling toggole on editor");
21608 // tell the editor that it's been pressed..
21609 this.editor.toggleSourceEdit(sourceEditMode);
21619 * @class Roo.bootstrap.Table.AbstractSelectionModel
21620 * @extends Roo.util.Observable
21621 * Abstract base class for grid SelectionModels. It provides the interface that should be
21622 * implemented by descendant classes. This class should not be directly instantiated.
21625 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21626 this.locked = false;
21627 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21631 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21632 /** @ignore Called by the grid automatically. Do not call directly. */
21633 init : function(grid){
21639 * Locks the selections.
21642 this.locked = true;
21646 * Unlocks the selections.
21648 unlock : function(){
21649 this.locked = false;
21653 * Returns true if the selections are locked.
21654 * @return {Boolean}
21656 isLocked : function(){
21657 return this.locked;
21661 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21662 * @class Roo.bootstrap.Table.RowSelectionModel
21663 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21664 * It supports multiple selections and keyboard selection/navigation.
21666 * @param {Object} config
21669 Roo.bootstrap.Table.RowSelectionModel = function(config){
21670 Roo.apply(this, config);
21671 this.selections = new Roo.util.MixedCollection(false, function(o){
21676 this.lastActive = false;
21680 * @event selectionchange
21681 * Fires when the selection changes
21682 * @param {SelectionModel} this
21684 "selectionchange" : true,
21686 * @event afterselectionchange
21687 * Fires after the selection changes (eg. by key press or clicking)
21688 * @param {SelectionModel} this
21690 "afterselectionchange" : true,
21692 * @event beforerowselect
21693 * Fires when a row is selected being selected, return false to cancel.
21694 * @param {SelectionModel} this
21695 * @param {Number} rowIndex The selected index
21696 * @param {Boolean} keepExisting False if other selections will be cleared
21698 "beforerowselect" : true,
21701 * Fires when a row is selected.
21702 * @param {SelectionModel} this
21703 * @param {Number} rowIndex The selected index
21704 * @param {Roo.data.Record} r The record
21706 "rowselect" : true,
21708 * @event rowdeselect
21709 * Fires when a row is deselected.
21710 * @param {SelectionModel} this
21711 * @param {Number} rowIndex The selected index
21713 "rowdeselect" : true
21715 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21716 this.locked = false;
21719 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21721 * @cfg {Boolean} singleSelect
21722 * True to allow selection of only one row at a time (defaults to false)
21724 singleSelect : false,
21727 initEvents : function(){
21729 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21730 this.grid.on("mousedown", this.handleMouseDown, this);
21731 }else{ // allow click to work like normal
21732 this.grid.on("rowclick", this.handleDragableRowClick, this);
21735 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21736 "up" : function(e){
21738 this.selectPrevious(e.shiftKey);
21739 }else if(this.last !== false && this.lastActive !== false){
21740 var last = this.last;
21741 this.selectRange(this.last, this.lastActive-1);
21742 this.grid.getView().focusRow(this.lastActive);
21743 if(last !== false){
21747 this.selectFirstRow();
21749 this.fireEvent("afterselectionchange", this);
21751 "down" : function(e){
21753 this.selectNext(e.shiftKey);
21754 }else if(this.last !== false && this.lastActive !== false){
21755 var last = this.last;
21756 this.selectRange(this.last, this.lastActive+1);
21757 this.grid.getView().focusRow(this.lastActive);
21758 if(last !== false){
21762 this.selectFirstRow();
21764 this.fireEvent("afterselectionchange", this);
21769 var view = this.grid.view;
21770 view.on("refresh", this.onRefresh, this);
21771 view.on("rowupdated", this.onRowUpdated, this);
21772 view.on("rowremoved", this.onRemove, this);
21776 onRefresh : function(){
21777 var ds = this.grid.dataSource, i, v = this.grid.view;
21778 var s = this.selections;
21779 s.each(function(r){
21780 if((i = ds.indexOfId(r.id)) != -1){
21789 onRemove : function(v, index, r){
21790 this.selections.remove(r);
21794 onRowUpdated : function(v, index, r){
21795 if(this.isSelected(r)){
21796 v.onRowSelect(index);
21802 * @param {Array} records The records to select
21803 * @param {Boolean} keepExisting (optional) True to keep existing selections
21805 selectRecords : function(records, keepExisting){
21807 this.clearSelections();
21809 var ds = this.grid.dataSource;
21810 for(var i = 0, len = records.length; i < len; i++){
21811 this.selectRow(ds.indexOf(records[i]), true);
21816 * Gets the number of selected rows.
21819 getCount : function(){
21820 return this.selections.length;
21824 * Selects the first row in the grid.
21826 selectFirstRow : function(){
21831 * Select the last row.
21832 * @param {Boolean} keepExisting (optional) True to keep existing selections
21834 selectLastRow : function(keepExisting){
21835 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21839 * Selects the row immediately following the last selected row.
21840 * @param {Boolean} keepExisting (optional) True to keep existing selections
21842 selectNext : function(keepExisting){
21843 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21844 this.selectRow(this.last+1, keepExisting);
21845 this.grid.getView().focusRow(this.last);
21850 * Selects the row that precedes the last selected row.
21851 * @param {Boolean} keepExisting (optional) True to keep existing selections
21853 selectPrevious : function(keepExisting){
21855 this.selectRow(this.last-1, keepExisting);
21856 this.grid.getView().focusRow(this.last);
21861 * Returns the selected records
21862 * @return {Array} Array of selected records
21864 getSelections : function(){
21865 return [].concat(this.selections.items);
21869 * Returns the first selected record.
21872 getSelected : function(){
21873 return this.selections.itemAt(0);
21878 * Clears all selections.
21880 clearSelections : function(fast){
21885 var ds = this.grid.dataSource;
21886 var s = this.selections;
21887 s.each(function(r){
21888 this.deselectRow(ds.indexOfId(r.id));
21892 this.selections.clear();
21899 * Selects all rows.
21901 selectAll : function(){
21905 this.selections.clear();
21906 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21907 this.selectRow(i, true);
21912 * Returns True if there is a selection.
21913 * @return {Boolean}
21915 hasSelection : function(){
21916 return this.selections.length > 0;
21920 * Returns True if the specified row is selected.
21921 * @param {Number/Record} record The record or index of the record to check
21922 * @return {Boolean}
21924 isSelected : function(index){
21925 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21926 return (r && this.selections.key(r.id) ? true : false);
21930 * Returns True if the specified record id is selected.
21931 * @param {String} id The id of record to check
21932 * @return {Boolean}
21934 isIdSelected : function(id){
21935 return (this.selections.key(id) ? true : false);
21939 handleMouseDown : function(e, t){
21940 var view = this.grid.getView(), rowIndex;
21941 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21944 if(e.shiftKey && this.last !== false){
21945 var last = this.last;
21946 this.selectRange(last, rowIndex, e.ctrlKey);
21947 this.last = last; // reset the last
21948 view.focusRow(rowIndex);
21950 var isSelected = this.isSelected(rowIndex);
21951 if(e.button !== 0 && isSelected){
21952 view.focusRow(rowIndex);
21953 }else if(e.ctrlKey && isSelected){
21954 this.deselectRow(rowIndex);
21955 }else if(!isSelected){
21956 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21957 view.focusRow(rowIndex);
21960 this.fireEvent("afterselectionchange", this);
21963 handleDragableRowClick : function(grid, rowIndex, e)
21965 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21966 this.selectRow(rowIndex, false);
21967 grid.view.focusRow(rowIndex);
21968 this.fireEvent("afterselectionchange", this);
21973 * Selects multiple rows.
21974 * @param {Array} rows Array of the indexes of the row to select
21975 * @param {Boolean} keepExisting (optional) True to keep existing selections
21977 selectRows : function(rows, keepExisting){
21979 this.clearSelections();
21981 for(var i = 0, len = rows.length; i < len; i++){
21982 this.selectRow(rows[i], true);
21987 * Selects a range of rows. All rows in between startRow and endRow are also selected.
21988 * @param {Number} startRow The index of the first row in the range
21989 * @param {Number} endRow The index of the last row in the range
21990 * @param {Boolean} keepExisting (optional) True to retain existing selections
21992 selectRange : function(startRow, endRow, keepExisting){
21997 this.clearSelections();
21999 if(startRow <= endRow){
22000 for(var i = startRow; i <= endRow; i++){
22001 this.selectRow(i, true);
22004 for(var i = startRow; i >= endRow; i--){
22005 this.selectRow(i, true);
22011 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22012 * @param {Number} startRow The index of the first row in the range
22013 * @param {Number} endRow The index of the last row in the range
22015 deselectRange : function(startRow, endRow, preventViewNotify){
22019 for(var i = startRow; i <= endRow; i++){
22020 this.deselectRow(i, preventViewNotify);
22026 * @param {Number} row The index of the row to select
22027 * @param {Boolean} keepExisting (optional) True to keep existing selections
22029 selectRow : function(index, keepExisting, preventViewNotify){
22030 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22033 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22034 if(!keepExisting || this.singleSelect){
22035 this.clearSelections();
22037 var r = this.grid.dataSource.getAt(index);
22038 this.selections.add(r);
22039 this.last = this.lastActive = index;
22040 if(!preventViewNotify){
22041 this.grid.getView().onRowSelect(index);
22043 this.fireEvent("rowselect", this, index, r);
22044 this.fireEvent("selectionchange", this);
22050 * @param {Number} row The index of the row to deselect
22052 deselectRow : function(index, preventViewNotify){
22056 if(this.last == index){
22059 if(this.lastActive == index){
22060 this.lastActive = false;
22062 var r = this.grid.dataSource.getAt(index);
22063 this.selections.remove(r);
22064 if(!preventViewNotify){
22065 this.grid.getView().onRowDeselect(index);
22067 this.fireEvent("rowdeselect", this, index);
22068 this.fireEvent("selectionchange", this);
22072 restoreLast : function(){
22074 this.last = this._last;
22079 acceptsNav : function(row, col, cm){
22080 return !cm.isHidden(col) && cm.isCellEditable(col, row);
22084 onEditorKey : function(field, e){
22085 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22090 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22092 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22094 }else if(k == e.ENTER && !e.ctrlKey){
22098 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22100 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22102 }else if(k == e.ESC){
22106 g.startEditing(newCell[0], newCell[1]);
22111 * Ext JS Library 1.1.1
22112 * Copyright(c) 2006-2007, Ext JS, LLC.
22114 * Originally Released Under LGPL - original licence link has changed is not relivant.
22117 * <script type="text/javascript">
22121 * @class Roo.bootstrap.PagingToolbar
22122 * @extends Roo.bootstrap.NavSimplebar
22123 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22125 * Create a new PagingToolbar
22126 * @param {Object} config The config object
22127 * @param {Roo.data.Store} store
22129 Roo.bootstrap.PagingToolbar = function(config)
22131 // old args format still supported... - xtype is prefered..
22132 // created from xtype...
22134 this.ds = config.dataSource;
22136 if (config.store && !this.ds) {
22137 this.store= Roo.factory(config.store, Roo.data);
22138 this.ds = this.store;
22139 this.ds.xmodule = this.xmodule || false;
22142 this.toolbarItems = [];
22143 if (config.items) {
22144 this.toolbarItems = config.items;
22147 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22152 this.bind(this.ds);
22155 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22159 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22161 * @cfg {Roo.data.Store} dataSource
22162 * The underlying data store providing the paged data
22165 * @cfg {String/HTMLElement/Element} container
22166 * container The id or element that will contain the toolbar
22169 * @cfg {Boolean} displayInfo
22170 * True to display the displayMsg (defaults to false)
22173 * @cfg {Number} pageSize
22174 * The number of records to display per page (defaults to 20)
22178 * @cfg {String} displayMsg
22179 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22181 displayMsg : 'Displaying {0} - {1} of {2}',
22183 * @cfg {String} emptyMsg
22184 * The message to display when no records are found (defaults to "No data to display")
22186 emptyMsg : 'No data to display',
22188 * Customizable piece of the default paging text (defaults to "Page")
22191 beforePageText : "Page",
22193 * Customizable piece of the default paging text (defaults to "of %0")
22196 afterPageText : "of {0}",
22198 * Customizable piece of the default paging text (defaults to "First Page")
22201 firstText : "First Page",
22203 * Customizable piece of the default paging text (defaults to "Previous Page")
22206 prevText : "Previous Page",
22208 * Customizable piece of the default paging text (defaults to "Next Page")
22211 nextText : "Next Page",
22213 * Customizable piece of the default paging text (defaults to "Last Page")
22216 lastText : "Last Page",
22218 * Customizable piece of the default paging text (defaults to "Refresh")
22221 refreshText : "Refresh",
22225 onRender : function(ct, position)
22227 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22228 this.navgroup.parentId = this.id;
22229 this.navgroup.onRender(this.el, null);
22230 // add the buttons to the navgroup
22232 if(this.displayInfo){
22233 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22234 this.displayEl = this.el.select('.x-paging-info', true).first();
22235 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22236 // this.displayEl = navel.el.select('span',true).first();
22242 Roo.each(_this.buttons, function(e){ // this might need to use render????
22243 Roo.factory(e).onRender(_this.el, null);
22247 Roo.each(_this.toolbarItems, function(e) {
22248 _this.navgroup.addItem(e);
22252 this.first = this.navgroup.addItem({
22253 tooltip: this.firstText,
22255 icon : 'fa fa-backward',
22257 preventDefault: true,
22258 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22261 this.prev = this.navgroup.addItem({
22262 tooltip: this.prevText,
22264 icon : 'fa fa-step-backward',
22266 preventDefault: true,
22267 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
22269 //this.addSeparator();
22272 var field = this.navgroup.addItem( {
22274 cls : 'x-paging-position',
22276 html : this.beforePageText +
22277 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22278 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
22281 this.field = field.el.select('input', true).first();
22282 this.field.on("keydown", this.onPagingKeydown, this);
22283 this.field.on("focus", function(){this.dom.select();});
22286 this.afterTextEl = field.el.select('.x-paging-after',true).first();
22287 //this.field.setHeight(18);
22288 //this.addSeparator();
22289 this.next = this.navgroup.addItem({
22290 tooltip: this.nextText,
22292 html : ' <i class="fa fa-step-forward">',
22294 preventDefault: true,
22295 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
22297 this.last = this.navgroup.addItem({
22298 tooltip: this.lastText,
22299 icon : 'fa fa-forward',
22302 preventDefault: true,
22303 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
22305 //this.addSeparator();
22306 this.loading = this.navgroup.addItem({
22307 tooltip: this.refreshText,
22308 icon: 'fa fa-refresh',
22309 preventDefault: true,
22310 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22316 updateInfo : function(){
22317 if(this.displayEl){
22318 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22319 var msg = count == 0 ?
22323 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
22325 this.displayEl.update(msg);
22330 onLoad : function(ds, r, o){
22331 this.cursor = o.params ? o.params.start : 0;
22332 var d = this.getPageData(),
22336 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22337 this.field.dom.value = ap;
22338 this.first.setDisabled(ap == 1);
22339 this.prev.setDisabled(ap == 1);
22340 this.next.setDisabled(ap == ps);
22341 this.last.setDisabled(ap == ps);
22342 this.loading.enable();
22347 getPageData : function(){
22348 var total = this.ds.getTotalCount();
22351 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22352 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22357 onLoadError : function(){
22358 this.loading.enable();
22362 onPagingKeydown : function(e){
22363 var k = e.getKey();
22364 var d = this.getPageData();
22366 var v = this.field.dom.value, pageNum;
22367 if(!v || isNaN(pageNum = parseInt(v, 10))){
22368 this.field.dom.value = d.activePage;
22371 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22372 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22375 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))
22377 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22378 this.field.dom.value = pageNum;
22379 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22382 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22384 var v = this.field.dom.value, pageNum;
22385 var increment = (e.shiftKey) ? 10 : 1;
22386 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22389 if(!v || isNaN(pageNum = parseInt(v, 10))) {
22390 this.field.dom.value = d.activePage;
22393 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22395 this.field.dom.value = parseInt(v, 10) + increment;
22396 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22397 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22404 beforeLoad : function(){
22406 this.loading.disable();
22411 onClick : function(which){
22420 ds.load({params:{start: 0, limit: this.pageSize}});
22423 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22426 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22429 var total = ds.getTotalCount();
22430 var extra = total % this.pageSize;
22431 var lastStart = extra ? (total - extra) : total-this.pageSize;
22432 ds.load({params:{start: lastStart, limit: this.pageSize}});
22435 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22441 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22442 * @param {Roo.data.Store} store The data store to unbind
22444 unbind : function(ds){
22445 ds.un("beforeload", this.beforeLoad, this);
22446 ds.un("load", this.onLoad, this);
22447 ds.un("loadexception", this.onLoadError, this);
22448 ds.un("remove", this.updateInfo, this);
22449 ds.un("add", this.updateInfo, this);
22450 this.ds = undefined;
22454 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22455 * @param {Roo.data.Store} store The data store to bind
22457 bind : function(ds){
22458 ds.on("beforeload", this.beforeLoad, this);
22459 ds.on("load", this.onLoad, this);
22460 ds.on("loadexception", this.onLoadError, this);
22461 ds.on("remove", this.updateInfo, this);
22462 ds.on("add", this.updateInfo, this);
22473 * @class Roo.bootstrap.MessageBar
22474 * @extends Roo.bootstrap.Component
22475 * Bootstrap MessageBar class
22476 * @cfg {String} html contents of the MessageBar
22477 * @cfg {String} weight (info | success | warning | danger) default info
22478 * @cfg {String} beforeClass insert the bar before the given class
22479 * @cfg {Boolean} closable (true | false) default false
22480 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22483 * Create a new Element
22484 * @param {Object} config The config object
22487 Roo.bootstrap.MessageBar = function(config){
22488 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22491 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22497 beforeClass: 'bootstrap-sticky-wrap',
22499 getAutoCreate : function(){
22503 cls: 'alert alert-dismissable alert-' + this.weight,
22508 html: this.html || ''
22514 cfg.cls += ' alert-messages-fixed';
22528 onRender : function(ct, position)
22530 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22533 var cfg = Roo.apply({}, this.getAutoCreate());
22537 cfg.cls += ' ' + this.cls;
22540 cfg.style = this.style;
22542 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22544 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22547 this.el.select('>button.close').on('click', this.hide, this);
22553 if (!this.rendered) {
22559 this.fireEvent('show', this);
22565 if (!this.rendered) {
22571 this.fireEvent('hide', this);
22574 update : function()
22576 // var e = this.el.dom.firstChild;
22578 // if(this.closable){
22579 // e = e.nextSibling;
22582 // e.data = this.html || '';
22584 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22600 * @class Roo.bootstrap.Graph
22601 * @extends Roo.bootstrap.Component
22602 * Bootstrap Graph class
22606 @cfg {String} graphtype bar | vbar | pie
22607 @cfg {number} g_x coodinator | centre x (pie)
22608 @cfg {number} g_y coodinator | centre y (pie)
22609 @cfg {number} g_r radius (pie)
22610 @cfg {number} g_height height of the chart (respected by all elements in the set)
22611 @cfg {number} g_width width of the chart (respected by all elements in the set)
22612 @cfg {Object} title The title of the chart
22615 -opts (object) options for the chart
22617 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22618 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22620 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.
22621 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22623 o stretch (boolean)
22625 -opts (object) options for the pie
22628 o startAngle (number)
22629 o endAngle (number)
22633 * Create a new Input
22634 * @param {Object} config The config object
22637 Roo.bootstrap.Graph = function(config){
22638 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22644 * The img click event for the img.
22645 * @param {Roo.EventObject} e
22651 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22662 //g_colors: this.colors,
22669 getAutoCreate : function(){
22680 onRender : function(ct,position){
22681 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22682 this.raphael = Raphael(this.el.dom);
22684 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22685 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22686 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22687 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22689 r.text(160, 10, "Single Series Chart").attr(txtattr);
22690 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22691 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22692 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22694 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22695 r.barchart(330, 10, 300, 220, data1);
22696 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22697 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22700 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22701 // r.barchart(30, 30, 560, 250, xdata, {
22702 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22703 // axis : "0 0 1 1",
22704 // axisxlabels : xdata
22705 // //yvalues : cols,
22708 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22710 // this.load(null,xdata,{
22711 // axis : "0 0 1 1",
22712 // axisxlabels : xdata
22717 load : function(graphtype,xdata,opts){
22718 this.raphael.clear();
22720 graphtype = this.graphtype;
22725 var r = this.raphael,
22726 fin = function () {
22727 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22729 fout = function () {
22730 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22732 pfin = function() {
22733 this.sector.stop();
22734 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22737 this.label[0].stop();
22738 this.label[0].attr({ r: 7.5 });
22739 this.label[1].attr({ "font-weight": 800 });
22742 pfout = function() {
22743 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22746 this.label[0].animate({ r: 5 }, 500, "bounce");
22747 this.label[1].attr({ "font-weight": 400 });
22753 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22756 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22759 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
22760 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22762 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22769 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22774 setTitle: function(o)
22779 initEvents: function() {
22782 this.el.on('click', this.onClick, this);
22786 onClick : function(e)
22788 Roo.log('img onclick');
22789 this.fireEvent('click', this, e);
22801 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22804 * @class Roo.bootstrap.dash.NumberBox
22805 * @extends Roo.bootstrap.Component
22806 * Bootstrap NumberBox class
22807 * @cfg {String} headline Box headline
22808 * @cfg {String} content Box content
22809 * @cfg {String} icon Box icon
22810 * @cfg {String} footer Footer text
22811 * @cfg {String} fhref Footer href
22814 * Create a new NumberBox
22815 * @param {Object} config The config object
22819 Roo.bootstrap.dash.NumberBox = function(config){
22820 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22824 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
22833 getAutoCreate : function(){
22837 cls : 'small-box ',
22845 cls : 'roo-headline',
22846 html : this.headline
22850 cls : 'roo-content',
22851 html : this.content
22865 cls : 'ion ' + this.icon
22874 cls : 'small-box-footer',
22875 href : this.fhref || '#',
22879 cfg.cn.push(footer);
22886 onRender : function(ct,position){
22887 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22894 setHeadline: function (value)
22896 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22899 setFooter: function (value, href)
22901 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22904 this.el.select('a.small-box-footer',true).first().attr('href', href);
22909 setContent: function (value)
22911 this.el.select('.roo-content',true).first().dom.innerHTML = value;
22914 initEvents: function()
22928 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22931 * @class Roo.bootstrap.dash.TabBox
22932 * @extends Roo.bootstrap.Component
22933 * Bootstrap TabBox class
22934 * @cfg {String} title Title of the TabBox
22935 * @cfg {String} icon Icon of the TabBox
22936 * @cfg {Boolean} showtabs (true|false) show the tabs default true
22937 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22940 * Create a new TabBox
22941 * @param {Object} config The config object
22945 Roo.bootstrap.dash.TabBox = function(config){
22946 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22951 * When a pane is added
22952 * @param {Roo.bootstrap.dash.TabPane} pane
22956 * @event activatepane
22957 * When a pane is activated
22958 * @param {Roo.bootstrap.dash.TabPane} pane
22960 "activatepane" : true
22968 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
22973 tabScrollable : false,
22975 getChildContainer : function()
22977 return this.el.select('.tab-content', true).first();
22980 getAutoCreate : function(){
22984 cls: 'pull-left header',
22992 cls: 'fa ' + this.icon
22998 cls: 'nav nav-tabs pull-right',
23004 if(this.tabScrollable){
23011 cls: 'nav nav-tabs pull-right',
23022 cls: 'nav-tabs-custom',
23027 cls: 'tab-content no-padding',
23035 initEvents : function()
23037 //Roo.log('add add pane handler');
23038 this.on('addpane', this.onAddPane, this);
23041 * Updates the box title
23042 * @param {String} html to set the title to.
23044 setTitle : function(value)
23046 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23048 onAddPane : function(pane)
23050 this.panes.push(pane);
23051 //Roo.log('addpane');
23053 // tabs are rendere left to right..
23054 if(!this.showtabs){
23058 var ctr = this.el.select('.nav-tabs', true).first();
23061 var existing = ctr.select('.nav-tab',true);
23062 var qty = existing.getCount();;
23065 var tab = ctr.createChild({
23067 cls : 'nav-tab' + (qty ? '' : ' active'),
23075 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23078 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23080 pane.el.addClass('active');
23085 onTabClick : function(ev,un,ob,pane)
23087 //Roo.log('tab - prev default');
23088 ev.preventDefault();
23091 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23092 pane.tab.addClass('active');
23093 //Roo.log(pane.title);
23094 this.getChildContainer().select('.tab-pane',true).removeClass('active');
23095 // technically we should have a deactivate event.. but maybe add later.
23096 // and it should not de-activate the selected tab...
23097 this.fireEvent('activatepane', pane);
23098 pane.el.addClass('active');
23099 pane.fireEvent('activate');
23104 getActivePane : function()
23107 Roo.each(this.panes, function(p) {
23108 if(p.el.hasClass('active')){
23129 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23131 * @class Roo.bootstrap.TabPane
23132 * @extends Roo.bootstrap.Component
23133 * Bootstrap TabPane class
23134 * @cfg {Boolean} active (false | true) Default false
23135 * @cfg {String} title title of panel
23139 * Create a new TabPane
23140 * @param {Object} config The config object
23143 Roo.bootstrap.dash.TabPane = function(config){
23144 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23150 * When a pane is activated
23151 * @param {Roo.bootstrap.dash.TabPane} pane
23158 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
23163 // the tabBox that this is attached to.
23166 getAutoCreate : function()
23174 cfg.cls += ' active';
23179 initEvents : function()
23181 //Roo.log('trigger add pane handler');
23182 this.parent().fireEvent('addpane', this)
23186 * Updates the tab title
23187 * @param {String} html to set the title to.
23189 setTitle: function(str)
23195 this.tab.select('a', true).first().dom.innerHTML = str;
23212 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23215 * @class Roo.bootstrap.menu.Menu
23216 * @extends Roo.bootstrap.Component
23217 * Bootstrap Menu class - container for Menu
23218 * @cfg {String} html Text of the menu
23219 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23220 * @cfg {String} icon Font awesome icon
23221 * @cfg {String} pos Menu align to (top | bottom) default bottom
23225 * Create a new Menu
23226 * @param {Object} config The config object
23230 Roo.bootstrap.menu.Menu = function(config){
23231 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23235 * @event beforeshow
23236 * Fires before this menu is displayed
23237 * @param {Roo.bootstrap.menu.Menu} this
23241 * @event beforehide
23242 * Fires before this menu is hidden
23243 * @param {Roo.bootstrap.menu.Menu} this
23248 * Fires after this menu is displayed
23249 * @param {Roo.bootstrap.menu.Menu} this
23254 * Fires after this menu is hidden
23255 * @param {Roo.bootstrap.menu.Menu} this
23260 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23261 * @param {Roo.bootstrap.menu.Menu} this
23262 * @param {Roo.EventObject} e
23269 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
23273 weight : 'default',
23278 getChildContainer : function() {
23279 if(this.isSubMenu){
23283 return this.el.select('ul.dropdown-menu', true).first();
23286 getAutoCreate : function()
23291 cls : 'roo-menu-text',
23299 cls : 'fa ' + this.icon
23310 cls : 'dropdown-button btn btn-' + this.weight,
23315 cls : 'dropdown-toggle btn btn-' + this.weight,
23325 cls : 'dropdown-menu'
23331 if(this.pos == 'top'){
23332 cfg.cls += ' dropup';
23335 if(this.isSubMenu){
23338 cls : 'dropdown-menu'
23345 onRender : function(ct, position)
23347 this.isSubMenu = ct.hasClass('dropdown-submenu');
23349 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23352 initEvents : function()
23354 if(this.isSubMenu){
23358 this.hidden = true;
23360 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23361 this.triggerEl.on('click', this.onTriggerPress, this);
23363 this.buttonEl = this.el.select('button.dropdown-button', true).first();
23364 this.buttonEl.on('click', this.onClick, this);
23370 if(this.isSubMenu){
23374 return this.el.select('ul.dropdown-menu', true).first();
23377 onClick : function(e)
23379 this.fireEvent("click", this, e);
23382 onTriggerPress : function(e)
23384 if (this.isVisible()) {
23391 isVisible : function(){
23392 return !this.hidden;
23397 this.fireEvent("beforeshow", this);
23399 this.hidden = false;
23400 this.el.addClass('open');
23402 Roo.get(document).on("mouseup", this.onMouseUp, this);
23404 this.fireEvent("show", this);
23411 this.fireEvent("beforehide", this);
23413 this.hidden = true;
23414 this.el.removeClass('open');
23416 Roo.get(document).un("mouseup", this.onMouseUp);
23418 this.fireEvent("hide", this);
23421 onMouseUp : function()
23435 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23438 * @class Roo.bootstrap.menu.Item
23439 * @extends Roo.bootstrap.Component
23440 * Bootstrap MenuItem class
23441 * @cfg {Boolean} submenu (true | false) default false
23442 * @cfg {String} html text of the item
23443 * @cfg {String} href the link
23444 * @cfg {Boolean} disable (true | false) default false
23445 * @cfg {Boolean} preventDefault (true | false) default true
23446 * @cfg {String} icon Font awesome icon
23447 * @cfg {String} pos Submenu align to (left | right) default right
23451 * Create a new Item
23452 * @param {Object} config The config object
23456 Roo.bootstrap.menu.Item = function(config){
23457 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23461 * Fires when the mouse is hovering over this menu
23462 * @param {Roo.bootstrap.menu.Item} this
23463 * @param {Roo.EventObject} e
23468 * Fires when the mouse exits this menu
23469 * @param {Roo.bootstrap.menu.Item} this
23470 * @param {Roo.EventObject} e
23476 * The raw click event for the entire grid.
23477 * @param {Roo.EventObject} e
23483 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23488 preventDefault: true,
23493 getAutoCreate : function()
23498 cls : 'roo-menu-item-text',
23506 cls : 'fa ' + this.icon
23515 href : this.href || '#',
23522 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23526 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23528 if(this.pos == 'left'){
23529 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23536 initEvents : function()
23538 this.el.on('mouseover', this.onMouseOver, this);
23539 this.el.on('mouseout', this.onMouseOut, this);
23541 this.el.select('a', true).first().on('click', this.onClick, this);
23545 onClick : function(e)
23547 if(this.preventDefault){
23548 e.preventDefault();
23551 this.fireEvent("click", this, e);
23554 onMouseOver : function(e)
23556 if(this.submenu && this.pos == 'left'){
23557 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23560 this.fireEvent("mouseover", this, e);
23563 onMouseOut : function(e)
23565 this.fireEvent("mouseout", this, e);
23577 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23580 * @class Roo.bootstrap.menu.Separator
23581 * @extends Roo.bootstrap.Component
23582 * Bootstrap Separator class
23585 * Create a new Separator
23586 * @param {Object} config The config object
23590 Roo.bootstrap.menu.Separator = function(config){
23591 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23594 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23596 getAutoCreate : function(){
23617 * @class Roo.bootstrap.Tooltip
23618 * Bootstrap Tooltip class
23619 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23620 * to determine which dom element triggers the tooltip.
23622 * It needs to add support for additional attributes like tooltip-position
23625 * Create a new Toolti
23626 * @param {Object} config The config object
23629 Roo.bootstrap.Tooltip = function(config){
23630 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23633 Roo.apply(Roo.bootstrap.Tooltip, {
23635 * @function init initialize tooltip monitoring.
23639 currentTip : false,
23640 currentRegion : false,
23646 Roo.get(document).on('mouseover', this.enter ,this);
23647 Roo.get(document).on('mouseout', this.leave, this);
23650 this.currentTip = new Roo.bootstrap.Tooltip();
23653 enter : function(ev)
23655 var dom = ev.getTarget();
23657 //Roo.log(['enter',dom]);
23658 var el = Roo.fly(dom);
23659 if (this.currentEl) {
23661 //Roo.log(this.currentEl);
23662 //Roo.log(this.currentEl.contains(dom));
23663 if (this.currentEl == el) {
23666 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23672 if (this.currentTip.el) {
23673 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23678 // you can not look for children, as if el is the body.. then everythign is the child..
23679 if (!el.attr('tooltip')) { //
23680 if (!el.select("[tooltip]").elements.length) {
23683 // is the mouse over this child...?
23684 bindEl = el.select("[tooltip]").first();
23685 var xy = ev.getXY();
23686 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23687 //Roo.log("not in region.");
23690 //Roo.log("child element over..");
23693 this.currentEl = bindEl;
23694 this.currentTip.bind(bindEl);
23695 this.currentRegion = Roo.lib.Region.getRegion(dom);
23696 this.currentTip.enter();
23699 leave : function(ev)
23701 var dom = ev.getTarget();
23702 //Roo.log(['leave',dom]);
23703 if (!this.currentEl) {
23708 if (dom != this.currentEl.dom) {
23711 var xy = ev.getXY();
23712 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23715 // only activate leave if mouse cursor is outside... bounding box..
23720 if (this.currentTip) {
23721 this.currentTip.leave();
23723 //Roo.log('clear currentEl');
23724 this.currentEl = false;
23729 'left' : ['r-l', [-2,0], 'right'],
23730 'right' : ['l-r', [2,0], 'left'],
23731 'bottom' : ['t-b', [0,2], 'top'],
23732 'top' : [ 'b-t', [0,-2], 'bottom']
23738 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
23743 delay : null, // can be { show : 300 , hide: 500}
23747 hoverState : null, //???
23749 placement : 'bottom',
23751 getAutoCreate : function(){
23758 cls : 'tooltip-arrow'
23761 cls : 'tooltip-inner'
23768 bind : function(el)
23774 enter : function () {
23776 if (this.timeout != null) {
23777 clearTimeout(this.timeout);
23780 this.hoverState = 'in';
23781 //Roo.log("enter - show");
23782 if (!this.delay || !this.delay.show) {
23787 this.timeout = setTimeout(function () {
23788 if (_t.hoverState == 'in') {
23791 }, this.delay.show);
23795 clearTimeout(this.timeout);
23797 this.hoverState = 'out';
23798 if (!this.delay || !this.delay.hide) {
23804 this.timeout = setTimeout(function () {
23805 //Roo.log("leave - timeout");
23807 if (_t.hoverState == 'out') {
23809 Roo.bootstrap.Tooltip.currentEl = false;
23817 this.render(document.body);
23820 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23822 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23824 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23826 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23828 var placement = typeof this.placement == 'function' ?
23829 this.placement.call(this, this.el, on_el) :
23832 var autoToken = /\s?auto?\s?/i;
23833 var autoPlace = autoToken.test(placement);
23835 placement = placement.replace(autoToken, '') || 'top';
23839 //this.el.setXY([0,0]);
23841 //this.el.dom.style.display='block';
23843 //this.el.appendTo(on_el);
23845 var p = this.getPosition();
23846 var box = this.el.getBox();
23852 var align = Roo.bootstrap.Tooltip.alignment[placement];
23854 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23856 if(placement == 'top' || placement == 'bottom'){
23858 placement = 'right';
23861 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23862 placement = 'left';
23866 align = Roo.bootstrap.Tooltip.alignment[placement];
23868 this.el.alignTo(this.bindEl, align[0],align[1]);
23869 //var arrow = this.el.select('.arrow',true).first();
23870 //arrow.set(align[2],
23872 this.el.addClass(placement);
23874 this.el.addClass('in fade');
23876 this.hoverState = null;
23878 if (this.el.hasClass('fade')) {
23889 //this.el.setXY([0,0]);
23890 this.el.removeClass('in');
23906 * @class Roo.bootstrap.LocationPicker
23907 * @extends Roo.bootstrap.Component
23908 * Bootstrap LocationPicker class
23909 * @cfg {Number} latitude Position when init default 0
23910 * @cfg {Number} longitude Position when init default 0
23911 * @cfg {Number} zoom default 15
23912 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23913 * @cfg {Boolean} mapTypeControl default false
23914 * @cfg {Boolean} disableDoubleClickZoom default false
23915 * @cfg {Boolean} scrollwheel default true
23916 * @cfg {Boolean} streetViewControl default false
23917 * @cfg {Number} radius default 0
23918 * @cfg {String} locationName
23919 * @cfg {Boolean} draggable default true
23920 * @cfg {Boolean} enableAutocomplete default false
23921 * @cfg {Boolean} enableReverseGeocode default true
23922 * @cfg {String} markerTitle
23925 * Create a new LocationPicker
23926 * @param {Object} config The config object
23930 Roo.bootstrap.LocationPicker = function(config){
23932 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23937 * Fires when the picker initialized.
23938 * @param {Roo.bootstrap.LocationPicker} this
23939 * @param {Google Location} location
23943 * @event positionchanged
23944 * Fires when the picker position changed.
23945 * @param {Roo.bootstrap.LocationPicker} this
23946 * @param {Google Location} location
23948 positionchanged : true,
23951 * Fires when the map resize.
23952 * @param {Roo.bootstrap.LocationPicker} this
23957 * Fires when the map show.
23958 * @param {Roo.bootstrap.LocationPicker} this
23963 * Fires when the map hide.
23964 * @param {Roo.bootstrap.LocationPicker} this
23969 * Fires when click the map.
23970 * @param {Roo.bootstrap.LocationPicker} this
23971 * @param {Map event} e
23975 * @event mapRightClick
23976 * Fires when right click the map.
23977 * @param {Roo.bootstrap.LocationPicker} this
23978 * @param {Map event} e
23980 mapRightClick : true,
23982 * @event markerClick
23983 * Fires when click the marker.
23984 * @param {Roo.bootstrap.LocationPicker} this
23985 * @param {Map event} e
23987 markerClick : true,
23989 * @event markerRightClick
23990 * Fires when right click the marker.
23991 * @param {Roo.bootstrap.LocationPicker} this
23992 * @param {Map event} e
23994 markerRightClick : true,
23996 * @event OverlayViewDraw
23997 * Fires when OverlayView Draw
23998 * @param {Roo.bootstrap.LocationPicker} this
24000 OverlayViewDraw : true,
24002 * @event OverlayViewOnAdd
24003 * Fires when OverlayView Draw
24004 * @param {Roo.bootstrap.LocationPicker} this
24006 OverlayViewOnAdd : true,
24008 * @event OverlayViewOnRemove
24009 * Fires when OverlayView Draw
24010 * @param {Roo.bootstrap.LocationPicker} this
24012 OverlayViewOnRemove : true,
24014 * @event OverlayViewShow
24015 * Fires when OverlayView Draw
24016 * @param {Roo.bootstrap.LocationPicker} this
24017 * @param {Pixel} cpx
24019 OverlayViewShow : true,
24021 * @event OverlayViewHide
24022 * Fires when OverlayView Draw
24023 * @param {Roo.bootstrap.LocationPicker} this
24025 OverlayViewHide : true,
24027 * @event loadexception
24028 * Fires when load google lib failed.
24029 * @param {Roo.bootstrap.LocationPicker} this
24031 loadexception : true
24036 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
24038 gMapContext: false,
24044 mapTypeControl: false,
24045 disableDoubleClickZoom: false,
24047 streetViewControl: false,
24051 enableAutocomplete: false,
24052 enableReverseGeocode: true,
24055 getAutoCreate: function()
24060 cls: 'roo-location-picker'
24066 initEvents: function(ct, position)
24068 if(!this.el.getWidth() || this.isApplied()){
24072 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24077 initial: function()
24079 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24080 this.fireEvent('loadexception', this);
24084 if(!this.mapTypeId){
24085 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24088 this.gMapContext = this.GMapContext();
24090 this.initOverlayView();
24092 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24096 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24097 _this.setPosition(_this.gMapContext.marker.position);
24100 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24101 _this.fireEvent('mapClick', this, event);
24105 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24106 _this.fireEvent('mapRightClick', this, event);
24110 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24111 _this.fireEvent('markerClick', this, event);
24115 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24116 _this.fireEvent('markerRightClick', this, event);
24120 this.setPosition(this.gMapContext.location);
24122 this.fireEvent('initial', this, this.gMapContext.location);
24125 initOverlayView: function()
24129 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24133 _this.fireEvent('OverlayViewDraw', _this);
24138 _this.fireEvent('OverlayViewOnAdd', _this);
24141 onRemove: function()
24143 _this.fireEvent('OverlayViewOnRemove', _this);
24146 show: function(cpx)
24148 _this.fireEvent('OverlayViewShow', _this, cpx);
24153 _this.fireEvent('OverlayViewHide', _this);
24159 fromLatLngToContainerPixel: function(event)
24161 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24164 isApplied: function()
24166 return this.getGmapContext() == false ? false : true;
24169 getGmapContext: function()
24171 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24174 GMapContext: function()
24176 var position = new google.maps.LatLng(this.latitude, this.longitude);
24178 var _map = new google.maps.Map(this.el.dom, {
24181 mapTypeId: this.mapTypeId,
24182 mapTypeControl: this.mapTypeControl,
24183 disableDoubleClickZoom: this.disableDoubleClickZoom,
24184 scrollwheel: this.scrollwheel,
24185 streetViewControl: this.streetViewControl,
24186 locationName: this.locationName,
24187 draggable: this.draggable,
24188 enableAutocomplete: this.enableAutocomplete,
24189 enableReverseGeocode: this.enableReverseGeocode
24192 var _marker = new google.maps.Marker({
24193 position: position,
24195 title: this.markerTitle,
24196 draggable: this.draggable
24203 location: position,
24204 radius: this.radius,
24205 locationName: this.locationName,
24206 addressComponents: {
24207 formatted_address: null,
24208 addressLine1: null,
24209 addressLine2: null,
24211 streetNumber: null,
24215 stateOrProvince: null
24218 domContainer: this.el.dom,
24219 geodecoder: new google.maps.Geocoder()
24223 drawCircle: function(center, radius, options)
24225 if (this.gMapContext.circle != null) {
24226 this.gMapContext.circle.setMap(null);
24230 options = Roo.apply({}, options, {
24231 strokeColor: "#0000FF",
24232 strokeOpacity: .35,
24234 fillColor: "#0000FF",
24238 options.map = this.gMapContext.map;
24239 options.radius = radius;
24240 options.center = center;
24241 this.gMapContext.circle = new google.maps.Circle(options);
24242 return this.gMapContext.circle;
24248 setPosition: function(location)
24250 this.gMapContext.location = location;
24251 this.gMapContext.marker.setPosition(location);
24252 this.gMapContext.map.panTo(location);
24253 this.drawCircle(location, this.gMapContext.radius, {});
24257 if (this.gMapContext.settings.enableReverseGeocode) {
24258 this.gMapContext.geodecoder.geocode({
24259 latLng: this.gMapContext.location
24260 }, function(results, status) {
24262 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24263 _this.gMapContext.locationName = results[0].formatted_address;
24264 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24266 _this.fireEvent('positionchanged', this, location);
24273 this.fireEvent('positionchanged', this, location);
24278 google.maps.event.trigger(this.gMapContext.map, "resize");
24280 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24282 this.fireEvent('resize', this);
24285 setPositionByLatLng: function(latitude, longitude)
24287 this.setPosition(new google.maps.LatLng(latitude, longitude));
24290 getCurrentPosition: function()
24293 latitude: this.gMapContext.location.lat(),
24294 longitude: this.gMapContext.location.lng()
24298 getAddressName: function()
24300 return this.gMapContext.locationName;
24303 getAddressComponents: function()
24305 return this.gMapContext.addressComponents;
24308 address_component_from_google_geocode: function(address_components)
24312 for (var i = 0; i < address_components.length; i++) {
24313 var component = address_components[i];
24314 if (component.types.indexOf("postal_code") >= 0) {
24315 result.postalCode = component.short_name;
24316 } else if (component.types.indexOf("street_number") >= 0) {
24317 result.streetNumber = component.short_name;
24318 } else if (component.types.indexOf("route") >= 0) {
24319 result.streetName = component.short_name;
24320 } else if (component.types.indexOf("neighborhood") >= 0) {
24321 result.city = component.short_name;
24322 } else if (component.types.indexOf("locality") >= 0) {
24323 result.city = component.short_name;
24324 } else if (component.types.indexOf("sublocality") >= 0) {
24325 result.district = component.short_name;
24326 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24327 result.stateOrProvince = component.short_name;
24328 } else if (component.types.indexOf("country") >= 0) {
24329 result.country = component.short_name;
24333 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24334 result.addressLine2 = "";
24338 setZoomLevel: function(zoom)
24340 this.gMapContext.map.setZoom(zoom);
24353 this.fireEvent('show', this);
24364 this.fireEvent('hide', this);
24369 Roo.apply(Roo.bootstrap.LocationPicker, {
24371 OverlayView : function(map, options)
24373 options = options || {};
24387 * @class Roo.bootstrap.Alert
24388 * @extends Roo.bootstrap.Component
24389 * Bootstrap Alert class
24390 * @cfg {String} title The title of alert
24391 * @cfg {String} html The content of alert
24392 * @cfg {String} weight ( success | info | warning | danger )
24393 * @cfg {String} faicon font-awesomeicon
24396 * Create a new alert
24397 * @param {Object} config The config object
24401 Roo.bootstrap.Alert = function(config){
24402 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24406 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
24413 getAutoCreate : function()
24422 cls : 'roo-alert-icon'
24427 cls : 'roo-alert-title',
24432 cls : 'roo-alert-text',
24439 cfg.cn[0].cls += ' fa ' + this.faicon;
24443 cfg.cls += ' alert-' + this.weight;
24449 initEvents: function()
24451 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24454 setTitle : function(str)
24456 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24459 setText : function(str)
24461 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24464 setWeight : function(weight)
24467 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24470 this.weight = weight;
24472 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24475 setIcon : function(icon)
24478 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24481 this.faicon = icon;
24483 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24504 * @class Roo.bootstrap.UploadCropbox
24505 * @extends Roo.bootstrap.Component
24506 * Bootstrap UploadCropbox class
24507 * @cfg {String} emptyText show when image has been loaded
24508 * @cfg {String} rotateNotify show when image too small to rotate
24509 * @cfg {Number} errorTimeout default 3000
24510 * @cfg {Number} minWidth default 300
24511 * @cfg {Number} minHeight default 300
24512 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24513 * @cfg {Boolean} isDocument (true|false) default false
24514 * @cfg {String} url action url
24515 * @cfg {String} paramName default 'imageUpload'
24516 * @cfg {String} method default POST
24517 * @cfg {Boolean} loadMask (true|false) default true
24518 * @cfg {Boolean} loadingText default 'Loading...'
24521 * Create a new UploadCropbox
24522 * @param {Object} config The config object
24525 Roo.bootstrap.UploadCropbox = function(config){
24526 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24530 * @event beforeselectfile
24531 * Fire before select file
24532 * @param {Roo.bootstrap.UploadCropbox} this
24534 "beforeselectfile" : true,
24537 * Fire after initEvent
24538 * @param {Roo.bootstrap.UploadCropbox} this
24543 * Fire after initEvent
24544 * @param {Roo.bootstrap.UploadCropbox} this
24545 * @param {String} data
24550 * Fire when preparing the file data
24551 * @param {Roo.bootstrap.UploadCropbox} this
24552 * @param {Object} file
24557 * Fire when get exception
24558 * @param {Roo.bootstrap.UploadCropbox} this
24559 * @param {XMLHttpRequest} xhr
24561 "exception" : true,
24563 * @event beforeloadcanvas
24564 * Fire before load the canvas
24565 * @param {Roo.bootstrap.UploadCropbox} this
24566 * @param {String} src
24568 "beforeloadcanvas" : true,
24571 * Fire when trash image
24572 * @param {Roo.bootstrap.UploadCropbox} this
24577 * Fire when download the image
24578 * @param {Roo.bootstrap.UploadCropbox} this
24582 * @event footerbuttonclick
24583 * Fire when footerbuttonclick
24584 * @param {Roo.bootstrap.UploadCropbox} this
24585 * @param {String} type
24587 "footerbuttonclick" : true,
24591 * @param {Roo.bootstrap.UploadCropbox} this
24596 * Fire when rotate the image
24597 * @param {Roo.bootstrap.UploadCropbox} this
24598 * @param {String} pos
24603 * Fire when inspect the file
24604 * @param {Roo.bootstrap.UploadCropbox} this
24605 * @param {Object} file
24610 * Fire when xhr upload the file
24611 * @param {Roo.bootstrap.UploadCropbox} this
24612 * @param {Object} data
24617 * Fire when arrange the file data
24618 * @param {Roo.bootstrap.UploadCropbox} this
24619 * @param {Object} formData
24624 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24627 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24629 emptyText : 'Click to upload image',
24630 rotateNotify : 'Image is too small to rotate',
24631 errorTimeout : 3000,
24645 cropType : 'image/jpeg',
24647 canvasLoaded : false,
24648 isDocument : false,
24650 paramName : 'imageUpload',
24652 loadingText : 'Loading...',
24655 getAutoCreate : function()
24659 cls : 'roo-upload-cropbox',
24663 cls : 'roo-upload-cropbox-selector',
24668 cls : 'roo-upload-cropbox-body',
24669 style : 'cursor:pointer',
24673 cls : 'roo-upload-cropbox-preview'
24677 cls : 'roo-upload-cropbox-thumb'
24681 cls : 'roo-upload-cropbox-empty-notify',
24682 html : this.emptyText
24686 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24687 html : this.rotateNotify
24693 cls : 'roo-upload-cropbox-footer',
24696 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24706 onRender : function(ct, position)
24708 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24710 if (this.buttons.length) {
24712 Roo.each(this.buttons, function(bb) {
24714 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24716 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24722 this.maskEl = this.el;
24726 initEvents : function()
24728 this.urlAPI = (window.createObjectURL && window) ||
24729 (window.URL && URL.revokeObjectURL && URL) ||
24730 (window.webkitURL && webkitURL);
24732 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24733 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24735 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24736 this.selectorEl.hide();
24738 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24739 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24741 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24742 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24743 this.thumbEl.hide();
24745 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24746 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24748 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24749 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24750 this.errorEl.hide();
24752 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24753 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24754 this.footerEl.hide();
24756 this.setThumbBoxSize();
24762 this.fireEvent('initial', this);
24769 window.addEventListener("resize", function() { _this.resize(); } );
24771 this.bodyEl.on('click', this.beforeSelectFile, this);
24774 this.bodyEl.on('touchstart', this.onTouchStart, this);
24775 this.bodyEl.on('touchmove', this.onTouchMove, this);
24776 this.bodyEl.on('touchend', this.onTouchEnd, this);
24780 this.bodyEl.on('mousedown', this.onMouseDown, this);
24781 this.bodyEl.on('mousemove', this.onMouseMove, this);
24782 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24783 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24784 Roo.get(document).on('mouseup', this.onMouseUp, this);
24787 this.selectorEl.on('change', this.onFileSelected, this);
24793 this.baseScale = 1;
24795 this.baseRotate = 1;
24796 this.dragable = false;
24797 this.pinching = false;
24800 this.cropData = false;
24801 this.notifyEl.dom.innerHTML = this.emptyText;
24803 this.selectorEl.dom.value = '';
24807 resize : function()
24809 if(this.fireEvent('resize', this) != false){
24810 this.setThumbBoxPosition();
24811 this.setCanvasPosition();
24815 onFooterButtonClick : function(e, el, o, type)
24818 case 'rotate-left' :
24819 this.onRotateLeft(e);
24821 case 'rotate-right' :
24822 this.onRotateRight(e);
24825 this.beforeSelectFile(e);
24840 this.fireEvent('footerbuttonclick', this, type);
24843 beforeSelectFile : function(e)
24845 e.preventDefault();
24847 if(this.fireEvent('beforeselectfile', this) != false){
24848 this.selectorEl.dom.click();
24852 onFileSelected : function(e)
24854 e.preventDefault();
24856 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24860 var file = this.selectorEl.dom.files[0];
24862 if(this.fireEvent('inspect', this, file) != false){
24863 this.prepare(file);
24868 trash : function(e)
24870 this.fireEvent('trash', this);
24873 download : function(e)
24875 this.fireEvent('download', this);
24878 loadCanvas : function(src)
24880 if(this.fireEvent('beforeloadcanvas', this, src) != false){
24884 this.imageEl = document.createElement('img');
24888 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24890 this.imageEl.src = src;
24894 onLoadCanvas : function()
24896 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24897 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24899 this.bodyEl.un('click', this.beforeSelectFile, this);
24901 this.notifyEl.hide();
24902 this.thumbEl.show();
24903 this.footerEl.show();
24905 this.baseRotateLevel();
24907 if(this.isDocument){
24908 this.setThumbBoxSize();
24911 this.setThumbBoxPosition();
24913 this.baseScaleLevel();
24919 this.canvasLoaded = true;
24922 this.maskEl.unmask();
24927 setCanvasPosition : function()
24929 if(!this.canvasEl){
24933 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24934 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24936 this.previewEl.setLeft(pw);
24937 this.previewEl.setTop(ph);
24941 onMouseDown : function(e)
24945 this.dragable = true;
24946 this.pinching = false;
24948 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24949 this.dragable = false;
24953 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24954 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24958 onMouseMove : function(e)
24962 if(!this.canvasLoaded){
24966 if (!this.dragable){
24970 var minX = Math.ceil(this.thumbEl.getLeft(true));
24971 var minY = Math.ceil(this.thumbEl.getTop(true));
24973 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24974 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24976 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24977 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24979 x = x - this.mouseX;
24980 y = y - this.mouseY;
24982 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24983 var bgY = Math.ceil(y + this.previewEl.getTop(true));
24985 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24986 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24988 this.previewEl.setLeft(bgX);
24989 this.previewEl.setTop(bgY);
24991 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24992 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24995 onMouseUp : function(e)
24999 this.dragable = false;
25002 onMouseWheel : function(e)
25006 this.startScale = this.scale;
25008 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25010 if(!this.zoomable()){
25011 this.scale = this.startScale;
25020 zoomable : function()
25022 var minScale = this.thumbEl.getWidth() / this.minWidth;
25024 if(this.minWidth < this.minHeight){
25025 minScale = this.thumbEl.getHeight() / this.minHeight;
25028 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25029 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25033 (this.rotate == 0 || this.rotate == 180) &&
25035 width > this.imageEl.OriginWidth ||
25036 height > this.imageEl.OriginHeight ||
25037 (width < this.minWidth && height < this.minHeight)
25045 (this.rotate == 90 || this.rotate == 270) &&
25047 width > this.imageEl.OriginWidth ||
25048 height > this.imageEl.OriginHeight ||
25049 (width < this.minHeight && height < this.minWidth)
25056 !this.isDocument &&
25057 (this.rotate == 0 || this.rotate == 180) &&
25059 width < this.minWidth ||
25060 width > this.imageEl.OriginWidth ||
25061 height < this.minHeight ||
25062 height > this.imageEl.OriginHeight
25069 !this.isDocument &&
25070 (this.rotate == 90 || this.rotate == 270) &&
25072 width < this.minHeight ||
25073 width > this.imageEl.OriginWidth ||
25074 height < this.minWidth ||
25075 height > this.imageEl.OriginHeight
25085 onRotateLeft : function(e)
25087 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25089 var minScale = this.thumbEl.getWidth() / this.minWidth;
25091 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25092 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25094 this.startScale = this.scale;
25096 while (this.getScaleLevel() < minScale){
25098 this.scale = this.scale + 1;
25100 if(!this.zoomable()){
25105 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25106 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25111 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25118 this.scale = this.startScale;
25120 this.onRotateFail();
25125 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25127 if(this.isDocument){
25128 this.setThumbBoxSize();
25129 this.setThumbBoxPosition();
25130 this.setCanvasPosition();
25135 this.fireEvent('rotate', this, 'left');
25139 onRotateRight : function(e)
25141 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25143 var minScale = this.thumbEl.getWidth() / this.minWidth;
25145 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25146 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25148 this.startScale = this.scale;
25150 while (this.getScaleLevel() < minScale){
25152 this.scale = this.scale + 1;
25154 if(!this.zoomable()){
25159 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25160 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25165 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25172 this.scale = this.startScale;
25174 this.onRotateFail();
25179 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25181 if(this.isDocument){
25182 this.setThumbBoxSize();
25183 this.setThumbBoxPosition();
25184 this.setCanvasPosition();
25189 this.fireEvent('rotate', this, 'right');
25192 onRotateFail : function()
25194 this.errorEl.show(true);
25198 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25203 this.previewEl.dom.innerHTML = '';
25205 var canvasEl = document.createElement("canvas");
25207 var contextEl = canvasEl.getContext("2d");
25209 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25210 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25211 var center = this.imageEl.OriginWidth / 2;
25213 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25214 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25215 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25216 center = this.imageEl.OriginHeight / 2;
25219 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25221 contextEl.translate(center, center);
25222 contextEl.rotate(this.rotate * Math.PI / 180);
25224 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25226 this.canvasEl = document.createElement("canvas");
25228 this.contextEl = this.canvasEl.getContext("2d");
25230 switch (this.rotate) {
25233 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25234 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25236 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25241 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25242 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25244 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25245 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);
25249 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25254 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25255 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25257 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25258 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);
25262 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);
25267 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25268 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25270 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25271 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25275 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);
25282 this.previewEl.appendChild(this.canvasEl);
25284 this.setCanvasPosition();
25289 if(!this.canvasLoaded){
25293 var imageCanvas = document.createElement("canvas");
25295 var imageContext = imageCanvas.getContext("2d");
25297 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25298 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25300 var center = imageCanvas.width / 2;
25302 imageContext.translate(center, center);
25304 imageContext.rotate(this.rotate * Math.PI / 180);
25306 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25308 var canvas = document.createElement("canvas");
25310 var context = canvas.getContext("2d");
25312 canvas.width = this.minWidth;
25313 canvas.height = this.minHeight;
25315 switch (this.rotate) {
25318 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25319 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25321 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25322 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25324 var targetWidth = this.minWidth - 2 * x;
25325 var targetHeight = this.minHeight - 2 * y;
25329 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25330 scale = targetWidth / width;
25333 if(x > 0 && y == 0){
25334 scale = targetHeight / height;
25337 if(x > 0 && y > 0){
25338 scale = targetWidth / width;
25340 if(width < height){
25341 scale = targetHeight / height;
25345 context.scale(scale, scale);
25347 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25348 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25350 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25351 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25353 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25358 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25359 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25361 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25362 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25364 var targetWidth = this.minWidth - 2 * x;
25365 var targetHeight = this.minHeight - 2 * y;
25369 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25370 scale = targetWidth / width;
25373 if(x > 0 && y == 0){
25374 scale = targetHeight / height;
25377 if(x > 0 && y > 0){
25378 scale = targetWidth / width;
25380 if(width < height){
25381 scale = targetHeight / height;
25385 context.scale(scale, scale);
25387 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25388 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25390 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25391 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25393 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25395 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25400 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25401 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25403 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25404 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25406 var targetWidth = this.minWidth - 2 * x;
25407 var targetHeight = this.minHeight - 2 * y;
25411 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25412 scale = targetWidth / width;
25415 if(x > 0 && y == 0){
25416 scale = targetHeight / height;
25419 if(x > 0 && y > 0){
25420 scale = targetWidth / width;
25422 if(width < height){
25423 scale = targetHeight / height;
25427 context.scale(scale, scale);
25429 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25430 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25432 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25433 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25435 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25436 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25438 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25443 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25444 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25446 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25447 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25449 var targetWidth = this.minWidth - 2 * x;
25450 var targetHeight = this.minHeight - 2 * y;
25454 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25455 scale = targetWidth / width;
25458 if(x > 0 && y == 0){
25459 scale = targetHeight / height;
25462 if(x > 0 && y > 0){
25463 scale = targetWidth / width;
25465 if(width < height){
25466 scale = targetHeight / height;
25470 context.scale(scale, scale);
25472 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25473 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25475 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25476 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25478 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25480 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25487 this.cropData = canvas.toDataURL(this.cropType);
25489 if(this.fireEvent('crop', this, this.cropData) !== false){
25490 this.process(this.file, this.cropData);
25497 setThumbBoxSize : function()
25501 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25502 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25503 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25505 this.minWidth = width;
25506 this.minHeight = height;
25508 if(this.rotate == 90 || this.rotate == 270){
25509 this.minWidth = height;
25510 this.minHeight = width;
25515 width = Math.ceil(this.minWidth * height / this.minHeight);
25517 if(this.minWidth > this.minHeight){
25519 height = Math.ceil(this.minHeight * width / this.minWidth);
25522 this.thumbEl.setStyle({
25523 width : width + 'px',
25524 height : height + 'px'
25531 setThumbBoxPosition : function()
25533 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25534 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25536 this.thumbEl.setLeft(x);
25537 this.thumbEl.setTop(y);
25541 baseRotateLevel : function()
25543 this.baseRotate = 1;
25546 typeof(this.exif) != 'undefined' &&
25547 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25548 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25550 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25553 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25557 baseScaleLevel : function()
25561 if(this.isDocument){
25563 if(this.baseRotate == 6 || this.baseRotate == 8){
25565 height = this.thumbEl.getHeight();
25566 this.baseScale = height / this.imageEl.OriginWidth;
25568 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25569 width = this.thumbEl.getWidth();
25570 this.baseScale = width / this.imageEl.OriginHeight;
25576 height = this.thumbEl.getHeight();
25577 this.baseScale = height / this.imageEl.OriginHeight;
25579 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25580 width = this.thumbEl.getWidth();
25581 this.baseScale = width / this.imageEl.OriginWidth;
25587 if(this.baseRotate == 6 || this.baseRotate == 8){
25589 width = this.thumbEl.getHeight();
25590 this.baseScale = width / this.imageEl.OriginHeight;
25592 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25593 height = this.thumbEl.getWidth();
25594 this.baseScale = height / this.imageEl.OriginHeight;
25597 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25598 height = this.thumbEl.getWidth();
25599 this.baseScale = height / this.imageEl.OriginHeight;
25601 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25602 width = this.thumbEl.getHeight();
25603 this.baseScale = width / this.imageEl.OriginWidth;
25610 width = this.thumbEl.getWidth();
25611 this.baseScale = width / this.imageEl.OriginWidth;
25613 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25614 height = this.thumbEl.getHeight();
25615 this.baseScale = height / this.imageEl.OriginHeight;
25618 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25620 height = this.thumbEl.getHeight();
25621 this.baseScale = height / this.imageEl.OriginHeight;
25623 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25624 width = this.thumbEl.getWidth();
25625 this.baseScale = width / this.imageEl.OriginWidth;
25633 getScaleLevel : function()
25635 return this.baseScale * Math.pow(1.1, this.scale);
25638 onTouchStart : function(e)
25640 if(!this.canvasLoaded){
25641 this.beforeSelectFile(e);
25645 var touches = e.browserEvent.touches;
25651 if(touches.length == 1){
25652 this.onMouseDown(e);
25656 if(touches.length != 2){
25662 for(var i = 0, finger; finger = touches[i]; i++){
25663 coords.push(finger.pageX, finger.pageY);
25666 var x = Math.pow(coords[0] - coords[2], 2);
25667 var y = Math.pow(coords[1] - coords[3], 2);
25669 this.startDistance = Math.sqrt(x + y);
25671 this.startScale = this.scale;
25673 this.pinching = true;
25674 this.dragable = false;
25678 onTouchMove : function(e)
25680 if(!this.pinching && !this.dragable){
25684 var touches = e.browserEvent.touches;
25691 this.onMouseMove(e);
25697 for(var i = 0, finger; finger = touches[i]; i++){
25698 coords.push(finger.pageX, finger.pageY);
25701 var x = Math.pow(coords[0] - coords[2], 2);
25702 var y = Math.pow(coords[1] - coords[3], 2);
25704 this.endDistance = Math.sqrt(x + y);
25706 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25708 if(!this.zoomable()){
25709 this.scale = this.startScale;
25717 onTouchEnd : function(e)
25719 this.pinching = false;
25720 this.dragable = false;
25724 process : function(file, crop)
25727 this.maskEl.mask(this.loadingText);
25730 this.xhr = new XMLHttpRequest();
25732 file.xhr = this.xhr;
25734 this.xhr.open(this.method, this.url, true);
25737 "Accept": "application/json",
25738 "Cache-Control": "no-cache",
25739 "X-Requested-With": "XMLHttpRequest"
25742 for (var headerName in headers) {
25743 var headerValue = headers[headerName];
25745 this.xhr.setRequestHeader(headerName, headerValue);
25751 this.xhr.onload = function()
25753 _this.xhrOnLoad(_this.xhr);
25756 this.xhr.onerror = function()
25758 _this.xhrOnError(_this.xhr);
25761 var formData = new FormData();
25763 formData.append('returnHTML', 'NO');
25766 formData.append('crop', crop);
25769 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25770 formData.append(this.paramName, file, file.name);
25773 if(typeof(file.filename) != 'undefined'){
25774 formData.append('filename', file.filename);
25777 if(typeof(file.mimetype) != 'undefined'){
25778 formData.append('mimetype', file.mimetype);
25781 if(this.fireEvent('arrange', this, formData) != false){
25782 this.xhr.send(formData);
25786 xhrOnLoad : function(xhr)
25789 this.maskEl.unmask();
25792 if (xhr.readyState !== 4) {
25793 this.fireEvent('exception', this, xhr);
25797 var response = Roo.decode(xhr.responseText);
25799 if(!response.success){
25800 this.fireEvent('exception', this, xhr);
25804 var response = Roo.decode(xhr.responseText);
25806 this.fireEvent('upload', this, response);
25810 xhrOnError : function()
25813 this.maskEl.unmask();
25816 Roo.log('xhr on error');
25818 var response = Roo.decode(xhr.responseText);
25824 prepare : function(file)
25827 this.maskEl.mask(this.loadingText);
25833 if(typeof(file) === 'string'){
25834 this.loadCanvas(file);
25838 if(!file || !this.urlAPI){
25843 this.cropType = file.type;
25847 if(this.fireEvent('prepare', this, this.file) != false){
25849 var reader = new FileReader();
25851 reader.onload = function (e) {
25852 if (e.target.error) {
25853 Roo.log(e.target.error);
25857 var buffer = e.target.result,
25858 dataView = new DataView(buffer),
25860 maxOffset = dataView.byteLength - 4,
25864 if (dataView.getUint16(0) === 0xffd8) {
25865 while (offset < maxOffset) {
25866 markerBytes = dataView.getUint16(offset);
25868 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25869 markerLength = dataView.getUint16(offset + 2) + 2;
25870 if (offset + markerLength > dataView.byteLength) {
25871 Roo.log('Invalid meta data: Invalid segment size.');
25875 if(markerBytes == 0xffe1){
25876 _this.parseExifData(
25883 offset += markerLength;
25893 var url = _this.urlAPI.createObjectURL(_this.file);
25895 _this.loadCanvas(url);
25900 reader.readAsArrayBuffer(this.file);
25906 parseExifData : function(dataView, offset, length)
25908 var tiffOffset = offset + 10,
25912 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25913 // No Exif data, might be XMP data instead
25917 // Check for the ASCII code for "Exif" (0x45786966):
25918 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25919 // No Exif data, might be XMP data instead
25922 if (tiffOffset + 8 > dataView.byteLength) {
25923 Roo.log('Invalid Exif data: Invalid segment size.');
25926 // Check for the two null bytes:
25927 if (dataView.getUint16(offset + 8) !== 0x0000) {
25928 Roo.log('Invalid Exif data: Missing byte alignment offset.');
25931 // Check the byte alignment:
25932 switch (dataView.getUint16(tiffOffset)) {
25934 littleEndian = true;
25937 littleEndian = false;
25940 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25943 // Check for the TIFF tag marker (0x002A):
25944 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25945 Roo.log('Invalid Exif data: Missing TIFF marker.');
25948 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25949 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25951 this.parseExifTags(
25954 tiffOffset + dirOffset,
25959 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25964 if (dirOffset + 6 > dataView.byteLength) {
25965 Roo.log('Invalid Exif data: Invalid directory offset.');
25968 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25969 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25970 if (dirEndOffset + 4 > dataView.byteLength) {
25971 Roo.log('Invalid Exif data: Invalid directory size.');
25974 for (i = 0; i < tagsNumber; i += 1) {
25978 dirOffset + 2 + 12 * i, // tag offset
25982 // Return the offset to the next directory:
25983 return dataView.getUint32(dirEndOffset, littleEndian);
25986 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
25988 var tag = dataView.getUint16(offset, littleEndian);
25990 this.exif[tag] = this.getExifValue(
25994 dataView.getUint16(offset + 2, littleEndian), // tag type
25995 dataView.getUint32(offset + 4, littleEndian), // tag length
26000 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26002 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26011 Roo.log('Invalid Exif data: Invalid tag type.');
26015 tagSize = tagType.size * length;
26016 // Determine if the value is contained in the dataOffset bytes,
26017 // or if the value at the dataOffset is a pointer to the actual data:
26018 dataOffset = tagSize > 4 ?
26019 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26020 if (dataOffset + tagSize > dataView.byteLength) {
26021 Roo.log('Invalid Exif data: Invalid data offset.');
26024 if (length === 1) {
26025 return tagType.getValue(dataView, dataOffset, littleEndian);
26028 for (i = 0; i < length; i += 1) {
26029 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26032 if (tagType.ascii) {
26034 // Concatenate the chars:
26035 for (i = 0; i < values.length; i += 1) {
26037 // Ignore the terminating NULL byte(s):
26038 if (c === '\u0000') {
26050 Roo.apply(Roo.bootstrap.UploadCropbox, {
26052 'Orientation': 0x0112
26056 1: 0, //'top-left',
26058 3: 180, //'bottom-right',
26059 // 4: 'bottom-left',
26061 6: 90, //'right-top',
26062 // 7: 'right-bottom',
26063 8: 270 //'left-bottom'
26067 // byte, 8-bit unsigned int:
26069 getValue: function (dataView, dataOffset) {
26070 return dataView.getUint8(dataOffset);
26074 // ascii, 8-bit byte:
26076 getValue: function (dataView, dataOffset) {
26077 return String.fromCharCode(dataView.getUint8(dataOffset));
26082 // short, 16 bit int:
26084 getValue: function (dataView, dataOffset, littleEndian) {
26085 return dataView.getUint16(dataOffset, littleEndian);
26089 // long, 32 bit int:
26091 getValue: function (dataView, dataOffset, littleEndian) {
26092 return dataView.getUint32(dataOffset, littleEndian);
26096 // rational = two long values, first is numerator, second is denominator:
26098 getValue: function (dataView, dataOffset, littleEndian) {
26099 return dataView.getUint32(dataOffset, littleEndian) /
26100 dataView.getUint32(dataOffset + 4, littleEndian);
26104 // slong, 32 bit signed int:
26106 getValue: function (dataView, dataOffset, littleEndian) {
26107 return dataView.getInt32(dataOffset, littleEndian);
26111 // srational, two slongs, first is numerator, second is denominator:
26113 getValue: function (dataView, dataOffset, littleEndian) {
26114 return dataView.getInt32(dataOffset, littleEndian) /
26115 dataView.getInt32(dataOffset + 4, littleEndian);
26125 cls : 'btn-group roo-upload-cropbox-rotate-left',
26126 action : 'rotate-left',
26130 cls : 'btn btn-default',
26131 html : '<i class="fa fa-undo"></i>'
26137 cls : 'btn-group roo-upload-cropbox-picture',
26138 action : 'picture',
26142 cls : 'btn btn-default',
26143 html : '<i class="fa fa-picture-o"></i>'
26149 cls : 'btn-group roo-upload-cropbox-rotate-right',
26150 action : 'rotate-right',
26154 cls : 'btn btn-default',
26155 html : '<i class="fa fa-repeat"></i>'
26163 cls : 'btn-group roo-upload-cropbox-rotate-left',
26164 action : 'rotate-left',
26168 cls : 'btn btn-default',
26169 html : '<i class="fa fa-undo"></i>'
26175 cls : 'btn-group roo-upload-cropbox-download',
26176 action : 'download',
26180 cls : 'btn btn-default',
26181 html : '<i class="fa fa-download"></i>'
26187 cls : 'btn-group roo-upload-cropbox-crop',
26192 cls : 'btn btn-default',
26193 html : '<i class="fa fa-crop"></i>'
26199 cls : 'btn-group roo-upload-cropbox-trash',
26204 cls : 'btn btn-default',
26205 html : '<i class="fa fa-trash"></i>'
26211 cls : 'btn-group roo-upload-cropbox-rotate-right',
26212 action : 'rotate-right',
26216 cls : 'btn btn-default',
26217 html : '<i class="fa fa-repeat"></i>'
26225 cls : 'btn-group roo-upload-cropbox-rotate-left',
26226 action : 'rotate-left',
26230 cls : 'btn btn-default',
26231 html : '<i class="fa fa-undo"></i>'
26237 cls : 'btn-group roo-upload-cropbox-rotate-right',
26238 action : 'rotate-right',
26242 cls : 'btn btn-default',
26243 html : '<i class="fa fa-repeat"></i>'
26256 * @class Roo.bootstrap.DocumentManager
26257 * @extends Roo.bootstrap.Component
26258 * Bootstrap DocumentManager class
26259 * @cfg {String} paramName default 'imageUpload'
26260 * @cfg {String} method default POST
26261 * @cfg {String} url action url
26262 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26263 * @cfg {Boolean} multiple multiple upload default true
26264 * @cfg {Number} thumbSize default 300
26265 * @cfg {String} fieldLabel
26266 * @cfg {Number} labelWidth default 4
26267 * @cfg {String} labelAlign (left|top) default left
26268 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26271 * Create a new DocumentManager
26272 * @param {Object} config The config object
26275 Roo.bootstrap.DocumentManager = function(config){
26276 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26281 * Fire when initial the DocumentManager
26282 * @param {Roo.bootstrap.DocumentManager} this
26287 * inspect selected file
26288 * @param {Roo.bootstrap.DocumentManager} this
26289 * @param {File} file
26294 * Fire when xhr load exception
26295 * @param {Roo.bootstrap.DocumentManager} this
26296 * @param {XMLHttpRequest} xhr
26298 "exception" : true,
26301 * prepare the form data
26302 * @param {Roo.bootstrap.DocumentManager} this
26303 * @param {Object} formData
26308 * Fire when remove the file
26309 * @param {Roo.bootstrap.DocumentManager} this
26310 * @param {Object} file
26315 * Fire after refresh the file
26316 * @param {Roo.bootstrap.DocumentManager} this
26321 * Fire after click the image
26322 * @param {Roo.bootstrap.DocumentManager} this
26323 * @param {Object} file
26328 * Fire when upload a image and editable set to true
26329 * @param {Roo.bootstrap.DocumentManager} this
26330 * @param {Object} file
26334 * @event beforeselectfile
26335 * Fire before select file
26336 * @param {Roo.bootstrap.DocumentManager} this
26338 "beforeselectfile" : true,
26341 * Fire before process file
26342 * @param {Roo.bootstrap.DocumentManager} this
26343 * @param {Object} file
26350 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
26359 paramName : 'imageUpload',
26362 labelAlign : 'left',
26369 getAutoCreate : function()
26371 var managerWidget = {
26373 cls : 'roo-document-manager',
26377 cls : 'roo-document-manager-selector',
26382 cls : 'roo-document-manager-uploader',
26386 cls : 'roo-document-manager-upload-btn',
26387 html : '<i class="fa fa-plus"></i>'
26398 cls : 'column col-md-12',
26403 if(this.fieldLabel.length){
26408 cls : 'column col-md-12',
26409 html : this.fieldLabel
26413 cls : 'column col-md-12',
26418 if(this.labelAlign == 'left'){
26422 cls : 'column col-md-' + this.labelWidth,
26423 html : this.fieldLabel
26427 cls : 'column col-md-' + (12 - this.labelWidth),
26437 cls : 'row clearfix',
26445 initEvents : function()
26447 this.managerEl = this.el.select('.roo-document-manager', true).first();
26448 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26450 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26451 this.selectorEl.hide();
26454 this.selectorEl.attr('multiple', 'multiple');
26457 this.selectorEl.on('change', this.onFileSelected, this);
26459 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26460 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26462 this.uploader.on('click', this.onUploaderClick, this);
26464 this.renderProgressDialog();
26468 window.addEventListener("resize", function() { _this.refresh(); } );
26470 this.fireEvent('initial', this);
26473 renderProgressDialog : function()
26477 this.progressDialog = new Roo.bootstrap.Modal({
26478 cls : 'roo-document-manager-progress-dialog',
26479 allow_close : false,
26489 btnclick : function() {
26490 _this.uploadCancel();
26496 this.progressDialog.render(Roo.get(document.body));
26498 this.progress = new Roo.bootstrap.Progress({
26499 cls : 'roo-document-manager-progress',
26504 this.progress.render(this.progressDialog.getChildContainer());
26506 this.progressBar = new Roo.bootstrap.ProgressBar({
26507 cls : 'roo-document-manager-progress-bar',
26510 aria_valuemax : 12,
26514 this.progressBar.render(this.progress.getChildContainer());
26517 onUploaderClick : function(e)
26519 e.preventDefault();
26521 if(this.fireEvent('beforeselectfile', this) != false){
26522 this.selectorEl.dom.click();
26527 onFileSelected : function(e)
26529 e.preventDefault();
26531 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26535 Roo.each(this.selectorEl.dom.files, function(file){
26536 if(this.fireEvent('inspect', this, file) != false){
26537 this.files.push(file);
26547 this.selectorEl.dom.value = '';
26549 if(!this.files.length){
26553 if(this.boxes > 0 && this.files.length > this.boxes){
26554 this.files = this.files.slice(0, this.boxes);
26557 this.uploader.show();
26559 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26560 this.uploader.hide();
26569 Roo.each(this.files, function(file){
26571 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26572 var f = this.renderPreview(file);
26577 if(file.type.indexOf('image') != -1){
26578 this.delegates.push(
26580 _this.process(file);
26581 }).createDelegate(this)
26589 _this.process(file);
26590 }).createDelegate(this)
26595 this.files = files;
26597 this.delegates = this.delegates.concat(docs);
26599 if(!this.delegates.length){
26604 this.progressBar.aria_valuemax = this.delegates.length;
26611 arrange : function()
26613 if(!this.delegates.length){
26614 this.progressDialog.hide();
26619 var delegate = this.delegates.shift();
26621 this.progressDialog.show();
26623 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26625 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26630 refresh : function()
26632 this.uploader.show();
26634 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26635 this.uploader.hide();
26638 Roo.isTouch ? this.closable(false) : this.closable(true);
26640 this.fireEvent('refresh', this);
26643 onRemove : function(e, el, o)
26645 e.preventDefault();
26647 this.fireEvent('remove', this, o);
26651 remove : function(o)
26655 Roo.each(this.files, function(file){
26656 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26665 this.files = files;
26672 Roo.each(this.files, function(file){
26677 file.target.remove();
26686 onClick : function(e, el, o)
26688 e.preventDefault();
26690 this.fireEvent('click', this, o);
26694 closable : function(closable)
26696 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26698 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26710 xhrOnLoad : function(xhr)
26712 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26716 if (xhr.readyState !== 4) {
26718 this.fireEvent('exception', this, xhr);
26722 var response = Roo.decode(xhr.responseText);
26724 if(!response.success){
26726 this.fireEvent('exception', this, xhr);
26730 var file = this.renderPreview(response.data);
26732 this.files.push(file);
26738 xhrOnError : function(xhr)
26740 Roo.log('xhr on error');
26742 var response = Roo.decode(xhr.responseText);
26749 process : function(file)
26751 if(this.fireEvent('process', this, file) !== false){
26752 if(this.editable && file.type.indexOf('image') != -1){
26753 this.fireEvent('edit', this, file);
26757 this.uploadStart(file, false);
26764 uploadStart : function(file, crop)
26766 this.xhr = new XMLHttpRequest();
26768 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26773 file.xhr = this.xhr;
26775 this.managerEl.createChild({
26777 cls : 'roo-document-manager-loading',
26781 tooltip : file.name,
26782 cls : 'roo-document-manager-thumb',
26783 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26789 this.xhr.open(this.method, this.url, true);
26792 "Accept": "application/json",
26793 "Cache-Control": "no-cache",
26794 "X-Requested-With": "XMLHttpRequest"
26797 for (var headerName in headers) {
26798 var headerValue = headers[headerName];
26800 this.xhr.setRequestHeader(headerName, headerValue);
26806 this.xhr.onload = function()
26808 _this.xhrOnLoad(_this.xhr);
26811 this.xhr.onerror = function()
26813 _this.xhrOnError(_this.xhr);
26816 var formData = new FormData();
26818 formData.append('returnHTML', 'NO');
26821 formData.append('crop', crop);
26824 formData.append(this.paramName, file, file.name);
26826 if(this.fireEvent('prepare', this, formData) != false){
26827 this.xhr.send(formData);
26831 uploadCancel : function()
26838 this.delegates = [];
26840 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26847 renderPreview : function(file)
26849 if(typeof(file.target) != 'undefined' && file.target){
26853 var previewEl = this.managerEl.createChild({
26855 cls : 'roo-document-manager-preview',
26859 tooltip : file.filename,
26860 cls : 'roo-document-manager-thumb',
26861 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26866 html : '<i class="fa fa-times-circle"></i>'
26871 var close = previewEl.select('button.close', true).first();
26873 close.on('click', this.onRemove, this, file);
26875 file.target = previewEl;
26877 var image = previewEl.select('img', true).first();
26881 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26883 image.on('click', this.onClick, this, file);
26889 onPreviewLoad : function(file, image)
26891 if(typeof(file.target) == 'undefined' || !file.target){
26895 var width = image.dom.naturalWidth || image.dom.width;
26896 var height = image.dom.naturalHeight || image.dom.height;
26898 if(width > height){
26899 file.target.addClass('wide');
26903 file.target.addClass('tall');
26908 uploadFromSource : function(file, crop)
26910 this.xhr = new XMLHttpRequest();
26912 this.managerEl.createChild({
26914 cls : 'roo-document-manager-loading',
26918 tooltip : file.name,
26919 cls : 'roo-document-manager-thumb',
26920 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26926 this.xhr.open(this.method, this.url, true);
26929 "Accept": "application/json",
26930 "Cache-Control": "no-cache",
26931 "X-Requested-With": "XMLHttpRequest"
26934 for (var headerName in headers) {
26935 var headerValue = headers[headerName];
26937 this.xhr.setRequestHeader(headerName, headerValue);
26943 this.xhr.onload = function()
26945 _this.xhrOnLoad(_this.xhr);
26948 this.xhr.onerror = function()
26950 _this.xhrOnError(_this.xhr);
26953 var formData = new FormData();
26955 formData.append('returnHTML', 'NO');
26957 formData.append('crop', crop);
26959 if(typeof(file.filename) != 'undefined'){
26960 formData.append('filename', file.filename);
26963 if(typeof(file.mimetype) != 'undefined'){
26964 formData.append('mimetype', file.mimetype);
26967 if(this.fireEvent('prepare', this, formData) != false){
26968 this.xhr.send(formData);
26978 * @class Roo.bootstrap.DocumentViewer
26979 * @extends Roo.bootstrap.Component
26980 * Bootstrap DocumentViewer class
26983 * Create a new DocumentViewer
26984 * @param {Object} config The config object
26987 Roo.bootstrap.DocumentViewer = function(config){
26988 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26993 * Fire after initEvent
26994 * @param {Roo.bootstrap.DocumentViewer} this
27000 * @param {Roo.bootstrap.DocumentViewer} this
27005 * Fire after trash button
27006 * @param {Roo.bootstrap.DocumentViewer} this
27013 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
27015 getAutoCreate : function()
27019 cls : 'roo-document-viewer',
27023 cls : 'roo-document-viewer-body',
27027 cls : 'roo-document-viewer-thumb',
27031 cls : 'roo-document-viewer-image'
27039 cls : 'roo-document-viewer-footer',
27042 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27050 cls : 'btn btn-default roo-document-viewer-trash',
27051 html : '<i class="fa fa-trash"></i>'
27064 initEvents : function()
27067 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27068 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27070 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27071 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27073 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27074 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27076 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27077 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27079 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27080 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27082 this.bodyEl.on('click', this.onClick, this);
27084 this.trashBtn.on('click', this.onTrash, this);
27088 initial : function()
27090 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27093 this.fireEvent('initial', this);
27097 onClick : function(e)
27099 e.preventDefault();
27101 this.fireEvent('click', this);
27104 onTrash : function(e)
27106 e.preventDefault();
27108 this.fireEvent('trash', this);
27120 * @class Roo.bootstrap.NavProgressBar
27121 * @extends Roo.bootstrap.Component
27122 * Bootstrap NavProgressBar class
27125 * Create a new nav progress bar
27126 * @param {Object} config The config object
27129 Roo.bootstrap.NavProgressBar = function(config){
27130 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27132 this.bullets = this.bullets || [];
27134 // Roo.bootstrap.NavProgressBar.register(this);
27138 * Fires when the active item changes
27139 * @param {Roo.bootstrap.NavProgressBar} this
27140 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27141 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
27148 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
27153 getAutoCreate : function()
27155 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27159 cls : 'roo-navigation-bar-group',
27163 cls : 'roo-navigation-top-bar'
27167 cls : 'roo-navigation-bullets-bar',
27171 cls : 'roo-navigation-bar'
27178 cls : 'roo-navigation-bottom-bar'
27188 initEvents: function()
27193 onRender : function(ct, position)
27195 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27197 if(this.bullets.length){
27198 Roo.each(this.bullets, function(b){
27207 addItem : function(cfg)
27209 var item = new Roo.bootstrap.NavProgressItem(cfg);
27211 item.parentId = this.id;
27212 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27215 var top = new Roo.bootstrap.Element({
27217 cls : 'roo-navigation-bar-text'
27220 var bottom = new Roo.bootstrap.Element({
27222 cls : 'roo-navigation-bar-text'
27225 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27226 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27228 var topText = new Roo.bootstrap.Element({
27230 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27233 var bottomText = new Roo.bootstrap.Element({
27235 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27238 topText.onRender(top.el, null);
27239 bottomText.onRender(bottom.el, null);
27242 item.bottomEl = bottom;
27245 this.barItems.push(item);
27250 getActive : function()
27252 var active = false;
27254 Roo.each(this.barItems, function(v){
27256 if (!v.isActive()) {
27268 setActiveItem : function(item)
27272 Roo.each(this.barItems, function(v){
27273 if (v.rid == item.rid) {
27277 if (v.isActive()) {
27278 v.setActive(false);
27283 item.setActive(true);
27285 this.fireEvent('changed', this, item, prev);
27288 getBarItem: function(rid)
27292 Roo.each(this.barItems, function(e) {
27293 if (e.rid != rid) {
27304 indexOfItem : function(item)
27308 Roo.each(this.barItems, function(v, i){
27310 if (v.rid != item.rid) {
27321 setActiveNext : function()
27323 var i = this.indexOfItem(this.getActive());
27325 if (i > this.barItems.length) {
27329 this.setActiveItem(this.barItems[i+1]);
27332 setActivePrev : function()
27334 var i = this.indexOfItem(this.getActive());
27340 this.setActiveItem(this.barItems[i-1]);
27343 format : function()
27345 if(!this.barItems.length){
27349 var width = 100 / this.barItems.length;
27351 Roo.each(this.barItems, function(i){
27352 i.el.setStyle('width', width + '%');
27353 i.topEl.el.setStyle('width', width + '%');
27354 i.bottomEl.el.setStyle('width', width + '%');
27363 * Nav Progress Item
27368 * @class Roo.bootstrap.NavProgressItem
27369 * @extends Roo.bootstrap.Component
27370 * Bootstrap NavProgressItem class
27371 * @cfg {String} rid the reference id
27372 * @cfg {Boolean} active (true|false) Is item active default false
27373 * @cfg {Boolean} disabled (true|false) Is item active default false
27374 * @cfg {String} html
27375 * @cfg {String} position (top|bottom) text position default bottom
27376 * @cfg {String} icon show icon instead of number
27379 * Create a new NavProgressItem
27380 * @param {Object} config The config object
27382 Roo.bootstrap.NavProgressItem = function(config){
27383 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27388 * The raw click event for the entire grid.
27389 * @param {Roo.bootstrap.NavProgressItem} this
27390 * @param {Roo.EventObject} e
27397 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
27403 position : 'bottom',
27406 getAutoCreate : function()
27408 var iconCls = 'roo-navigation-bar-item-icon';
27410 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27414 cls: 'roo-navigation-bar-item',
27424 cfg.cls += ' active';
27427 cfg.cls += ' disabled';
27433 disable : function()
27435 this.setDisabled(true);
27438 enable : function()
27440 this.setDisabled(false);
27443 initEvents: function()
27445 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27447 this.iconEl.on('click', this.onClick, this);
27450 onClick : function(e)
27452 e.preventDefault();
27458 if(this.fireEvent('click', this, e) === false){
27462 this.parent().setActiveItem(this);
27465 isActive: function ()
27467 return this.active;
27470 setActive : function(state)
27472 if(this.active == state){
27476 this.active = state;
27479 this.el.addClass('active');
27483 this.el.removeClass('active');
27488 setDisabled : function(state)
27490 if(this.disabled == state){
27494 this.disabled = state;
27497 this.el.addClass('disabled');
27501 this.el.removeClass('disabled');
27504 tooltipEl : function()
27506 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27519 * @class Roo.bootstrap.FieldLabel
27520 * @extends Roo.bootstrap.Component
27521 * Bootstrap FieldLabel class
27522 * @cfg {String} html contents of the element
27523 * @cfg {String} tag tag of the element default label
27524 * @cfg {String} cls class of the element
27525 * @cfg {String} target label target
27526 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27527 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27528 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27529 * @cfg {String} iconTooltip default "This field is required"
27532 * Create a new FieldLabel
27533 * @param {Object} config The config object
27536 Roo.bootstrap.FieldLabel = function(config){
27537 Roo.bootstrap.Element.superclass.constructor.call(this, config);
27542 * Fires after the field has been marked as invalid.
27543 * @param {Roo.form.FieldLabel} this
27544 * @param {String} msg The validation message
27549 * Fires after the field has been validated with no errors.
27550 * @param {Roo.form.FieldLabel} this
27556 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
27563 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27564 validClass : 'text-success fa fa-lg fa-check',
27565 iconTooltip : 'This field is required',
27567 getAutoCreate : function(){
27571 cls : 'roo-bootstrap-field-label ' + this.cls,
27577 tooltip : this.iconTooltip
27589 initEvents: function()
27591 Roo.bootstrap.Element.superclass.initEvents.call(this);
27593 this.iconEl = this.el.select('i', true).first();
27595 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27597 Roo.bootstrap.FieldLabel.register(this);
27601 * Mark this field as valid
27603 markValid : function()
27605 this.iconEl.show();
27607 this.iconEl.removeClass(this.invalidClass);
27609 this.iconEl.addClass(this.validClass);
27611 this.fireEvent('valid', this);
27615 * Mark this field as invalid
27616 * @param {String} msg The validation message
27618 markInvalid : function(msg)
27620 this.iconEl.show();
27622 this.iconEl.removeClass(this.validClass);
27624 this.iconEl.addClass(this.invalidClass);
27626 this.fireEvent('invalid', this, msg);
27632 Roo.apply(Roo.bootstrap.FieldLabel, {
27637 * register a FieldLabel Group
27638 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27640 register : function(label)
27642 if(this.groups.hasOwnProperty(label.target)){
27646 this.groups[label.target] = label;
27650 * fetch a FieldLabel Group based on the target
27651 * @param {string} target
27652 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27654 get: function(target) {
27655 if (typeof(this.groups[target]) == 'undefined') {
27659 return this.groups[target] ;
27668 * page DateSplitField.
27674 * @class Roo.bootstrap.DateSplitField
27675 * @extends Roo.bootstrap.Component
27676 * Bootstrap DateSplitField class
27677 * @cfg {string} fieldLabel - the label associated
27678 * @cfg {Number} labelWidth set the width of label (0-12)
27679 * @cfg {String} labelAlign (top|left)
27680 * @cfg {Boolean} dayAllowBlank (true|false) default false
27681 * @cfg {Boolean} monthAllowBlank (true|false) default false
27682 * @cfg {Boolean} yearAllowBlank (true|false) default false
27683 * @cfg {string} dayPlaceholder
27684 * @cfg {string} monthPlaceholder
27685 * @cfg {string} yearPlaceholder
27686 * @cfg {string} dayFormat default 'd'
27687 * @cfg {string} monthFormat default 'm'
27688 * @cfg {string} yearFormat default 'Y'
27692 * Create a new DateSplitField
27693 * @param {Object} config The config object
27696 Roo.bootstrap.DateSplitField = function(config){
27697 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27703 * getting the data of years
27704 * @param {Roo.bootstrap.DateSplitField} this
27705 * @param {Object} years
27710 * getting the data of days
27711 * @param {Roo.bootstrap.DateSplitField} this
27712 * @param {Object} days
27717 * Fires after the field has been marked as invalid.
27718 * @param {Roo.form.Field} this
27719 * @param {String} msg The validation message
27724 * Fires after the field has been validated with no errors.
27725 * @param {Roo.form.Field} this
27731 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
27734 labelAlign : 'top',
27736 dayAllowBlank : false,
27737 monthAllowBlank : false,
27738 yearAllowBlank : false,
27739 dayPlaceholder : '',
27740 monthPlaceholder : '',
27741 yearPlaceholder : '',
27745 isFormField : true,
27747 getAutoCreate : function()
27751 cls : 'row roo-date-split-field-group',
27756 cls : 'form-hidden-field roo-date-split-field-group-value',
27762 if(this.fieldLabel){
27765 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27769 html : this.fieldLabel
27775 Roo.each(['day', 'month', 'year'], function(t){
27778 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27785 inputEl: function ()
27787 return this.el.select('.roo-date-split-field-group-value', true).first();
27790 onRender : function(ct, position)
27794 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27796 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27798 this.dayField = new Roo.bootstrap.ComboBox({
27799 allowBlank : this.dayAllowBlank,
27800 alwaysQuery : true,
27801 displayField : 'value',
27804 forceSelection : true,
27806 placeholder : this.dayPlaceholder,
27807 selectOnFocus : true,
27808 tpl : '<div class="select2-result"><b>{value}</b></div>',
27809 triggerAction : 'all',
27811 valueField : 'value',
27812 store : new Roo.data.SimpleStore({
27813 data : (function() {
27815 _this.fireEvent('days', _this, days);
27818 fields : [ 'value' ]
27821 select : function (_self, record, index)
27823 _this.setValue(_this.getValue());
27828 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27830 this.monthField = new Roo.bootstrap.MonthField({
27831 after : '<i class=\"fa fa-calendar\"></i>',
27832 allowBlank : this.monthAllowBlank,
27833 placeholder : this.monthPlaceholder,
27836 render : function (_self)
27838 this.el.select('span.input-group-addon', true).first().on('click', function(e){
27839 e.preventDefault();
27843 select : function (_self, oldvalue, newvalue)
27845 _this.setValue(_this.getValue());
27850 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27852 this.yearField = new Roo.bootstrap.ComboBox({
27853 allowBlank : this.yearAllowBlank,
27854 alwaysQuery : true,
27855 displayField : 'value',
27858 forceSelection : true,
27860 placeholder : this.yearPlaceholder,
27861 selectOnFocus : true,
27862 tpl : '<div class="select2-result"><b>{value}</b></div>',
27863 triggerAction : 'all',
27865 valueField : 'value',
27866 store : new Roo.data.SimpleStore({
27867 data : (function() {
27869 _this.fireEvent('years', _this, years);
27872 fields : [ 'value' ]
27875 select : function (_self, record, index)
27877 _this.setValue(_this.getValue());
27882 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
27885 setValue : function(v, format)
27887 this.inputEl.dom.value = v;
27889 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
27891 var d = Date.parseDate(v, f);
27898 this.setDay(d.format(this.dayFormat));
27899 this.setMonth(d.format(this.monthFormat));
27900 this.setYear(d.format(this.yearFormat));
27907 setDay : function(v)
27909 this.dayField.setValue(v);
27910 this.inputEl.dom.value = this.getValue();
27915 setMonth : function(v)
27917 this.monthField.setValue(v, true);
27918 this.inputEl.dom.value = this.getValue();
27923 setYear : function(v)
27925 this.yearField.setValue(v);
27926 this.inputEl.dom.value = this.getValue();
27931 getDay : function()
27933 return this.dayField.getValue();
27936 getMonth : function()
27938 return this.monthField.getValue();
27941 getYear : function()
27943 return this.yearField.getValue();
27946 getValue : function()
27948 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
27950 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
27960 this.inputEl.dom.value = '';
27965 validate : function()
27967 var d = this.dayField.validate();
27968 var m = this.monthField.validate();
27969 var y = this.yearField.validate();
27974 (!this.dayAllowBlank && !d) ||
27975 (!this.monthAllowBlank && !m) ||
27976 (!this.yearAllowBlank && !y)
27981 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
27990 this.markInvalid();
27995 markValid : function()
27998 var label = this.el.select('label', true).first();
27999 var icon = this.el.select('i.fa-star', true).first();
28005 this.fireEvent('valid', this);
28009 * Mark this field as invalid
28010 * @param {String} msg The validation message
28012 markInvalid : function(msg)
28015 var label = this.el.select('label', true).first();
28016 var icon = this.el.select('i.fa-star', true).first();
28018 if(label && !icon){
28019 this.el.select('.roo-date-split-field-label', true).createChild({
28021 cls : 'text-danger fa fa-lg fa-star',
28022 tooltip : 'This field is required',
28023 style : 'margin-right:5px;'
28027 this.fireEvent('invalid', this, msg);
28030 clearInvalid : function()
28032 var label = this.el.select('label', true).first();
28033 var icon = this.el.select('i.fa-star', true).first();
28039 this.fireEvent('valid', this);
28042 getName: function()