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, is_body);
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, is_body)
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.
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, {
393 is_body : true,// just to make sure it's constructed?
398 onRender : function(ct, position)
400 /* Roo.log("Roo.bootstrap.Body - onRender");
401 if (this.cls && this.cls.length) {
402 Roo.get(document.body).addClass(this.cls);
422 * @class Roo.bootstrap.ButtonGroup
423 * @extends Roo.bootstrap.Component
424 * Bootstrap ButtonGroup class
425 * @cfg {String} size lg | sm | xs (default empty normal)
426 * @cfg {String} align vertical | justified (default none)
427 * @cfg {String} direction up | down (default down)
428 * @cfg {Boolean} toolbar false | true
429 * @cfg {Boolean} btn true | false
434 * @param {Object} config The config object
437 Roo.bootstrap.ButtonGroup = function(config){
438 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
441 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
449 getAutoCreate : function(){
455 cfg.html = this.html || cfg.html;
466 if (['vertical','justified'].indexOf(this.align)!==-1) {
467 cfg.cls = 'btn-group-' + this.align;
469 if (this.align == 'justified') {
470 console.log(this.items);
474 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
475 cfg.cls += ' btn-group-' + this.size;
478 if (this.direction == 'up') {
479 cfg.cls += ' dropup' ;
495 * @class Roo.bootstrap.Button
496 * @extends Roo.bootstrap.Component
497 * Bootstrap Button class
498 * @cfg {String} html The button content
499 * @cfg {String} weight ( primary | success | info | warning | danger | link ) default
500 * @cfg {String} size ( lg | sm | xs)
501 * @cfg {String} tag ( a | input | submit)
502 * @cfg {String} href empty or href
503 * @cfg {Boolean} disabled default false;
504 * @cfg {Boolean} isClose default false;
505 * @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)
506 * @cfg {String} badge text for badge
507 * @cfg {String} theme default
508 * @cfg {Boolean} inverse
509 * @cfg {Boolean} toggle
510 * @cfg {String} ontext text for on toggle state
511 * @cfg {String} offtext text for off toggle state
512 * @cfg {Boolean} defaulton
513 * @cfg {Boolean} preventDefault default true
514 * @cfg {Boolean} removeClass remove the standard class..
515 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
518 * Create a new button
519 * @param {Object} config The config object
523 Roo.bootstrap.Button = function(config){
524 Roo.bootstrap.Button.superclass.constructor.call(this, config);
529 * When a butotn is pressed
530 * @param {Roo.bootstrap.Button} this
531 * @param {Roo.EventObject} e
536 * After the button has been toggles
537 * @param {Roo.EventObject} e
538 * @param {boolean} pressed (also available as button.pressed)
544 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
562 preventDefault: true,
571 getAutoCreate : function(){
579 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
580 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
585 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
587 if (this.toggle == true) {
590 cls: 'slider-frame roo-button',
595 'data-off-text':'OFF',
596 cls: 'slider-button',
602 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
603 cfg.cls += ' '+this.weight;
612 cfg["aria-hidden"] = true;
614 cfg.html = "×";
620 if (this.theme==='default') {
621 cfg.cls = 'btn roo-button';
623 //if (this.parentType != 'Navbar') {
624 this.weight = this.weight.length ? this.weight : 'default';
626 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
628 cfg.cls += ' btn-' + this.weight;
630 } else if (this.theme==='glow') {
633 cfg.cls = 'btn-glow roo-button';
635 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
637 cfg.cls += ' ' + this.weight;
643 this.cls += ' inverse';
648 cfg.cls += ' active';
652 cfg.disabled = 'disabled';
656 Roo.log('changing to ul' );
658 this.glyphicon = 'caret';
661 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
663 //gsRoo.log(this.parentType);
664 if (this.parentType === 'Navbar' && !this.parent().bar) {
665 Roo.log('changing to li?');
674 href : this.href || '#'
677 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
678 cfg.cls += ' dropdown';
685 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
687 if (this.glyphicon) {
688 cfg.html = ' ' + cfg.html;
693 cls: 'glyphicon glyphicon-' + this.glyphicon
703 // cfg.cls='btn roo-button';
707 var value = cfg.html;
712 cls: 'glyphicon glyphicon-' + this.glyphicon,
731 cfg.cls += ' dropdown';
732 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
735 if (cfg.tag !== 'a' && this.href !== '') {
736 throw "Tag must be a to set href.";
737 } else if (this.href.length > 0) {
738 cfg.href = this.href;
741 if(this.removeClass){
746 cfg.target = this.target;
751 initEvents: function() {
752 // Roo.log('init events?');
753 // Roo.log(this.el.dom);
756 if (typeof (this.menu) != 'undefined') {
757 this.menu.parentType = this.xtype;
758 this.menu.triggerEl = this.el;
759 this.addxtype(Roo.apply({}, this.menu));
763 if (this.el.hasClass('roo-button')) {
764 this.el.on('click', this.onClick, this);
766 this.el.select('.roo-button').on('click', this.onClick, this);
769 if(this.removeClass){
770 this.el.on('click', this.onClick, this);
773 this.el.enableDisplayMode();
776 onClick : function(e)
783 Roo.log('button on click ');
784 if(this.preventDefault){
787 if (this.pressed === true || this.pressed === false) {
788 this.pressed = !this.pressed;
789 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
790 this.fireEvent('toggle', this, e, this.pressed);
794 this.fireEvent('click', this, e);
798 * Enables this button
802 this.disabled = false;
803 this.el.removeClass('disabled');
807 * Disable this button
811 this.disabled = true;
812 this.el.addClass('disabled');
815 * sets the active state on/off,
816 * @param {Boolean} state (optional) Force a particular state
818 setActive : function(v) {
820 this.el[v ? 'addClass' : 'removeClass']('active');
823 * toggles the current active state
825 toggleActive : function()
827 var active = this.el.hasClass('active');
828 this.setActive(!active);
832 setText : function(str)
834 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
838 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
861 * @class Roo.bootstrap.Column
862 * @extends Roo.bootstrap.Component
863 * Bootstrap Column class
864 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
865 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
866 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
867 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
868 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
869 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
870 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
871 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
874 * @cfg {Boolean} hidden (true|false) hide the element
875 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
876 * @cfg {String} fa (ban|check|...) font awesome icon
877 * @cfg {Number} fasize (1|2|....) font awsome size
879 * @cfg {String} icon (info-sign|check|...) glyphicon name
881 * @cfg {String} html content of column.
884 * Create a new Column
885 * @param {Object} config The config object
888 Roo.bootstrap.Column = function(config){
889 Roo.bootstrap.Column.superclass.constructor.call(this, config);
892 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
910 getAutoCreate : function(){
911 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
919 ['xs','sm','md','lg'].map(function(size){
920 //Roo.log( size + ':' + settings[size]);
922 if (settings[size+'off'] !== false) {
923 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
926 if (settings[size] === false) {
930 if (!settings[size]) { // 0 = hidden
931 cfg.cls += ' hidden-' + size;
934 cfg.cls += ' col-' + size + '-' + settings[size];
939 cfg.cls += ' hidden';
942 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
943 cfg.cls +=' alert alert-' + this.alert;
947 if (this.html.length) {
948 cfg.html = this.html;
952 if (this.fasize > 1) {
953 fasize = ' fa-' + this.fasize + 'x';
955 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
960 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
979 * @class Roo.bootstrap.Container
980 * @extends Roo.bootstrap.Component
981 * Bootstrap Container class
982 * @cfg {Boolean} jumbotron is it a jumbotron element
983 * @cfg {String} html content of element
984 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
985 * @cfg {String} panel (primary|success|info|warning|danger) render as panel - type - primary/success.....
986 * @cfg {String} header content of header (for panel)
987 * @cfg {String} footer content of footer (for panel)
988 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
989 * @cfg {String} tag (header|aside|section) type of HTML tag.
990 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
991 * @cfg {String} fa font awesome icon
992 * @cfg {String} icon (info-sign|check|...) glyphicon name
993 * @cfg {Boolean} hidden (true|false) hide the element
994 * @cfg {Boolean} expandable (true|false) default false
995 * @cfg {Boolean} expanded (true|false) default true
996 * @cfg {String} rheader contet on the right of header
997 * @cfg {Boolean} clickable (true|false) default false
1001 * Create a new Container
1002 * @param {Object} config The config object
1005 Roo.bootstrap.Container = function(config){
1006 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1012 * After the panel has been expand
1014 * @param {Roo.bootstrap.Container} this
1019 * After the panel has been collapsed
1021 * @param {Roo.bootstrap.Container} this
1026 * When a element is chick
1027 * @param {Roo.bootstrap.Container} this
1028 * @param {Roo.EventObject} e
1034 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1052 getChildContainer : function() {
1058 if (this.panel.length) {
1059 return this.el.select('.panel-body',true).first();
1066 getAutoCreate : function(){
1069 tag : this.tag || 'div',
1073 if (this.jumbotron) {
1074 cfg.cls = 'jumbotron';
1079 // - this is applied by the parent..
1081 // cfg.cls = this.cls + '';
1084 if (this.sticky.length) {
1086 var bd = Roo.get(document.body);
1087 if (!bd.hasClass('bootstrap-sticky')) {
1088 bd.addClass('bootstrap-sticky');
1089 Roo.select('html',true).setStyle('height', '100%');
1092 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1096 if (this.well.length) {
1097 switch (this.well) {
1100 cfg.cls +=' well well-' +this.well;
1109 cfg.cls += ' hidden';
1113 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1114 cfg.cls +=' alert alert-' + this.alert;
1119 if (this.panel.length) {
1120 cfg.cls += ' panel panel-' + this.panel;
1122 if (this.header.length) {
1126 if(this.expandable){
1128 cfg.cls = cfg.cls + ' expandable';
1132 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1140 cls : 'panel-title',
1141 html : (this.expandable ? ' ' : '') + this.header
1145 cls: 'panel-header-right',
1151 cls : 'panel-heading',
1152 style : this.expandable ? 'cursor: pointer' : '',
1160 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1165 if (this.footer.length) {
1167 cls : 'panel-footer',
1176 body.html = this.html || cfg.html;
1177 // prefix with the icons..
1179 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1182 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1187 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1188 cfg.cls = 'container';
1194 initEvents: function()
1196 if(this.expandable){
1197 var headerEl = this.headerEl();
1200 headerEl.on('click', this.onToggleClick, this);
1205 this.el.on('click', this.onClick, this);
1210 onToggleClick : function()
1212 var headerEl = this.headerEl();
1228 if(this.fireEvent('expand', this)) {
1230 this.expanded = true;
1232 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1234 this.el.select('.panel-body',true).first().removeClass('hide');
1236 var toggleEl = this.toggleEl();
1242 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1247 collapse : function()
1249 if(this.fireEvent('collapse', this)) {
1251 this.expanded = false;
1253 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1254 this.el.select('.panel-body',true).first().addClass('hide');
1256 var toggleEl = this.toggleEl();
1262 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1266 toggleEl : function()
1268 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1272 return this.el.select('.panel-heading .fa',true).first();
1275 headerEl : function()
1277 if(!this.el || !this.panel.length || !this.header.length){
1281 return this.el.select('.panel-heading',true).first()
1284 titleEl : function()
1286 if(!this.el || !this.panel.length || !this.header.length){
1290 return this.el.select('.panel-title',true).first();
1293 setTitle : function(v)
1295 var titleEl = this.titleEl();
1301 titleEl.dom.innerHTML = v;
1304 getTitle : function()
1307 var titleEl = this.titleEl();
1313 return titleEl.dom.innerHTML;
1316 setRightTitle : function(v)
1318 var t = this.el.select('.panel-header-right',true).first();
1324 t.dom.innerHTML = v;
1327 onClick : function(e)
1331 this.fireEvent('click', this, e);
1345 * @class Roo.bootstrap.Img
1346 * @extends Roo.bootstrap.Component
1347 * Bootstrap Img class
1348 * @cfg {Boolean} imgResponsive false | true
1349 * @cfg {String} border rounded | circle | thumbnail
1350 * @cfg {String} src image source
1351 * @cfg {String} alt image alternative text
1352 * @cfg {String} href a tag href
1353 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1354 * @cfg {String} xsUrl xs image source
1355 * @cfg {String} smUrl sm image source
1356 * @cfg {String} mdUrl md image source
1357 * @cfg {String} lgUrl lg image source
1360 * Create a new Input
1361 * @param {Object} config The config object
1364 Roo.bootstrap.Img = function(config){
1365 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1371 * The img click event for the img.
1372 * @param {Roo.EventObject} e
1378 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1380 imgResponsive: true,
1390 getAutoCreate : function()
1392 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1393 return this.createSingleImg();
1398 cls: 'roo-image-responsive-group',
1403 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1405 if(!_this[size + 'Url']){
1411 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1412 html: _this.html || cfg.html,
1413 src: _this[size + 'Url']
1416 img.cls += ' roo-image-responsive-' + size;
1418 var s = ['xs', 'sm', 'md', 'lg'];
1420 s.splice(s.indexOf(size), 1);
1422 Roo.each(s, function(ss){
1423 img.cls += ' hidden-' + ss;
1426 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1427 cfg.cls += ' img-' + _this.border;
1431 cfg.alt = _this.alt;
1444 a.target = _this.target;
1448 cfg.cn.push((_this.href) ? a : img);
1455 createSingleImg : function()
1459 cls: (this.imgResponsive) ? 'img-responsive' : '',
1461 src : 'about:blank' // just incase src get's set to undefined?!?
1464 cfg.html = this.html || cfg.html;
1466 cfg.src = this.src || cfg.src;
1468 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1469 cfg.cls += ' img-' + this.border;
1486 a.target = this.target;
1491 return (this.href) ? a : cfg;
1494 initEvents: function()
1497 this.el.on('click', this.onClick, this);
1502 onClick : function(e)
1504 Roo.log('img onclick');
1505 this.fireEvent('click', this, e);
1519 * @class Roo.bootstrap.Link
1520 * @extends Roo.bootstrap.Component
1521 * Bootstrap Link Class
1522 * @cfg {String} alt image alternative text
1523 * @cfg {String} href a tag href
1524 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1525 * @cfg {String} html the content of the link.
1526 * @cfg {String} anchor name for the anchor link
1528 * @cfg {Boolean} preventDefault (true | false) default false
1532 * Create a new Input
1533 * @param {Object} config The config object
1536 Roo.bootstrap.Link = function(config){
1537 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1543 * The img click event for the img.
1544 * @param {Roo.EventObject} e
1550 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1554 preventDefault: false,
1558 getAutoCreate : function()
1564 // anchor's do not require html/href...
1565 if (this.anchor === false) {
1566 cfg.html = this.html || '';
1567 cfg.href = this.href || '#';
1569 cfg.name = this.anchor;
1570 if (this.html !== false) {
1571 cfg.html = this.html;
1573 if (this.href !== false) {
1574 cfg.href = this.href;
1578 if(this.alt !== false){
1583 if(this.target !== false) {
1584 cfg.target = this.target;
1590 initEvents: function() {
1592 if(!this.href || this.preventDefault){
1593 this.el.on('click', this.onClick, this);
1597 onClick : function(e)
1599 if(this.preventDefault){
1602 //Roo.log('img onclick');
1603 this.fireEvent('click', this, e);
1616 * @class Roo.bootstrap.Header
1617 * @extends Roo.bootstrap.Component
1618 * Bootstrap Header class
1619 * @cfg {String} html content of header
1620 * @cfg {Number} level (1|2|3|4|5|6) default 1
1623 * Create a new Header
1624 * @param {Object} config The config object
1628 Roo.bootstrap.Header = function(config){
1629 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1632 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1640 getAutoCreate : function(){
1645 tag: 'h' + (1 *this.level),
1646 html: this.html || ''
1658 * Ext JS Library 1.1.1
1659 * Copyright(c) 2006-2007, Ext JS, LLC.
1661 * Originally Released Under LGPL - original licence link has changed is not relivant.
1664 * <script type="text/javascript">
1668 * @class Roo.bootstrap.MenuMgr
1669 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1672 Roo.bootstrap.MenuMgr = function(){
1673 var menus, active, groups = {}, attached = false, lastShow = new Date();
1675 // private - called when first menu is created
1678 active = new Roo.util.MixedCollection();
1679 Roo.get(document).addKeyListener(27, function(){
1680 if(active.length > 0){
1688 if(active && active.length > 0){
1689 var c = active.clone();
1699 if(active.length < 1){
1700 Roo.get(document).un("mouseup", onMouseDown);
1708 var last = active.last();
1709 lastShow = new Date();
1712 Roo.get(document).on("mouseup", onMouseDown);
1717 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1718 m.parentMenu.activeChild = m;
1719 }else if(last && last.isVisible()){
1720 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1725 function onBeforeHide(m){
1727 m.activeChild.hide();
1729 if(m.autoHideTimer){
1730 clearTimeout(m.autoHideTimer);
1731 delete m.autoHideTimer;
1736 function onBeforeShow(m){
1737 var pm = m.parentMenu;
1738 if(!pm && !m.allowOtherMenus){
1740 }else if(pm && pm.activeChild && active != m){
1741 pm.activeChild.hide();
1745 // private this should really trigger on mouseup..
1746 function onMouseDown(e){
1747 Roo.log("on Mouse Up");
1749 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1750 Roo.log("MenuManager hideAll");
1759 function onBeforeCheck(mi, state){
1761 var g = groups[mi.group];
1762 for(var i = 0, l = g.length; i < l; i++){
1764 g[i].setChecked(false);
1773 * Hides all menus that are currently visible
1775 hideAll : function(){
1780 register : function(menu){
1784 menus[menu.id] = menu;
1785 menu.on("beforehide", onBeforeHide);
1786 menu.on("hide", onHide);
1787 menu.on("beforeshow", onBeforeShow);
1788 menu.on("show", onShow);
1790 if(g && menu.events["checkchange"]){
1794 groups[g].push(menu);
1795 menu.on("checkchange", onCheck);
1800 * Returns a {@link Roo.menu.Menu} object
1801 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1802 * be used to generate and return a new Menu instance.
1804 get : function(menu){
1805 if(typeof menu == "string"){ // menu id
1807 }else if(menu.events){ // menu instance
1810 /*else if(typeof menu.length == 'number'){ // array of menu items?
1811 return new Roo.bootstrap.Menu({items:menu});
1812 }else{ // otherwise, must be a config
1813 return new Roo.bootstrap.Menu(menu);
1820 unregister : function(menu){
1821 delete menus[menu.id];
1822 menu.un("beforehide", onBeforeHide);
1823 menu.un("hide", onHide);
1824 menu.un("beforeshow", onBeforeShow);
1825 menu.un("show", onShow);
1827 if(g && menu.events["checkchange"]){
1828 groups[g].remove(menu);
1829 menu.un("checkchange", onCheck);
1834 registerCheckable : function(menuItem){
1835 var g = menuItem.group;
1840 groups[g].push(menuItem);
1841 menuItem.on("beforecheckchange", onBeforeCheck);
1846 unregisterCheckable : function(menuItem){
1847 var g = menuItem.group;
1849 groups[g].remove(menuItem);
1850 menuItem.un("beforecheckchange", onBeforeCheck);
1862 * @class Roo.bootstrap.Menu
1863 * @extends Roo.bootstrap.Component
1864 * Bootstrap Menu class - container for MenuItems
1865 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1866 * @cfg {bool} hidden if the menu should be hidden when rendered.
1870 * @param {Object} config The config object
1874 Roo.bootstrap.Menu = function(config){
1875 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1876 if (this.registerMenu && this.type != 'treeview') {
1877 Roo.bootstrap.MenuMgr.register(this);
1882 * Fires before this menu is displayed
1883 * @param {Roo.menu.Menu} this
1888 * Fires before this menu is hidden
1889 * @param {Roo.menu.Menu} this
1894 * Fires after this menu is displayed
1895 * @param {Roo.menu.Menu} this
1900 * Fires after this menu is hidden
1901 * @param {Roo.menu.Menu} this
1906 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1907 * @param {Roo.menu.Menu} this
1908 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1909 * @param {Roo.EventObject} e
1914 * Fires when the mouse is hovering over this menu
1915 * @param {Roo.menu.Menu} this
1916 * @param {Roo.EventObject} e
1917 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1922 * Fires when the mouse exits this menu
1923 * @param {Roo.menu.Menu} this
1924 * @param {Roo.EventObject} e
1925 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1930 * Fires when a menu item contained in this menu is clicked
1931 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1932 * @param {Roo.EventObject} e
1936 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1939 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1943 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1946 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1948 registerMenu : true,
1950 menuItems :false, // stores the menu items..
1957 getChildContainer : function() {
1961 getAutoCreate : function(){
1963 //if (['right'].indexOf(this.align)!==-1) {
1964 // cfg.cn[1].cls += ' pull-right'
1970 cls : 'dropdown-menu' ,
1971 style : 'z-index:1000'
1975 if (this.type === 'submenu') {
1976 cfg.cls = 'submenu active';
1978 if (this.type === 'treeview') {
1979 cfg.cls = 'treeview-menu';
1984 initEvents : function() {
1986 // Roo.log("ADD event");
1987 // Roo.log(this.triggerEl.dom);
1988 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1990 this.triggerEl.addClass('dropdown-toggle');
1994 this.el.on('touchstart' , this.onTouch, this);
1996 this.el.on('click' , this.onClick, this);
1998 this.el.on("mouseover", this.onMouseOver, this);
1999 this.el.on("mouseout", this.onMouseOut, this);
2003 findTargetItem : function(e)
2005 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2009 //Roo.log(t); Roo.log(t.id);
2011 //Roo.log(this.menuitems);
2012 return this.menuitems.get(t.id);
2014 //return this.items.get(t.menuItemId);
2020 onTouch : function(e)
2022 //e.stopEvent(); this make the user popdown broken
2026 onClick : function(e)
2028 Roo.log("menu.onClick");
2029 var t = this.findTargetItem(e);
2030 if(!t || t.isContainer){
2035 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2036 if(t == this.activeItem && t.shouldDeactivate(e)){
2037 this.activeItem.deactivate();
2038 delete this.activeItem;
2042 this.setActiveItem(t, true);
2050 Roo.log('pass click event');
2054 this.fireEvent("click", this, t, e);
2058 onMouseOver : function(e){
2059 var t = this.findTargetItem(e);
2062 // if(t.canActivate && !t.disabled){
2063 // this.setActiveItem(t, true);
2067 this.fireEvent("mouseover", this, e, t);
2069 isVisible : function(){
2070 return !this.hidden;
2072 onMouseOut : function(e){
2073 var t = this.findTargetItem(e);
2076 // if(t == this.activeItem && t.shouldDeactivate(e)){
2077 // this.activeItem.deactivate();
2078 // delete this.activeItem;
2081 this.fireEvent("mouseout", this, e, t);
2086 * Displays this menu relative to another element
2087 * @param {String/HTMLElement/Roo.Element} element The element to align to
2088 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2089 * the element (defaults to this.defaultAlign)
2090 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2092 show : function(el, pos, parentMenu){
2093 this.parentMenu = parentMenu;
2097 this.fireEvent("beforeshow", this);
2098 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2101 * Displays this menu at a specific xy position
2102 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2103 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2105 showAt : function(xy, parentMenu, /* private: */_e){
2106 this.parentMenu = parentMenu;
2111 this.fireEvent("beforeshow", this);
2112 //xy = this.el.adjustForConstraints(xy);
2116 this.hideMenuItems();
2117 this.hidden = false;
2118 this.triggerEl.addClass('open');
2120 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2121 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2126 this.fireEvent("show", this);
2132 this.doFocus.defer(50, this);
2136 doFocus : function(){
2138 this.focusEl.focus();
2143 * Hides this menu and optionally all parent menus
2144 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2146 hide : function(deep)
2149 this.hideMenuItems();
2150 if(this.el && this.isVisible()){
2151 this.fireEvent("beforehide", this);
2152 if(this.activeItem){
2153 this.activeItem.deactivate();
2154 this.activeItem = null;
2156 this.triggerEl.removeClass('open');;
2158 this.fireEvent("hide", this);
2160 if(deep === true && this.parentMenu){
2161 this.parentMenu.hide(true);
2165 onTriggerPress : function(e)
2168 Roo.log('trigger press');
2169 //Roo.log(e.getTarget());
2170 // Roo.log(this.triggerEl.dom);
2172 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2173 var pel = Roo.get(e.getTarget());
2174 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2179 if (this.isVisible()) {
2184 this.show(this.triggerEl, false, false);
2193 hideMenuItems : function()
2195 Roo.log("hide Menu Items");
2199 //$(backdrop).remove()
2200 this.el.select('.open',true).each(function(aa) {
2202 aa.removeClass('open');
2203 //var parent = getParent($(this))
2204 //var relatedTarget = { relatedTarget: this }
2206 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2207 //if (e.isDefaultPrevented()) return
2208 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2211 addxtypeChild : function (tree, cntr) {
2212 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2214 this.menuitems.add(comp);
2235 * @class Roo.bootstrap.MenuItem
2236 * @extends Roo.bootstrap.Component
2237 * Bootstrap MenuItem class
2238 * @cfg {String} html the menu label
2239 * @cfg {String} href the link
2240 * @cfg {Boolean} preventDefault do not trigger A href on clicks.
2241 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2242 * @cfg {Boolean} active used on sidebars to highlight active itesm
2243 * @cfg {String} fa favicon to show on left of menu item.
2244 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2248 * Create a new MenuItem
2249 * @param {Object} config The config object
2253 Roo.bootstrap.MenuItem = function(config){
2254 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2259 * The raw click event for the entire grid.
2260 * @param {Roo.bootstrap.MenuItem} this
2261 * @param {Roo.EventObject} e
2267 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2271 preventDefault: true,
2272 isContainer : false,
2276 getAutoCreate : function(){
2278 if(this.isContainer){
2281 cls: 'dropdown-menu-item'
2295 if (this.fa !== false) {
2298 cls : 'fa fa-' + this.fa
2307 cls: 'dropdown-menu-item',
2310 if (this.parent().type == 'treeview') {
2311 cfg.cls = 'treeview-menu';
2314 cfg.cls += ' active';
2319 anc.href = this.href || cfg.cn[0].href ;
2320 ctag.html = this.html || cfg.cn[0].html ;
2324 initEvents: function()
2326 if (this.parent().type == 'treeview') {
2327 this.el.select('a').on('click', this.onClick, this);
2330 this.menu.parentType = this.xtype;
2331 this.menu.triggerEl = this.el;
2332 this.menu = this.addxtype(Roo.apply({}, this.menu));
2336 onClick : function(e)
2338 Roo.log('item on click ');
2339 //if(this.preventDefault){
2340 // e.preventDefault();
2342 //this.parent().hideMenuItems();
2344 this.fireEvent('click', this, e);
2363 * @class Roo.bootstrap.MenuSeparator
2364 * @extends Roo.bootstrap.Component
2365 * Bootstrap MenuSeparator class
2368 * Create a new MenuItem
2369 * @param {Object} config The config object
2373 Roo.bootstrap.MenuSeparator = function(config){
2374 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2377 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2379 getAutoCreate : function(){
2398 * @class Roo.bootstrap.Modal
2399 * @extends Roo.bootstrap.Component
2400 * Bootstrap Modal class
2401 * @cfg {String} title Title of dialog
2402 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2403 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2404 * @cfg {Boolean} specificTitle default false
2405 * @cfg {Array} buttons Array of buttons or standard button set..
2406 * @cfg {String} buttonPosition (left|right|center) default right
2407 * @cfg {Boolean} animate default true
2408 * @cfg {Boolean} allow_close default true
2411 * Create a new Modal Dialog
2412 * @param {Object} config The config object
2415 Roo.bootstrap.Modal = function(config){
2416 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2421 * The raw btnclick event for the button
2422 * @param {Roo.EventObject} e
2426 this.buttons = this.buttons || [];
2429 this.tmpl = Roo.factory(this.tmpl);
2434 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2436 title : 'test dialog',
2446 specificTitle: false,
2448 buttonPosition: 'right',
2462 onRender : function(ct, position)
2464 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2467 var cfg = Roo.apply({}, this.getAutoCreate());
2470 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2472 //if (!cfg.name.length) {
2476 cfg.cls += ' ' + this.cls;
2479 cfg.style = this.style;
2481 this.el = Roo.get(document.body).createChild(cfg, position);
2483 //var type = this.el.dom.type;
2486 if(this.tabIndex !== undefined){
2487 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2491 this.bodyEl = this.el.select('.modal-body',true).first();
2492 this.closeEl = this.el.select('.modal-header .close', true).first();
2493 this.footerEl = this.el.select('.modal-footer',true).first();
2494 this.titleEl = this.el.select('.modal-title',true).first();
2498 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2499 this.maskEl.enableDisplayMode("block");
2501 //this.el.addClass("x-dlg-modal");
2503 if (this.buttons.length) {
2504 Roo.each(this.buttons, function(bb) {
2505 var b = Roo.apply({}, bb);
2506 b.xns = b.xns || Roo.bootstrap;
2507 b.xtype = b.xtype || 'Button';
2508 if (typeof(b.listeners) == 'undefined') {
2509 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2512 var btn = Roo.factory(b);
2514 btn.onRender(this.el.select('.modal-footer div').first());
2518 // render the children.
2521 if(typeof(this.items) != 'undefined'){
2522 var items = this.items;
2525 for(var i =0;i < items.length;i++) {
2526 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2530 this.items = nitems;
2532 // where are these used - they used to be body/close/footer
2536 //this.el.addClass([this.fieldClass, this.cls]);
2540 getAutoCreate : function(){
2545 html : this.html || ''
2550 cls : 'modal-title',
2554 if(this.specificTitle){
2560 if (this.allow_close) {
2571 style : 'display: none',
2574 cls: "modal-dialog",
2577 cls : "modal-content",
2580 cls : 'modal-header',
2585 cls : 'modal-footer',
2589 cls: 'btn-' + this.buttonPosition
2606 modal.cls += ' fade';
2612 getChildContainer : function() {
2617 getButtonContainer : function() {
2618 return this.el.select('.modal-footer div',true).first();
2621 initEvents : function()
2623 if (this.allow_close) {
2624 this.closeEl.on('click', this.hide, this);
2629 window.addEventListener("resize", function() { _this.resize(); } );
2635 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2640 if (!this.rendered) {
2644 this.el.setStyle('display', 'block');
2646 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2649 this.el.addClass('in');
2652 this.el.addClass('in');
2656 // not sure how we can show data in here..
2658 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2661 Roo.get(document.body).addClass("x-body-masked");
2662 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2664 this.el.setStyle('zIndex', '10001');
2666 this.fireEvent('show', this);
2674 Roo.get(document.body).removeClass("x-body-masked");
2675 this.el.removeClass('in');
2676 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2678 if(this.animate){ // why
2680 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2682 this.el.setStyle('display', 'none');
2685 this.fireEvent('hide', this);
2688 addButton : function(str, cb)
2692 var b = Roo.apply({}, { html : str } );
2693 b.xns = b.xns || Roo.bootstrap;
2694 b.xtype = b.xtype || 'Button';
2695 if (typeof(b.listeners) == 'undefined') {
2696 b.listeners = { click : cb.createDelegate(this) };
2699 var btn = Roo.factory(b);
2701 btn.onRender(this.el.select('.modal-footer div').first());
2707 setDefaultButton : function(btn)
2709 //this.el.select('.modal-footer').()
2713 resizeTo: function(w,h)
2717 this.el.select('.modal-dialog',true).first().setWidth(w);
2718 if (this.diff === false) {
2719 this.diff = this.el.select('.modal-dialog',true).first().getHeight() - this.el.select('.modal-body',true).first().getHeight();
2722 this.el.select('.modal-body',true).first().setHeight(h-this.diff);
2726 setContentSize : function(w, h)
2730 onButtonClick: function(btn,e)
2733 this.fireEvent('btnclick', btn.name, e);
2736 * Set the title of the Dialog
2737 * @param {String} str new Title
2739 setTitle: function(str) {
2740 this.titleEl.dom.innerHTML = str;
2743 * Set the body of the Dialog
2744 * @param {String} str new Title
2746 setBody: function(str) {
2747 this.bodyEl.dom.innerHTML = str;
2750 * Set the body of the Dialog using the template
2751 * @param {Obj} data - apply this data to the template and replace the body contents.
2753 applyBody: function(obj)
2756 Roo.log("Error - using apply Body without a template");
2759 this.tmpl.overwrite(this.bodyEl, obj);
2765 Roo.apply(Roo.bootstrap.Modal, {
2767 * Button config that displays a single OK button
2776 * Button config that displays Yes and No buttons
2792 * Button config that displays OK and Cancel buttons
2807 * Button config that displays Yes, No and Cancel buttons
2830 * messagebox - can be used as a replace
2834 * @class Roo.MessageBox
2835 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2839 Roo.Msg.alert('Status', 'Changes saved successfully.');
2841 // Prompt for user data:
2842 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2844 // process text value...
2848 // Show a dialog using config options:
2850 title:'Save Changes?',
2851 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2852 buttons: Roo.Msg.YESNOCANCEL,
2859 Roo.bootstrap.MessageBox = function(){
2860 var dlg, opt, mask, waitTimer;
2861 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2862 var buttons, activeTextEl, bwidth;
2866 var handleButton = function(button){
2868 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2872 var handleHide = function(){
2874 dlg.el.removeClass(opt.cls);
2877 // Roo.TaskMgr.stop(waitTimer);
2878 // waitTimer = null;
2883 var updateButtons = function(b){
2886 buttons["ok"].hide();
2887 buttons["cancel"].hide();
2888 buttons["yes"].hide();
2889 buttons["no"].hide();
2890 //dlg.footer.dom.style.display = 'none';
2893 dlg.footerEl.dom.style.display = '';
2894 for(var k in buttons){
2895 if(typeof buttons[k] != "function"){
2898 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2899 width += buttons[k].el.getWidth()+15;
2909 var handleEsc = function(d, k, e){
2910 if(opt && opt.closable !== false){
2920 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2921 * @return {Roo.BasicDialog} The BasicDialog element
2923 getDialog : function(){
2925 dlg = new Roo.bootstrap.Modal( {
2928 //constraintoviewport:false,
2930 //collapsible : false,
2935 //buttonAlign:"center",
2936 closeClick : function(){
2937 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2940 handleButton("cancel");
2945 dlg.on("hide", handleHide);
2947 //dlg.addKeyListener(27, handleEsc);
2949 this.buttons = buttons;
2950 var bt = this.buttonText;
2951 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2952 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2953 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2954 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2956 bodyEl = dlg.bodyEl.createChild({
2958 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2959 '<textarea class="roo-mb-textarea"></textarea>' +
2960 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2962 msgEl = bodyEl.dom.firstChild;
2963 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2964 textboxEl.enableDisplayMode();
2965 textboxEl.addKeyListener([10,13], function(){
2966 if(dlg.isVisible() && opt && opt.buttons){
2969 }else if(opt.buttons.yes){
2970 handleButton("yes");
2974 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2975 textareaEl.enableDisplayMode();
2976 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2977 progressEl.enableDisplayMode();
2978 var pf = progressEl.dom.firstChild;
2980 pp = Roo.get(pf.firstChild);
2981 pp.setHeight(pf.offsetHeight);
2989 * Updates the message box body text
2990 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2991 * the XHTML-compliant non-breaking space character '&#160;')
2992 * @return {Roo.MessageBox} This message box
2994 updateText : function(text){
2995 if(!dlg.isVisible() && !opt.width){
2996 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2998 msgEl.innerHTML = text || ' ';
3000 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3001 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3003 Math.min(opt.width || cw , this.maxWidth),
3004 Math.max(opt.minWidth || this.minWidth, bwidth)
3007 activeTextEl.setWidth(w);
3009 if(dlg.isVisible()){
3010 dlg.fixedcenter = false;
3012 // to big, make it scroll. = But as usual stupid IE does not support
3015 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3016 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3017 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3019 bodyEl.dom.style.height = '';
3020 bodyEl.dom.style.overflowY = '';
3023 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3025 bodyEl.dom.style.overflowX = '';
3028 dlg.setContentSize(w, bodyEl.getHeight());
3029 if(dlg.isVisible()){
3030 dlg.fixedcenter = true;
3036 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3037 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3038 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3039 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3040 * @return {Roo.MessageBox} This message box
3042 updateProgress : function(value, text){
3044 this.updateText(text);
3046 if (pp) { // weird bug on my firefox - for some reason this is not defined
3047 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3053 * Returns true if the message box is currently displayed
3054 * @return {Boolean} True if the message box is visible, else false
3056 isVisible : function(){
3057 return dlg && dlg.isVisible();
3061 * Hides the message box if it is displayed
3064 if(this.isVisible()){
3070 * Displays a new message box, or reinitializes an existing message box, based on the config options
3071 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3072 * The following config object properties are supported:
3074 Property Type Description
3075 ---------- --------------- ------------------------------------------------------------------------------------
3076 animEl String/Element An id or Element from which the message box should animate as it opens and
3077 closes (defaults to undefined)
3078 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3079 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3080 closable Boolean False to hide the top-right close button (defaults to true). Note that
3081 progress and wait dialogs will ignore this property and always hide the
3082 close button as they can only be closed programmatically.
3083 cls String A custom CSS class to apply to the message box element
3084 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3085 displayed (defaults to 75)
3086 fn Function A callback function to execute after closing the dialog. The arguments to the
3087 function will be btn (the name of the button that was clicked, if applicable,
3088 e.g. "ok"), and text (the value of the active text field, if applicable).
3089 Progress and wait dialogs will ignore this option since they do not respond to
3090 user actions and can only be closed programmatically, so any required function
3091 should be called by the same code after it closes the dialog.
3092 icon String A CSS class that provides a background image to be used as an icon for
3093 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3094 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3095 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3096 modal Boolean False to allow user interaction with the page while the message box is
3097 displayed (defaults to true)
3098 msg String A string that will replace the existing message box body text (defaults
3099 to the XHTML-compliant non-breaking space character ' ')
3100 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3101 progress Boolean True to display a progress bar (defaults to false)
3102 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3103 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3104 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3105 title String The title text
3106 value String The string value to set into the active textbox element if displayed
3107 wait Boolean True to display a progress bar (defaults to false)
3108 width Number The width of the dialog in pixels
3115 msg: 'Please enter your address:',
3117 buttons: Roo.MessageBox.OKCANCEL,
3120 animEl: 'addAddressBtn'
3123 * @param {Object} config Configuration options
3124 * @return {Roo.MessageBox} This message box
3126 show : function(options)
3129 // this causes nightmares if you show one dialog after another
3130 // especially on callbacks..
3132 if(this.isVisible()){
3135 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3136 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3137 Roo.log("New Dialog Message:" + options.msg )
3138 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3139 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3142 var d = this.getDialog();
3144 d.setTitle(opt.title || " ");
3145 d.closeEl.setDisplayed(opt.closable !== false);
3146 activeTextEl = textboxEl;
3147 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3152 textareaEl.setHeight(typeof opt.multiline == "number" ?
3153 opt.multiline : this.defaultTextHeight);
3154 activeTextEl = textareaEl;
3163 progressEl.setDisplayed(opt.progress === true);
3164 this.updateProgress(0);
3165 activeTextEl.dom.value = opt.value || "";
3167 dlg.setDefaultButton(activeTextEl);
3169 var bs = opt.buttons;
3173 }else if(bs && bs.yes){
3174 db = buttons["yes"];
3176 dlg.setDefaultButton(db);
3178 bwidth = updateButtons(opt.buttons);
3179 this.updateText(opt.msg);
3181 d.el.addClass(opt.cls);
3183 d.proxyDrag = opt.proxyDrag === true;
3184 d.modal = opt.modal !== false;
3185 d.mask = opt.modal !== false ? mask : false;
3187 // force it to the end of the z-index stack so it gets a cursor in FF
3188 document.body.appendChild(dlg.el.dom);
3189 d.animateTarget = null;
3190 d.show(options.animEl);
3196 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3197 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3198 * and closing the message box when the process is complete.
3199 * @param {String} title The title bar text
3200 * @param {String} msg The message box body text
3201 * @return {Roo.MessageBox} This message box
3203 progress : function(title, msg){
3210 minWidth: this.minProgressWidth,
3217 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3218 * If a callback function is passed it will be called after the user clicks the button, and the
3219 * id of the button that was clicked will be passed as the only parameter to the callback
3220 * (could also be the top-right close button).
3221 * @param {String} title The title bar text
3222 * @param {String} msg The message box body text
3223 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3224 * @param {Object} scope (optional) The scope of the callback function
3225 * @return {Roo.MessageBox} This message box
3227 alert : function(title, msg, fn, scope){
3240 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3241 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3242 * You are responsible for closing the message box when the process is complete.
3243 * @param {String} msg The message box body text
3244 * @param {String} title (optional) The title bar text
3245 * @return {Roo.MessageBox} This message box
3247 wait : function(msg, title){
3258 waitTimer = Roo.TaskMgr.start({
3260 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3268 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3269 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3270 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3271 * @param {String} title The title bar text
3272 * @param {String} msg The message box body text
3273 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3274 * @param {Object} scope (optional) The scope of the callback function
3275 * @return {Roo.MessageBox} This message box
3277 confirm : function(title, msg, fn, scope){
3281 buttons: this.YESNO,
3290 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3291 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3292 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3293 * (could also be the top-right close button) and the text that was entered will be passed as the two
3294 * parameters to the callback.
3295 * @param {String} title The title bar text
3296 * @param {String} msg The message box body text
3297 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3298 * @param {Object} scope (optional) The scope of the callback function
3299 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3300 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3301 * @return {Roo.MessageBox} This message box
3303 prompt : function(title, msg, fn, scope, multiline){
3307 buttons: this.OKCANCEL,
3312 multiline: multiline,
3319 * Button config that displays a single OK button
3324 * Button config that displays Yes and No buttons
3327 YESNO : {yes:true, no:true},
3329 * Button config that displays OK and Cancel buttons
3332 OKCANCEL : {ok:true, cancel:true},
3334 * Button config that displays Yes, No and Cancel buttons
3337 YESNOCANCEL : {yes:true, no:true, cancel:true},
3340 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3343 defaultTextHeight : 75,
3345 * The maximum width in pixels of the message box (defaults to 600)
3350 * The minimum width in pixels of the message box (defaults to 100)
3355 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3356 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3359 minProgressWidth : 250,
3361 * An object containing the default button text strings that can be overriden for localized language support.
3362 * Supported properties are: ok, cancel, yes and no.
3363 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3376 * Shorthand for {@link Roo.MessageBox}
3378 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3379 Roo.Msg = Roo.Msg || Roo.MessageBox;
3388 * @class Roo.bootstrap.Navbar
3389 * @extends Roo.bootstrap.Component
3390 * Bootstrap Navbar class
3393 * Create a new Navbar
3394 * @param {Object} config The config object
3398 Roo.bootstrap.Navbar = function(config){
3399 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3403 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3412 getAutoCreate : function(){
3415 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3419 initEvents :function ()
3421 //Roo.log(this.el.select('.navbar-toggle',true));
3422 this.el.select('.navbar-toggle',true).on('click', function() {
3423 // Roo.log('click');
3424 this.el.select('.navbar-collapse',true).toggleClass('in');
3432 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3434 var size = this.el.getSize();
3435 this.maskEl.setSize(size.width, size.height);
3436 this.maskEl.enableDisplayMode("block");
3445 getChildContainer : function()
3447 if (this.el.select('.collapse').getCount()) {
3448 return this.el.select('.collapse',true).first();
3481 * @class Roo.bootstrap.NavSimplebar
3482 * @extends Roo.bootstrap.Navbar
3483 * Bootstrap Sidebar class
3485 * @cfg {Boolean} inverse is inverted color
3487 * @cfg {String} type (nav | pills | tabs)
3488 * @cfg {Boolean} arrangement stacked | justified
3489 * @cfg {String} align (left | right) alignment
3491 * @cfg {Boolean} main (true|false) main nav bar? default false
3492 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3494 * @cfg {String} tag (header|footer|nav|div) default is nav
3500 * Create a new Sidebar
3501 * @param {Object} config The config object
3505 Roo.bootstrap.NavSimplebar = function(config){
3506 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3509 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3525 getAutoCreate : function(){
3529 tag : this.tag || 'div',
3542 this.type = this.type || 'nav';
3543 if (['tabs','pills'].indexOf(this.type)!==-1) {
3544 cfg.cn[0].cls += ' nav-' + this.type
3548 if (this.type!=='nav') {
3549 Roo.log('nav type must be nav/tabs/pills')
3551 cfg.cn[0].cls += ' navbar-nav'
3557 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3558 cfg.cn[0].cls += ' nav-' + this.arrangement;
3562 if (this.align === 'right') {
3563 cfg.cn[0].cls += ' navbar-right';
3567 cfg.cls += ' navbar-inverse';
3594 * @class Roo.bootstrap.NavHeaderbar
3595 * @extends Roo.bootstrap.NavSimplebar
3596 * Bootstrap Sidebar class
3598 * @cfg {String} brand what is brand
3599 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3600 * @cfg {String} brand_href href of the brand
3601 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3602 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3603 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3604 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3607 * Create a new Sidebar
3608 * @param {Object} config The config object
3612 Roo.bootstrap.NavHeaderbar = function(config){
3613 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3617 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3624 desktopCenter : false,
3627 getAutoCreate : function(){
3630 tag: this.nav || 'nav',
3637 if (this.desktopCenter) {
3638 cn.push({cls : 'container', cn : []});
3645 cls: 'navbar-header',
3650 cls: 'navbar-toggle',
3651 'data-toggle': 'collapse',
3656 html: 'Toggle navigation'
3678 cls: 'collapse navbar-collapse',
3682 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3684 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3685 cfg.cls += ' navbar-' + this.position;
3687 // tag can override this..
3689 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3692 if (this.brand !== '') {
3695 href: this.brand_href ? this.brand_href : '#',
3696 cls: 'navbar-brand',
3704 cfg.cls += ' main-nav';
3712 getHeaderChildContainer : function()
3714 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3715 return this.el.select('.navbar-header',true).first();
3718 return this.getChildContainer();
3722 initEvents : function()
3724 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3726 if (this.autohide) {
3731 Roo.get(document).on('scroll',function(e) {
3732 var ns = Roo.get(document).getScroll().top;
3733 var os = prevScroll;
3737 ft.removeClass('slideDown');
3738 ft.addClass('slideUp');
3741 ft.removeClass('slideUp');
3742 ft.addClass('slideDown');
3763 * @class Roo.bootstrap.NavSidebar
3764 * @extends Roo.bootstrap.Navbar
3765 * Bootstrap Sidebar class
3768 * Create a new Sidebar
3769 * @param {Object} config The config object
3773 Roo.bootstrap.NavSidebar = function(config){
3774 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3777 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3779 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3781 getAutoCreate : function(){
3786 cls: 'sidebar sidebar-nav'
3808 * @class Roo.bootstrap.NavGroup
3809 * @extends Roo.bootstrap.Component
3810 * Bootstrap NavGroup class
3811 * @cfg {String} align (left|right)
3812 * @cfg {Boolean} inverse
3813 * @cfg {String} type (nav|pills|tab) default nav
3814 * @cfg {String} navId - reference Id for navbar.
3818 * Create a new nav group
3819 * @param {Object} config The config object
3822 Roo.bootstrap.NavGroup = function(config){
3823 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3826 Roo.bootstrap.NavGroup.register(this);
3830 * Fires when the active item changes
3831 * @param {Roo.bootstrap.NavGroup} this
3832 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3833 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3840 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3851 getAutoCreate : function()
3853 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3860 if (['tabs','pills'].indexOf(this.type)!==-1) {
3861 cfg.cls += ' nav-' + this.type
3863 if (this.type!=='nav') {
3864 Roo.log('nav type must be nav/tabs/pills')
3866 cfg.cls += ' navbar-nav'
3869 if (this.parent().sidebar) {
3872 cls: 'dashboard-menu sidebar-menu'
3878 if (this.form === true) {
3884 if (this.align === 'right') {
3885 cfg.cls += ' navbar-right';
3887 cfg.cls += ' navbar-left';
3891 if (this.align === 'right') {
3892 cfg.cls += ' navbar-right';
3896 cfg.cls += ' navbar-inverse';
3904 * sets the active Navigation item
3905 * @param {Roo.bootstrap.NavItem} the new current navitem
3907 setActiveItem : function(item)
3910 Roo.each(this.navItems, function(v){
3915 v.setActive(false, true);
3922 item.setActive(true, true);
3923 this.fireEvent('changed', this, item, prev);
3928 * gets the active Navigation item
3929 * @return {Roo.bootstrap.NavItem} the current navitem
3931 getActive : function()
3935 Roo.each(this.navItems, function(v){
3946 indexOfNav : function()
3950 Roo.each(this.navItems, function(v,i){
3961 * adds a Navigation item
3962 * @param {Roo.bootstrap.NavItem} the navitem to add
3964 addItem : function(cfg)
3966 var cn = new Roo.bootstrap.NavItem(cfg);
3968 cn.parentId = this.id;
3969 cn.onRender(this.el, null);
3973 * register a Navigation item
3974 * @param {Roo.bootstrap.NavItem} the navitem to add
3976 register : function(item)
3978 this.navItems.push( item);
3979 item.navId = this.navId;
3984 * clear all the Navigation item
3987 clearAll : function()
3990 this.el.dom.innerHTML = '';
3993 getNavItem: function(tabId)
3996 Roo.each(this.navItems, function(e) {
3997 if (e.tabId == tabId) {
4007 setActiveNext : function()
4009 var i = this.indexOfNav(this.getActive());
4010 if (i > this.navItems.length) {
4013 this.setActiveItem(this.navItems[i+1]);
4015 setActivePrev : function()
4017 var i = this.indexOfNav(this.getActive());
4021 this.setActiveItem(this.navItems[i-1]);
4023 clearWasActive : function(except) {
4024 Roo.each(this.navItems, function(e) {
4025 if (e.tabId != except.tabId && e.was_active) {
4026 e.was_active = false;
4033 getWasActive : function ()
4036 Roo.each(this.navItems, function(e) {
4051 Roo.apply(Roo.bootstrap.NavGroup, {
4055 * register a Navigation Group
4056 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4058 register : function(navgrp)
4060 this.groups[navgrp.navId] = navgrp;
4064 * fetch a Navigation Group based on the navigation ID
4065 * @param {string} the navgroup to add
4066 * @returns {Roo.bootstrap.NavGroup} the navgroup
4068 get: function(navId) {
4069 if (typeof(this.groups[navId]) == 'undefined') {
4071 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4073 return this.groups[navId] ;
4088 * @class Roo.bootstrap.NavItem
4089 * @extends Roo.bootstrap.Component
4090 * Bootstrap Navbar.NavItem class
4091 * @cfg {String} href link to
4092 * @cfg {String} html content of button
4093 * @cfg {String} badge text inside badge
4094 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4095 * @cfg {String} glyphicon name of glyphicon
4096 * @cfg {String} icon name of font awesome icon
4097 * @cfg {Boolean} active Is item active
4098 * @cfg {Boolean} disabled Is item disabled
4100 * @cfg {Boolean} preventDefault (true | false) default false
4101 * @cfg {String} tabId the tab that this item activates.
4102 * @cfg {String} tagtype (a|span) render as a href or span?
4103 * @cfg {Boolean} animateRef (true|false) link to element default false
4106 * Create a new Navbar Item
4107 * @param {Object} config The config object
4109 Roo.bootstrap.NavItem = function(config){
4110 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4115 * The raw click event for the entire grid.
4116 * @param {Roo.EventObject} e
4121 * Fires when the active item active state changes
4122 * @param {Roo.bootstrap.NavItem} this
4123 * @param {boolean} state the new state
4129 * Fires when scroll to element
4130 * @param {Roo.bootstrap.NavItem} this
4131 * @param {Object} options
4132 * @param {Roo.EventObject} e
4140 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4148 preventDefault : false,
4155 getAutoCreate : function(){
4164 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4166 if (this.disabled) {
4167 cfg.cls += ' disabled';
4170 if (this.href || this.html || this.glyphicon || this.icon) {
4174 href : this.href || "#",
4175 html: this.html || ''
4180 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4183 if(this.glyphicon) {
4184 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4189 cfg.cn[0].html += " <span class='caret'></span>";
4193 if (this.badge !== '') {
4195 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4203 initEvents: function()
4205 if (typeof (this.menu) != 'undefined') {
4206 this.menu.parentType = this.xtype;
4207 this.menu.triggerEl = this.el;
4208 this.menu = this.addxtype(Roo.apply({}, this.menu));
4211 this.el.select('a',true).on('click', this.onClick, this);
4213 if(this.tagtype == 'span'){
4214 this.el.select('span',true).on('click', this.onClick, this);
4217 // at this point parent should be available..
4218 this.parent().register(this);
4221 onClick : function(e)
4223 if (e.getTarget('.dropdown-menu-item')) {
4224 // did you click on a menu itemm.... - then don't trigger onclick..
4229 this.preventDefault ||
4232 Roo.log("NavItem - prevent Default?");
4236 if (this.disabled) {
4240 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4241 if (tg && tg.transition) {
4242 Roo.log("waiting for the transitionend");
4248 //Roo.log("fire event clicked");
4249 if(this.fireEvent('click', this, e) === false){
4253 if(this.tagtype == 'span'){
4257 //Roo.log(this.href);
4258 var ael = this.el.select('a',true).first();
4261 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4262 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4263 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4264 return; // ignore... - it's a 'hash' to another page.
4266 Roo.log("NavItem - prevent Default?");
4268 this.scrollToElement(e);
4272 var p = this.parent();
4274 if (['tabs','pills'].indexOf(p.type)!==-1) {
4275 if (typeof(p.setActiveItem) !== 'undefined') {
4276 p.setActiveItem(this);
4280 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4281 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4282 // remove the collapsed menu expand...
4283 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4287 isActive: function () {
4290 setActive : function(state, fire, is_was_active)
4292 if (this.active && !state && this.navId) {
4293 this.was_active = true;
4294 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4296 nv.clearWasActive(this);
4300 this.active = state;
4303 this.el.removeClass('active');
4304 } else if (!this.el.hasClass('active')) {
4305 this.el.addClass('active');
4308 this.fireEvent('changed', this, state);
4311 // show a panel if it's registered and related..
4313 if (!this.navId || !this.tabId || !state || is_was_active) {
4317 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4321 var pan = tg.getPanelByName(this.tabId);
4325 // if we can not flip to new panel - go back to old nav highlight..
4326 if (false == tg.showPanel(pan)) {
4327 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4329 var onav = nv.getWasActive();
4331 onav.setActive(true, false, true);
4340 // this should not be here...
4341 setDisabled : function(state)
4343 this.disabled = state;
4345 this.el.removeClass('disabled');
4346 } else if (!this.el.hasClass('disabled')) {
4347 this.el.addClass('disabled');
4353 * Fetch the element to display the tooltip on.
4354 * @return {Roo.Element} defaults to this.el
4356 tooltipEl : function()
4358 return this.el.select('' + this.tagtype + '', true).first();
4361 scrollToElement : function(e)
4363 var c = document.body;
4366 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4368 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4369 c = document.documentElement;
4372 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4378 var o = target.calcOffsetsTo(c);
4385 this.fireEvent('scrollto', this, options, e);
4387 Roo.get(c).scrollTo('top', options.value, true);
4400 * <span> icon </span>
4401 * <span> text </span>
4402 * <span>badge </span>
4406 * @class Roo.bootstrap.NavSidebarItem
4407 * @extends Roo.bootstrap.NavItem
4408 * Bootstrap Navbar.NavSidebarItem class
4409 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4410 * {bool} open is the menu open
4412 * Create a new Navbar Button
4413 * @param {Object} config The config object
4415 Roo.bootstrap.NavSidebarItem = function(config){
4416 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4421 * The raw click event for the entire grid.
4422 * @param {Roo.EventObject} e
4427 * Fires when the active item active state changes
4428 * @param {Roo.bootstrap.NavSidebarItem} this
4429 * @param {boolean} state the new state
4437 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4439 badgeWeight : 'default',
4443 getAutoCreate : function(){
4448 href : this.href || '#',
4460 html : this.html || ''
4465 cfg.cls += ' active';
4468 if (this.disabled) {
4469 cfg.cls += ' disabled';
4472 cfg.cls += ' open x-open';
4475 if (this.glyphicon || this.icon) {
4476 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4477 a.cn.push({ tag : 'i', cls : c }) ;
4482 if (this.badge !== '') {
4484 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4488 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4489 a.cls += 'dropdown-toggle treeview' ;
4500 initEvents : function()
4502 if (typeof (this.menu) != 'undefined') {
4503 this.menu.parentType = this.xtype;
4504 this.menu.triggerEl = this.el;
4505 this.menu = this.addxtype(Roo.apply({}, this.menu));
4508 this.el.on('click', this.onClick, this);
4511 if(this.badge !== ''){
4513 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4518 onClick : function(e)
4525 if(this.preventDefault){
4529 this.fireEvent('click', this);
4532 disable : function()
4534 this.setDisabled(true);
4539 this.setDisabled(false);
4542 setDisabled : function(state)
4544 if(this.disabled == state){
4548 this.disabled = state;
4551 this.el.addClass('disabled');
4555 this.el.removeClass('disabled');
4560 setActive : function(state)
4562 if(this.active == state){
4566 this.active = state;
4569 this.el.addClass('active');
4573 this.el.removeClass('active');
4578 isActive: function ()
4583 setBadge : function(str)
4589 this.badgeEl.dom.innerHTML = str;
4606 * @class Roo.bootstrap.Row
4607 * @extends Roo.bootstrap.Component
4608 * Bootstrap Row class (contains columns...)
4612 * @param {Object} config The config object
4615 Roo.bootstrap.Row = function(config){
4616 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4619 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4621 getAutoCreate : function(){
4640 * @class Roo.bootstrap.Element
4641 * @extends Roo.bootstrap.Component
4642 * Bootstrap Element class
4643 * @cfg {String} html contents of the element
4644 * @cfg {String} tag tag of the element
4645 * @cfg {String} cls class of the element
4646 * @cfg {Boolean} preventDefault (true|false) default false
4647 * @cfg {Boolean} clickable (true|false) default false
4650 * Create a new Element
4651 * @param {Object} config The config object
4654 Roo.bootstrap.Element = function(config){
4655 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4661 * When a element is chick
4662 * @param {Roo.bootstrap.Element} this
4663 * @param {Roo.EventObject} e
4669 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4674 preventDefault: false,
4677 getAutoCreate : function(){
4688 initEvents: function()
4690 Roo.bootstrap.Element.superclass.initEvents.call(this);
4693 this.el.on('click', this.onClick, this);
4698 onClick : function(e)
4700 if(this.preventDefault){
4704 this.fireEvent('click', this, e);
4707 getValue : function()
4709 return this.el.dom.innerHTML;
4712 setValue : function(value)
4714 this.el.dom.innerHTML = value;
4729 * @class Roo.bootstrap.Pagination
4730 * @extends Roo.bootstrap.Component
4731 * Bootstrap Pagination class
4732 * @cfg {String} size xs | sm | md | lg
4733 * @cfg {Boolean} inverse false | true
4736 * Create a new Pagination
4737 * @param {Object} config The config object
4740 Roo.bootstrap.Pagination = function(config){
4741 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4744 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4750 getAutoCreate : function(){
4756 cfg.cls += ' inverse';
4762 cfg.cls += " " + this.cls;
4780 * @class Roo.bootstrap.PaginationItem
4781 * @extends Roo.bootstrap.Component
4782 * Bootstrap PaginationItem class
4783 * @cfg {String} html text
4784 * @cfg {String} href the link
4785 * @cfg {Boolean} preventDefault (true | false) default true
4786 * @cfg {Boolean} active (true | false) default false
4787 * @cfg {Boolean} disabled default false
4791 * Create a new PaginationItem
4792 * @param {Object} config The config object
4796 Roo.bootstrap.PaginationItem = function(config){
4797 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4802 * The raw click event for the entire grid.
4803 * @param {Roo.EventObject} e
4809 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4813 preventDefault: true,
4818 getAutoCreate : function(){
4824 href : this.href ? this.href : '#',
4825 html : this.html ? this.html : ''
4835 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4839 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4845 initEvents: function() {
4847 this.el.on('click', this.onClick, this);
4850 onClick : function(e)
4852 Roo.log('PaginationItem on click ');
4853 if(this.preventDefault){
4861 this.fireEvent('click', this, e);
4877 * @class Roo.bootstrap.Slider
4878 * @extends Roo.bootstrap.Component
4879 * Bootstrap Slider class
4882 * Create a new Slider
4883 * @param {Object} config The config object
4886 Roo.bootstrap.Slider = function(config){
4887 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4890 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4892 getAutoCreate : function(){
4896 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4900 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4912 * Ext JS Library 1.1.1
4913 * Copyright(c) 2006-2007, Ext JS, LLC.
4915 * Originally Released Under LGPL - original licence link has changed is not relivant.
4918 * <script type="text/javascript">
4923 * @class Roo.grid.ColumnModel
4924 * @extends Roo.util.Observable
4925 * This is the default implementation of a ColumnModel used by the Grid. It defines
4926 * the columns in the grid.
4929 var colModel = new Roo.grid.ColumnModel([
4930 {header: "Ticker", width: 60, sortable: true, locked: true},
4931 {header: "Company Name", width: 150, sortable: true},
4932 {header: "Market Cap.", width: 100, sortable: true},
4933 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4934 {header: "Employees", width: 100, sortable: true, resizable: false}
4939 * The config options listed for this class are options which may appear in each
4940 * individual column definition.
4941 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4943 * @param {Object} config An Array of column config objects. See this class's
4944 * config objects for details.
4946 Roo.grid.ColumnModel = function(config){
4948 * The config passed into the constructor
4950 this.config = config;
4953 // if no id, create one
4954 // if the column does not have a dataIndex mapping,
4955 // map it to the order it is in the config
4956 for(var i = 0, len = config.length; i < len; i++){
4958 if(typeof c.dataIndex == "undefined"){
4961 if(typeof c.renderer == "string"){
4962 c.renderer = Roo.util.Format[c.renderer];
4964 if(typeof c.id == "undefined"){
4967 if(c.editor && c.editor.xtype){
4968 c.editor = Roo.factory(c.editor, Roo.grid);
4970 if(c.editor && c.editor.isFormField){
4971 c.editor = new Roo.grid.GridEditor(c.editor);
4973 this.lookup[c.id] = c;
4977 * The width of columns which have no width specified (defaults to 100)
4980 this.defaultWidth = 100;
4983 * Default sortable of columns which have no sortable specified (defaults to false)
4986 this.defaultSortable = false;
4990 * @event widthchange
4991 * Fires when the width of a column changes.
4992 * @param {ColumnModel} this
4993 * @param {Number} columnIndex The column index
4994 * @param {Number} newWidth The new width
4996 "widthchange": true,
4998 * @event headerchange
4999 * Fires when the text of a header changes.
5000 * @param {ColumnModel} this
5001 * @param {Number} columnIndex The column index
5002 * @param {Number} newText The new header text
5004 "headerchange": true,
5006 * @event hiddenchange
5007 * Fires when a column is hidden or "unhidden".
5008 * @param {ColumnModel} this
5009 * @param {Number} columnIndex The column index
5010 * @param {Boolean} hidden true if hidden, false otherwise
5012 "hiddenchange": true,
5014 * @event columnmoved
5015 * Fires when a column is moved.
5016 * @param {ColumnModel} this
5017 * @param {Number} oldIndex
5018 * @param {Number} newIndex
5020 "columnmoved" : true,
5022 * @event columlockchange
5023 * Fires when a column's locked state is changed
5024 * @param {ColumnModel} this
5025 * @param {Number} colIndex
5026 * @param {Boolean} locked true if locked
5028 "columnlockchange" : true
5030 Roo.grid.ColumnModel.superclass.constructor.call(this);
5032 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5034 * @cfg {String} header The header text to display in the Grid view.
5037 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5038 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5039 * specified, the column's index is used as an index into the Record's data Array.
5042 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5043 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5046 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5047 * Defaults to the value of the {@link #defaultSortable} property.
5048 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5051 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5054 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5057 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5060 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5063 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5064 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5065 * default renderer uses the raw data value. If an object is returned (bootstrap only)
5066 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5069 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5072 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5075 * @cfg {String} cursor (Optional)
5078 * @cfg {String} tooltip (Optional)
5081 * @cfg {Number} xs (Optional)
5084 * @cfg {Number} sm (Optional)
5087 * @cfg {Number} md (Optional)
5090 * @cfg {Number} lg (Optional)
5093 * Returns the id of the column at the specified index.
5094 * @param {Number} index The column index
5095 * @return {String} the id
5097 getColumnId : function(index){
5098 return this.config[index].id;
5102 * Returns the column for a specified id.
5103 * @param {String} id The column id
5104 * @return {Object} the column
5106 getColumnById : function(id){
5107 return this.lookup[id];
5112 * Returns the column for a specified dataIndex.
5113 * @param {String} dataIndex The column dataIndex
5114 * @return {Object|Boolean} the column or false if not found
5116 getColumnByDataIndex: function(dataIndex){
5117 var index = this.findColumnIndex(dataIndex);
5118 return index > -1 ? this.config[index] : false;
5122 * Returns the index for a specified column id.
5123 * @param {String} id The column id
5124 * @return {Number} the index, or -1 if not found
5126 getIndexById : function(id){
5127 for(var i = 0, len = this.config.length; i < len; i++){
5128 if(this.config[i].id == id){
5136 * Returns the index for a specified column dataIndex.
5137 * @param {String} dataIndex The column dataIndex
5138 * @return {Number} the index, or -1 if not found
5141 findColumnIndex : function(dataIndex){
5142 for(var i = 0, len = this.config.length; i < len; i++){
5143 if(this.config[i].dataIndex == dataIndex){
5151 moveColumn : function(oldIndex, newIndex){
5152 var c = this.config[oldIndex];
5153 this.config.splice(oldIndex, 1);
5154 this.config.splice(newIndex, 0, c);
5155 this.dataMap = null;
5156 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5159 isLocked : function(colIndex){
5160 return this.config[colIndex].locked === true;
5163 setLocked : function(colIndex, value, suppressEvent){
5164 if(this.isLocked(colIndex) == value){
5167 this.config[colIndex].locked = value;
5169 this.fireEvent("columnlockchange", this, colIndex, value);
5173 getTotalLockedWidth : function(){
5175 for(var i = 0; i < this.config.length; i++){
5176 if(this.isLocked(i) && !this.isHidden(i)){
5177 this.totalWidth += this.getColumnWidth(i);
5183 getLockedCount : function(){
5184 for(var i = 0, len = this.config.length; i < len; i++){
5185 if(!this.isLocked(i)){
5190 return this.config.length;
5194 * Returns the number of columns.
5197 getColumnCount : function(visibleOnly){
5198 if(visibleOnly === true){
5200 for(var i = 0, len = this.config.length; i < len; i++){
5201 if(!this.isHidden(i)){
5207 return this.config.length;
5211 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5212 * @param {Function} fn
5213 * @param {Object} scope (optional)
5214 * @return {Array} result
5216 getColumnsBy : function(fn, scope){
5218 for(var i = 0, len = this.config.length; i < len; i++){
5219 var c = this.config[i];
5220 if(fn.call(scope||this, c, i) === true){
5228 * Returns true if the specified column is sortable.
5229 * @param {Number} col The column index
5232 isSortable : function(col){
5233 if(typeof this.config[col].sortable == "undefined"){
5234 return this.defaultSortable;
5236 return this.config[col].sortable;
5240 * Returns the rendering (formatting) function defined for the column.
5241 * @param {Number} col The column index.
5242 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5244 getRenderer : function(col){
5245 if(!this.config[col].renderer){
5246 return Roo.grid.ColumnModel.defaultRenderer;
5248 return this.config[col].renderer;
5252 * Sets the rendering (formatting) function for a column.
5253 * @param {Number} col The column index
5254 * @param {Function} fn The function to use to process the cell's raw data
5255 * to return HTML markup for the grid view. The render function is called with
5256 * the following parameters:<ul>
5257 * <li>Data value.</li>
5258 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5259 * <li>css A CSS style string to apply to the table cell.</li>
5260 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5261 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5262 * <li>Row index</li>
5263 * <li>Column index</li>
5264 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5266 setRenderer : function(col, fn){
5267 this.config[col].renderer = fn;
5271 * Returns the width for the specified column.
5272 * @param {Number} col The column index
5275 getColumnWidth : function(col){
5276 return this.config[col].width * 1 || this.defaultWidth;
5280 * Sets the width for a column.
5281 * @param {Number} col The column index
5282 * @param {Number} width The new width
5284 setColumnWidth : function(col, width, suppressEvent){
5285 this.config[col].width = width;
5286 this.totalWidth = null;
5288 this.fireEvent("widthchange", this, col, width);
5293 * Returns the total width of all columns.
5294 * @param {Boolean} includeHidden True to include hidden column widths
5297 getTotalWidth : function(includeHidden){
5298 if(!this.totalWidth){
5299 this.totalWidth = 0;
5300 for(var i = 0, len = this.config.length; i < len; i++){
5301 if(includeHidden || !this.isHidden(i)){
5302 this.totalWidth += this.getColumnWidth(i);
5306 return this.totalWidth;
5310 * Returns the header for the specified column.
5311 * @param {Number} col The column index
5314 getColumnHeader : function(col){
5315 return this.config[col].header;
5319 * Sets the header for a column.
5320 * @param {Number} col The column index
5321 * @param {String} header The new header
5323 setColumnHeader : function(col, header){
5324 this.config[col].header = header;
5325 this.fireEvent("headerchange", this, col, header);
5329 * Returns the tooltip for the specified column.
5330 * @param {Number} col The column index
5333 getColumnTooltip : function(col){
5334 return this.config[col].tooltip;
5337 * Sets the tooltip for a column.
5338 * @param {Number} col The column index
5339 * @param {String} tooltip The new tooltip
5341 setColumnTooltip : function(col, tooltip){
5342 this.config[col].tooltip = tooltip;
5346 * Returns the dataIndex for the specified column.
5347 * @param {Number} col The column index
5350 getDataIndex : function(col){
5351 return this.config[col].dataIndex;
5355 * Sets the dataIndex for a column.
5356 * @param {Number} col The column index
5357 * @param {Number} dataIndex The new dataIndex
5359 setDataIndex : function(col, dataIndex){
5360 this.config[col].dataIndex = dataIndex;
5366 * Returns true if the cell is editable.
5367 * @param {Number} colIndex The column index
5368 * @param {Number} rowIndex The row index - this is nto actually used..?
5371 isCellEditable : function(colIndex, rowIndex){
5372 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5376 * Returns the editor defined for the cell/column.
5377 * return false or null to disable editing.
5378 * @param {Number} colIndex The column index
5379 * @param {Number} rowIndex The row index
5382 getCellEditor : function(colIndex, rowIndex){
5383 return this.config[colIndex].editor;
5387 * Sets if a column is editable.
5388 * @param {Number} col The column index
5389 * @param {Boolean} editable True if the column is editable
5391 setEditable : function(col, editable){
5392 this.config[col].editable = editable;
5397 * Returns true if the column is hidden.
5398 * @param {Number} colIndex The column index
5401 isHidden : function(colIndex){
5402 return this.config[colIndex].hidden;
5407 * Returns true if the column width cannot be changed
5409 isFixed : function(colIndex){
5410 return this.config[colIndex].fixed;
5414 * Returns true if the column can be resized
5417 isResizable : function(colIndex){
5418 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5421 * Sets if a column is hidden.
5422 * @param {Number} colIndex The column index
5423 * @param {Boolean} hidden True if the column is hidden
5425 setHidden : function(colIndex, hidden){
5426 this.config[colIndex].hidden = hidden;
5427 this.totalWidth = null;
5428 this.fireEvent("hiddenchange", this, colIndex, hidden);
5432 * Sets the editor for a column.
5433 * @param {Number} col The column index
5434 * @param {Object} editor The editor object
5436 setEditor : function(col, editor){
5437 this.config[col].editor = editor;
5441 Roo.grid.ColumnModel.defaultRenderer = function(value){
5442 if(typeof value == "string" && value.length < 1){
5448 // Alias for backwards compatibility
5449 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5452 * Ext JS Library 1.1.1
5453 * Copyright(c) 2006-2007, Ext JS, LLC.
5455 * Originally Released Under LGPL - original licence link has changed is not relivant.
5458 * <script type="text/javascript">
5462 * @class Roo.LoadMask
5463 * A simple utility class for generically masking elements while loading data. If the element being masked has
5464 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5465 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5466 * element's UpdateManager load indicator and will be destroyed after the initial load.
5468 * Create a new LoadMask
5469 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5470 * @param {Object} config The config object
5472 Roo.LoadMask = function(el, config){
5473 this.el = Roo.get(el);
5474 Roo.apply(this, config);
5476 this.store.on('beforeload', this.onBeforeLoad, this);
5477 this.store.on('load', this.onLoad, this);
5478 this.store.on('loadexception', this.onLoadException, this);
5479 this.removeMask = false;
5481 var um = this.el.getUpdateManager();
5482 um.showLoadIndicator = false; // disable the default indicator
5483 um.on('beforeupdate', this.onBeforeLoad, this);
5484 um.on('update', this.onLoad, this);
5485 um.on('failure', this.onLoad, this);
5486 this.removeMask = true;
5490 Roo.LoadMask.prototype = {
5492 * @cfg {Boolean} removeMask
5493 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5494 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5498 * The text to display in a centered loading message box (defaults to 'Loading...')
5502 * @cfg {String} msgCls
5503 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5505 msgCls : 'x-mask-loading',
5508 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5514 * Disables the mask to prevent it from being displayed
5516 disable : function(){
5517 this.disabled = true;
5521 * Enables the mask so that it can be displayed
5523 enable : function(){
5524 this.disabled = false;
5527 onLoadException : function()
5531 if (typeof(arguments[3]) != 'undefined') {
5532 Roo.MessageBox.alert("Error loading",arguments[3]);
5536 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5537 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5546 this.el.unmask(this.removeMask);
5551 this.el.unmask(this.removeMask);
5555 onBeforeLoad : function(){
5557 this.el.mask(this.msg, this.msgCls);
5562 destroy : function(){
5564 this.store.un('beforeload', this.onBeforeLoad, this);
5565 this.store.un('load', this.onLoad, this);
5566 this.store.un('loadexception', this.onLoadException, this);
5568 var um = this.el.getUpdateManager();
5569 um.un('beforeupdate', this.onBeforeLoad, this);
5570 um.un('update', this.onLoad, this);
5571 um.un('failure', this.onLoad, this);
5582 * @class Roo.bootstrap.Table
5583 * @extends Roo.bootstrap.Component
5584 * Bootstrap Table class
5585 * @cfg {String} cls table class
5586 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5587 * @cfg {String} bgcolor Specifies the background color for a table
5588 * @cfg {Number} border Specifies whether the table cells should have borders or not
5589 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5590 * @cfg {Number} cellspacing Specifies the space between cells
5591 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5592 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5593 * @cfg {String} sortable Specifies that the table should be sortable
5594 * @cfg {String} summary Specifies a summary of the content of a table
5595 * @cfg {Number} width Specifies the width of a table
5596 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5598 * @cfg {boolean} striped Should the rows be alternative striped
5599 * @cfg {boolean} bordered Add borders to the table
5600 * @cfg {boolean} hover Add hover highlighting
5601 * @cfg {boolean} condensed Format condensed
5602 * @cfg {boolean} responsive Format condensed
5603 * @cfg {Boolean} loadMask (true|false) default false
5604 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5605 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5606 * @cfg {Boolean} rowSelection (true|false) default false
5607 * @cfg {Boolean} cellSelection (true|false) default false
5608 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5612 * Create a new Table
5613 * @param {Object} config The config object
5616 Roo.bootstrap.Table = function(config){
5617 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5620 this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5621 this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5622 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5623 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5627 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5628 this.sm = this.selModel;
5629 this.sm.xmodule = this.xmodule || false;
5631 if (this.cm && typeof(this.cm.config) == 'undefined') {
5632 this.colModel = new Roo.grid.ColumnModel(this.cm);
5633 this.cm = this.colModel;
5634 this.cm.xmodule = this.xmodule || false;
5637 this.store= Roo.factory(this.store, Roo.data);
5638 this.ds = this.store;
5639 this.ds.xmodule = this.xmodule || false;
5642 if (this.footer && this.store) {
5643 this.footer.dataSource = this.ds;
5644 this.footer = Roo.factory(this.footer);
5651 * Fires when a cell is clicked
5652 * @param {Roo.bootstrap.Table} this
5653 * @param {Roo.Element} el
5654 * @param {Number} rowIndex
5655 * @param {Number} columnIndex
5656 * @param {Roo.EventObject} e
5660 * @event celldblclick
5661 * Fires when a cell is double clicked
5662 * @param {Roo.bootstrap.Table} this
5663 * @param {Roo.Element} el
5664 * @param {Number} rowIndex
5665 * @param {Number} columnIndex
5666 * @param {Roo.EventObject} e
5668 "celldblclick" : true,
5671 * Fires when a row is clicked
5672 * @param {Roo.bootstrap.Table} this
5673 * @param {Roo.Element} el
5674 * @param {Number} rowIndex
5675 * @param {Roo.EventObject} e
5679 * @event rowdblclick
5680 * Fires when a row is double clicked
5681 * @param {Roo.bootstrap.Table} this
5682 * @param {Roo.Element} el
5683 * @param {Number} rowIndex
5684 * @param {Roo.EventObject} e
5686 "rowdblclick" : true,
5689 * Fires when a mouseover occur
5690 * @param {Roo.bootstrap.Table} this
5691 * @param {Roo.Element} el
5692 * @param {Number} rowIndex
5693 * @param {Number} columnIndex
5694 * @param {Roo.EventObject} e
5699 * Fires when a mouseout occur
5700 * @param {Roo.bootstrap.Table} this
5701 * @param {Roo.Element} el
5702 * @param {Number} rowIndex
5703 * @param {Number} columnIndex
5704 * @param {Roo.EventObject} e
5709 * Fires when a row is rendered, so you can change add a style to it.
5710 * @param {Roo.bootstrap.Table} this
5711 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5715 * @event rowsrendered
5716 * Fires when all the rows have been rendered
5717 * @param {Roo.bootstrap.Table} this
5719 'rowsrendered' : true
5724 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5749 rowSelection : false,
5750 cellSelection : false,
5753 // Roo.Element - the tbody
5756 getAutoCreate : function(){
5757 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5766 cfg.cls += ' table-striped';
5770 cfg.cls += ' table-hover';
5772 if (this.bordered) {
5773 cfg.cls += ' table-bordered';
5775 if (this.condensed) {
5776 cfg.cls += ' table-condensed';
5778 if (this.responsive) {
5779 cfg.cls += ' table-responsive';
5783 cfg.cls+= ' ' +this.cls;
5786 // this lot should be simplifed...
5789 cfg.align=this.align;
5792 cfg.bgcolor=this.bgcolor;
5795 cfg.border=this.border;
5797 if (this.cellpadding) {
5798 cfg.cellpadding=this.cellpadding;
5800 if (this.cellspacing) {
5801 cfg.cellspacing=this.cellspacing;
5804 cfg.frame=this.frame;
5807 cfg.rules=this.rules;
5809 if (this.sortable) {
5810 cfg.sortable=this.sortable;
5813 cfg.summary=this.summary;
5816 cfg.width=this.width;
5819 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5822 if(this.store || this.cm){
5823 if(this.headerShow){
5824 cfg.cn.push(this.renderHeader());
5827 cfg.cn.push(this.renderBody());
5829 if(this.footerShow){
5830 cfg.cn.push(this.renderFooter());
5833 cfg.cls+= ' TableGrid';
5836 return { cn : [ cfg ] };
5839 initEvents : function()
5841 if(!this.store || !this.cm){
5845 //Roo.log('initEvents with ds!!!!');
5847 this.mainBody = this.el.select('tbody', true).first();
5852 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5853 e.on('click', _this.sort, _this);
5856 this.el.on("click", this.onClick, this);
5857 this.el.on("dblclick", this.onDblClick, this);
5859 // why is this done????? = it breaks dialogs??
5860 //this.parent().el.setStyle('position', 'relative');
5864 this.footer.parentId = this.id;
5865 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5868 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5870 this.store.on('load', this.onLoad, this);
5871 this.store.on('beforeload', this.onBeforeLoad, this);
5872 this.store.on('update', this.onUpdate, this);
5873 this.store.on('add', this.onAdd, this);
5877 onMouseover : function(e, el)
5879 var cell = Roo.get(el);
5885 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5886 cell = cell.findParent('td', false, true);
5889 var row = cell.findParent('tr', false, true);
5890 var cellIndex = cell.dom.cellIndex;
5891 var rowIndex = row.dom.rowIndex - 1; // start from 0
5893 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5897 onMouseout : function(e, el)
5899 var cell = Roo.get(el);
5905 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5906 cell = cell.findParent('td', false, true);
5909 var row = cell.findParent('tr', false, true);
5910 var cellIndex = cell.dom.cellIndex;
5911 var rowIndex = row.dom.rowIndex - 1; // start from 0
5913 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5917 onClick : function(e, el)
5919 var cell = Roo.get(el);
5921 if(!cell || (!this.cellSelection && !this.rowSelection)){
5925 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5926 cell = cell.findParent('td', false, true);
5929 if(!cell || typeof(cell) == 'undefined'){
5933 var row = cell.findParent('tr', false, true);
5935 if(!row || typeof(row) == 'undefined'){
5939 var cellIndex = cell.dom.cellIndex;
5940 var rowIndex = this.getRowIndex(row);
5942 // why??? - should these not be based on SelectionModel?
5943 if(this.cellSelection){
5944 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5947 if(this.rowSelection){
5948 this.fireEvent('rowclick', this, row, rowIndex, e);
5954 onDblClick : function(e,el)
5956 var cell = Roo.get(el);
5958 if(!cell || (!this.CellSelection && !this.RowSelection)){
5962 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5963 cell = cell.findParent('td', false, true);
5966 if(!cell || typeof(cell) == 'undefined'){
5970 var row = cell.findParent('tr', false, true);
5972 if(!row || typeof(row) == 'undefined'){
5976 var cellIndex = cell.dom.cellIndex;
5977 var rowIndex = this.getRowIndex(row);
5979 if(this.CellSelection){
5980 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5983 if(this.RowSelection){
5984 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5988 sort : function(e,el)
5990 var col = Roo.get(el);
5992 if(!col.hasClass('sortable')){
5996 var sort = col.attr('sort');
5999 if(col.hasClass('glyphicon-arrow-up')){
6003 this.store.sortInfo = {field : sort, direction : dir};
6006 Roo.log("calling footer first");
6007 this.footer.onClick('first');
6010 this.store.load({ params : { start : 0 } });
6014 renderHeader : function()
6023 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6025 var config = cm.config[i];
6030 html: cm.getColumnHeader(i)
6035 if(typeof(config.lgHeader) != 'undefined'){
6036 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6039 if(typeof(config.mdHeader) != 'undefined'){
6040 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6043 if(typeof(config.smHeader) != 'undefined'){
6044 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6047 if(typeof(config.xsHeader) != 'undefined'){
6048 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6055 if(typeof(config.tooltip) != 'undefined'){
6056 c.tooltip = config.tooltip;
6059 if(typeof(config.colspan) != 'undefined'){
6060 c.colspan = config.colspan;
6063 if(typeof(config.hidden) != 'undefined' && config.hidden){
6064 c.style += ' display:none;';
6067 if(typeof(config.dataIndex) != 'undefined'){
6068 c.sort = config.dataIndex;
6071 if(typeof(config.sortable) != 'undefined' && config.sortable){
6075 if(typeof(config.align) != 'undefined' && config.align.length){
6076 c.style += ' text-align:' + config.align + ';';
6079 if(typeof(config.width) != 'undefined'){
6080 c.style += ' width:' + config.width + 'px;';
6083 if(typeof(config.cls) != 'undefined'){
6084 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6087 ['xs','sm','md','lg'].map(function(size){
6089 if(typeof(config[size]) == 'undefined'){
6093 if (!config[size]) { // 0 = hidden
6094 c.cls += ' hidden-' + size;
6098 c.cls += ' col-' + size + '-' + config[size];
6108 renderBody : function()
6118 colspan : this.cm.getColumnCount()
6128 renderFooter : function()
6138 colspan : this.cm.getColumnCount()
6152 // Roo.log('ds onload');
6157 var ds = this.store;
6159 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6160 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6162 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6163 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6166 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6167 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6171 var tbody = this.mainBody;
6173 if(ds.getCount() > 0){
6174 ds.data.each(function(d,rowIndex){
6175 var row = this.renderRow(cm, ds, rowIndex);
6177 tbody.createChild(row);
6181 if(row.cellObjects.length){
6182 Roo.each(row.cellObjects, function(r){
6183 _this.renderCellObject(r);
6190 Roo.each(this.el.select('tbody td', true).elements, function(e){
6191 e.on('mouseover', _this.onMouseover, _this);
6194 Roo.each(this.el.select('tbody td', true).elements, function(e){
6195 e.on('mouseout', _this.onMouseout, _this);
6197 this.fireEvent('rowsrendered', this);
6198 //if(this.loadMask){
6199 // this.maskEl.hide();
6204 onUpdate : function(ds,record)
6206 this.refreshRow(record);
6209 onRemove : function(ds, record, index, isUpdate){
6210 if(isUpdate !== true){
6211 this.fireEvent("beforerowremoved", this, index, record);
6213 var bt = this.mainBody.dom;
6215 var rows = this.el.select('tbody > tr', true).elements;
6217 if(typeof(rows[index]) != 'undefined'){
6218 bt.removeChild(rows[index].dom);
6221 // if(bt.rows[index]){
6222 // bt.removeChild(bt.rows[index]);
6225 if(isUpdate !== true){
6226 //this.stripeRows(index);
6227 //this.syncRowHeights(index, index);
6229 this.fireEvent("rowremoved", this, index, record);
6233 onAdd : function(ds, records, rowIndex)
6235 //Roo.log('on Add called');
6236 // - note this does not handle multiple adding very well..
6237 var bt = this.mainBody.dom;
6238 for (var i =0 ; i < records.length;i++) {
6239 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6240 //Roo.log(records[i]);
6241 //Roo.log(this.store.getAt(rowIndex+i));
6242 this.insertRow(this.store, rowIndex + i, false);
6249 refreshRow : function(record){
6250 var ds = this.store, index;
6251 if(typeof record == 'number'){
6253 record = ds.getAt(index);
6255 index = ds.indexOf(record);
6257 this.insertRow(ds, index, true);
6258 this.onRemove(ds, record, index+1, true);
6259 //this.syncRowHeights(index, index);
6261 this.fireEvent("rowupdated", this, index, record);
6264 insertRow : function(dm, rowIndex, isUpdate){
6267 this.fireEvent("beforerowsinserted", this, rowIndex);
6269 //var s = this.getScrollState();
6270 var row = this.renderRow(this.cm, this.store, rowIndex);
6271 // insert before rowIndex..
6272 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6276 if(row.cellObjects.length){
6277 Roo.each(row.cellObjects, function(r){
6278 _this.renderCellObject(r);
6283 this.fireEvent("rowsinserted", this, rowIndex);
6284 //this.syncRowHeights(firstRow, lastRow);
6285 //this.stripeRows(firstRow);
6292 getRowDom : function(rowIndex)
6294 var rows = this.el.select('tbody > tr', true).elements;
6296 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6299 // returns the object tree for a tr..
6302 renderRow : function(cm, ds, rowIndex)
6305 var d = ds.getAt(rowIndex);
6312 var cellObjects = [];
6314 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6315 var config = cm.config[i];
6317 var renderer = cm.getRenderer(i);
6321 if(typeof(renderer) !== 'undefined'){
6322 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6324 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6325 // and are rendered into the cells after the row is rendered - using the id for the element.
6327 if(typeof(value) === 'object'){
6337 rowIndex : rowIndex,
6342 this.fireEvent('rowclass', this, rowcfg);
6346 cls : rowcfg.rowClass,
6348 html: (typeof(value) === 'object') ? '' : value
6355 if(typeof(config.colspan) != 'undefined'){
6356 td.colspan = config.colspan;
6359 if(typeof(config.hidden) != 'undefined' && config.hidden){
6360 td.style += ' display:none;';
6363 if(typeof(config.align) != 'undefined' && config.align.length){
6364 td.style += ' text-align:' + config.align + ';';
6367 if(typeof(config.width) != 'undefined'){
6368 td.style += ' width:' + config.width + 'px;';
6371 if(typeof(config.cursor) != 'undefined'){
6372 td.style += ' cursor:' + config.cursor + ';';
6375 if(typeof(config.cls) != 'undefined'){
6376 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6379 ['xs','sm','md','lg'].map(function(size){
6381 if(typeof(config[size]) == 'undefined'){
6385 if (!config[size]) { // 0 = hidden
6386 td.cls += ' hidden-' + size;
6390 td.cls += ' col-' + size + '-' + config[size];
6398 row.cellObjects = cellObjects;
6406 onBeforeLoad : function()
6408 //Roo.log('ds onBeforeLoad');
6412 //if(this.loadMask){
6413 // this.maskEl.show();
6421 this.el.select('tbody', true).first().dom.innerHTML = '';
6424 * Show or hide a row.
6425 * @param {Number} rowIndex to show or hide
6426 * @param {Boolean} state hide
6428 setRowVisibility : function(rowIndex, state)
6430 var bt = this.mainBody.dom;
6432 var rows = this.el.select('tbody > tr', true).elements;
6434 if(typeof(rows[rowIndex]) == 'undefined'){
6437 rows[rowIndex].dom.style.display = state ? '' : 'none';
6441 getSelectionModel : function(){
6443 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6445 return this.selModel;
6448 * Render the Roo.bootstrap object from renderder
6450 renderCellObject : function(r)
6454 var t = r.cfg.render(r.container);
6457 Roo.each(r.cfg.cn, function(c){
6459 container: t.getChildContainer(),
6462 _this.renderCellObject(child);
6467 getRowIndex : function(row)
6471 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6494 * @class Roo.bootstrap.TableCell
6495 * @extends Roo.bootstrap.Component
6496 * Bootstrap TableCell class
6497 * @cfg {String} html cell contain text
6498 * @cfg {String} cls cell class
6499 * @cfg {String} tag cell tag (td|th) default td
6500 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6501 * @cfg {String} align Aligns the content in a cell
6502 * @cfg {String} axis Categorizes cells
6503 * @cfg {String} bgcolor Specifies the background color of a cell
6504 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6505 * @cfg {Number} colspan Specifies the number of columns a cell should span
6506 * @cfg {String} headers Specifies one or more header cells a cell is related to
6507 * @cfg {Number} height Sets the height of a cell
6508 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6509 * @cfg {Number} rowspan Sets the number of rows a cell should span
6510 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6511 * @cfg {String} valign Vertical aligns the content in a cell
6512 * @cfg {Number} width Specifies the width of a cell
6515 * Create a new TableCell
6516 * @param {Object} config The config object
6519 Roo.bootstrap.TableCell = function(config){
6520 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6523 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6543 getAutoCreate : function(){
6544 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6564 cfg.align=this.align
6570 cfg.bgcolor=this.bgcolor
6573 cfg.charoff=this.charoff
6576 cfg.colspan=this.colspan
6579 cfg.headers=this.headers
6582 cfg.height=this.height
6585 cfg.nowrap=this.nowrap
6588 cfg.rowspan=this.rowspan
6591 cfg.scope=this.scope
6594 cfg.valign=this.valign
6597 cfg.width=this.width
6616 * @class Roo.bootstrap.TableRow
6617 * @extends Roo.bootstrap.Component
6618 * Bootstrap TableRow class
6619 * @cfg {String} cls row class
6620 * @cfg {String} align Aligns the content in a table row
6621 * @cfg {String} bgcolor Specifies a background color for a table row
6622 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6623 * @cfg {String} valign Vertical aligns the content in a table row
6626 * Create a new TableRow
6627 * @param {Object} config The config object
6630 Roo.bootstrap.TableRow = function(config){
6631 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6634 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6642 getAutoCreate : function(){
6643 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6653 cfg.align = this.align;
6656 cfg.bgcolor = this.bgcolor;
6659 cfg.charoff = this.charoff;
6662 cfg.valign = this.valign;
6680 * @class Roo.bootstrap.TableBody
6681 * @extends Roo.bootstrap.Component
6682 * Bootstrap TableBody class
6683 * @cfg {String} cls element class
6684 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6685 * @cfg {String} align Aligns the content inside the element
6686 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6687 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6690 * Create a new TableBody
6691 * @param {Object} config The config object
6694 Roo.bootstrap.TableBody = function(config){
6695 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6698 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6706 getAutoCreate : function(){
6707 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6721 cfg.align = this.align;
6724 cfg.charoff = this.charoff;
6727 cfg.valign = this.valign;
6734 // initEvents : function()
6741 // this.store = Roo.factory(this.store, Roo.data);
6742 // this.store.on('load', this.onLoad, this);
6744 // this.store.load();
6748 // onLoad: function ()
6750 // this.fireEvent('load', this);
6760 * Ext JS Library 1.1.1
6761 * Copyright(c) 2006-2007, Ext JS, LLC.
6763 * Originally Released Under LGPL - original licence link has changed is not relivant.
6766 * <script type="text/javascript">
6769 // as we use this in bootstrap.
6770 Roo.namespace('Roo.form');
6772 * @class Roo.form.Action
6773 * Internal Class used to handle form actions
6775 * @param {Roo.form.BasicForm} el The form element or its id
6776 * @param {Object} config Configuration options
6781 // define the action interface
6782 Roo.form.Action = function(form, options){
6784 this.options = options || {};
6787 * Client Validation Failed
6790 Roo.form.Action.CLIENT_INVALID = 'client';
6792 * Server Validation Failed
6795 Roo.form.Action.SERVER_INVALID = 'server';
6797 * Connect to Server Failed
6800 Roo.form.Action.CONNECT_FAILURE = 'connect';
6802 * Reading Data from Server Failed
6805 Roo.form.Action.LOAD_FAILURE = 'load';
6807 Roo.form.Action.prototype = {
6809 failureType : undefined,
6810 response : undefined,
6814 run : function(options){
6819 success : function(response){
6824 handleResponse : function(response){
6828 // default connection failure
6829 failure : function(response){
6831 this.response = response;
6832 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6833 this.form.afterAction(this, false);
6836 processResponse : function(response){
6837 this.response = response;
6838 if(!response.responseText){
6841 this.result = this.handleResponse(response);
6845 // utility functions used internally
6846 getUrl : function(appendParams){
6847 var url = this.options.url || this.form.url || this.form.el.dom.action;
6849 var p = this.getParams();
6851 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6857 getMethod : function(){
6858 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6861 getParams : function(){
6862 var bp = this.form.baseParams;
6863 var p = this.options.params;
6865 if(typeof p == "object"){
6866 p = Roo.urlEncode(Roo.applyIf(p, bp));
6867 }else if(typeof p == 'string' && bp){
6868 p += '&' + Roo.urlEncode(bp);
6871 p = Roo.urlEncode(bp);
6876 createCallback : function(){
6878 success: this.success,
6879 failure: this.failure,
6881 timeout: (this.form.timeout*1000),
6882 upload: this.form.fileUpload ? this.success : undefined
6887 Roo.form.Action.Submit = function(form, options){
6888 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6891 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6894 haveProgress : false,
6895 uploadComplete : false,
6897 // uploadProgress indicator.
6898 uploadProgress : function()
6900 if (!this.form.progressUrl) {
6904 if (!this.haveProgress) {
6905 Roo.MessageBox.progress("Uploading", "Uploading");
6907 if (this.uploadComplete) {
6908 Roo.MessageBox.hide();
6912 this.haveProgress = true;
6914 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6916 var c = new Roo.data.Connection();
6918 url : this.form.progressUrl,
6923 success : function(req){
6924 //console.log(data);
6928 rdata = Roo.decode(req.responseText)
6930 Roo.log("Invalid data from server..");
6934 if (!rdata || !rdata.success) {
6936 Roo.MessageBox.alert(Roo.encode(rdata));
6939 var data = rdata.data;
6941 if (this.uploadComplete) {
6942 Roo.MessageBox.hide();
6947 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6948 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6951 this.uploadProgress.defer(2000,this);
6954 failure: function(data) {
6955 Roo.log('progress url failed ');
6966 // run get Values on the form, so it syncs any secondary forms.
6967 this.form.getValues();
6969 var o = this.options;
6970 var method = this.getMethod();
6971 var isPost = method == 'POST';
6972 if(o.clientValidation === false || this.form.isValid()){
6974 if (this.form.progressUrl) {
6975 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6976 (new Date() * 1) + '' + Math.random());
6981 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6982 form:this.form.el.dom,
6983 url:this.getUrl(!isPost),
6985 params:isPost ? this.getParams() : null,
6986 isUpload: this.form.fileUpload
6989 this.uploadProgress();
6991 }else if (o.clientValidation !== false){ // client validation failed
6992 this.failureType = Roo.form.Action.CLIENT_INVALID;
6993 this.form.afterAction(this, false);
6997 success : function(response)
6999 this.uploadComplete= true;
7000 if (this.haveProgress) {
7001 Roo.MessageBox.hide();
7005 var result = this.processResponse(response);
7006 if(result === true || result.success){
7007 this.form.afterAction(this, true);
7011 this.form.markInvalid(result.errors);
7012 this.failureType = Roo.form.Action.SERVER_INVALID;
7014 this.form.afterAction(this, false);
7016 failure : function(response)
7018 this.uploadComplete= true;
7019 if (this.haveProgress) {
7020 Roo.MessageBox.hide();
7023 this.response = response;
7024 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7025 this.form.afterAction(this, false);
7028 handleResponse : function(response){
7029 if(this.form.errorReader){
7030 var rs = this.form.errorReader.read(response);
7033 for(var i = 0, len = rs.records.length; i < len; i++) {
7034 var r = rs.records[i];
7038 if(errors.length < 1){
7042 success : rs.success,
7048 ret = Roo.decode(response.responseText);
7052 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7062 Roo.form.Action.Load = function(form, options){
7063 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7064 this.reader = this.form.reader;
7067 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7072 Roo.Ajax.request(Roo.apply(
7073 this.createCallback(), {
7074 method:this.getMethod(),
7075 url:this.getUrl(false),
7076 params:this.getParams()
7080 success : function(response){
7082 var result = this.processResponse(response);
7083 if(result === true || !result.success || !result.data){
7084 this.failureType = Roo.form.Action.LOAD_FAILURE;
7085 this.form.afterAction(this, false);
7088 this.form.clearInvalid();
7089 this.form.setValues(result.data);
7090 this.form.afterAction(this, true);
7093 handleResponse : function(response){
7094 if(this.form.reader){
7095 var rs = this.form.reader.read(response);
7096 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7098 success : rs.success,
7102 return Roo.decode(response.responseText);
7106 Roo.form.Action.ACTION_TYPES = {
7107 'load' : Roo.form.Action.Load,
7108 'submit' : Roo.form.Action.Submit
7117 * @class Roo.bootstrap.Form
7118 * @extends Roo.bootstrap.Component
7119 * Bootstrap Form class
7120 * @cfg {String} method GET | POST (default POST)
7121 * @cfg {String} labelAlign top | left (default top)
7122 * @cfg {String} align left | right - for navbars
7123 * @cfg {Boolean} loadMask load mask when submit (default true)
7128 * @param {Object} config The config object
7132 Roo.bootstrap.Form = function(config){
7133 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7136 * @event clientvalidation
7137 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7138 * @param {Form} this
7139 * @param {Boolean} valid true if the form has passed client-side validation
7141 clientvalidation: true,
7143 * @event beforeaction
7144 * Fires before any action is performed. Return false to cancel the action.
7145 * @param {Form} this
7146 * @param {Action} action The action to be performed
7150 * @event actionfailed
7151 * Fires when an action fails.
7152 * @param {Form} this
7153 * @param {Action} action The action that failed
7155 actionfailed : true,
7157 * @event actioncomplete
7158 * Fires when an action is completed.
7159 * @param {Form} this
7160 * @param {Action} action The action that completed
7162 actioncomplete : true
7167 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7170 * @cfg {String} method
7171 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7176 * The URL to use for form actions if one isn't supplied in the action options.
7179 * @cfg {Boolean} fileUpload
7180 * Set to true if this form is a file upload.
7184 * @cfg {Object} baseParams
7185 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7189 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7193 * @cfg {Sting} align (left|right) for navbar forms
7198 activeAction : null,
7201 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7202 * element by passing it or its id or mask the form itself by passing in true.
7205 waitMsgTarget : false,
7209 getAutoCreate : function(){
7213 method : this.method || 'POST',
7214 id : this.id || Roo.id(),
7217 if (this.parent().xtype.match(/^Nav/)) {
7218 cfg.cls = 'navbar-form navbar-' + this.align;
7222 if (this.labelAlign == 'left' ) {
7223 cfg.cls += ' form-horizontal';
7229 initEvents : function()
7231 this.el.on('submit', this.onSubmit, this);
7232 // this was added as random key presses on the form where triggering form submit.
7233 this.el.on('keypress', function(e) {
7234 if (e.getCharCode() != 13) {
7237 // we might need to allow it for textareas.. and some other items.
7238 // check e.getTarget().
7240 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7244 Roo.log("keypress blocked");
7252 onSubmit : function(e){
7257 * Returns true if client-side validation on the form is successful.
7260 isValid : function(){
7261 var items = this.getItems();
7263 items.each(function(f){
7272 * Returns true if any fields in this form have changed since their original load.
7275 isDirty : function(){
7277 var items = this.getItems();
7278 items.each(function(f){
7288 * Performs a predefined action (submit or load) or custom actions you define on this form.
7289 * @param {String} actionName The name of the action type
7290 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7291 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7292 * accept other config options):
7294 Property Type Description
7295 ---------------- --------------- ----------------------------------------------------------------------------------
7296 url String The url for the action (defaults to the form's url)
7297 method String The form method to use (defaults to the form's method, or POST if not defined)
7298 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7299 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7300 validate the form on the client (defaults to false)
7302 * @return {BasicForm} this
7304 doAction : function(action, options){
7305 if(typeof action == 'string'){
7306 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7308 if(this.fireEvent('beforeaction', this, action) !== false){
7309 this.beforeAction(action);
7310 action.run.defer(100, action);
7316 beforeAction : function(action){
7317 var o = action.options;
7320 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7322 // not really supported yet.. ??
7324 //if(this.waitMsgTarget === true){
7325 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7326 //}else if(this.waitMsgTarget){
7327 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7328 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7330 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7336 afterAction : function(action, success){
7337 this.activeAction = null;
7338 var o = action.options;
7340 //if(this.waitMsgTarget === true){
7342 //}else if(this.waitMsgTarget){
7343 // this.waitMsgTarget.unmask();
7345 // Roo.MessageBox.updateProgress(1);
7346 // Roo.MessageBox.hide();
7353 Roo.callback(o.success, o.scope, [this, action]);
7354 this.fireEvent('actioncomplete', this, action);
7358 // failure condition..
7359 // we have a scenario where updates need confirming.
7360 // eg. if a locking scenario exists..
7361 // we look for { errors : { needs_confirm : true }} in the response.
7363 (typeof(action.result) != 'undefined') &&
7364 (typeof(action.result.errors) != 'undefined') &&
7365 (typeof(action.result.errors.needs_confirm) != 'undefined')
7368 Roo.log("not supported yet");
7371 Roo.MessageBox.confirm(
7372 "Change requires confirmation",
7373 action.result.errorMsg,
7378 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7388 Roo.callback(o.failure, o.scope, [this, action]);
7389 // show an error message if no failed handler is set..
7390 if (!this.hasListener('actionfailed')) {
7391 Roo.log("need to add dialog support");
7393 Roo.MessageBox.alert("Error",
7394 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7395 action.result.errorMsg :
7396 "Saving Failed, please check your entries or try again"
7401 this.fireEvent('actionfailed', this, action);
7406 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7407 * @param {String} id The value to search for
7410 findField : function(id){
7411 var items = this.getItems();
7412 var field = items.get(id);
7414 items.each(function(f){
7415 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7422 return field || null;
7425 * Mark fields in this form invalid in bulk.
7426 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7427 * @return {BasicForm} this
7429 markInvalid : function(errors){
7430 if(errors instanceof Array){
7431 for(var i = 0, len = errors.length; i < len; i++){
7432 var fieldError = errors[i];
7433 var f = this.findField(fieldError.id);
7435 f.markInvalid(fieldError.msg);
7441 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7442 field.markInvalid(errors[id]);
7446 //Roo.each(this.childForms || [], function (f) {
7447 // f.markInvalid(errors);
7454 * Set values for fields in this form in bulk.
7455 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7456 * @return {BasicForm} this
7458 setValues : function(values){
7459 if(values instanceof Array){ // array of objects
7460 for(var i = 0, len = values.length; i < len; i++){
7462 var f = this.findField(v.id);
7464 f.setValue(v.value);
7465 if(this.trackResetOnLoad){
7466 f.originalValue = f.getValue();
7470 }else{ // object hash
7473 if(typeof values[id] != 'function' && (field = this.findField(id))){
7475 if (field.setFromData &&
7477 field.displayField &&
7478 // combos' with local stores can
7479 // be queried via setValue()
7480 // to set their value..
7481 (field.store && !field.store.isLocal)
7485 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7486 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7487 field.setFromData(sd);
7490 field.setValue(values[id]);
7494 if(this.trackResetOnLoad){
7495 field.originalValue = field.getValue();
7501 //Roo.each(this.childForms || [], function (f) {
7502 // f.setValues(values);
7509 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7510 * they are returned as an array.
7511 * @param {Boolean} asString
7514 getValues : function(asString){
7515 //if (this.childForms) {
7516 // copy values from the child forms
7517 // Roo.each(this.childForms, function (f) {
7518 // this.setValues(f.getValues());
7524 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7525 if(asString === true){
7528 return Roo.urlDecode(fs);
7532 * Returns the fields in this form as an object with key/value pairs.
7533 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7536 getFieldValues : function(with_hidden)
7538 var items = this.getItems();
7540 items.each(function(f){
7544 var v = f.getValue();
7545 if (f.inputType =='radio') {
7546 if (typeof(ret[f.getName()]) == 'undefined') {
7547 ret[f.getName()] = ''; // empty..
7550 if (!f.el.dom.checked) {
7558 // not sure if this supported any more..
7559 if ((typeof(v) == 'object') && f.getRawValue) {
7560 v = f.getRawValue() ; // dates..
7562 // combo boxes where name != hiddenName...
7563 if (f.name != f.getName()) {
7564 ret[f.name] = f.getRawValue();
7566 ret[f.getName()] = v;
7573 * Clears all invalid messages in this form.
7574 * @return {BasicForm} this
7576 clearInvalid : function(){
7577 var items = this.getItems();
7579 items.each(function(f){
7590 * @return {BasicForm} this
7593 var items = this.getItems();
7594 items.each(function(f){
7598 Roo.each(this.childForms || [], function (f) {
7605 getItems : function()
7607 var r=new Roo.util.MixedCollection(false, function(o){
7608 return o.id || (o.id = Roo.id());
7610 var iter = function(el) {
7617 Roo.each(el.items,function(e) {
7637 * Ext JS Library 1.1.1
7638 * Copyright(c) 2006-2007, Ext JS, LLC.
7640 * Originally Released Under LGPL - original licence link has changed is not relivant.
7643 * <script type="text/javascript">
7646 * @class Roo.form.VTypes
7647 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7650 Roo.form.VTypes = function(){
7651 // closure these in so they are only created once.
7652 var alpha = /^[a-zA-Z_]+$/;
7653 var alphanum = /^[a-zA-Z0-9_]+$/;
7654 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7655 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7657 // All these messages and functions are configurable
7660 * The function used to validate email addresses
7661 * @param {String} value The email address
7663 'email' : function(v){
7664 return email.test(v);
7667 * The error text to display when the email validation function returns false
7670 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7672 * The keystroke filter mask to be applied on email input
7675 'emailMask' : /[a-z0-9_\.\-@]/i,
7678 * The function used to validate URLs
7679 * @param {String} value The URL
7681 'url' : function(v){
7685 * The error text to display when the url validation function returns false
7688 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7691 * The function used to validate alpha values
7692 * @param {String} value The value
7694 'alpha' : function(v){
7695 return alpha.test(v);
7698 * The error text to display when the alpha validation function returns false
7701 'alphaText' : 'This field should only contain letters and _',
7703 * The keystroke filter mask to be applied on alpha input
7706 'alphaMask' : /[a-z_]/i,
7709 * The function used to validate alphanumeric values
7710 * @param {String} value The value
7712 'alphanum' : function(v){
7713 return alphanum.test(v);
7716 * The error text to display when the alphanumeric validation function returns false
7719 'alphanumText' : 'This field should only contain letters, numbers and _',
7721 * The keystroke filter mask to be applied on alphanumeric input
7724 'alphanumMask' : /[a-z0-9_]/i
7734 * @class Roo.bootstrap.Input
7735 * @extends Roo.bootstrap.Component
7736 * Bootstrap Input class
7737 * @cfg {Boolean} disabled is it disabled
7738 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7739 * @cfg {String} name name of the input
7740 * @cfg {string} fieldLabel - the label associated
7741 * @cfg {string} placeholder - placeholder to put in text.
7742 * @cfg {string} before - input group add on before
7743 * @cfg {string} after - input group add on after
7744 * @cfg {string} size - (lg|sm) or leave empty..
7745 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7746 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7747 * @cfg {Number} md colspan out of 12 for computer-sized screens
7748 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7749 * @cfg {string} value default value of the input
7750 * @cfg {Number} labelWidth set the width of label (0-12)
7751 * @cfg {String} labelAlign (top|left)
7752 * @cfg {Boolean} readOnly Specifies that the field should be read-only
7753 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7755 * @cfg {String} align (left|center|right) Default left
7756 * @cfg {Boolean} forceFeedback (true|false) Default false
7762 * Create a new Input
7763 * @param {Object} config The config object
7766 Roo.bootstrap.Input = function(config){
7767 Roo.bootstrap.Input.superclass.constructor.call(this, config);
7772 * Fires when this field receives input focus.
7773 * @param {Roo.form.Field} this
7778 * Fires when this field loses input focus.
7779 * @param {Roo.form.Field} this
7784 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
7785 * {@link Roo.EventObject#getKey} to determine which key was pressed.
7786 * @param {Roo.form.Field} this
7787 * @param {Roo.EventObject} e The event object
7792 * Fires just before the field blurs if the field value has changed.
7793 * @param {Roo.form.Field} this
7794 * @param {Mixed} newValue The new value
7795 * @param {Mixed} oldValue The original value
7800 * Fires after the field has been marked as invalid.
7801 * @param {Roo.form.Field} this
7802 * @param {String} msg The validation message
7807 * Fires after the field has been validated with no errors.
7808 * @param {Roo.form.Field} this
7813 * Fires after the key up
7814 * @param {Roo.form.Field} this
7815 * @param {Roo.EventObject} e The event Object
7821 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
7823 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7824 automatic validation (defaults to "keyup").
7826 validationEvent : "keyup",
7828 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7830 validateOnBlur : true,
7832 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7834 validationDelay : 250,
7836 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7838 focusClass : "x-form-focus", // not needed???
7842 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7844 invalidClass : "has-warning",
7847 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7849 validClass : "has-success",
7852 * @cfg {Boolean} hasFeedback (true|false) default true
7857 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7859 invalidFeedbackClass : "glyphicon-warning-sign",
7862 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7864 validFeedbackClass : "glyphicon-ok",
7867 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7869 selectOnFocus : false,
7872 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7876 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7881 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7883 disableKeyFilter : false,
7886 * @cfg {Boolean} disabled True to disable the field (defaults to false).
7890 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7894 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7896 blankText : "This field is required",
7899 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7903 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7905 maxLength : Number.MAX_VALUE,
7907 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7909 minLengthText : "The minimum length for this field is {0}",
7911 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7913 maxLengthText : "The maximum length for this field is {0}",
7917 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7918 * If available, this function will be called only after the basic validators all return true, and will be passed the
7919 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7923 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7924 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7925 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
7929 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7933 autocomplete: false,
7952 formatedValue : false,
7953 forceFeedback : false,
7955 parentLabelAlign : function()
7958 while (parent.parent()) {
7959 parent = parent.parent();
7960 if (typeof(parent.labelAlign) !='undefined') {
7961 return parent.labelAlign;
7968 getAutoCreate : function(){
7970 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7976 if(this.inputType != 'hidden'){
7977 cfg.cls = 'form-group' //input-group
7983 type : this.inputType,
7985 cls : 'form-control',
7986 placeholder : this.placeholder || '',
7987 autocomplete : this.autocomplete || 'new-password'
7992 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7995 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7996 input.maxLength = this.maxLength;
7999 if (this.disabled) {
8000 input.disabled=true;
8003 if (this.readOnly) {
8004 input.readonly=true;
8008 input.name = this.name;
8011 input.cls += ' input-' + this.size;
8014 ['xs','sm','md','lg'].map(function(size){
8015 if (settings[size]) {
8016 cfg.cls += ' col-' + size + '-' + settings[size];
8020 var inputblock = input;
8024 cls: 'glyphicon form-control-feedback'
8027 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8030 cls : 'has-feedback',
8038 if (this.before || this.after) {
8041 cls : 'input-group',
8045 if (this.before && typeof(this.before) == 'string') {
8047 inputblock.cn.push({
8049 cls : 'roo-input-before input-group-addon',
8053 if (this.before && typeof(this.before) == 'object') {
8054 this.before = Roo.factory(this.before);
8056 inputblock.cn.push({
8058 cls : 'roo-input-before input-group-' +
8059 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8063 inputblock.cn.push(input);
8065 if (this.after && typeof(this.after) == 'string') {
8066 inputblock.cn.push({
8068 cls : 'roo-input-after input-group-addon',
8072 if (this.after && typeof(this.after) == 'object') {
8073 this.after = Roo.factory(this.after);
8075 inputblock.cn.push({
8077 cls : 'roo-input-after input-group-' +
8078 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8082 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8083 inputblock.cls += ' has-feedback';
8084 inputblock.cn.push(feedback);
8088 if (align ==='left' && this.fieldLabel.length) {
8095 cls : 'control-label col-sm-' + this.labelWidth,
8096 html : this.fieldLabel
8100 cls : "col-sm-" + (12 - this.labelWidth),
8107 } else if ( this.fieldLabel.length) {
8113 //cls : 'input-group-addon',
8114 html : this.fieldLabel
8133 if (this.parentType === 'Navbar' && this.parent().bar) {
8134 cfg.cls += ' navbar-form';
8141 * return the real input element.
8143 inputEl: function ()
8145 return this.el.select('input.form-control',true).first();
8148 tooltipEl : function()
8150 return this.inputEl();
8153 setDisabled : function(v)
8155 var i = this.inputEl().dom;
8157 i.removeAttribute('disabled');
8161 i.setAttribute('disabled','true');
8163 initEvents : function()
8166 this.inputEl().on("keydown" , this.fireKey, this);
8167 this.inputEl().on("focus", this.onFocus, this);
8168 this.inputEl().on("blur", this.onBlur, this);
8170 this.inputEl().relayEvent('keyup', this);
8172 // reference to original value for reset
8173 this.originalValue = this.getValue();
8174 //Roo.form.TextField.superclass.initEvents.call(this);
8175 if(this.validationEvent == 'keyup'){
8176 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8177 this.inputEl().on('keyup', this.filterValidation, this);
8179 else if(this.validationEvent !== false){
8180 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8183 if(this.selectOnFocus){
8184 this.on("focus", this.preFocus, this);
8187 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8188 this.inputEl().on("keypress", this.filterKeys, this);
8191 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8192 this.el.on("click", this.autoSize, this);
8195 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8196 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8199 if (typeof(this.before) == 'object') {
8200 this.before.render(this.el.select('.roo-input-before',true).first());
8202 if (typeof(this.after) == 'object') {
8203 this.after.render(this.el.select('.roo-input-after',true).first());
8208 filterValidation : function(e){
8209 if(!e.isNavKeyPress()){
8210 this.validationTask.delay(this.validationDelay);
8214 * Validates the field value
8215 * @return {Boolean} True if the value is valid, else false
8217 validate : function(){
8218 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8219 if(this.disabled || this.validateValue(this.getRawValue())){
8230 * Validates a value according to the field's validation rules and marks the field as invalid
8231 * if the validation fails
8232 * @param {Mixed} value The value to validate
8233 * @return {Boolean} True if the value is valid, else false
8235 validateValue : function(value){
8236 if(value.length < 1) { // if it's blank
8237 if(this.allowBlank){
8243 if(value.length < this.minLength){
8246 if(value.length > this.maxLength){
8250 var vt = Roo.form.VTypes;
8251 if(!vt[this.vtype](value, this)){
8255 if(typeof this.validator == "function"){
8256 var msg = this.validator(value);
8262 if(this.regex && !this.regex.test(value)){
8272 fireKey : function(e){
8273 //Roo.log('field ' + e.getKey());
8274 if(e.isNavKeyPress()){
8275 this.fireEvent("specialkey", this, e);
8278 focus : function (selectText){
8280 this.inputEl().focus();
8281 if(selectText === true){
8282 this.inputEl().dom.select();
8288 onFocus : function(){
8289 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8290 // this.el.addClass(this.focusClass);
8293 this.hasFocus = true;
8294 this.startValue = this.getValue();
8295 this.fireEvent("focus", this);
8299 beforeBlur : Roo.emptyFn,
8303 onBlur : function(){
8305 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8306 //this.el.removeClass(this.focusClass);
8308 this.hasFocus = false;
8309 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8312 var v = this.getValue();
8313 if(String(v) !== String(this.startValue)){
8314 this.fireEvent('change', this, v, this.startValue);
8316 this.fireEvent("blur", this);
8320 * Resets the current field value to the originally loaded value and clears any validation messages
8323 this.setValue(this.originalValue);
8327 * Returns the name of the field
8328 * @return {Mixed} name The name field
8330 getName: function(){
8334 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8335 * @return {Mixed} value The field value
8337 getValue : function(){
8339 var v = this.inputEl().getValue();
8344 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8345 * @return {Mixed} value The field value
8347 getRawValue : function(){
8348 var v = this.inputEl().getValue();
8354 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8355 * @param {Mixed} value The value to set
8357 setRawValue : function(v){
8358 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8361 selectText : function(start, end){
8362 var v = this.getRawValue();
8364 start = start === undefined ? 0 : start;
8365 end = end === undefined ? v.length : end;
8366 var d = this.inputEl().dom;
8367 if(d.setSelectionRange){
8368 d.setSelectionRange(start, end);
8369 }else if(d.createTextRange){
8370 var range = d.createTextRange();
8371 range.moveStart("character", start);
8372 range.moveEnd("character", v.length-end);
8379 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8380 * @param {Mixed} value The value to set
8382 setValue : function(v){
8385 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8391 processValue : function(value){
8392 if(this.stripCharsRe){
8393 var newValue = value.replace(this.stripCharsRe, '');
8394 if(newValue !== value){
8395 this.setRawValue(newValue);
8402 preFocus : function(){
8404 if(this.selectOnFocus){
8405 this.inputEl().dom.select();
8408 filterKeys : function(e){
8410 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8413 var c = e.getCharCode(), cc = String.fromCharCode(c);
8414 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8417 if(!this.maskRe.test(cc)){
8422 * Clear any invalid styles/messages for this field
8424 clearInvalid : function(){
8426 if(!this.el || this.preventMark){ // not rendered
8430 var label = this.el.select('label', true).first();
8431 var icon = this.el.select('i.fa-star', true).first();
8437 this.el.removeClass(this.invalidClass);
8439 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8441 var feedback = this.el.select('.form-control-feedback', true).first();
8444 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8449 this.fireEvent('valid', this);
8453 * Mark this field as valid
8455 markValid : function()
8457 if(!this.el || this.preventMark){ // not rendered
8461 this.el.removeClass([this.invalidClass, this.validClass]);
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]);
8469 if(this.disabled || this.allowBlank){
8473 var formGroup = this.el.findParent('.form-group', false, true);
8477 var label = formGroup.select('label', true).first();
8478 var icon = formGroup.select('i.fa-star', true).first();
8485 this.el.addClass(this.validClass);
8487 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8489 var feedback = this.el.select('.form-control-feedback', true).first();
8492 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8493 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8498 this.fireEvent('valid', this);
8502 * Mark this field as invalid
8503 * @param {String} msg The validation message
8505 markInvalid : function(msg)
8507 if(!this.el || this.preventMark){ // not rendered
8511 this.el.removeClass([this.invalidClass, this.validClass]);
8513 var feedback = this.el.select('.form-control-feedback', true).first();
8516 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8519 if(this.disabled || this.allowBlank){
8523 var formGroup = this.el.findParent('.form-group', false, true);
8526 var label = formGroup.select('label', true).first();
8527 var icon = formGroup.select('i.fa-star', true).first();
8529 if(!this.getValue().length && label && !icon){
8530 this.el.findParent('.form-group', false, true).createChild({
8532 cls : 'text-danger fa fa-lg fa-star',
8533 tooltip : 'This field is required',
8534 style : 'margin-right:5px;'
8540 this.el.addClass(this.invalidClass);
8542 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8544 var feedback = this.el.select('.form-control-feedback', true).first();
8547 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8549 if(this.getValue().length || this.forceFeedback){
8550 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8557 this.fireEvent('invalid', this, msg);
8560 SafariOnKeyDown : function(event)
8562 // this is a workaround for a password hang bug on chrome/ webkit.
8564 var isSelectAll = false;
8566 if(this.inputEl().dom.selectionEnd > 0){
8567 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8569 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8570 event.preventDefault();
8575 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8577 event.preventDefault();
8578 // this is very hacky as keydown always get's upper case.
8580 var cc = String.fromCharCode(event.getCharCode());
8581 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8585 adjustWidth : function(tag, w){
8586 tag = tag.toLowerCase();
8587 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8588 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8592 if(tag == 'textarea'){
8595 }else if(Roo.isOpera){
8599 if(tag == 'textarea'){
8618 * @class Roo.bootstrap.TextArea
8619 * @extends Roo.bootstrap.Input
8620 * Bootstrap TextArea class
8621 * @cfg {Number} cols Specifies the visible width of a text area
8622 * @cfg {Number} rows Specifies the visible number of lines in a text area
8623 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8624 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8625 * @cfg {string} html text
8628 * Create a new TextArea
8629 * @param {Object} config The config object
8632 Roo.bootstrap.TextArea = function(config){
8633 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8637 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8647 getAutoCreate : function(){
8649 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8660 value : this.value || '',
8661 html: this.html || '',
8662 cls : 'form-control',
8663 placeholder : this.placeholder || ''
8667 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8668 input.maxLength = this.maxLength;
8672 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8676 input.cols = this.cols;
8679 if (this.readOnly) {
8680 input.readonly = true;
8684 input.name = this.name;
8688 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8692 ['xs','sm','md','lg'].map(function(size){
8693 if (settings[size]) {
8694 cfg.cls += ' col-' + size + '-' + settings[size];
8698 var inputblock = input;
8700 if(this.hasFeedback && !this.allowBlank){
8704 cls: 'glyphicon form-control-feedback'
8708 cls : 'has-feedback',
8717 if (this.before || this.after) {
8720 cls : 'input-group',
8724 inputblock.cn.push({
8726 cls : 'input-group-addon',
8731 inputblock.cn.push(input);
8733 if(this.hasFeedback && !this.allowBlank){
8734 inputblock.cls += ' has-feedback';
8735 inputblock.cn.push(feedback);
8739 inputblock.cn.push({
8741 cls : 'input-group-addon',
8748 if (align ==='left' && this.fieldLabel.length) {
8749 // Roo.log("left and has label");
8755 cls : 'control-label col-sm-' + this.labelWidth,
8756 html : this.fieldLabel
8760 cls : "col-sm-" + (12 - this.labelWidth),
8767 } else if ( this.fieldLabel.length) {
8768 // Roo.log(" label");
8773 //cls : 'input-group-addon',
8774 html : this.fieldLabel
8784 // Roo.log(" no label && no align");
8794 if (this.disabled) {
8795 input.disabled=true;
8802 * return the real textarea element.
8804 inputEl: function ()
8806 return this.el.select('textarea.form-control',true).first();
8810 * Clear any invalid styles/messages for this field
8812 clearInvalid : function()
8815 if(!this.el || this.preventMark){ // not rendered
8819 var label = this.el.select('label', true).first();
8820 var icon = this.el.select('i.fa-star', true).first();
8826 this.el.removeClass(this.invalidClass);
8828 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8830 var feedback = this.el.select('.form-control-feedback', true).first();
8833 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8838 this.fireEvent('valid', this);
8842 * Mark this field as valid
8844 markValid : function()
8846 if(!this.el || this.preventMark){ // not rendered
8850 this.el.removeClass([this.invalidClass, this.validClass]);
8852 var feedback = this.el.select('.form-control-feedback', true).first();
8855 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8858 if(this.disabled || this.allowBlank){
8862 var label = this.el.select('label', true).first();
8863 var icon = this.el.select('i.fa-star', true).first();
8869 this.el.addClass(this.validClass);
8871 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8873 var feedback = this.el.select('.form-control-feedback', true).first();
8876 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8877 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8882 this.fireEvent('valid', this);
8886 * Mark this field as invalid
8887 * @param {String} msg The validation message
8889 markInvalid : function(msg)
8891 if(!this.el || this.preventMark){ // not rendered
8895 this.el.removeClass([this.invalidClass, this.validClass]);
8897 var feedback = this.el.select('.form-control-feedback', true).first();
8900 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8903 if(this.disabled || this.allowBlank){
8907 var label = this.el.select('label', true).first();
8908 var icon = this.el.select('i.fa-star', true).first();
8910 if(!this.getValue().length && label && !icon){
8911 this.el.createChild({
8913 cls : 'text-danger fa fa-lg fa-star',
8914 tooltip : 'This field is required',
8915 style : 'margin-right:5px;'
8919 this.el.addClass(this.invalidClass);
8921 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8923 var feedback = this.el.select('.form-control-feedback', true).first();
8926 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8928 if(this.getValue().length || this.forceFeedback){
8929 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8936 this.fireEvent('invalid', this, msg);
8944 * trigger field - base class for combo..
8949 * @class Roo.bootstrap.TriggerField
8950 * @extends Roo.bootstrap.Input
8951 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8952 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8953 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8954 * for which you can provide a custom implementation. For example:
8956 var trigger = new Roo.bootstrap.TriggerField();
8957 trigger.onTriggerClick = myTriggerFn;
8958 trigger.applyTo('my-field');
8961 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8962 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8963 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
8964 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8965 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8968 * Create a new TriggerField.
8969 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8970 * to the base TextField)
8972 Roo.bootstrap.TriggerField = function(config){
8973 this.mimicing = false;
8974 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8977 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
8979 * @cfg {String} triggerClass A CSS class to apply to the trigger
8982 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8987 * @cfg {Boolean} removable (true|false) special filter default false
8991 /** @cfg {Boolean} grow @hide */
8992 /** @cfg {Number} growMin @hide */
8993 /** @cfg {Number} growMax @hide */
8999 autoSize: Roo.emptyFn,
9006 actionMode : 'wrap',
9011 getAutoCreate : function(){
9013 var align = this.labelAlign || this.parentLabelAlign();
9018 cls: 'form-group' //input-group
9025 type : this.inputType,
9026 cls : 'form-control',
9027 autocomplete: 'new-password',
9028 placeholder : this.placeholder || ''
9032 input.name = this.name;
9035 input.cls += ' input-' + this.size;
9038 if (this.disabled) {
9039 input.disabled=true;
9042 var inputblock = input;
9044 if(this.hasFeedback && !this.allowBlank){
9048 cls: 'glyphicon form-control-feedback'
9051 if(this.removable && !this.editable && !this.tickable){
9053 cls : 'has-feedback',
9059 cls : 'roo-combo-removable-btn close'
9066 cls : 'has-feedback',
9075 if(this.removable && !this.editable && !this.tickable){
9077 cls : 'roo-removable',
9083 cls : 'roo-combo-removable-btn close'
9090 if (this.before || this.after) {
9093 cls : 'input-group',
9097 inputblock.cn.push({
9099 cls : 'input-group-addon',
9104 inputblock.cn.push(input);
9106 if(this.hasFeedback && !this.allowBlank){
9107 inputblock.cls += ' has-feedback';
9108 inputblock.cn.push(feedback);
9112 inputblock.cn.push({
9114 cls : 'input-group-addon',
9127 cls: 'form-hidden-field'
9141 cls: 'form-hidden-field'
9145 cls: 'select2-choices',
9149 cls: 'select2-search-field',
9162 cls: 'select2-container input-group',
9167 // cls: 'typeahead typeahead-long dropdown-menu',
9168 // style: 'display:none'
9173 if(!this.multiple && this.showToggleBtn){
9179 if (this.caret != false) {
9182 cls: 'fa fa-' + this.caret
9189 cls : 'input-group-addon btn dropdown-toggle',
9194 cls: 'combobox-clear',
9208 combobox.cls += ' select2-container-multi';
9211 if (align ==='left' && this.fieldLabel.length) {
9213 // Roo.log("left and has label");
9219 cls : 'control-label col-sm-' + this.labelWidth,
9220 html : this.fieldLabel
9224 cls : "col-sm-" + (12 - this.labelWidth),
9231 } else if ( this.fieldLabel.length) {
9232 // Roo.log(" label");
9237 //cls : 'input-group-addon',
9238 html : this.fieldLabel
9248 // Roo.log(" no label && no align");
9255 ['xs','sm','md','lg'].map(function(size){
9256 if (settings[size]) {
9257 cfg.cls += ' col-' + size + '-' + settings[size];
9268 onResize : function(w, h){
9269 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9270 // if(typeof w == 'number'){
9271 // var x = w - this.trigger.getWidth();
9272 // this.inputEl().setWidth(this.adjustWidth('input', x));
9273 // this.trigger.setStyle('left', x+'px');
9278 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9281 getResizeEl : function(){
9282 return this.inputEl();
9286 getPositionEl : function(){
9287 return this.inputEl();
9291 alignErrorIcon : function(){
9292 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9296 initEvents : function(){
9300 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9301 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9302 if(!this.multiple && this.showToggleBtn){
9303 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9304 if(this.hideTrigger){
9305 this.trigger.setDisplayed(false);
9307 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9311 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9314 if(this.removable && !this.editable && !this.tickable){
9315 var close = this.closeTriggerEl();
9318 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9319 close.on('click', this.removeBtnClick, this, close);
9323 //this.trigger.addClassOnOver('x-form-trigger-over');
9324 //this.trigger.addClassOnClick('x-form-trigger-click');
9327 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9331 closeTriggerEl : function()
9333 var close = this.el.select('.roo-combo-removable-btn', true).first();
9334 return close ? close : false;
9337 removeBtnClick : function(e, h, el)
9341 if(this.fireEvent("remove", this) !== false){
9346 createList : function()
9348 this.list = Roo.get(document.body).createChild({
9350 cls: 'typeahead typeahead-long dropdown-menu',
9351 style: 'display:none'
9354 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9359 initTrigger : function(){
9364 onDestroy : function(){
9366 this.trigger.removeAllListeners();
9367 // this.trigger.remove();
9370 // this.wrap.remove();
9372 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9376 onFocus : function(){
9377 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9380 this.wrap.addClass('x-trigger-wrap-focus');
9381 this.mimicing = true;
9382 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9383 if(this.monitorTab){
9384 this.el.on("keydown", this.checkTab, this);
9391 checkTab : function(e){
9392 if(e.getKey() == e.TAB){
9398 onBlur : function(){
9403 mimicBlur : function(e, t){
9405 if(!this.wrap.contains(t) && this.validateBlur()){
9412 triggerBlur : function(){
9413 this.mimicing = false;
9414 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9415 if(this.monitorTab){
9416 this.el.un("keydown", this.checkTab, this);
9418 //this.wrap.removeClass('x-trigger-wrap-focus');
9419 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9423 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9424 validateBlur : function(e, t){
9429 onDisable : function(){
9430 this.inputEl().dom.disabled = true;
9431 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9433 // this.wrap.addClass('x-item-disabled');
9438 onEnable : function(){
9439 this.inputEl().dom.disabled = false;
9440 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9442 // this.el.removeClass('x-item-disabled');
9447 onShow : function(){
9448 var ae = this.getActionEl();
9451 ae.dom.style.display = '';
9452 ae.dom.style.visibility = 'visible';
9458 onHide : function(){
9459 var ae = this.getActionEl();
9460 ae.dom.style.display = 'none';
9464 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9465 * by an implementing function.
9467 * @param {EventObject} e
9469 onTriggerClick : Roo.emptyFn
9473 * Ext JS Library 1.1.1
9474 * Copyright(c) 2006-2007, Ext JS, LLC.
9476 * Originally Released Under LGPL - original licence link has changed is not relivant.
9479 * <script type="text/javascript">
9484 * @class Roo.data.SortTypes
9486 * Defines the default sorting (casting?) comparison functions used when sorting data.
9488 Roo.data.SortTypes = {
9490 * Default sort that does nothing
9491 * @param {Mixed} s The value being converted
9492 * @return {Mixed} The comparison value
9499 * The regular expression used to strip tags
9503 stripTagsRE : /<\/?[^>]+>/gi,
9506 * Strips all HTML tags to sort on text only
9507 * @param {Mixed} s The value being converted
9508 * @return {String} The comparison value
9510 asText : function(s){
9511 return String(s).replace(this.stripTagsRE, "");
9515 * Strips all HTML tags to sort on text only - Case insensitive
9516 * @param {Mixed} s The value being converted
9517 * @return {String} The comparison value
9519 asUCText : function(s){
9520 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9524 * Case insensitive string
9525 * @param {Mixed} s The value being converted
9526 * @return {String} The comparison value
9528 asUCString : function(s) {
9529 return String(s).toUpperCase();
9534 * @param {Mixed} s The value being converted
9535 * @return {Number} The comparison value
9537 asDate : function(s) {
9541 if(s instanceof Date){
9544 return Date.parse(String(s));
9549 * @param {Mixed} s The value being converted
9550 * @return {Float} The comparison value
9552 asFloat : function(s) {
9553 var val = parseFloat(String(s).replace(/,/g, ""));
9562 * @param {Mixed} s The value being converted
9563 * @return {Number} The comparison value
9565 asInt : function(s) {
9566 var val = parseInt(String(s).replace(/,/g, ""));
9574 * Ext JS Library 1.1.1
9575 * Copyright(c) 2006-2007, Ext JS, LLC.
9577 * Originally Released Under LGPL - original licence link has changed is not relivant.
9580 * <script type="text/javascript">
9584 * @class Roo.data.Record
9585 * Instances of this class encapsulate both record <em>definition</em> information, and record
9586 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9587 * to access Records cached in an {@link Roo.data.Store} object.<br>
9589 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9590 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9593 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9595 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9596 * {@link #create}. The parameters are the same.
9597 * @param {Array} data An associative Array of data values keyed by the field name.
9598 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9599 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9600 * not specified an integer id is generated.
9602 Roo.data.Record = function(data, id){
9603 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9608 * Generate a constructor for a specific record layout.
9609 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9610 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9611 * Each field definition object may contain the following properties: <ul>
9612 * <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,
9613 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9614 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9615 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9616 * is being used, then this is a string containing the javascript expression to reference the data relative to
9617 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9618 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9619 * this may be omitted.</p></li>
9620 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9621 * <ul><li>auto (Default, implies no conversion)</li>
9626 * <li>date</li></ul></p></li>
9627 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9628 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9629 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9630 * by the Reader into an object that will be stored in the Record. It is passed the
9631 * following parameters:<ul>
9632 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9634 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9636 * <br>usage:<br><pre><code>
9637 var TopicRecord = Roo.data.Record.create(
9638 {name: 'title', mapping: 'topic_title'},
9639 {name: 'author', mapping: 'username'},
9640 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9641 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9642 {name: 'lastPoster', mapping: 'user2'},
9643 {name: 'excerpt', mapping: 'post_text'}
9646 var myNewRecord = new TopicRecord({
9647 title: 'Do my job please',
9650 lastPost: new Date(),
9651 lastPoster: 'Animal',
9652 excerpt: 'No way dude!'
9654 myStore.add(myNewRecord);
9659 Roo.data.Record.create = function(o){
9661 f.superclass.constructor.apply(this, arguments);
9663 Roo.extend(f, Roo.data.Record);
9664 var p = f.prototype;
9665 p.fields = new Roo.util.MixedCollection(false, function(field){
9668 for(var i = 0, len = o.length; i < len; i++){
9669 p.fields.add(new Roo.data.Field(o[i]));
9671 f.getField = function(name){
9672 return p.fields.get(name);
9677 Roo.data.Record.AUTO_ID = 1000;
9678 Roo.data.Record.EDIT = 'edit';
9679 Roo.data.Record.REJECT = 'reject';
9680 Roo.data.Record.COMMIT = 'commit';
9682 Roo.data.Record.prototype = {
9684 * Readonly flag - true if this record has been modified.
9693 join : function(store){
9698 * Set the named field to the specified value.
9699 * @param {String} name The name of the field to set.
9700 * @param {Object} value The value to set the field to.
9702 set : function(name, value){
9703 if(this.data[name] == value){
9710 if(typeof this.modified[name] == 'undefined'){
9711 this.modified[name] = this.data[name];
9713 this.data[name] = value;
9714 if(!this.editing && this.store){
9715 this.store.afterEdit(this);
9720 * Get the value of the named field.
9721 * @param {String} name The name of the field to get the value of.
9722 * @return {Object} The value of the field.
9724 get : function(name){
9725 return this.data[name];
9729 beginEdit : function(){
9730 this.editing = true;
9735 cancelEdit : function(){
9736 this.editing = false;
9737 delete this.modified;
9741 endEdit : function(){
9742 this.editing = false;
9743 if(this.dirty && this.store){
9744 this.store.afterEdit(this);
9749 * Usually called by the {@link Roo.data.Store} which owns the Record.
9750 * Rejects all changes made to the Record since either creation, or the last commit operation.
9751 * Modified fields are reverted to their original values.
9753 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9754 * of reject operations.
9756 reject : function(){
9757 var m = this.modified;
9759 if(typeof m[n] != "function"){
9760 this.data[n] = m[n];
9764 delete this.modified;
9765 this.editing = false;
9767 this.store.afterReject(this);
9772 * Usually called by the {@link Roo.data.Store} which owns the Record.
9773 * Commits all changes made to the Record since either creation, or the last commit operation.
9775 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9776 * of commit operations.
9778 commit : function(){
9780 delete this.modified;
9781 this.editing = false;
9783 this.store.afterCommit(this);
9788 hasError : function(){
9789 return this.error != null;
9793 clearError : function(){
9798 * Creates a copy of this record.
9799 * @param {String} id (optional) A new record id if you don't want to use this record's id
9802 copy : function(newId) {
9803 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9807 * Ext JS Library 1.1.1
9808 * Copyright(c) 2006-2007, Ext JS, LLC.
9810 * Originally Released Under LGPL - original licence link has changed is not relivant.
9813 * <script type="text/javascript">
9819 * @class Roo.data.Store
9820 * @extends Roo.util.Observable
9821 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9822 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9824 * 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
9825 * has no knowledge of the format of the data returned by the Proxy.<br>
9827 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9828 * instances from the data object. These records are cached and made available through accessor functions.
9830 * Creates a new Store.
9831 * @param {Object} config A config object containing the objects needed for the Store to access data,
9832 * and read the data into Records.
9834 Roo.data.Store = function(config){
9835 this.data = new Roo.util.MixedCollection(false);
9836 this.data.getKey = function(o){
9839 this.baseParams = {};
9846 "multisort" : "_multisort"
9849 if(config && config.data){
9850 this.inlineData = config.data;
9854 Roo.apply(this, config);
9856 if(this.reader){ // reader passed
9857 this.reader = Roo.factory(this.reader, Roo.data);
9858 this.reader.xmodule = this.xmodule || false;
9859 if(!this.recordType){
9860 this.recordType = this.reader.recordType;
9862 if(this.reader.onMetaChange){
9863 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9867 if(this.recordType){
9868 this.fields = this.recordType.prototype.fields;
9874 * @event datachanged
9875 * Fires when the data cache has changed, and a widget which is using this Store
9876 * as a Record cache should refresh its view.
9877 * @param {Store} this
9882 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9883 * @param {Store} this
9884 * @param {Object} meta The JSON metadata
9889 * Fires when Records have been added to the Store
9890 * @param {Store} this
9891 * @param {Roo.data.Record[]} records The array of Records added
9892 * @param {Number} index The index at which the record(s) were added
9897 * Fires when a Record has been removed from the Store
9898 * @param {Store} this
9899 * @param {Roo.data.Record} record The Record that was removed
9900 * @param {Number} index The index at which the record was removed
9905 * Fires when a Record has been updated
9906 * @param {Store} this
9907 * @param {Roo.data.Record} record The Record that was updated
9908 * @param {String} operation The update operation being performed. Value may be one of:
9910 Roo.data.Record.EDIT
9911 Roo.data.Record.REJECT
9912 Roo.data.Record.COMMIT
9918 * Fires when the data cache has been cleared.
9919 * @param {Store} this
9924 * Fires before a request is made for a new data object. If the beforeload handler returns false
9925 * the load action will be canceled.
9926 * @param {Store} this
9927 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9931 * @event beforeloadadd
9932 * Fires after a new set of Records has been loaded.
9933 * @param {Store} this
9934 * @param {Roo.data.Record[]} records The Records that were loaded
9935 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9937 beforeloadadd : true,
9940 * Fires after a new set of Records has been loaded, before they are added to the store.
9941 * @param {Store} this
9942 * @param {Roo.data.Record[]} records The Records that were loaded
9943 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9944 * @params {Object} return from reader
9948 * @event loadexception
9949 * Fires if an exception occurs in the Proxy during loading.
9950 * Called with the signature of the Proxy's "loadexception" event.
9951 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9954 * @param {Object} return from JsonData.reader() - success, totalRecords, records
9955 * @param {Object} load options
9956 * @param {Object} jsonData from your request (normally this contains the Exception)
9958 loadexception : true
9962 this.proxy = Roo.factory(this.proxy, Roo.data);
9963 this.proxy.xmodule = this.xmodule || false;
9964 this.relayEvents(this.proxy, ["loadexception"]);
9966 this.sortToggle = {};
9967 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9969 Roo.data.Store.superclass.constructor.call(this);
9971 if(this.inlineData){
9972 this.loadData(this.inlineData);
9973 delete this.inlineData;
9977 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9979 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
9980 * without a remote query - used by combo/forms at present.
9984 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9987 * @cfg {Array} data Inline data to be loaded when the store is initialized.
9990 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9991 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9994 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9995 * on any HTTP request
9998 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10001 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10005 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10006 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10008 remoteSort : false,
10011 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10012 * loaded or when a record is removed. (defaults to false).
10014 pruneModifiedRecords : false,
10017 lastOptions : null,
10020 * Add Records to the Store and fires the add event.
10021 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10023 add : function(records){
10024 records = [].concat(records);
10025 for(var i = 0, len = records.length; i < len; i++){
10026 records[i].join(this);
10028 var index = this.data.length;
10029 this.data.addAll(records);
10030 this.fireEvent("add", this, records, index);
10034 * Remove a Record from the Store and fires the remove event.
10035 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10037 remove : function(record){
10038 var index = this.data.indexOf(record);
10039 this.data.removeAt(index);
10040 if(this.pruneModifiedRecords){
10041 this.modified.remove(record);
10043 this.fireEvent("remove", this, record, index);
10047 * Remove all Records from the Store and fires the clear event.
10049 removeAll : function(){
10051 if(this.pruneModifiedRecords){
10052 this.modified = [];
10054 this.fireEvent("clear", this);
10058 * Inserts Records to the Store at the given index and fires the add event.
10059 * @param {Number} index The start index at which to insert the passed Records.
10060 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10062 insert : function(index, records){
10063 records = [].concat(records);
10064 for(var i = 0, len = records.length; i < len; i++){
10065 this.data.insert(index, records[i]);
10066 records[i].join(this);
10068 this.fireEvent("add", this, records, index);
10072 * Get the index within the cache of the passed Record.
10073 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10074 * @return {Number} The index of the passed Record. Returns -1 if not found.
10076 indexOf : function(record){
10077 return this.data.indexOf(record);
10081 * Get the index within the cache of the Record with the passed id.
10082 * @param {String} id The id of the Record to find.
10083 * @return {Number} The index of the Record. Returns -1 if not found.
10085 indexOfId : function(id){
10086 return this.data.indexOfKey(id);
10090 * Get the Record with the specified id.
10091 * @param {String} id The id of the Record to find.
10092 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10094 getById : function(id){
10095 return this.data.key(id);
10099 * Get the Record at the specified index.
10100 * @param {Number} index The index of the Record to find.
10101 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10103 getAt : function(index){
10104 return this.data.itemAt(index);
10108 * Returns a range of Records between specified indices.
10109 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10110 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10111 * @return {Roo.data.Record[]} An array of Records
10113 getRange : function(start, end){
10114 return this.data.getRange(start, end);
10118 storeOptions : function(o){
10119 o = Roo.apply({}, o);
10122 this.lastOptions = o;
10126 * Loads the Record cache from the configured Proxy using the configured Reader.
10128 * If using remote paging, then the first load call must specify the <em>start</em>
10129 * and <em>limit</em> properties in the options.params property to establish the initial
10130 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10132 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10133 * and this call will return before the new data has been loaded. Perform any post-processing
10134 * in a callback function, or in a "load" event handler.</strong>
10136 * @param {Object} options An object containing properties which control loading options:<ul>
10137 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10138 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10139 * passed the following arguments:<ul>
10140 * <li>r : Roo.data.Record[]</li>
10141 * <li>options: Options object from the load call</li>
10142 * <li>success: Boolean success indicator</li></ul></li>
10143 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10144 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10147 load : function(options){
10148 options = options || {};
10149 if(this.fireEvent("beforeload", this, options) !== false){
10150 this.storeOptions(options);
10151 var p = Roo.apply(options.params || {}, this.baseParams);
10152 // if meta was not loaded from remote source.. try requesting it.
10153 if (!this.reader.metaFromRemote) {
10154 p._requestMeta = 1;
10156 if(this.sortInfo && this.remoteSort){
10157 var pn = this.paramNames;
10158 p[pn["sort"]] = this.sortInfo.field;
10159 p[pn["dir"]] = this.sortInfo.direction;
10161 if (this.multiSort) {
10162 var pn = this.paramNames;
10163 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10166 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10171 * Reloads the Record cache from the configured Proxy using the configured Reader and
10172 * the options from the last load operation performed.
10173 * @param {Object} options (optional) An object containing properties which may override the options
10174 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10175 * the most recently used options are reused).
10177 reload : function(options){
10178 this.load(Roo.applyIf(options||{}, this.lastOptions));
10182 // Called as a callback by the Reader during a load operation.
10183 loadRecords : function(o, options, success){
10184 if(!o || success === false){
10185 if(success !== false){
10186 this.fireEvent("load", this, [], options, o);
10188 if(options.callback){
10189 options.callback.call(options.scope || this, [], options, false);
10193 // if data returned failure - throw an exception.
10194 if (o.success === false) {
10195 // show a message if no listener is registered.
10196 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10197 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10199 // loadmask wil be hooked into this..
10200 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10203 var r = o.records, t = o.totalRecords || r.length;
10205 this.fireEvent("beforeloadadd", this, r, options, o);
10207 if(!options || options.add !== true){
10208 if(this.pruneModifiedRecords){
10209 this.modified = [];
10211 for(var i = 0, len = r.length; i < len; i++){
10215 this.data = this.snapshot;
10216 delete this.snapshot;
10219 this.data.addAll(r);
10220 this.totalLength = t;
10222 this.fireEvent("datachanged", this);
10224 this.totalLength = Math.max(t, this.data.length+r.length);
10227 this.fireEvent("load", this, r, options, o);
10228 if(options.callback){
10229 options.callback.call(options.scope || this, r, options, true);
10235 * Loads data from a passed data block. A Reader which understands the format of the data
10236 * must have been configured in the constructor.
10237 * @param {Object} data The data block from which to read the Records. The format of the data expected
10238 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10239 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10241 loadData : function(o, append){
10242 var r = this.reader.readRecords(o);
10243 this.loadRecords(r, {add: append}, true);
10247 * Gets the number of cached records.
10249 * <em>If using paging, this may not be the total size of the dataset. If the data object
10250 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10251 * the data set size</em>
10253 getCount : function(){
10254 return this.data.length || 0;
10258 * Gets the total number of records in the dataset as returned by the server.
10260 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10261 * the dataset size</em>
10263 getTotalCount : function(){
10264 return this.totalLength || 0;
10268 * Returns the sort state of the Store as an object with two properties:
10270 field {String} The name of the field by which the Records are sorted
10271 direction {String} The sort order, "ASC" or "DESC"
10274 getSortState : function(){
10275 return this.sortInfo;
10279 applySort : function(){
10280 if(this.sortInfo && !this.remoteSort){
10281 var s = this.sortInfo, f = s.field;
10282 var st = this.fields.get(f).sortType;
10283 var fn = function(r1, r2){
10284 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10285 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10287 this.data.sort(s.direction, fn);
10288 if(this.snapshot && this.snapshot != this.data){
10289 this.snapshot.sort(s.direction, fn);
10295 * Sets the default sort column and order to be used by the next load operation.
10296 * @param {String} fieldName The name of the field to sort by.
10297 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10299 setDefaultSort : function(field, dir){
10300 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10304 * Sort the Records.
10305 * If remote sorting is used, the sort is performed on the server, and the cache is
10306 * reloaded. If local sorting is used, the cache is sorted internally.
10307 * @param {String} fieldName The name of the field to sort by.
10308 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10310 sort : function(fieldName, dir){
10311 var f = this.fields.get(fieldName);
10313 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10315 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10316 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10321 this.sortToggle[f.name] = dir;
10322 this.sortInfo = {field: f.name, direction: dir};
10323 if(!this.remoteSort){
10325 this.fireEvent("datachanged", this);
10327 this.load(this.lastOptions);
10332 * Calls the specified function for each of the Records in the cache.
10333 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10334 * Returning <em>false</em> aborts and exits the iteration.
10335 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10337 each : function(fn, scope){
10338 this.data.each(fn, scope);
10342 * Gets all records modified since the last commit. Modified records are persisted across load operations
10343 * (e.g., during paging).
10344 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10346 getModifiedRecords : function(){
10347 return this.modified;
10351 createFilterFn : function(property, value, anyMatch){
10352 if(!value.exec){ // not a regex
10353 value = String(value);
10354 if(value.length == 0){
10357 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10359 return function(r){
10360 return value.test(r.data[property]);
10365 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10366 * @param {String} property A field on your records
10367 * @param {Number} start The record index to start at (defaults to 0)
10368 * @param {Number} end The last record index to include (defaults to length - 1)
10369 * @return {Number} The sum
10371 sum : function(property, start, end){
10372 var rs = this.data.items, v = 0;
10373 start = start || 0;
10374 end = (end || end === 0) ? end : rs.length-1;
10376 for(var i = start; i <= end; i++){
10377 v += (rs[i].data[property] || 0);
10383 * Filter the records by a specified property.
10384 * @param {String} field A field on your records
10385 * @param {String/RegExp} value Either a string that the field
10386 * should start with or a RegExp to test against the field
10387 * @param {Boolean} anyMatch True to match any part not just the beginning
10389 filter : function(property, value, anyMatch){
10390 var fn = this.createFilterFn(property, value, anyMatch);
10391 return fn ? this.filterBy(fn) : this.clearFilter();
10395 * Filter by a function. The specified function will be called with each
10396 * record in this data source. If the function returns true the record is included,
10397 * otherwise it is filtered.
10398 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10399 * @param {Object} scope (optional) The scope of the function (defaults to this)
10401 filterBy : function(fn, scope){
10402 this.snapshot = this.snapshot || this.data;
10403 this.data = this.queryBy(fn, scope||this);
10404 this.fireEvent("datachanged", this);
10408 * Query the records by a specified property.
10409 * @param {String} field A field on your records
10410 * @param {String/RegExp} value Either a string that the field
10411 * should start with or a RegExp to test against the field
10412 * @param {Boolean} anyMatch True to match any part not just the beginning
10413 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10415 query : function(property, value, anyMatch){
10416 var fn = this.createFilterFn(property, value, anyMatch);
10417 return fn ? this.queryBy(fn) : this.data.clone();
10421 * Query by a function. The specified function will be called with each
10422 * record in this data source. If the function returns true the record is included
10424 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10425 * @param {Object} scope (optional) The scope of the function (defaults to this)
10426 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10428 queryBy : function(fn, scope){
10429 var data = this.snapshot || this.data;
10430 return data.filterBy(fn, scope||this);
10434 * Collects unique values for a particular dataIndex from this store.
10435 * @param {String} dataIndex The property to collect
10436 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10437 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10438 * @return {Array} An array of the unique values
10440 collect : function(dataIndex, allowNull, bypassFilter){
10441 var d = (bypassFilter === true && this.snapshot) ?
10442 this.snapshot.items : this.data.items;
10443 var v, sv, r = [], l = {};
10444 for(var i = 0, len = d.length; i < len; i++){
10445 v = d[i].data[dataIndex];
10447 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10456 * Revert to a view of the Record cache with no filtering applied.
10457 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10459 clearFilter : function(suppressEvent){
10460 if(this.snapshot && this.snapshot != this.data){
10461 this.data = this.snapshot;
10462 delete this.snapshot;
10463 if(suppressEvent !== true){
10464 this.fireEvent("datachanged", this);
10470 afterEdit : function(record){
10471 if(this.modified.indexOf(record) == -1){
10472 this.modified.push(record);
10474 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10478 afterReject : function(record){
10479 this.modified.remove(record);
10480 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10484 afterCommit : function(record){
10485 this.modified.remove(record);
10486 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10490 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10491 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10493 commitChanges : function(){
10494 var m = this.modified.slice(0);
10495 this.modified = [];
10496 for(var i = 0, len = m.length; i < len; i++){
10502 * Cancel outstanding changes on all changed records.
10504 rejectChanges : function(){
10505 var m = this.modified.slice(0);
10506 this.modified = [];
10507 for(var i = 0, len = m.length; i < len; i++){
10512 onMetaChange : function(meta, rtype, o){
10513 this.recordType = rtype;
10514 this.fields = rtype.prototype.fields;
10515 delete this.snapshot;
10516 this.sortInfo = meta.sortInfo || this.sortInfo;
10517 this.modified = [];
10518 this.fireEvent('metachange', this, this.reader.meta);
10521 moveIndex : function(data, type)
10523 var index = this.indexOf(data);
10525 var newIndex = index + type;
10529 this.insert(newIndex, data);
10534 * Ext JS Library 1.1.1
10535 * Copyright(c) 2006-2007, Ext JS, LLC.
10537 * Originally Released Under LGPL - original licence link has changed is not relivant.
10540 * <script type="text/javascript">
10544 * @class Roo.data.SimpleStore
10545 * @extends Roo.data.Store
10546 * Small helper class to make creating Stores from Array data easier.
10547 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10548 * @cfg {Array} fields An array of field definition objects, or field name strings.
10549 * @cfg {Array} data The multi-dimensional array of data
10551 * @param {Object} config
10553 Roo.data.SimpleStore = function(config){
10554 Roo.data.SimpleStore.superclass.constructor.call(this, {
10556 reader: new Roo.data.ArrayReader({
10559 Roo.data.Record.create(config.fields)
10561 proxy : new Roo.data.MemoryProxy(config.data)
10565 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10567 * Ext JS Library 1.1.1
10568 * Copyright(c) 2006-2007, Ext JS, LLC.
10570 * Originally Released Under LGPL - original licence link has changed is not relivant.
10573 * <script type="text/javascript">
10578 * @extends Roo.data.Store
10579 * @class Roo.data.JsonStore
10580 * Small helper class to make creating Stores for JSON data easier. <br/>
10582 var store = new Roo.data.JsonStore({
10583 url: 'get-images.php',
10585 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10588 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10589 * JsonReader and HttpProxy (unless inline data is provided).</b>
10590 * @cfg {Array} fields An array of field definition objects, or field name strings.
10592 * @param {Object} config
10594 Roo.data.JsonStore = function(c){
10595 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10596 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10597 reader: new Roo.data.JsonReader(c, c.fields)
10600 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10602 * Ext JS Library 1.1.1
10603 * Copyright(c) 2006-2007, Ext JS, LLC.
10605 * Originally Released Under LGPL - original licence link has changed is not relivant.
10608 * <script type="text/javascript">
10612 Roo.data.Field = function(config){
10613 if(typeof config == "string"){
10614 config = {name: config};
10616 Roo.apply(this, config);
10619 this.type = "auto";
10622 var st = Roo.data.SortTypes;
10623 // named sortTypes are supported, here we look them up
10624 if(typeof this.sortType == "string"){
10625 this.sortType = st[this.sortType];
10628 // set default sortType for strings and dates
10629 if(!this.sortType){
10632 this.sortType = st.asUCString;
10635 this.sortType = st.asDate;
10638 this.sortType = st.none;
10643 var stripRe = /[\$,%]/g;
10645 // prebuilt conversion function for this field, instead of
10646 // switching every time we're reading a value
10648 var cv, dateFormat = this.dateFormat;
10653 cv = function(v){ return v; };
10656 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10660 return v !== undefined && v !== null && v !== '' ?
10661 parseInt(String(v).replace(stripRe, ""), 10) : '';
10666 return v !== undefined && v !== null && v !== '' ?
10667 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10672 cv = function(v){ return v === true || v === "true" || v == 1; };
10679 if(v instanceof Date){
10683 if(dateFormat == "timestamp"){
10684 return new Date(v*1000);
10686 return Date.parseDate(v, dateFormat);
10688 var parsed = Date.parse(v);
10689 return parsed ? new Date(parsed) : null;
10698 Roo.data.Field.prototype = {
10706 * Ext JS Library 1.1.1
10707 * Copyright(c) 2006-2007, Ext JS, LLC.
10709 * Originally Released Under LGPL - original licence link has changed is not relivant.
10712 * <script type="text/javascript">
10715 // Base class for reading structured data from a data source. This class is intended to be
10716 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10719 * @class Roo.data.DataReader
10720 * Base class for reading structured data from a data source. This class is intended to be
10721 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10724 Roo.data.DataReader = function(meta, recordType){
10728 this.recordType = recordType instanceof Array ?
10729 Roo.data.Record.create(recordType) : recordType;
10732 Roo.data.DataReader.prototype = {
10734 * Create an empty record
10735 * @param {Object} data (optional) - overlay some values
10736 * @return {Roo.data.Record} record created.
10738 newRow : function(d) {
10740 this.recordType.prototype.fields.each(function(c) {
10742 case 'int' : da[c.name] = 0; break;
10743 case 'date' : da[c.name] = new Date(); break;
10744 case 'float' : da[c.name] = 0.0; break;
10745 case 'boolean' : da[c.name] = false; break;
10746 default : da[c.name] = ""; break;
10750 return new this.recordType(Roo.apply(da, d));
10755 * Ext JS Library 1.1.1
10756 * Copyright(c) 2006-2007, Ext JS, LLC.
10758 * Originally Released Under LGPL - original licence link has changed is not relivant.
10761 * <script type="text/javascript">
10765 * @class Roo.data.DataProxy
10766 * @extends Roo.data.Observable
10767 * This class is an abstract base class for implementations which provide retrieval of
10768 * unformatted data objects.<br>
10770 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10771 * (of the appropriate type which knows how to parse the data object) to provide a block of
10772 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10774 * Custom implementations must implement the load method as described in
10775 * {@link Roo.data.HttpProxy#load}.
10777 Roo.data.DataProxy = function(){
10780 * @event beforeload
10781 * Fires before a network request is made to retrieve a data object.
10782 * @param {Object} This DataProxy object.
10783 * @param {Object} params The params parameter to the load function.
10788 * Fires before the load method's callback is called.
10789 * @param {Object} This DataProxy object.
10790 * @param {Object} o The data object.
10791 * @param {Object} arg The callback argument object passed to the load function.
10795 * @event loadexception
10796 * Fires if an Exception occurs during data retrieval.
10797 * @param {Object} This DataProxy object.
10798 * @param {Object} o The data object.
10799 * @param {Object} arg The callback argument object passed to the load function.
10800 * @param {Object} e The Exception.
10802 loadexception : true
10804 Roo.data.DataProxy.superclass.constructor.call(this);
10807 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10810 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10814 * Ext JS Library 1.1.1
10815 * Copyright(c) 2006-2007, Ext JS, LLC.
10817 * Originally Released Under LGPL - original licence link has changed is not relivant.
10820 * <script type="text/javascript">
10823 * @class Roo.data.MemoryProxy
10824 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10825 * to the Reader when its load method is called.
10827 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10829 Roo.data.MemoryProxy = function(data){
10833 Roo.data.MemoryProxy.superclass.constructor.call(this);
10837 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10839 * Load data from the requested source (in this case an in-memory
10840 * data object passed to the constructor), read the data object into
10841 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10842 * process that block using the passed callback.
10843 * @param {Object} params This parameter is not used by the MemoryProxy class.
10844 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10845 * object into a block of Roo.data.Records.
10846 * @param {Function} callback The function into which to pass the block of Roo.data.records.
10847 * The function must be passed <ul>
10848 * <li>The Record block object</li>
10849 * <li>The "arg" argument from the load function</li>
10850 * <li>A boolean success indicator</li>
10852 * @param {Object} scope The scope in which to call the callback
10853 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10855 load : function(params, reader, callback, scope, arg){
10856 params = params || {};
10859 result = reader.readRecords(this.data);
10861 this.fireEvent("loadexception", this, arg, null, e);
10862 callback.call(scope, null, arg, false);
10865 callback.call(scope, result, arg, true);
10869 update : function(params, records){
10874 * Ext JS Library 1.1.1
10875 * Copyright(c) 2006-2007, Ext JS, LLC.
10877 * Originally Released Under LGPL - original licence link has changed is not relivant.
10880 * <script type="text/javascript">
10883 * @class Roo.data.HttpProxy
10884 * @extends Roo.data.DataProxy
10885 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10886 * configured to reference a certain URL.<br><br>
10888 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10889 * from which the running page was served.<br><br>
10891 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10893 * Be aware that to enable the browser to parse an XML document, the server must set
10894 * the Content-Type header in the HTTP response to "text/xml".
10896 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10897 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
10898 * will be used to make the request.
10900 Roo.data.HttpProxy = function(conn){
10901 Roo.data.HttpProxy.superclass.constructor.call(this);
10902 // is conn a conn config or a real conn?
10904 this.useAjax = !conn || !conn.events;
10908 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10909 // thse are take from connection...
10912 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10915 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10916 * extra parameters to each request made by this object. (defaults to undefined)
10919 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10920 * to each request made by this object. (defaults to undefined)
10923 * @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)
10926 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10929 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10935 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10939 * Return the {@link Roo.data.Connection} object being used by this Proxy.
10940 * @return {Connection} The Connection object. This object may be used to subscribe to events on
10941 * a finer-grained basis than the DataProxy events.
10943 getConnection : function(){
10944 return this.useAjax ? Roo.Ajax : this.conn;
10948 * Load data from the configured {@link Roo.data.Connection}, read the data object into
10949 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10950 * process that block using the passed callback.
10951 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10952 * for the request to the remote server.
10953 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10954 * object into a block of Roo.data.Records.
10955 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10956 * The function must be passed <ul>
10957 * <li>The Record block object</li>
10958 * <li>The "arg" argument from the load function</li>
10959 * <li>A boolean success indicator</li>
10961 * @param {Object} scope The scope in which to call the callback
10962 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10964 load : function(params, reader, callback, scope, arg){
10965 if(this.fireEvent("beforeload", this, params) !== false){
10967 params : params || {},
10969 callback : callback,
10974 callback : this.loadResponse,
10978 Roo.applyIf(o, this.conn);
10979 if(this.activeRequest){
10980 Roo.Ajax.abort(this.activeRequest);
10982 this.activeRequest = Roo.Ajax.request(o);
10984 this.conn.request(o);
10987 callback.call(scope||this, null, arg, false);
10992 loadResponse : function(o, success, response){
10993 delete this.activeRequest;
10995 this.fireEvent("loadexception", this, o, response);
10996 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11001 result = o.reader.read(response);
11003 this.fireEvent("loadexception", this, o, response, e);
11004 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11008 this.fireEvent("load", this, o, o.request.arg);
11009 o.request.callback.call(o.request.scope, result, o.request.arg, true);
11013 update : function(dataSet){
11018 updateResponse : function(dataSet){
11023 * Ext JS Library 1.1.1
11024 * Copyright(c) 2006-2007, Ext JS, LLC.
11026 * Originally Released Under LGPL - original licence link has changed is not relivant.
11029 * <script type="text/javascript">
11033 * @class Roo.data.ScriptTagProxy
11034 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11035 * other than the originating domain of the running page.<br><br>
11037 * <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
11038 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11040 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11041 * source code that is used as the source inside a <script> tag.<br><br>
11043 * In order for the browser to process the returned data, the server must wrap the data object
11044 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11045 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11046 * depending on whether the callback name was passed:
11049 boolean scriptTag = false;
11050 String cb = request.getParameter("callback");
11053 response.setContentType("text/javascript");
11055 response.setContentType("application/x-json");
11057 Writer out = response.getWriter();
11059 out.write(cb + "(");
11061 out.print(dataBlock.toJsonString());
11068 * @param {Object} config A configuration object.
11070 Roo.data.ScriptTagProxy = function(config){
11071 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11072 Roo.apply(this, config);
11073 this.head = document.getElementsByTagName("head")[0];
11076 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11078 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11080 * @cfg {String} url The URL from which to request the data object.
11083 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11087 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11088 * the server the name of the callback function set up by the load call to process the returned data object.
11089 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11090 * javascript output which calls this named function passing the data object as its only parameter.
11092 callbackParam : "callback",
11094 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11095 * name to the request.
11100 * Load data from the configured URL, read the data object into
11101 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11102 * process that block using the passed callback.
11103 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11104 * for the request to the remote server.
11105 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11106 * object into a block of Roo.data.Records.
11107 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11108 * The function must be passed <ul>
11109 * <li>The Record block object</li>
11110 * <li>The "arg" argument from the load function</li>
11111 * <li>A boolean success indicator</li>
11113 * @param {Object} scope The scope in which to call the callback
11114 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11116 load : function(params, reader, callback, scope, arg){
11117 if(this.fireEvent("beforeload", this, params) !== false){
11119 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11121 var url = this.url;
11122 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11124 url += "&_dc=" + (new Date().getTime());
11126 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11129 cb : "stcCallback"+transId,
11130 scriptId : "stcScript"+transId,
11134 callback : callback,
11140 window[trans.cb] = function(o){
11141 conn.handleResponse(o, trans);
11144 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11146 if(this.autoAbort !== false){
11150 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11152 var script = document.createElement("script");
11153 script.setAttribute("src", url);
11154 script.setAttribute("type", "text/javascript");
11155 script.setAttribute("id", trans.scriptId);
11156 this.head.appendChild(script);
11158 this.trans = trans;
11160 callback.call(scope||this, null, arg, false);
11165 isLoading : function(){
11166 return this.trans ? true : false;
11170 * Abort the current server request.
11172 abort : function(){
11173 if(this.isLoading()){
11174 this.destroyTrans(this.trans);
11179 destroyTrans : function(trans, isLoaded){
11180 this.head.removeChild(document.getElementById(trans.scriptId));
11181 clearTimeout(trans.timeoutId);
11183 window[trans.cb] = undefined;
11185 delete window[trans.cb];
11188 // if hasn't been loaded, wait for load to remove it to prevent script error
11189 window[trans.cb] = function(){
11190 window[trans.cb] = undefined;
11192 delete window[trans.cb];
11199 handleResponse : function(o, trans){
11200 this.trans = false;
11201 this.destroyTrans(trans, true);
11204 result = trans.reader.readRecords(o);
11206 this.fireEvent("loadexception", this, o, trans.arg, e);
11207 trans.callback.call(trans.scope||window, null, trans.arg, false);
11210 this.fireEvent("load", this, o, trans.arg);
11211 trans.callback.call(trans.scope||window, result, trans.arg, true);
11215 handleFailure : function(trans){
11216 this.trans = false;
11217 this.destroyTrans(trans, false);
11218 this.fireEvent("loadexception", this, null, trans.arg);
11219 trans.callback.call(trans.scope||window, null, trans.arg, false);
11223 * Ext JS Library 1.1.1
11224 * Copyright(c) 2006-2007, Ext JS, LLC.
11226 * Originally Released Under LGPL - original licence link has changed is not relivant.
11229 * <script type="text/javascript">
11233 * @class Roo.data.JsonReader
11234 * @extends Roo.data.DataReader
11235 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11236 * based on mappings in a provided Roo.data.Record constructor.
11238 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11239 * in the reply previously.
11244 var RecordDef = Roo.data.Record.create([
11245 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11246 {name: 'occupation'} // This field will use "occupation" as the mapping.
11248 var myReader = new Roo.data.JsonReader({
11249 totalProperty: "results", // The property which contains the total dataset size (optional)
11250 root: "rows", // The property which contains an Array of row objects
11251 id: "id" // The property within each row object that provides an ID for the record (optional)
11255 * This would consume a JSON file like this:
11257 { 'results': 2, 'rows': [
11258 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11259 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11262 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11263 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11264 * paged from the remote server.
11265 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11266 * @cfg {String} root name of the property which contains the Array of row objects.
11267 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11268 * @cfg {Array} fields Array of field definition objects
11270 * Create a new JsonReader
11271 * @param {Object} meta Metadata configuration options
11272 * @param {Object} recordType Either an Array of field definition objects,
11273 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11275 Roo.data.JsonReader = function(meta, recordType){
11278 // set some defaults:
11279 Roo.applyIf(meta, {
11280 totalProperty: 'total',
11281 successProperty : 'success',
11286 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11288 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11291 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11292 * Used by Store query builder to append _requestMeta to params.
11295 metaFromRemote : false,
11297 * This method is only used by a DataProxy which has retrieved data from a remote server.
11298 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11299 * @return {Object} data A data block which is used by an Roo.data.Store object as
11300 * a cache of Roo.data.Records.
11302 read : function(response){
11303 var json = response.responseText;
11305 var o = /* eval:var:o */ eval("("+json+")");
11307 throw {message: "JsonReader.read: Json object not found"};
11313 this.metaFromRemote = true;
11314 this.meta = o.metaData;
11315 this.recordType = Roo.data.Record.create(o.metaData.fields);
11316 this.onMetaChange(this.meta, this.recordType, o);
11318 return this.readRecords(o);
11321 // private function a store will implement
11322 onMetaChange : function(meta, recordType, o){
11329 simpleAccess: function(obj, subsc) {
11336 getJsonAccessor: function(){
11338 return function(expr) {
11340 return(re.test(expr))
11341 ? new Function("obj", "return obj." + expr)
11346 return Roo.emptyFn;
11351 * Create a data block containing Roo.data.Records from an XML document.
11352 * @param {Object} o An object which contains an Array of row objects in the property specified
11353 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11354 * which contains the total size of the dataset.
11355 * @return {Object} data A data block which is used by an Roo.data.Store object as
11356 * a cache of Roo.data.Records.
11358 readRecords : function(o){
11360 * After any data loads, the raw JSON data is available for further custom processing.
11364 var s = this.meta, Record = this.recordType,
11365 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11367 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11369 if(s.totalProperty) {
11370 this.getTotal = this.getJsonAccessor(s.totalProperty);
11372 if(s.successProperty) {
11373 this.getSuccess = this.getJsonAccessor(s.successProperty);
11375 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11377 var g = this.getJsonAccessor(s.id);
11378 this.getId = function(rec) {
11380 return (r === undefined || r === "") ? null : r;
11383 this.getId = function(){return null;};
11386 for(var jj = 0; jj < fl; jj++){
11388 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11389 this.ef[jj] = this.getJsonAccessor(map);
11393 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11394 if(s.totalProperty){
11395 var vt = parseInt(this.getTotal(o), 10);
11400 if(s.successProperty){
11401 var vs = this.getSuccess(o);
11402 if(vs === false || vs === 'false'){
11407 for(var i = 0; i < c; i++){
11410 var id = this.getId(n);
11411 for(var j = 0; j < fl; j++){
11413 var v = this.ef[j](n);
11415 Roo.log('missing convert for ' + f.name);
11419 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11421 var record = new Record(values, id);
11423 records[i] = record;
11429 totalRecords : totalRecords
11434 * Ext JS Library 1.1.1
11435 * Copyright(c) 2006-2007, Ext JS, LLC.
11437 * Originally Released Under LGPL - original licence link has changed is not relivant.
11440 * <script type="text/javascript">
11444 * @class Roo.data.ArrayReader
11445 * @extends Roo.data.DataReader
11446 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11447 * Each element of that Array represents a row of data fields. The
11448 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11449 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11453 var RecordDef = Roo.data.Record.create([
11454 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11455 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11457 var myReader = new Roo.data.ArrayReader({
11458 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11462 * This would consume an Array like this:
11464 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11466 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11468 * Create a new JsonReader
11469 * @param {Object} meta Metadata configuration options.
11470 * @param {Object} recordType Either an Array of field definition objects
11471 * as specified to {@link Roo.data.Record#create},
11472 * or an {@link Roo.data.Record} object
11473 * created using {@link Roo.data.Record#create}.
11475 Roo.data.ArrayReader = function(meta, recordType){
11476 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11479 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11481 * Create a data block containing Roo.data.Records from an XML document.
11482 * @param {Object} o An Array of row objects which represents the dataset.
11483 * @return {Object} data A data block which is used by an Roo.data.Store object as
11484 * a cache of Roo.data.Records.
11486 readRecords : function(o){
11487 var sid = this.meta ? this.meta.id : null;
11488 var recordType = this.recordType, fields = recordType.prototype.fields;
11491 for(var i = 0; i < root.length; i++){
11494 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11495 for(var j = 0, jlen = fields.length; j < jlen; j++){
11496 var f = fields.items[j];
11497 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11498 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11500 values[f.name] = v;
11502 var record = new recordType(values, id);
11504 records[records.length] = record;
11508 totalRecords : records.length
11517 * @class Roo.bootstrap.ComboBox
11518 * @extends Roo.bootstrap.TriggerField
11519 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11520 * @cfg {Boolean} append (true|false) default false
11521 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11522 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11523 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11524 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11525 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11526 * @cfg {Boolean} animate default true
11527 * @cfg {Boolean} emptyResultText only for touch device
11528 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11530 * Create a new ComboBox.
11531 * @param {Object} config Configuration options
11533 Roo.bootstrap.ComboBox = function(config){
11534 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11538 * Fires when the dropdown list is expanded
11539 * @param {Roo.bootstrap.ComboBox} combo This combo box
11544 * Fires when the dropdown list is collapsed
11545 * @param {Roo.bootstrap.ComboBox} combo This combo box
11549 * @event beforeselect
11550 * Fires before a list item is selected. Return false to cancel the selection.
11551 * @param {Roo.bootstrap.ComboBox} combo This combo box
11552 * @param {Roo.data.Record} record The data record returned from the underlying store
11553 * @param {Number} index The index of the selected item in the dropdown list
11555 'beforeselect' : true,
11558 * Fires when a list item is selected
11559 * @param {Roo.bootstrap.ComboBox} combo This combo box
11560 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11561 * @param {Number} index The index of the selected item in the dropdown list
11565 * @event beforequery
11566 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11567 * The event object passed has these properties:
11568 * @param {Roo.bootstrap.ComboBox} combo This combo box
11569 * @param {String} query The query
11570 * @param {Boolean} forceAll true to force "all" query
11571 * @param {Boolean} cancel true to cancel the query
11572 * @param {Object} e The query event object
11574 'beforequery': true,
11577 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11578 * @param {Roo.bootstrap.ComboBox} combo This combo box
11583 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11584 * @param {Roo.bootstrap.ComboBox} combo This combo box
11585 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11590 * Fires when the remove value from the combobox array
11591 * @param {Roo.bootstrap.ComboBox} combo This combo box
11595 * @event specialfilter
11596 * Fires when specialfilter
11597 * @param {Roo.bootstrap.ComboBox} combo This combo box
11599 'specialfilter' : true,
11602 * Fires when tick the element
11603 * @param {Roo.bootstrap.ComboBox} combo This combo box
11607 * @event touchviewdisplay
11608 * Fires when touch view require special display (default is using displayField)
11609 * @param {Roo.bootstrap.ComboBox} combo This combo box
11610 * @param {Object} cfg set html .
11612 'touchviewdisplay' : true
11617 this.tickItems = [];
11619 this.selectedIndex = -1;
11620 if(this.mode == 'local'){
11621 if(config.queryDelay === undefined){
11622 this.queryDelay = 10;
11624 if(config.minChars === undefined){
11630 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11633 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11634 * rendering into an Roo.Editor, defaults to false)
11637 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11638 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11641 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11644 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11645 * the dropdown list (defaults to undefined, with no header element)
11649 * @cfg {String/Roo.Template} tpl The template to use to render the output
11653 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11655 listWidth: undefined,
11657 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11658 * mode = 'remote' or 'text' if mode = 'local')
11660 displayField: undefined,
11663 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11664 * mode = 'remote' or 'value' if mode = 'local').
11665 * Note: use of a valueField requires the user make a selection
11666 * in order for a value to be mapped.
11668 valueField: undefined,
11672 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11673 * field's data value (defaults to the underlying DOM element's name)
11675 hiddenName: undefined,
11677 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11681 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11683 selectedClass: 'active',
11686 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11690 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11691 * anchor positions (defaults to 'tl-bl')
11693 listAlign: 'tl-bl?',
11695 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11699 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11700 * query specified by the allQuery config option (defaults to 'query')
11702 triggerAction: 'query',
11704 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11705 * (defaults to 4, does not apply if editable = false)
11709 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11710 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11714 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11715 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11719 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11720 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11724 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11725 * when editable = true (defaults to false)
11727 selectOnFocus:false,
11729 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11731 queryParam: 'query',
11733 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
11734 * when mode = 'remote' (defaults to 'Loading...')
11736 loadingText: 'Loading...',
11738 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11742 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11746 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11747 * traditional select (defaults to true)
11751 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11755 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11759 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11760 * listWidth has a higher value)
11764 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11765 * allow the user to set arbitrary text into the field (defaults to false)
11767 forceSelection:false,
11769 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11770 * if typeAhead = true (defaults to 250)
11772 typeAheadDelay : 250,
11774 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11775 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11777 valueNotFoundText : undefined,
11779 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11781 blockFocus : false,
11784 * @cfg {Boolean} disableClear Disable showing of clear button.
11786 disableClear : false,
11788 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
11790 alwaysQuery : false,
11793 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
11798 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11800 invalidClass : "has-warning",
11803 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11805 validClass : "has-success",
11808 * @cfg {Boolean} specialFilter (true|false) special filter default false
11810 specialFilter : false,
11813 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11815 mobileTouchView : true,
11827 btnPosition : 'right',
11828 triggerList : true,
11829 showToggleBtn : true,
11831 emptyResultText: 'Empty',
11832 triggerText : 'Select',
11834 // element that contains real text value.. (when hidden is used..)
11836 getAutoCreate : function()
11844 if(Roo.isTouch && this.mobileTouchView){
11845 cfg = this.getAutoCreateTouchView();
11852 if(!this.tickable){
11853 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11858 * ComboBox with tickable selections
11861 var align = this.labelAlign || this.parentLabelAlign();
11864 cls : 'form-group roo-combobox-tickable' //input-group
11869 cls : 'tickable-buttons',
11874 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11875 html : this.triggerText
11881 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11888 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11895 buttons.cn.unshift({
11897 cls: 'select2-search-field-input'
11903 Roo.each(buttons.cn, function(c){
11905 c.cls += ' btn-' + _this.size;
11908 if (_this.disabled) {
11919 cls: 'form-hidden-field'
11923 cls: 'select2-choices',
11927 cls: 'select2-search-field',
11939 cls: 'select2-container input-group select2-container-multi',
11944 // cls: 'typeahead typeahead-long dropdown-menu',
11945 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
11950 if(this.hasFeedback && !this.allowBlank){
11954 cls: 'glyphicon form-control-feedback'
11957 combobox.cn.push(feedback);
11960 if (align ==='left' && this.fieldLabel.length) {
11962 // Roo.log("left and has label");
11968 cls : 'control-label col-sm-' + this.labelWidth,
11969 html : this.fieldLabel
11973 cls : "col-sm-" + (12 - this.labelWidth),
11980 } else if ( this.fieldLabel.length) {
11981 // Roo.log(" label");
11986 //cls : 'input-group-addon',
11987 html : this.fieldLabel
11997 // Roo.log(" no label && no align");
12004 ['xs','sm','md','lg'].map(function(size){
12005 if (settings[size]) {
12006 cfg.cls += ' col-' + size + '-' + settings[size];
12014 _initEventsCalled : false,
12017 initEvents: function()
12020 if (this._initEventsCalled) { // as we call render... prevent looping...
12023 this._initEventsCalled = true;
12026 throw "can not find store for combo";
12029 this.store = Roo.factory(this.store, Roo.data);
12031 // if we are building from html. then this element is so complex, that we can not really
12032 // use the rendered HTML.
12033 // so we have to trash and replace the previous code.
12034 if (Roo.XComponent.build_from_html) {
12036 // remove this element....
12037 var e = this.el.dom, k=0;
12038 while (e ) { e = e.previousSibling; ++k;}
12043 this.rendered = false;
12045 this.render(this.parent().getChildContainer(true), k);
12056 if(Roo.isTouch && this.mobileTouchView){
12057 this.initTouchView();
12062 this.initTickableEvents();
12066 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12068 if(this.hiddenName){
12070 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12072 this.hiddenField.dom.value =
12073 this.hiddenValue !== undefined ? this.hiddenValue :
12074 this.value !== undefined ? this.value : '';
12076 // prevent input submission
12077 this.el.dom.removeAttribute('name');
12078 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12083 // this.el.dom.setAttribute('autocomplete', 'off');
12086 var cls = 'x-combo-list';
12088 //this.list = new Roo.Layer({
12089 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12095 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12096 _this.list.setWidth(lw);
12099 this.list.on('mouseover', this.onViewOver, this);
12100 this.list.on('mousemove', this.onViewMove, this);
12102 this.list.on('scroll', this.onViewScroll, this);
12105 this.list.swallowEvent('mousewheel');
12106 this.assetHeight = 0;
12109 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12110 this.assetHeight += this.header.getHeight();
12113 this.innerList = this.list.createChild({cls:cls+'-inner'});
12114 this.innerList.on('mouseover', this.onViewOver, this);
12115 this.innerList.on('mousemove', this.onViewMove, this);
12116 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12118 if(this.allowBlank && !this.pageSize && !this.disableClear){
12119 this.footer = this.list.createChild({cls:cls+'-ft'});
12120 this.pageTb = new Roo.Toolbar(this.footer);
12124 this.footer = this.list.createChild({cls:cls+'-ft'});
12125 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12126 {pageSize: this.pageSize});
12130 if (this.pageTb && this.allowBlank && !this.disableClear) {
12132 this.pageTb.add(new Roo.Toolbar.Fill(), {
12133 cls: 'x-btn-icon x-btn-clear',
12135 handler: function()
12138 _this.clearValue();
12139 _this.onSelect(false, -1);
12144 this.assetHeight += this.footer.getHeight();
12149 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12152 this.view = new Roo.View(this.list, this.tpl, {
12153 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12155 //this.view.wrapEl.setDisplayed(false);
12156 this.view.on('click', this.onViewClick, this);
12160 this.store.on('beforeload', this.onBeforeLoad, this);
12161 this.store.on('load', this.onLoad, this);
12162 this.store.on('loadexception', this.onLoadException, this);
12164 if(this.resizable){
12165 this.resizer = new Roo.Resizable(this.list, {
12166 pinned:true, handles:'se'
12168 this.resizer.on('resize', function(r, w, h){
12169 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12170 this.listWidth = w;
12171 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12172 this.restrictHeight();
12174 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12177 if(!this.editable){
12178 this.editable = true;
12179 this.setEditable(false);
12184 if (typeof(this.events.add.listeners) != 'undefined') {
12186 this.addicon = this.wrap.createChild(
12187 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12189 this.addicon.on('click', function(e) {
12190 this.fireEvent('add', this);
12193 if (typeof(this.events.edit.listeners) != 'undefined') {
12195 this.editicon = this.wrap.createChild(
12196 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12197 if (this.addicon) {
12198 this.editicon.setStyle('margin-left', '40px');
12200 this.editicon.on('click', function(e) {
12202 // we fire even if inothing is selected..
12203 this.fireEvent('edit', this, this.lastData );
12209 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12210 "up" : function(e){
12211 this.inKeyMode = true;
12215 "down" : function(e){
12216 if(!this.isExpanded()){
12217 this.onTriggerClick();
12219 this.inKeyMode = true;
12224 "enter" : function(e){
12225 // this.onViewClick();
12229 if(this.fireEvent("specialkey", this, e)){
12230 this.onViewClick(false);
12236 "esc" : function(e){
12240 "tab" : function(e){
12243 if(this.fireEvent("specialkey", this, e)){
12244 this.onViewClick(false);
12252 doRelay : function(foo, bar, hname){
12253 if(hname == 'down' || this.scope.isExpanded()){
12254 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12263 this.queryDelay = Math.max(this.queryDelay || 10,
12264 this.mode == 'local' ? 10 : 250);
12267 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12269 if(this.typeAhead){
12270 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12272 if(this.editable !== false){
12273 this.inputEl().on("keyup", this.onKeyUp, this);
12275 if(this.forceSelection){
12276 this.inputEl().on('blur', this.doForce, this);
12280 this.choices = this.el.select('ul.select2-choices', true).first();
12281 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12285 initTickableEvents: function()
12289 if(this.hiddenName){
12291 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12293 this.hiddenField.dom.value =
12294 this.hiddenValue !== undefined ? this.hiddenValue :
12295 this.value !== undefined ? this.value : '';
12297 // prevent input submission
12298 this.el.dom.removeAttribute('name');
12299 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12304 // this.list = this.el.select('ul.dropdown-menu',true).first();
12306 this.choices = this.el.select('ul.select2-choices', true).first();
12307 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12308 if(this.triggerList){
12309 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12312 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12313 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12315 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12316 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12318 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12319 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12321 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12322 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12323 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12326 this.cancelBtn.hide();
12331 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12332 _this.list.setWidth(lw);
12335 this.list.on('mouseover', this.onViewOver, this);
12336 this.list.on('mousemove', this.onViewMove, this);
12338 this.list.on('scroll', this.onViewScroll, this);
12341 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>';
12344 this.view = new Roo.View(this.list, this.tpl, {
12345 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12348 //this.view.wrapEl.setDisplayed(false);
12349 this.view.on('click', this.onViewClick, this);
12353 this.store.on('beforeload', this.onBeforeLoad, this);
12354 this.store.on('load', this.onLoad, this);
12355 this.store.on('loadexception', this.onLoadException, this);
12358 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12359 "up" : function(e){
12360 this.inKeyMode = true;
12364 "down" : function(e){
12365 this.inKeyMode = true;
12369 "enter" : function(e){
12370 if(this.fireEvent("specialkey", this, e)){
12371 this.onViewClick(false);
12377 "esc" : function(e){
12378 this.onTickableFooterButtonClick(e, false, false);
12381 "tab" : function(e){
12382 this.fireEvent("specialkey", this, e);
12384 this.onTickableFooterButtonClick(e, false, false);
12391 doRelay : function(e, fn, key){
12392 if(this.scope.isExpanded()){
12393 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12402 this.queryDelay = Math.max(this.queryDelay || 10,
12403 this.mode == 'local' ? 10 : 250);
12406 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12408 if(this.typeAhead){
12409 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12412 if(this.editable !== false){
12413 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12418 onDestroy : function(){
12420 this.view.setStore(null);
12421 this.view.el.removeAllListeners();
12422 this.view.el.remove();
12423 this.view.purgeListeners();
12426 this.list.dom.innerHTML = '';
12430 this.store.un('beforeload', this.onBeforeLoad, this);
12431 this.store.un('load', this.onLoad, this);
12432 this.store.un('loadexception', this.onLoadException, this);
12434 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12438 fireKey : function(e){
12439 if(e.isNavKeyPress() && !this.list.isVisible()){
12440 this.fireEvent("specialkey", this, e);
12445 onResize: function(w, h){
12446 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12448 // if(typeof w != 'number'){
12449 // // we do not handle it!?!?
12452 // var tw = this.trigger.getWidth();
12453 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12454 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12456 // this.inputEl().setWidth( this.adjustWidth('input', x));
12458 // //this.trigger.setStyle('left', x+'px');
12460 // if(this.list && this.listWidth === undefined){
12461 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12462 // this.list.setWidth(lw);
12463 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12471 * Allow or prevent the user from directly editing the field text. If false is passed,
12472 * the user will only be able to select from the items defined in the dropdown list. This method
12473 * is the runtime equivalent of setting the 'editable' config option at config time.
12474 * @param {Boolean} value True to allow the user to directly edit the field text
12476 setEditable : function(value){
12477 if(value == this.editable){
12480 this.editable = value;
12482 this.inputEl().dom.setAttribute('readOnly', true);
12483 this.inputEl().on('mousedown', this.onTriggerClick, this);
12484 this.inputEl().addClass('x-combo-noedit');
12486 this.inputEl().dom.setAttribute('readOnly', false);
12487 this.inputEl().un('mousedown', this.onTriggerClick, this);
12488 this.inputEl().removeClass('x-combo-noedit');
12494 onBeforeLoad : function(combo,opts){
12495 if(!this.hasFocus){
12499 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12501 this.restrictHeight();
12502 this.selectedIndex = -1;
12506 onLoad : function(){
12508 this.hasQuery = false;
12510 if(!this.hasFocus){
12514 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12515 this.loading.hide();
12518 if(this.store.getCount() > 0){
12520 this.restrictHeight();
12521 if(this.lastQuery == this.allQuery){
12522 if(this.editable && !this.tickable){
12523 this.inputEl().dom.select();
12527 !this.selectByValue(this.value, true) &&
12530 !this.store.lastOptions ||
12531 typeof(this.store.lastOptions.add) == 'undefined' ||
12532 this.store.lastOptions.add != true
12535 this.select(0, true);
12538 if(this.autoFocus){
12541 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12542 this.taTask.delay(this.typeAheadDelay);
12546 this.onEmptyResults();
12552 onLoadException : function()
12554 this.hasQuery = false;
12556 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12557 this.loading.hide();
12560 if(this.tickable && this.editable){
12565 // only causes errors at present
12566 //Roo.log(this.store.reader.jsonData);
12567 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12569 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12575 onTypeAhead : function(){
12576 if(this.store.getCount() > 0){
12577 var r = this.store.getAt(0);
12578 var newValue = r.data[this.displayField];
12579 var len = newValue.length;
12580 var selStart = this.getRawValue().length;
12582 if(selStart != len){
12583 this.setRawValue(newValue);
12584 this.selectText(selStart, newValue.length);
12590 onSelect : function(record, index){
12592 if(this.fireEvent('beforeselect', this, record, index) !== false){
12594 this.setFromData(index > -1 ? record.data : false);
12597 this.fireEvent('select', this, record, index);
12602 * Returns the currently selected field value or empty string if no value is set.
12603 * @return {String} value The selected value
12605 getValue : function(){
12608 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12611 if(this.valueField){
12612 return typeof this.value != 'undefined' ? this.value : '';
12614 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12619 * Clears any text/value currently set in the field
12621 clearValue : function(){
12622 if(this.hiddenField){
12623 this.hiddenField.dom.value = '';
12626 this.setRawValue('');
12627 this.lastSelectionText = '';
12628 this.lastData = false;
12630 var close = this.closeTriggerEl();
12639 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12640 * will be displayed in the field. If the value does not match the data value of an existing item,
12641 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12642 * Otherwise the field will be blank (although the value will still be set).
12643 * @param {String} value The value to match
12645 setValue : function(v){
12652 if(this.valueField){
12653 var r = this.findRecord(this.valueField, v);
12655 text = r.data[this.displayField];
12656 }else if(this.valueNotFoundText !== undefined){
12657 text = this.valueNotFoundText;
12660 this.lastSelectionText = text;
12661 if(this.hiddenField){
12662 this.hiddenField.dom.value = v;
12664 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12667 var close = this.closeTriggerEl();
12670 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12674 * @property {Object} the last set data for the element
12679 * Sets the value of the field based on a object which is related to the record format for the store.
12680 * @param {Object} value the value to set as. or false on reset?
12682 setFromData : function(o){
12689 var dv = ''; // display value
12690 var vv = ''; // value value..
12692 if (this.displayField) {
12693 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12695 // this is an error condition!!!
12696 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12699 if(this.valueField){
12700 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12703 var close = this.closeTriggerEl();
12706 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12709 if(this.hiddenField){
12710 this.hiddenField.dom.value = vv;
12712 this.lastSelectionText = dv;
12713 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12717 // no hidden field.. - we store the value in 'value', but still display
12718 // display field!!!!
12719 this.lastSelectionText = dv;
12720 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12727 reset : function(){
12728 // overridden so that last data is reset..
12735 this.setValue(this.originalValue);
12736 this.clearInvalid();
12737 this.lastData = false;
12739 this.view.clearSelections();
12743 findRecord : function(prop, value){
12745 if(this.store.getCount() > 0){
12746 this.store.each(function(r){
12747 if(r.data[prop] == value){
12757 getName: function()
12759 // returns hidden if it's set..
12760 if (!this.rendered) {return ''};
12761 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
12765 onViewMove : function(e, t){
12766 this.inKeyMode = false;
12770 onViewOver : function(e, t){
12771 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12774 var item = this.view.findItemFromChild(t);
12777 var index = this.view.indexOf(item);
12778 this.select(index, false);
12783 onViewClick : function(view, doFocus, el, e)
12785 var index = this.view.getSelectedIndexes()[0];
12787 var r = this.store.getAt(index);
12791 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12798 Roo.each(this.tickItems, function(v,k){
12800 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12802 _this.tickItems.splice(k, 1);
12804 if(typeof(e) == 'undefined' && view == false){
12805 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12817 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
12818 this.tickItems.push(r.data);
12821 if(typeof(e) == 'undefined' && view == false){
12822 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12829 this.onSelect(r, index);
12831 if(doFocus !== false && !this.blockFocus){
12832 this.inputEl().focus();
12837 restrictHeight : function(){
12838 //this.innerList.dom.style.height = '';
12839 //var inner = this.innerList.dom;
12840 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12841 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12842 //this.list.beginUpdate();
12843 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12844 this.list.alignTo(this.inputEl(), this.listAlign);
12845 this.list.alignTo(this.inputEl(), this.listAlign);
12846 //this.list.endUpdate();
12850 onEmptyResults : function(){
12852 if(this.tickable && this.editable){
12853 this.restrictHeight();
12861 * Returns true if the dropdown list is expanded, else false.
12863 isExpanded : function(){
12864 return this.list.isVisible();
12868 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12869 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12870 * @param {String} value The data value of the item to select
12871 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12872 * selected item if it is not currently in view (defaults to true)
12873 * @return {Boolean} True if the value matched an item in the list, else false
12875 selectByValue : function(v, scrollIntoView){
12876 if(v !== undefined && v !== null){
12877 var r = this.findRecord(this.valueField || this.displayField, v);
12879 this.select(this.store.indexOf(r), scrollIntoView);
12887 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12888 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12889 * @param {Number} index The zero-based index of the list item to select
12890 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12891 * selected item if it is not currently in view (defaults to true)
12893 select : function(index, scrollIntoView){
12894 this.selectedIndex = index;
12895 this.view.select(index);
12896 if(scrollIntoView !== false){
12897 var el = this.view.getNode(index);
12899 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12902 this.list.scrollChildIntoView(el, false);
12908 selectNext : function(){
12909 var ct = this.store.getCount();
12911 if(this.selectedIndex == -1){
12913 }else if(this.selectedIndex < ct-1){
12914 this.select(this.selectedIndex+1);
12920 selectPrev : function(){
12921 var ct = this.store.getCount();
12923 if(this.selectedIndex == -1){
12925 }else if(this.selectedIndex != 0){
12926 this.select(this.selectedIndex-1);
12932 onKeyUp : function(e){
12933 if(this.editable !== false && !e.isSpecialKey()){
12934 this.lastKey = e.getKey();
12935 this.dqTask.delay(this.queryDelay);
12940 validateBlur : function(){
12941 return !this.list || !this.list.isVisible();
12945 initQuery : function(){
12947 var v = this.getRawValue();
12949 if(this.tickable && this.editable){
12950 v = this.tickableInputEl().getValue();
12957 doForce : function(){
12958 if(this.inputEl().dom.value.length > 0){
12959 this.inputEl().dom.value =
12960 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12966 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
12967 * query allowing the query action to be canceled if needed.
12968 * @param {String} query The SQL query to execute
12969 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12970 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
12971 * saved in the current store (defaults to false)
12973 doQuery : function(q, forceAll){
12975 if(q === undefined || q === null){
12980 forceAll: forceAll,
12984 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12989 forceAll = qe.forceAll;
12990 if(forceAll === true || (q.length >= this.minChars)){
12992 this.hasQuery = true;
12994 if(this.lastQuery != q || this.alwaysQuery){
12995 this.lastQuery = q;
12996 if(this.mode == 'local'){
12997 this.selectedIndex = -1;
12999 this.store.clearFilter();
13002 if(this.specialFilter){
13003 this.fireEvent('specialfilter', this);
13008 this.store.filter(this.displayField, q);
13011 this.store.fireEvent("datachanged", this.store);
13018 this.store.baseParams[this.queryParam] = q;
13020 var options = {params : this.getParams(q)};
13023 options.add = true;
13024 options.params.start = this.page * this.pageSize;
13027 this.store.load(options);
13030 * this code will make the page width larger, at the beginning, the list not align correctly,
13031 * we should expand the list on onLoad
13032 * so command out it
13037 this.selectedIndex = -1;
13042 this.loadNext = false;
13046 getParams : function(q){
13048 //p[this.queryParam] = q;
13052 p.limit = this.pageSize;
13058 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13060 collapse : function(){
13061 if(!this.isExpanded()){
13068 this.hasFocus = false;
13070 this.cancelBtn.hide();
13071 this.trigger.show();
13074 this.tickableInputEl().dom.value = '';
13075 this.tickableInputEl().blur();
13080 Roo.get(document).un('mousedown', this.collapseIf, this);
13081 Roo.get(document).un('mousewheel', this.collapseIf, this);
13082 if (!this.editable) {
13083 Roo.get(document).un('keydown', this.listKeyPress, this);
13085 this.fireEvent('collapse', this);
13089 collapseIf : function(e){
13090 var in_combo = e.within(this.el);
13091 var in_list = e.within(this.list);
13092 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13094 if (in_combo || in_list || is_list) {
13095 //e.stopPropagation();
13100 this.onTickableFooterButtonClick(e, false, false);
13108 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13110 expand : function(){
13112 if(this.isExpanded() || !this.hasFocus){
13116 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13117 this.list.setWidth(lw);
13124 this.restrictHeight();
13128 this.tickItems = Roo.apply([], this.item);
13131 this.cancelBtn.show();
13132 this.trigger.hide();
13135 this.tickableInputEl().focus();
13140 Roo.get(document).on('mousedown', this.collapseIf, this);
13141 Roo.get(document).on('mousewheel', this.collapseIf, this);
13142 if (!this.editable) {
13143 Roo.get(document).on('keydown', this.listKeyPress, this);
13146 this.fireEvent('expand', this);
13150 // Implements the default empty TriggerField.onTriggerClick function
13151 onTriggerClick : function(e)
13153 Roo.log('trigger click');
13155 if(this.disabled || !this.triggerList){
13160 this.loadNext = false;
13162 if(this.isExpanded()){
13164 if (!this.blockFocus) {
13165 this.inputEl().focus();
13169 this.hasFocus = true;
13170 if(this.triggerAction == 'all') {
13171 this.doQuery(this.allQuery, true);
13173 this.doQuery(this.getRawValue());
13175 if (!this.blockFocus) {
13176 this.inputEl().focus();
13181 onTickableTriggerClick : function(e)
13188 this.loadNext = false;
13189 this.hasFocus = true;
13191 if(this.triggerAction == 'all') {
13192 this.doQuery(this.allQuery, true);
13194 this.doQuery(this.getRawValue());
13198 onSearchFieldClick : function(e)
13200 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13201 this.onTickableFooterButtonClick(e, false, false);
13205 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13210 this.loadNext = false;
13211 this.hasFocus = true;
13213 if(this.triggerAction == 'all') {
13214 this.doQuery(this.allQuery, true);
13216 this.doQuery(this.getRawValue());
13220 listKeyPress : function(e)
13222 //Roo.log('listkeypress');
13223 // scroll to first matching element based on key pres..
13224 if (e.isSpecialKey()) {
13227 var k = String.fromCharCode(e.getKey()).toUpperCase();
13230 var csel = this.view.getSelectedNodes();
13231 var cselitem = false;
13233 var ix = this.view.indexOf(csel[0]);
13234 cselitem = this.store.getAt(ix);
13235 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13241 this.store.each(function(v) {
13243 // start at existing selection.
13244 if (cselitem.id == v.id) {
13250 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13251 match = this.store.indexOf(v);
13257 if (match === false) {
13258 return true; // no more action?
13261 this.view.select(match);
13262 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13263 sn.scrollIntoView(sn.dom.parentNode, false);
13266 onViewScroll : function(e, t){
13268 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){
13272 this.hasQuery = true;
13274 this.loading = this.list.select('.loading', true).first();
13276 if(this.loading === null){
13277 this.list.createChild({
13279 cls: 'loading select2-more-results select2-active',
13280 html: 'Loading more results...'
13283 this.loading = this.list.select('.loading', true).first();
13285 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13287 this.loading.hide();
13290 this.loading.show();
13295 this.loadNext = true;
13297 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13302 addItem : function(o)
13304 var dv = ''; // display value
13306 if (this.displayField) {
13307 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13309 // this is an error condition!!!
13310 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13317 var choice = this.choices.createChild({
13319 cls: 'select2-search-choice',
13328 cls: 'select2-search-choice-close',
13333 }, this.searchField);
13335 var close = choice.select('a.select2-search-choice-close', true).first();
13337 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13345 this.inputEl().dom.value = '';
13350 onRemoveItem : function(e, _self, o)
13352 e.preventDefault();
13354 this.lastItem = Roo.apply([], this.item);
13356 var index = this.item.indexOf(o.data) * 1;
13359 Roo.log('not this item?!');
13363 this.item.splice(index, 1);
13368 this.fireEvent('remove', this, e);
13374 syncValue : function()
13376 if(!this.item.length){
13383 Roo.each(this.item, function(i){
13384 if(_this.valueField){
13385 value.push(i[_this.valueField]);
13392 this.value = value.join(',');
13394 if(this.hiddenField){
13395 this.hiddenField.dom.value = this.value;
13398 this.store.fireEvent("datachanged", this.store);
13401 clearItem : function()
13403 if(!this.multiple){
13409 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13417 if(this.tickable && !Roo.isTouch){
13418 this.view.refresh();
13422 inputEl: function ()
13424 if(Roo.isTouch && this.mobileTouchView){
13425 return this.el.select('input.form-control',true).first();
13429 return this.searchField;
13432 return this.el.select('input.form-control',true).first();
13436 onTickableFooterButtonClick : function(e, btn, el)
13438 e.preventDefault();
13440 this.lastItem = Roo.apply([], this.item);
13442 if(btn && btn.name == 'cancel'){
13443 this.tickItems = Roo.apply([], this.item);
13452 Roo.each(this.tickItems, function(o){
13460 validate : function()
13462 var v = this.getRawValue();
13465 v = this.getValue();
13468 if(this.disabled || this.allowBlank || v.length){
13473 this.markInvalid();
13477 tickableInputEl : function()
13479 if(!this.tickable || !this.editable){
13480 return this.inputEl();
13483 return this.inputEl().select('.select2-search-field-input', true).first();
13487 getAutoCreateTouchView : function()
13492 cls: 'form-group' //input-group
13498 type : this.inputType,
13499 cls : 'form-control x-combo-noedit',
13500 autocomplete: 'new-password',
13501 placeholder : this.placeholder || '',
13506 input.name = this.name;
13510 input.cls += ' input-' + this.size;
13513 if (this.disabled) {
13514 input.disabled = true;
13525 inputblock.cls += ' input-group';
13527 inputblock.cn.unshift({
13529 cls : 'input-group-addon',
13534 if(this.removable && !this.multiple){
13535 inputblock.cls += ' roo-removable';
13537 inputblock.cn.push({
13540 cls : 'roo-combo-removable-btn close'
13544 if(this.hasFeedback && !this.allowBlank){
13546 inputblock.cls += ' has-feedback';
13548 inputblock.cn.push({
13550 cls: 'glyphicon form-control-feedback'
13557 inputblock.cls += (this.before) ? '' : ' input-group';
13559 inputblock.cn.push({
13561 cls : 'input-group-addon',
13572 cls: 'form-hidden-field'
13586 cls: 'form-hidden-field'
13590 cls: 'select2-choices',
13594 cls: 'select2-search-field',
13607 cls: 'select2-container input-group',
13614 combobox.cls += ' select2-container-multi';
13617 var align = this.labelAlign || this.parentLabelAlign();
13621 if(this.fieldLabel.length){
13623 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13624 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13629 cls : 'control-label ' + lw,
13630 html : this.fieldLabel
13642 var settings = this;
13644 ['xs','sm','md','lg'].map(function(size){
13645 if (settings[size]) {
13646 cfg.cls += ' col-' + size + '-' + settings[size];
13653 initTouchView : function()
13655 this.renderTouchView();
13657 this.touchViewEl.on('scroll', function(){
13658 this.el.dom.scrollTop = 0;
13661 this.originalValue = this.getValue();
13663 this.inputEl().on("click", this.showTouchView, this);
13665 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13666 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13668 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13670 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13671 this.store.on('load', this.onTouchViewLoad, this);
13672 this.store.on('loadexception', this.onTouchViewLoadException, this);
13674 if(this.hiddenName){
13676 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13678 this.hiddenField.dom.value =
13679 this.hiddenValue !== undefined ? this.hiddenValue :
13680 this.value !== undefined ? this.value : '';
13682 this.el.dom.removeAttribute('name');
13683 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13687 this.choices = this.el.select('ul.select2-choices', true).first();
13688 this.searchField = this.el.select('ul li.select2-search-field', true).first();
13691 if(this.removable && !this.multiple){
13692 var close = this.closeTriggerEl();
13694 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13695 close.on('click', this.removeBtnClick, this, close);
13699 * fix the bug in Safari iOS8
13701 this.inputEl().on("focus", function(e){
13702 document.activeElement.blur();
13710 renderTouchView : function()
13712 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13713 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13715 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13716 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13718 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13719 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13720 this.touchViewBodyEl.setStyle('overflow', 'auto');
13722 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13723 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13725 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13726 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13730 showTouchView : function()
13736 this.touchViewHeaderEl.hide();
13738 if(this.fieldLabel.length){
13739 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13740 this.touchViewHeaderEl.show();
13743 this.touchViewEl.show();
13745 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13746 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13748 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13750 if(this.fieldLabel.length){
13751 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13754 this.touchViewBodyEl.setHeight(bodyHeight);
13758 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13760 this.touchViewEl.addClass('in');
13763 this.doTouchViewQuery();
13767 hideTouchView : function()
13769 this.touchViewEl.removeClass('in');
13773 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13775 this.touchViewEl.setStyle('display', 'none');
13780 setTouchViewValue : function()
13787 Roo.each(this.tickItems, function(o){
13792 this.hideTouchView();
13795 doTouchViewQuery : function()
13804 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13808 if(!this.alwaysQuery || this.mode == 'local'){
13809 this.onTouchViewLoad();
13816 onTouchViewBeforeLoad : function(combo,opts)
13822 onTouchViewLoad : function()
13824 if(this.store.getCount() < 1){
13825 this.onTouchViewEmptyResults();
13829 this.clearTouchView();
13831 var rawValue = this.getRawValue();
13833 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13835 this.tickItems = [];
13837 this.store.data.each(function(d, rowIndex){
13838 var row = this.touchViewListGroup.createChild(template);
13840 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13843 html : d.data[this.displayField]
13846 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
13847 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
13851 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13852 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13855 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13856 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13857 this.tickItems.push(d.data);
13860 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13864 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13866 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13868 if(this.fieldLabel.length){
13869 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13872 var listHeight = this.touchViewListGroup.getHeight();
13876 if(firstChecked && listHeight > bodyHeight){
13877 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13882 onTouchViewLoadException : function()
13884 this.hideTouchView();
13887 onTouchViewEmptyResults : function()
13889 this.clearTouchView();
13891 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13893 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13897 clearTouchView : function()
13899 this.touchViewListGroup.dom.innerHTML = '';
13902 onTouchViewClick : function(e, el, o)
13904 e.preventDefault();
13907 var rowIndex = o.rowIndex;
13909 var r = this.store.getAt(rowIndex);
13911 if(!this.multiple){
13912 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13913 c.dom.removeAttribute('checked');
13916 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13918 this.setFromData(r.data);
13920 var close = this.closeTriggerEl();
13926 this.hideTouchView();
13928 this.fireEvent('select', this, r, rowIndex);
13933 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13934 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13935 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13939 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13940 this.addItem(r.data);
13941 this.tickItems.push(r.data);
13947 * @cfg {Boolean} grow
13951 * @cfg {Number} growMin
13955 * @cfg {Number} growMax
13964 Roo.apply(Roo.bootstrap.ComboBox, {
13968 cls: 'modal-header',
13990 cls: 'list-group-item',
13994 cls: 'roo-combobox-list-group-item-value'
13998 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14012 listItemCheckbox : {
14014 cls: 'list-group-item',
14018 cls: 'roo-combobox-list-group-item-value'
14022 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14038 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14043 cls: 'modal-footer',
14051 cls: 'col-xs-6 text-left',
14054 cls: 'btn btn-danger roo-touch-view-cancel',
14060 cls: 'col-xs-6 text-right',
14063 cls: 'btn btn-success roo-touch-view-ok',
14074 Roo.apply(Roo.bootstrap.ComboBox, {
14076 touchViewTemplate : {
14078 cls: 'modal fade roo-combobox-touch-view',
14082 cls: 'modal-dialog',
14083 style : 'position:fixed', // we have to fix position....
14087 cls: 'modal-content',
14089 Roo.bootstrap.ComboBox.header,
14090 Roo.bootstrap.ComboBox.body,
14091 Roo.bootstrap.ComboBox.footer
14100 * Ext JS Library 1.1.1
14101 * Copyright(c) 2006-2007, Ext JS, LLC.
14103 * Originally Released Under LGPL - original licence link has changed is not relivant.
14106 * <script type="text/javascript">
14111 * @extends Roo.util.Observable
14112 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14113 * This class also supports single and multi selection modes. <br>
14114 * Create a data model bound view:
14116 var store = new Roo.data.Store(...);
14118 var view = new Roo.View({
14120 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14122 singleSelect: true,
14123 selectedClass: "ydataview-selected",
14127 // listen for node click?
14128 view.on("click", function(vw, index, node, e){
14129 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14133 dataModel.load("foobar.xml");
14135 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14137 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14138 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14140 * Note: old style constructor is still suported (container, template, config)
14143 * Create a new View
14144 * @param {Object} config The config object
14147 Roo.View = function(config, depreciated_tpl, depreciated_config){
14149 this.parent = false;
14151 if (typeof(depreciated_tpl) == 'undefined') {
14152 // new way.. - universal constructor.
14153 Roo.apply(this, config);
14154 this.el = Roo.get(this.el);
14157 this.el = Roo.get(config);
14158 this.tpl = depreciated_tpl;
14159 Roo.apply(this, depreciated_config);
14161 this.wrapEl = this.el.wrap().wrap();
14162 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14165 if(typeof(this.tpl) == "string"){
14166 this.tpl = new Roo.Template(this.tpl);
14168 // support xtype ctors..
14169 this.tpl = new Roo.factory(this.tpl, Roo);
14173 this.tpl.compile();
14178 * @event beforeclick
14179 * Fires before a click is processed. Returns false to cancel the default action.
14180 * @param {Roo.View} this
14181 * @param {Number} index The index of the target node
14182 * @param {HTMLElement} node The target node
14183 * @param {Roo.EventObject} e The raw event object
14185 "beforeclick" : true,
14188 * Fires when a template node is clicked.
14189 * @param {Roo.View} this
14190 * @param {Number} index The index of the target node
14191 * @param {HTMLElement} node The target node
14192 * @param {Roo.EventObject} e The raw event object
14197 * Fires when a template node is double clicked.
14198 * @param {Roo.View} this
14199 * @param {Number} index The index of the target node
14200 * @param {HTMLElement} node The target node
14201 * @param {Roo.EventObject} e The raw event object
14205 * @event contextmenu
14206 * Fires when a template node is right clicked.
14207 * @param {Roo.View} this
14208 * @param {Number} index The index of the target node
14209 * @param {HTMLElement} node The target node
14210 * @param {Roo.EventObject} e The raw event object
14212 "contextmenu" : true,
14214 * @event selectionchange
14215 * Fires when the selected nodes change.
14216 * @param {Roo.View} this
14217 * @param {Array} selections Array of the selected nodes
14219 "selectionchange" : true,
14222 * @event beforeselect
14223 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14224 * @param {Roo.View} this
14225 * @param {HTMLElement} node The node to be selected
14226 * @param {Array} selections Array of currently selected nodes
14228 "beforeselect" : true,
14230 * @event preparedata
14231 * Fires on every row to render, to allow you to change the data.
14232 * @param {Roo.View} this
14233 * @param {Object} data to be rendered (change this)
14235 "preparedata" : true
14243 "click": this.onClick,
14244 "dblclick": this.onDblClick,
14245 "contextmenu": this.onContextMenu,
14249 this.selections = [];
14251 this.cmp = new Roo.CompositeElementLite([]);
14253 this.store = Roo.factory(this.store, Roo.data);
14254 this.setStore(this.store, true);
14257 if ( this.footer && this.footer.xtype) {
14259 var fctr = this.wrapEl.appendChild(document.createElement("div"));
14261 this.footer.dataSource = this.store;
14262 this.footer.container = fctr;
14263 this.footer = Roo.factory(this.footer, Roo);
14264 fctr.insertFirst(this.el);
14266 // this is a bit insane - as the paging toolbar seems to detach the el..
14267 // dom.parentNode.parentNode.parentNode
14268 // they get detached?
14272 Roo.View.superclass.constructor.call(this);
14277 Roo.extend(Roo.View, Roo.util.Observable, {
14280 * @cfg {Roo.data.Store} store Data store to load data from.
14285 * @cfg {String|Roo.Element} el The container element.
14290 * @cfg {String|Roo.Template} tpl The template used by this View
14294 * @cfg {String} dataName the named area of the template to use as the data area
14295 * Works with domtemplates roo-name="name"
14299 * @cfg {String} selectedClass The css class to add to selected nodes
14301 selectedClass : "x-view-selected",
14303 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14308 * @cfg {String} text to display on mask (default Loading)
14312 * @cfg {Boolean} multiSelect Allow multiple selection
14314 multiSelect : false,
14316 * @cfg {Boolean} singleSelect Allow single selection
14318 singleSelect: false,
14321 * @cfg {Boolean} toggleSelect - selecting
14323 toggleSelect : false,
14326 * @cfg {Boolean} tickable - selecting
14331 * Returns the element this view is bound to.
14332 * @return {Roo.Element}
14334 getEl : function(){
14335 return this.wrapEl;
14341 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14343 refresh : function(){
14344 //Roo.log('refresh');
14347 // if we are using something like 'domtemplate', then
14348 // the what gets used is:
14349 // t.applySubtemplate(NAME, data, wrapping data..)
14350 // the outer template then get' applied with
14351 // the store 'extra data'
14352 // and the body get's added to the
14353 // roo-name="data" node?
14354 // <span class='roo-tpl-{name}'></span> ?????
14358 this.clearSelections();
14359 this.el.update("");
14361 var records = this.store.getRange();
14362 if(records.length < 1) {
14364 // is this valid?? = should it render a template??
14366 this.el.update(this.emptyText);
14370 if (this.dataName) {
14371 this.el.update(t.apply(this.store.meta)); //????
14372 el = this.el.child('.roo-tpl-' + this.dataName);
14375 for(var i = 0, len = records.length; i < len; i++){
14376 var data = this.prepareData(records[i].data, i, records[i]);
14377 this.fireEvent("preparedata", this, data, i, records[i]);
14379 var d = Roo.apply({}, data);
14382 Roo.apply(d, {'roo-id' : Roo.id()});
14386 Roo.each(this.parent.item, function(item){
14387 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14390 Roo.apply(d, {'roo-data-checked' : 'checked'});
14394 html[html.length] = Roo.util.Format.trim(
14396 t.applySubtemplate(this.dataName, d, this.store.meta) :
14403 el.update(html.join(""));
14404 this.nodes = el.dom.childNodes;
14405 this.updateIndexes(0);
14410 * Function to override to reformat the data that is sent to
14411 * the template for each node.
14412 * DEPRICATED - use the preparedata event handler.
14413 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14414 * a JSON object for an UpdateManager bound view).
14416 prepareData : function(data, index, record)
14418 this.fireEvent("preparedata", this, data, index, record);
14422 onUpdate : function(ds, record){
14423 // Roo.log('on update');
14424 this.clearSelections();
14425 var index = this.store.indexOf(record);
14426 var n = this.nodes[index];
14427 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14428 n.parentNode.removeChild(n);
14429 this.updateIndexes(index, index);
14435 onAdd : function(ds, records, index)
14437 //Roo.log(['on Add', ds, records, index] );
14438 this.clearSelections();
14439 if(this.nodes.length == 0){
14443 var n = this.nodes[index];
14444 for(var i = 0, len = records.length; i < len; i++){
14445 var d = this.prepareData(records[i].data, i, records[i]);
14447 this.tpl.insertBefore(n, d);
14450 this.tpl.append(this.el, d);
14453 this.updateIndexes(index);
14456 onRemove : function(ds, record, index){
14457 // Roo.log('onRemove');
14458 this.clearSelections();
14459 var el = this.dataName ?
14460 this.el.child('.roo-tpl-' + this.dataName) :
14463 el.dom.removeChild(this.nodes[index]);
14464 this.updateIndexes(index);
14468 * Refresh an individual node.
14469 * @param {Number} index
14471 refreshNode : function(index){
14472 this.onUpdate(this.store, this.store.getAt(index));
14475 updateIndexes : function(startIndex, endIndex){
14476 var ns = this.nodes;
14477 startIndex = startIndex || 0;
14478 endIndex = endIndex || ns.length - 1;
14479 for(var i = startIndex; i <= endIndex; i++){
14480 ns[i].nodeIndex = i;
14485 * Changes the data store this view uses and refresh the view.
14486 * @param {Store} store
14488 setStore : function(store, initial){
14489 if(!initial && this.store){
14490 this.store.un("datachanged", this.refresh);
14491 this.store.un("add", this.onAdd);
14492 this.store.un("remove", this.onRemove);
14493 this.store.un("update", this.onUpdate);
14494 this.store.un("clear", this.refresh);
14495 this.store.un("beforeload", this.onBeforeLoad);
14496 this.store.un("load", this.onLoad);
14497 this.store.un("loadexception", this.onLoad);
14501 store.on("datachanged", this.refresh, this);
14502 store.on("add", this.onAdd, this);
14503 store.on("remove", this.onRemove, this);
14504 store.on("update", this.onUpdate, this);
14505 store.on("clear", this.refresh, this);
14506 store.on("beforeload", this.onBeforeLoad, this);
14507 store.on("load", this.onLoad, this);
14508 store.on("loadexception", this.onLoad, this);
14516 * onbeforeLoad - masks the loading area.
14519 onBeforeLoad : function(store,opts)
14521 //Roo.log('onBeforeLoad');
14523 this.el.update("");
14525 this.el.mask(this.mask ? this.mask : "Loading" );
14527 onLoad : function ()
14534 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14535 * @param {HTMLElement} node
14536 * @return {HTMLElement} The template node
14538 findItemFromChild : function(node){
14539 var el = this.dataName ?
14540 this.el.child('.roo-tpl-' + this.dataName,true) :
14543 if(!node || node.parentNode == el){
14546 var p = node.parentNode;
14547 while(p && p != el){
14548 if(p.parentNode == el){
14557 onClick : function(e){
14558 var item = this.findItemFromChild(e.getTarget());
14560 var index = this.indexOf(item);
14561 if(this.onItemClick(item, index, e) !== false){
14562 this.fireEvent("click", this, index, item, e);
14565 this.clearSelections();
14570 onContextMenu : function(e){
14571 var item = this.findItemFromChild(e.getTarget());
14573 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14578 onDblClick : function(e){
14579 var item = this.findItemFromChild(e.getTarget());
14581 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14585 onItemClick : function(item, index, e)
14587 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14590 if (this.toggleSelect) {
14591 var m = this.isSelected(item) ? 'unselect' : 'select';
14594 _t[m](item, true, false);
14597 if(this.multiSelect || this.singleSelect){
14598 if(this.multiSelect && e.shiftKey && this.lastSelection){
14599 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14601 this.select(item, this.multiSelect && e.ctrlKey);
14602 this.lastSelection = item;
14605 if(!this.tickable){
14606 e.preventDefault();
14614 * Get the number of selected nodes.
14617 getSelectionCount : function(){
14618 return this.selections.length;
14622 * Get the currently selected nodes.
14623 * @return {Array} An array of HTMLElements
14625 getSelectedNodes : function(){
14626 return this.selections;
14630 * Get the indexes of the selected nodes.
14633 getSelectedIndexes : function(){
14634 var indexes = [], s = this.selections;
14635 for(var i = 0, len = s.length; i < len; i++){
14636 indexes.push(s[i].nodeIndex);
14642 * Clear all selections
14643 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14645 clearSelections : function(suppressEvent){
14646 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14647 this.cmp.elements = this.selections;
14648 this.cmp.removeClass(this.selectedClass);
14649 this.selections = [];
14650 if(!suppressEvent){
14651 this.fireEvent("selectionchange", this, this.selections);
14657 * Returns true if the passed node is selected
14658 * @param {HTMLElement/Number} node The node or node index
14659 * @return {Boolean}
14661 isSelected : function(node){
14662 var s = this.selections;
14666 node = this.getNode(node);
14667 return s.indexOf(node) !== -1;
14672 * @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
14673 * @param {Boolean} keepExisting (optional) true to keep existing selections
14674 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14676 select : function(nodeInfo, keepExisting, suppressEvent){
14677 if(nodeInfo instanceof Array){
14679 this.clearSelections(true);
14681 for(var i = 0, len = nodeInfo.length; i < len; i++){
14682 this.select(nodeInfo[i], true, true);
14686 var node = this.getNode(nodeInfo);
14687 if(!node || this.isSelected(node)){
14688 return; // already selected.
14691 this.clearSelections(true);
14694 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14695 Roo.fly(node).addClass(this.selectedClass);
14696 this.selections.push(node);
14697 if(!suppressEvent){
14698 this.fireEvent("selectionchange", this, this.selections);
14706 * @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
14707 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14708 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14710 unselect : function(nodeInfo, keepExisting, suppressEvent)
14712 if(nodeInfo instanceof Array){
14713 Roo.each(this.selections, function(s) {
14714 this.unselect(s, nodeInfo);
14718 var node = this.getNode(nodeInfo);
14719 if(!node || !this.isSelected(node)){
14720 //Roo.log("not selected");
14721 return; // not selected.
14725 Roo.each(this.selections, function(s) {
14727 Roo.fly(node).removeClass(this.selectedClass);
14734 this.selections= ns;
14735 this.fireEvent("selectionchange", this, this.selections);
14739 * Gets a template node.
14740 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14741 * @return {HTMLElement} The node or null if it wasn't found
14743 getNode : function(nodeInfo){
14744 if(typeof nodeInfo == "string"){
14745 return document.getElementById(nodeInfo);
14746 }else if(typeof nodeInfo == "number"){
14747 return this.nodes[nodeInfo];
14753 * Gets a range template nodes.
14754 * @param {Number} startIndex
14755 * @param {Number} endIndex
14756 * @return {Array} An array of nodes
14758 getNodes : function(start, end){
14759 var ns = this.nodes;
14760 start = start || 0;
14761 end = typeof end == "undefined" ? ns.length - 1 : end;
14764 for(var i = start; i <= end; i++){
14768 for(var i = start; i >= end; i--){
14776 * Finds the index of the passed node
14777 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14778 * @return {Number} The index of the node or -1
14780 indexOf : function(node){
14781 node = this.getNode(node);
14782 if(typeof node.nodeIndex == "number"){
14783 return node.nodeIndex;
14785 var ns = this.nodes;
14786 for(var i = 0, len = ns.length; i < len; i++){
14797 * based on jquery fullcalendar
14801 Roo.bootstrap = Roo.bootstrap || {};
14803 * @class Roo.bootstrap.Calendar
14804 * @extends Roo.bootstrap.Component
14805 * Bootstrap Calendar class
14806 * @cfg {Boolean} loadMask (true|false) default false
14807 * @cfg {Object} header generate the user specific header of the calendar, default false
14810 * Create a new Container
14811 * @param {Object} config The config object
14816 Roo.bootstrap.Calendar = function(config){
14817 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14821 * Fires when a date is selected
14822 * @param {DatePicker} this
14823 * @param {Date} date The selected date
14827 * @event monthchange
14828 * Fires when the displayed month changes
14829 * @param {DatePicker} this
14830 * @param {Date} date The selected month
14832 'monthchange': true,
14834 * @event evententer
14835 * Fires when mouse over an event
14836 * @param {Calendar} this
14837 * @param {event} Event
14839 'evententer': true,
14841 * @event eventleave
14842 * Fires when the mouse leaves an
14843 * @param {Calendar} this
14846 'eventleave': true,
14848 * @event eventclick
14849 * Fires when the mouse click an
14850 * @param {Calendar} this
14859 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
14862 * @cfg {Number} startDay
14863 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14871 getAutoCreate : function(){
14874 var fc_button = function(name, corner, style, content ) {
14875 return Roo.apply({},{
14877 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
14879 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14882 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14893 style : 'width:100%',
14900 cls : 'fc-header-left',
14902 fc_button('prev', 'left', 'arrow', '‹' ),
14903 fc_button('next', 'right', 'arrow', '›' ),
14904 { tag: 'span', cls: 'fc-header-space' },
14905 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
14913 cls : 'fc-header-center',
14917 cls: 'fc-header-title',
14920 html : 'month / year'
14928 cls : 'fc-header-right',
14930 /* fc_button('month', 'left', '', 'month' ),
14931 fc_button('week', '', '', 'week' ),
14932 fc_button('day', 'right', '', 'day' )
14944 header = this.header;
14947 var cal_heads = function() {
14949 // fixme - handle this.
14951 for (var i =0; i < Date.dayNames.length; i++) {
14952 var d = Date.dayNames[i];
14955 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14956 html : d.substring(0,3)
14960 ret[0].cls += ' fc-first';
14961 ret[6].cls += ' fc-last';
14964 var cal_cell = function(n) {
14967 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14972 cls: 'fc-day-number',
14976 cls: 'fc-day-content',
14980 style: 'position: relative;' // height: 17px;
14992 var cal_rows = function() {
14995 for (var r = 0; r < 6; r++) {
15002 for (var i =0; i < Date.dayNames.length; i++) {
15003 var d = Date.dayNames[i];
15004 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15007 row.cn[0].cls+=' fc-first';
15008 row.cn[0].cn[0].style = 'min-height:90px';
15009 row.cn[6].cls+=' fc-last';
15013 ret[0].cls += ' fc-first';
15014 ret[4].cls += ' fc-prev-last';
15015 ret[5].cls += ' fc-last';
15022 cls: 'fc-border-separate',
15023 style : 'width:100%',
15031 cls : 'fc-first fc-last',
15049 cls : 'fc-content',
15050 style : "position: relative;",
15053 cls : 'fc-view fc-view-month fc-grid',
15054 style : 'position: relative',
15055 unselectable : 'on',
15058 cls : 'fc-event-container',
15059 style : 'position:absolute;z-index:8;top:0;left:0;'
15077 initEvents : function()
15080 throw "can not find store for calendar";
15086 style: "text-align:center",
15090 style: "background-color:white;width:50%;margin:250 auto",
15094 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15105 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15107 var size = this.el.select('.fc-content', true).first().getSize();
15108 this.maskEl.setSize(size.width, size.height);
15109 this.maskEl.enableDisplayMode("block");
15110 if(!this.loadMask){
15111 this.maskEl.hide();
15114 this.store = Roo.factory(this.store, Roo.data);
15115 this.store.on('load', this.onLoad, this);
15116 this.store.on('beforeload', this.onBeforeLoad, this);
15120 this.cells = this.el.select('.fc-day',true);
15121 //Roo.log(this.cells);
15122 this.textNodes = this.el.query('.fc-day-number');
15123 this.cells.addClassOnOver('fc-state-hover');
15125 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15126 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15127 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15128 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15130 this.on('monthchange', this.onMonthChange, this);
15132 this.update(new Date().clearTime());
15135 resize : function() {
15136 var sz = this.el.getSize();
15138 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15139 this.el.select('.fc-day-content div',true).setHeight(34);
15144 showPrevMonth : function(e){
15145 this.update(this.activeDate.add("mo", -1));
15147 showToday : function(e){
15148 this.update(new Date().clearTime());
15151 showNextMonth : function(e){
15152 this.update(this.activeDate.add("mo", 1));
15156 showPrevYear : function(){
15157 this.update(this.activeDate.add("y", -1));
15161 showNextYear : function(){
15162 this.update(this.activeDate.add("y", 1));
15167 update : function(date)
15169 var vd = this.activeDate;
15170 this.activeDate = date;
15171 // if(vd && this.el){
15172 // var t = date.getTime();
15173 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15174 // Roo.log('using add remove');
15176 // this.fireEvent('monthchange', this, date);
15178 // this.cells.removeClass("fc-state-highlight");
15179 // this.cells.each(function(c){
15180 // if(c.dateValue == t){
15181 // c.addClass("fc-state-highlight");
15182 // setTimeout(function(){
15183 // try{c.dom.firstChild.focus();}catch(e){}
15193 var days = date.getDaysInMonth();
15195 var firstOfMonth = date.getFirstDateOfMonth();
15196 var startingPos = firstOfMonth.getDay()-this.startDay;
15198 if(startingPos < this.startDay){
15202 var pm = date.add(Date.MONTH, -1);
15203 var prevStart = pm.getDaysInMonth()-startingPos;
15205 this.cells = this.el.select('.fc-day',true);
15206 this.textNodes = this.el.query('.fc-day-number');
15207 this.cells.addClassOnOver('fc-state-hover');
15209 var cells = this.cells.elements;
15210 var textEls = this.textNodes;
15212 Roo.each(cells, function(cell){
15213 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15216 days += startingPos;
15218 // convert everything to numbers so it's fast
15219 var day = 86400000;
15220 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15223 //Roo.log(prevStart);
15225 var today = new Date().clearTime().getTime();
15226 var sel = date.clearTime().getTime();
15227 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15228 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15229 var ddMatch = this.disabledDatesRE;
15230 var ddText = this.disabledDatesText;
15231 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15232 var ddaysText = this.disabledDaysText;
15233 var format = this.format;
15235 var setCellClass = function(cal, cell){
15239 //Roo.log('set Cell Class');
15241 var t = d.getTime();
15245 cell.dateValue = t;
15247 cell.className += " fc-today";
15248 cell.className += " fc-state-highlight";
15249 cell.title = cal.todayText;
15252 // disable highlight in other month..
15253 //cell.className += " fc-state-highlight";
15258 cell.className = " fc-state-disabled";
15259 cell.title = cal.minText;
15263 cell.className = " fc-state-disabled";
15264 cell.title = cal.maxText;
15268 if(ddays.indexOf(d.getDay()) != -1){
15269 cell.title = ddaysText;
15270 cell.className = " fc-state-disabled";
15273 if(ddMatch && format){
15274 var fvalue = d.dateFormat(format);
15275 if(ddMatch.test(fvalue)){
15276 cell.title = ddText.replace("%0", fvalue);
15277 cell.className = " fc-state-disabled";
15281 if (!cell.initialClassName) {
15282 cell.initialClassName = cell.dom.className;
15285 cell.dom.className = cell.initialClassName + ' ' + cell.className;
15290 for(; i < startingPos; i++) {
15291 textEls[i].innerHTML = (++prevStart);
15292 d.setDate(d.getDate()+1);
15294 cells[i].className = "fc-past fc-other-month";
15295 setCellClass(this, cells[i]);
15300 for(; i < days; i++){
15301 intDay = i - startingPos + 1;
15302 textEls[i].innerHTML = (intDay);
15303 d.setDate(d.getDate()+1);
15305 cells[i].className = ''; // "x-date-active";
15306 setCellClass(this, cells[i]);
15310 for(; i < 42; i++) {
15311 textEls[i].innerHTML = (++extraDays);
15312 d.setDate(d.getDate()+1);
15314 cells[i].className = "fc-future fc-other-month";
15315 setCellClass(this, cells[i]);
15318 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15320 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15322 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15323 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15325 if(totalRows != 6){
15326 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15327 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15330 this.fireEvent('monthchange', this, date);
15334 if(!this.internalRender){
15335 var main = this.el.dom.firstChild;
15336 var w = main.offsetWidth;
15337 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15338 Roo.fly(main).setWidth(w);
15339 this.internalRender = true;
15340 // opera does not respect the auto grow header center column
15341 // then, after it gets a width opera refuses to recalculate
15342 // without a second pass
15343 if(Roo.isOpera && !this.secondPass){
15344 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15345 this.secondPass = true;
15346 this.update.defer(10, this, [date]);
15353 findCell : function(dt) {
15354 dt = dt.clearTime().getTime();
15356 this.cells.each(function(c){
15357 //Roo.log("check " +c.dateValue + '?=' + dt);
15358 if(c.dateValue == dt){
15368 findCells : function(ev) {
15369 var s = ev.start.clone().clearTime().getTime();
15371 var e= ev.end.clone().clearTime().getTime();
15374 this.cells.each(function(c){
15375 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15377 if(c.dateValue > e){
15380 if(c.dateValue < s){
15389 // findBestRow: function(cells)
15393 // for (var i =0 ; i < cells.length;i++) {
15394 // ret = Math.max(cells[i].rows || 0,ret);
15401 addItem : function(ev)
15403 // look for vertical location slot in
15404 var cells = this.findCells(ev);
15406 // ev.row = this.findBestRow(cells);
15408 // work out the location.
15412 for(var i =0; i < cells.length; i++) {
15414 cells[i].row = cells[0].row;
15417 cells[i].row = cells[i].row + 1;
15427 if (crow.start.getY() == cells[i].getY()) {
15429 crow.end = cells[i];
15446 cells[0].events.push(ev);
15448 this.calevents.push(ev);
15451 clearEvents: function() {
15453 if(!this.calevents){
15457 Roo.each(this.cells.elements, function(c){
15463 Roo.each(this.calevents, function(e) {
15464 Roo.each(e.els, function(el) {
15465 el.un('mouseenter' ,this.onEventEnter, this);
15466 el.un('mouseleave' ,this.onEventLeave, this);
15471 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15477 renderEvents: function()
15481 this.cells.each(function(c) {
15490 if(c.row != c.events.length){
15491 r = 4 - (4 - (c.row - c.events.length));
15494 c.events = ev.slice(0, r);
15495 c.more = ev.slice(r);
15497 if(c.more.length && c.more.length == 1){
15498 c.events.push(c.more.pop());
15501 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15505 this.cells.each(function(c) {
15507 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15510 for (var e = 0; e < c.events.length; e++){
15511 var ev = c.events[e];
15512 var rows = ev.rows;
15514 for(var i = 0; i < rows.length; i++) {
15516 // how many rows should it span..
15519 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15520 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15522 unselectable : "on",
15525 cls: 'fc-event-inner',
15529 // cls: 'fc-event-time',
15530 // html : cells.length > 1 ? '' : ev.time
15534 cls: 'fc-event-title',
15535 html : String.format('{0}', ev.title)
15542 cls: 'ui-resizable-handle ui-resizable-e',
15543 html : '  '
15550 cfg.cls += ' fc-event-start';
15552 if ((i+1) == rows.length) {
15553 cfg.cls += ' fc-event-end';
15556 var ctr = _this.el.select('.fc-event-container',true).first();
15557 var cg = ctr.createChild(cfg);
15559 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15560 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15562 var r = (c.more.length) ? 1 : 0;
15563 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15564 cg.setWidth(ebox.right - sbox.x -2);
15566 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15567 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15568 cg.on('click', _this.onEventClick, _this, ev);
15579 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15580 style : 'position: absolute',
15581 unselectable : "on",
15584 cls: 'fc-event-inner',
15588 cls: 'fc-event-title',
15596 cls: 'ui-resizable-handle ui-resizable-e',
15597 html : '  '
15603 var ctr = _this.el.select('.fc-event-container',true).first();
15604 var cg = ctr.createChild(cfg);
15606 var sbox = c.select('.fc-day-content',true).first().getBox();
15607 var ebox = c.select('.fc-day-content',true).first().getBox();
15609 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15610 cg.setWidth(ebox.right - sbox.x -2);
15612 cg.on('click', _this.onMoreEventClick, _this, c.more);
15622 onEventEnter: function (e, el,event,d) {
15623 this.fireEvent('evententer', this, el, event);
15626 onEventLeave: function (e, el,event,d) {
15627 this.fireEvent('eventleave', this, el, event);
15630 onEventClick: function (e, el,event,d) {
15631 this.fireEvent('eventclick', this, el, event);
15634 onMonthChange: function () {
15638 onMoreEventClick: function(e, el, more)
15642 this.calpopover.placement = 'right';
15643 this.calpopover.setTitle('More');
15645 this.calpopover.setContent('');
15647 var ctr = this.calpopover.el.select('.popover-content', true).first();
15649 Roo.each(more, function(m){
15651 cls : 'fc-event-hori fc-event-draggable',
15654 var cg = ctr.createChild(cfg);
15656 cg.on('click', _this.onEventClick, _this, m);
15659 this.calpopover.show(el);
15664 onLoad: function ()
15666 this.calevents = [];
15669 if(this.store.getCount() > 0){
15670 this.store.data.each(function(d){
15673 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15674 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15675 time : d.data.start_time,
15676 title : d.data.title,
15677 description : d.data.description,
15678 venue : d.data.venue
15683 this.renderEvents();
15685 if(this.calevents.length && this.loadMask){
15686 this.maskEl.hide();
15690 onBeforeLoad: function()
15692 this.clearEvents();
15694 this.maskEl.show();
15708 * @class Roo.bootstrap.Popover
15709 * @extends Roo.bootstrap.Component
15710 * Bootstrap Popover class
15711 * @cfg {String} html contents of the popover (or false to use children..)
15712 * @cfg {String} title of popover (or false to hide)
15713 * @cfg {String} placement how it is placed
15714 * @cfg {String} trigger click || hover (or false to trigger manually)
15715 * @cfg {String} over what (parent or false to trigger manually.)
15716 * @cfg {Number} delay - delay before showing
15719 * Create a new Popover
15720 * @param {Object} config The config object
15723 Roo.bootstrap.Popover = function(config){
15724 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15730 * After the popover show
15732 * @param {Roo.bootstrap.Popover} this
15737 * After the popover hide
15739 * @param {Roo.bootstrap.Popover} this
15745 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
15747 title: 'Fill in a title',
15750 placement : 'right',
15751 trigger : 'hover', // hover
15757 can_build_overlaid : false,
15759 getChildContainer : function()
15761 return this.el.select('.popover-content',true).first();
15764 getAutoCreate : function(){
15767 cls : 'popover roo-dynamic',
15768 style: 'display:block',
15774 cls : 'popover-inner',
15778 cls: 'popover-title',
15782 cls : 'popover-content',
15793 setTitle: function(str)
15796 this.el.select('.popover-title',true).first().dom.innerHTML = str;
15798 setContent: function(str)
15801 this.el.select('.popover-content',true).first().dom.innerHTML = str;
15803 // as it get's added to the bottom of the page.
15804 onRender : function(ct, position)
15806 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15808 var cfg = Roo.apply({}, this.getAutoCreate());
15812 cfg.cls += ' ' + this.cls;
15815 cfg.style = this.style;
15817 //Roo.log("adding to ");
15818 this.el = Roo.get(document.body).createChild(cfg, position);
15819 // Roo.log(this.el);
15824 initEvents : function()
15826 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15827 this.el.enableDisplayMode('block');
15829 if (this.over === false) {
15832 if (this.triggers === false) {
15835 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15836 var triggers = this.trigger ? this.trigger.split(' ') : [];
15837 Roo.each(triggers, function(trigger) {
15839 if (trigger == 'click') {
15840 on_el.on('click', this.toggle, this);
15841 } else if (trigger != 'manual') {
15842 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
15843 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15845 on_el.on(eventIn ,this.enter, this);
15846 on_el.on(eventOut, this.leave, this);
15857 toggle : function () {
15858 this.hoverState == 'in' ? this.leave() : this.enter();
15861 enter : function () {
15864 clearTimeout(this.timeout);
15866 this.hoverState = 'in';
15868 if (!this.delay || !this.delay.show) {
15873 this.timeout = setTimeout(function () {
15874 if (_t.hoverState == 'in') {
15877 }, this.delay.show)
15879 leave : function() {
15880 clearTimeout(this.timeout);
15882 this.hoverState = 'out';
15884 if (!this.delay || !this.delay.hide) {
15889 this.timeout = setTimeout(function () {
15890 if (_t.hoverState == 'out') {
15893 }, this.delay.hide)
15896 show : function (on_el)
15899 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15902 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15903 if (this.html !== false) {
15904 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15906 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15907 if (!this.title.length) {
15908 this.el.select('.popover-title',true).hide();
15911 var placement = typeof this.placement == 'function' ?
15912 this.placement.call(this, this.el, on_el) :
15915 var autoToken = /\s?auto?\s?/i;
15916 var autoPlace = autoToken.test(placement);
15918 placement = placement.replace(autoToken, '') || 'top';
15922 //this.el.setXY([0,0]);
15924 this.el.dom.style.display='block';
15925 this.el.addClass(placement);
15927 //this.el.appendTo(on_el);
15929 var p = this.getPosition();
15930 var box = this.el.getBox();
15935 var align = Roo.bootstrap.Popover.alignment[placement];
15936 this.el.alignTo(on_el, align[0],align[1]);
15937 //var arrow = this.el.select('.arrow',true).first();
15938 //arrow.set(align[2],
15940 this.el.addClass('in');
15943 if (this.el.hasClass('fade')) {
15947 this.fireEvent('show', this);
15952 this.el.setXY([0,0]);
15953 this.el.removeClass('in');
15955 this.hoverState = null;
15957 this.fireEvent('hide', this);
15962 Roo.bootstrap.Popover.alignment = {
15963 'left' : ['r-l', [-10,0], 'right'],
15964 'right' : ['l-r', [10,0], 'left'],
15965 'bottom' : ['t-b', [0,10], 'top'],
15966 'top' : [ 'b-t', [0,-10], 'bottom']
15977 * @class Roo.bootstrap.Progress
15978 * @extends Roo.bootstrap.Component
15979 * Bootstrap Progress class
15980 * @cfg {Boolean} striped striped of the progress bar
15981 * @cfg {Boolean} active animated of the progress bar
15985 * Create a new Progress
15986 * @param {Object} config The config object
15989 Roo.bootstrap.Progress = function(config){
15990 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15993 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
15998 getAutoCreate : function(){
16006 cfg.cls += ' progress-striped';
16010 cfg.cls += ' active';
16029 * @class Roo.bootstrap.ProgressBar
16030 * @extends Roo.bootstrap.Component
16031 * Bootstrap ProgressBar class
16032 * @cfg {Number} aria_valuenow aria-value now
16033 * @cfg {Number} aria_valuemin aria-value min
16034 * @cfg {Number} aria_valuemax aria-value max
16035 * @cfg {String} label label for the progress bar
16036 * @cfg {String} panel (success | info | warning | danger )
16037 * @cfg {String} role role of the progress bar
16038 * @cfg {String} sr_only text
16042 * Create a new ProgressBar
16043 * @param {Object} config The config object
16046 Roo.bootstrap.ProgressBar = function(config){
16047 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16050 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
16054 aria_valuemax : 100,
16060 getAutoCreate : function()
16065 cls: 'progress-bar',
16066 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16078 cfg.role = this.role;
16081 if(this.aria_valuenow){
16082 cfg['aria-valuenow'] = this.aria_valuenow;
16085 if(this.aria_valuemin){
16086 cfg['aria-valuemin'] = this.aria_valuemin;
16089 if(this.aria_valuemax){
16090 cfg['aria-valuemax'] = this.aria_valuemax;
16093 if(this.label && !this.sr_only){
16094 cfg.html = this.label;
16098 cfg.cls += ' progress-bar-' + this.panel;
16104 update : function(aria_valuenow)
16106 this.aria_valuenow = aria_valuenow;
16108 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16123 * @class Roo.bootstrap.TabGroup
16124 * @extends Roo.bootstrap.Column
16125 * Bootstrap Column class
16126 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16127 * @cfg {Boolean} carousel true to make the group behave like a carousel
16128 * @cfg {Boolean} bullets show bullets for the panels
16129 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16130 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16131 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16134 * Create a new TabGroup
16135 * @param {Object} config The config object
16138 Roo.bootstrap.TabGroup = function(config){
16139 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16141 this.navId = Roo.id();
16144 Roo.bootstrap.TabGroup.register(this);
16148 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16151 transition : false,
16156 slideOnTouch : false,
16158 getAutoCreate : function()
16160 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16162 cfg.cls += ' tab-content';
16164 if (this.carousel) {
16165 cfg.cls += ' carousel slide';
16168 cls : 'carousel-inner'
16171 if(this.bullets && !Roo.isTouch){
16174 cls : 'carousel-bullets',
16178 if(this.bullets_cls){
16179 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16182 for (var i = 0; i < this.bullets; i++){
16184 cls : 'bullet bullet-' + i
16192 cfg.cn[0].cn = bullets;
16199 initEvents: function()
16201 if(Roo.isTouch && this.slideOnTouch){
16202 this.el.on("touchstart", this.onTouchStart, this);
16205 if(this.autoslide){
16208 this.slideFn = window.setInterval(function() {
16209 _this.showPanelNext();
16215 onTouchStart : function(e, el, o)
16217 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16221 this.showPanelNext();
16224 getChildContainer : function()
16226 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16230 * register a Navigation item
16231 * @param {Roo.bootstrap.NavItem} the navitem to add
16233 register : function(item)
16235 this.tabs.push( item);
16236 item.navId = this.navId; // not really needed..
16241 getActivePanel : function()
16244 Roo.each(this.tabs, function(t) {
16254 getPanelByName : function(n)
16257 Roo.each(this.tabs, function(t) {
16258 if (t.tabId == n) {
16266 indexOfPanel : function(p)
16269 Roo.each(this.tabs, function(t,i) {
16270 if (t.tabId == p.tabId) {
16279 * show a specific panel
16280 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16281 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16283 showPanel : function (pan)
16285 if(this.transition || typeof(pan) == 'undefined'){
16286 Roo.log("waiting for the transitionend");
16290 if (typeof(pan) == 'number') {
16291 pan = this.tabs[pan];
16294 if (typeof(pan) == 'string') {
16295 pan = this.getPanelByName(pan);
16298 var cur = this.getActivePanel();
16301 Roo.log('pan or acitve pan is undefined');
16305 if (pan.tabId == this.getActivePanel().tabId) {
16309 if (false === cur.fireEvent('beforedeactivate')) {
16313 if(this.bullets > 0 && !Roo.isTouch){
16314 this.setActiveBullet(this.indexOfPanel(pan));
16317 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16319 this.transition = true;
16320 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16321 var lr = dir == 'next' ? 'left' : 'right';
16322 pan.el.addClass(dir); // or prev
16323 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16324 cur.el.addClass(lr); // or right
16325 pan.el.addClass(lr);
16328 cur.el.on('transitionend', function() {
16329 Roo.log("trans end?");
16331 pan.el.removeClass([lr,dir]);
16332 pan.setActive(true);
16334 cur.el.removeClass([lr]);
16335 cur.setActive(false);
16337 _this.transition = false;
16339 }, this, { single: true } );
16344 cur.setActive(false);
16345 pan.setActive(true);
16350 showPanelNext : function()
16352 var i = this.indexOfPanel(this.getActivePanel());
16354 if (i >= this.tabs.length - 1 && !this.autoslide) {
16358 if (i >= this.tabs.length - 1 && this.autoslide) {
16362 this.showPanel(this.tabs[i+1]);
16365 showPanelPrev : function()
16367 var i = this.indexOfPanel(this.getActivePanel());
16369 if (i < 1 && !this.autoslide) {
16373 if (i < 1 && this.autoslide) {
16374 i = this.tabs.length;
16377 this.showPanel(this.tabs[i-1]);
16381 addBullet: function()
16383 if(!this.bullets || Roo.isTouch){
16386 var ctr = this.el.select('.carousel-bullets',true).first();
16387 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16388 var bullet = ctr.createChild({
16389 cls : 'bullet bullet-' + i
16390 },ctr.dom.lastChild);
16395 bullet.on('click', (function(e, el, o, ii, t){
16397 e.preventDefault();
16399 this.showPanel(ii);
16401 if(this.autoslide && this.slideFn){
16402 clearInterval(this.slideFn);
16403 this.slideFn = window.setInterval(function() {
16404 _this.showPanelNext();
16408 }).createDelegate(this, [i, bullet], true));
16413 setActiveBullet : function(i)
16419 Roo.each(this.el.select('.bullet', true).elements, function(el){
16420 el.removeClass('selected');
16423 var bullet = this.el.select('.bullet-' + i, true).first();
16429 bullet.addClass('selected');
16440 Roo.apply(Roo.bootstrap.TabGroup, {
16444 * register a Navigation Group
16445 * @param {Roo.bootstrap.NavGroup} the navgroup to add
16447 register : function(navgrp)
16449 this.groups[navgrp.navId] = navgrp;
16453 * fetch a Navigation Group based on the navigation ID
16454 * if one does not exist , it will get created.
16455 * @param {string} the navgroup to add
16456 * @returns {Roo.bootstrap.NavGroup} the navgroup
16458 get: function(navId) {
16459 if (typeof(this.groups[navId]) == 'undefined') {
16460 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16462 return this.groups[navId] ;
16477 * @class Roo.bootstrap.TabPanel
16478 * @extends Roo.bootstrap.Component
16479 * Bootstrap TabPanel class
16480 * @cfg {Boolean} active panel active
16481 * @cfg {String} html panel content
16482 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16483 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16487 * Create a new TabPanel
16488 * @param {Object} config The config object
16491 Roo.bootstrap.TabPanel = function(config){
16492 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16496 * Fires when the active status changes
16497 * @param {Roo.bootstrap.TabPanel} this
16498 * @param {Boolean} state the new state
16503 * @event beforedeactivate
16504 * Fires before a tab is de-activated - can be used to do validation on a form.
16505 * @param {Roo.bootstrap.TabPanel} this
16506 * @return {Boolean} false if there is an error
16509 'beforedeactivate': true
16512 this.tabId = this.tabId || Roo.id();
16516 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16523 getAutoCreate : function(){
16526 // item is needed for carousel - not sure if it has any effect otherwise
16527 cls: 'tab-pane item',
16528 html: this.html || ''
16532 cfg.cls += ' active';
16536 cfg.tabId = this.tabId;
16543 initEvents: function()
16545 var p = this.parent();
16546 this.navId = this.navId || p.navId;
16548 if (typeof(this.navId) != 'undefined') {
16549 // not really needed.. but just in case.. parent should be a NavGroup.
16550 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16554 var i = tg.tabs.length - 1;
16556 if(this.active && tg.bullets > 0 && i < tg.bullets){
16557 tg.setActiveBullet(i);
16564 onRender : function(ct, position)
16566 // Roo.log("Call onRender: " + this.xtype);
16568 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16576 setActive: function(state)
16578 Roo.log("panel - set active " + this.tabId + "=" + state);
16580 this.active = state;
16582 this.el.removeClass('active');
16584 } else if (!this.el.hasClass('active')) {
16585 this.el.addClass('active');
16588 this.fireEvent('changed', this, state);
16605 * @class Roo.bootstrap.DateField
16606 * @extends Roo.bootstrap.Input
16607 * Bootstrap DateField class
16608 * @cfg {Number} weekStart default 0
16609 * @cfg {String} viewMode default empty, (months|years)
16610 * @cfg {String} minViewMode default empty, (months|years)
16611 * @cfg {Number} startDate default -Infinity
16612 * @cfg {Number} endDate default Infinity
16613 * @cfg {Boolean} todayHighlight default false
16614 * @cfg {Boolean} todayBtn default false
16615 * @cfg {Boolean} calendarWeeks default false
16616 * @cfg {Object} daysOfWeekDisabled default empty
16617 * @cfg {Boolean} singleMode default false (true | false)
16619 * @cfg {Boolean} keyboardNavigation default true
16620 * @cfg {String} language default en
16623 * Create a new DateField
16624 * @param {Object} config The config object
16627 Roo.bootstrap.DateField = function(config){
16628 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16632 * Fires when this field show.
16633 * @param {Roo.bootstrap.DateField} this
16634 * @param {Mixed} date The date value
16639 * Fires when this field hide.
16640 * @param {Roo.bootstrap.DateField} this
16641 * @param {Mixed} date The date value
16646 * Fires when select a date.
16647 * @param {Roo.bootstrap.DateField} this
16648 * @param {Mixed} date The date value
16652 * @event beforeselect
16653 * Fires when before select a date.
16654 * @param {Roo.bootstrap.DateField} this
16655 * @param {Mixed} date The date value
16657 beforeselect : true
16661 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16664 * @cfg {String} format
16665 * The default date format string which can be overriden for localization support. The format must be
16666 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16670 * @cfg {String} altFormats
16671 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16672 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16674 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16682 todayHighlight : false,
16688 keyboardNavigation: true,
16690 calendarWeeks: false,
16692 startDate: -Infinity,
16696 daysOfWeekDisabled: [],
16700 singleMode : false,
16702 UTCDate: function()
16704 return new Date(Date.UTC.apply(Date, arguments));
16707 UTCToday: function()
16709 var today = new Date();
16710 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16713 getDate: function() {
16714 var d = this.getUTCDate();
16715 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16718 getUTCDate: function() {
16722 setDate: function(d) {
16723 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16726 setUTCDate: function(d) {
16728 this.setValue(this.formatDate(this.date));
16731 onRender: function(ct, position)
16734 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16736 this.language = this.language || 'en';
16737 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16738 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16740 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16741 this.format = this.format || 'm/d/y';
16742 this.isInline = false;
16743 this.isInput = true;
16744 this.component = this.el.select('.add-on', true).first() || false;
16745 this.component = (this.component && this.component.length === 0) ? false : this.component;
16746 this.hasInput = this.component && this.inputEL().length;
16748 if (typeof(this.minViewMode === 'string')) {
16749 switch (this.minViewMode) {
16751 this.minViewMode = 1;
16754 this.minViewMode = 2;
16757 this.minViewMode = 0;
16762 if (typeof(this.viewMode === 'string')) {
16763 switch (this.viewMode) {
16776 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16778 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16780 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16782 this.picker().on('mousedown', this.onMousedown, this);
16783 this.picker().on('click', this.onClick, this);
16785 this.picker().addClass('datepicker-dropdown');
16787 this.startViewMode = this.viewMode;
16789 if(this.singleMode){
16790 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16791 v.setVisibilityMode(Roo.Element.DISPLAY);
16795 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16796 v.setStyle('width', '189px');
16800 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16801 if(!this.calendarWeeks){
16806 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16807 v.attr('colspan', function(i, val){
16808 return parseInt(val) + 1;
16813 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16815 this.setStartDate(this.startDate);
16816 this.setEndDate(this.endDate);
16818 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16825 if(this.isInline) {
16830 picker : function()
16832 return this.pickerEl;
16833 // return this.el.select('.datepicker', true).first();
16836 fillDow: function()
16838 var dowCnt = this.weekStart;
16847 if(this.calendarWeeks){
16855 while (dowCnt < this.weekStart + 7) {
16859 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16863 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16866 fillMonths: function()
16869 var months = this.picker().select('>.datepicker-months td', true).first();
16871 months.dom.innerHTML = '';
16877 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16880 months.createChild(month);
16887 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;
16889 if (this.date < this.startDate) {
16890 this.viewDate = new Date(this.startDate);
16891 } else if (this.date > this.endDate) {
16892 this.viewDate = new Date(this.endDate);
16894 this.viewDate = new Date(this.date);
16902 var d = new Date(this.viewDate),
16903 year = d.getUTCFullYear(),
16904 month = d.getUTCMonth(),
16905 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16906 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16907 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16908 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16909 currentDate = this.date && this.date.valueOf(),
16910 today = this.UTCToday();
16912 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16914 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16916 // this.picker.select('>tfoot th.today').
16917 // .text(dates[this.language].today)
16918 // .toggle(this.todayBtn !== false);
16920 this.updateNavArrows();
16923 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16925 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16927 prevMonth.setUTCDate(day);
16929 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16931 var nextMonth = new Date(prevMonth);
16933 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16935 nextMonth = nextMonth.valueOf();
16937 var fillMonths = false;
16939 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16941 while(prevMonth.valueOf() < nextMonth) {
16944 if (prevMonth.getUTCDay() === this.weekStart) {
16946 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16954 if(this.calendarWeeks){
16955 // ISO 8601: First week contains first thursday.
16956 // ISO also states week starts on Monday, but we can be more abstract here.
16958 // Start of current week: based on weekstart/current date
16959 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16960 // Thursday of this week
16961 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16962 // First Thursday of year, year from thursday
16963 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16964 // Calendar week: ms between thursdays, div ms per day, div 7 days
16965 calWeek = (th - yth) / 864e5 / 7 + 1;
16967 fillMonths.cn.push({
16975 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16977 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16980 if (this.todayHighlight &&
16981 prevMonth.getUTCFullYear() == today.getFullYear() &&
16982 prevMonth.getUTCMonth() == today.getMonth() &&
16983 prevMonth.getUTCDate() == today.getDate()) {
16984 clsName += ' today';
16987 if (currentDate && prevMonth.valueOf() === currentDate) {
16988 clsName += ' active';
16991 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16992 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16993 clsName += ' disabled';
16996 fillMonths.cn.push({
16998 cls: 'day ' + clsName,
16999 html: prevMonth.getDate()
17002 prevMonth.setDate(prevMonth.getDate()+1);
17005 var currentYear = this.date && this.date.getUTCFullYear();
17006 var currentMonth = this.date && this.date.getUTCMonth();
17008 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17010 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17011 v.removeClass('active');
17013 if(currentYear === year && k === currentMonth){
17014 v.addClass('active');
17017 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17018 v.addClass('disabled');
17024 year = parseInt(year/10, 10) * 10;
17026 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17028 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17031 for (var i = -1; i < 11; i++) {
17032 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17034 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17042 showMode: function(dir)
17045 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17048 Roo.each(this.picker().select('>div',true).elements, function(v){
17049 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17052 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17057 if(this.isInline) {
17061 this.picker().removeClass(['bottom', 'top']);
17063 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17065 * place to the top of element!
17069 this.picker().addClass('top');
17070 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17075 this.picker().addClass('bottom');
17077 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17080 parseDate : function(value)
17082 if(!value || value instanceof Date){
17085 var v = Date.parseDate(value, this.format);
17086 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17087 v = Date.parseDate(value, 'Y-m-d');
17089 if(!v && this.altFormats){
17090 if(!this.altFormatsArray){
17091 this.altFormatsArray = this.altFormats.split("|");
17093 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17094 v = Date.parseDate(value, this.altFormatsArray[i]);
17100 formatDate : function(date, fmt)
17102 return (!date || !(date instanceof Date)) ?
17103 date : date.dateFormat(fmt || this.format);
17106 onFocus : function()
17108 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17112 onBlur : function()
17114 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17116 var d = this.inputEl().getValue();
17125 this.picker().show();
17129 this.fireEvent('show', this, this.date);
17134 if(this.isInline) {
17137 this.picker().hide();
17138 this.viewMode = this.startViewMode;
17141 this.fireEvent('hide', this, this.date);
17145 onMousedown: function(e)
17147 e.stopPropagation();
17148 e.preventDefault();
17153 Roo.bootstrap.DateField.superclass.keyup.call(this);
17157 setValue: function(v)
17159 if(this.fireEvent('beforeselect', this, v) !== false){
17160 var d = new Date(this.parseDate(v) ).clearTime();
17162 if(isNaN(d.getTime())){
17163 this.date = this.viewDate = '';
17164 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17168 v = this.formatDate(d);
17170 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17172 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17176 this.fireEvent('select', this, this.date);
17180 getValue: function()
17182 return this.formatDate(this.date);
17185 fireKey: function(e)
17187 if (!this.picker().isVisible()){
17188 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17194 var dateChanged = false,
17196 newDate, newViewDate;
17201 e.preventDefault();
17205 if (!this.keyboardNavigation) {
17208 dir = e.keyCode == 37 ? -1 : 1;
17211 newDate = this.moveYear(this.date, dir);
17212 newViewDate = this.moveYear(this.viewDate, dir);
17213 } else if (e.shiftKey){
17214 newDate = this.moveMonth(this.date, dir);
17215 newViewDate = this.moveMonth(this.viewDate, dir);
17217 newDate = new Date(this.date);
17218 newDate.setUTCDate(this.date.getUTCDate() + dir);
17219 newViewDate = new Date(this.viewDate);
17220 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17222 if (this.dateWithinRange(newDate)){
17223 this.date = newDate;
17224 this.viewDate = newViewDate;
17225 this.setValue(this.formatDate(this.date));
17227 e.preventDefault();
17228 dateChanged = true;
17233 if (!this.keyboardNavigation) {
17236 dir = e.keyCode == 38 ? -1 : 1;
17238 newDate = this.moveYear(this.date, dir);
17239 newViewDate = this.moveYear(this.viewDate, dir);
17240 } else if (e.shiftKey){
17241 newDate = this.moveMonth(this.date, dir);
17242 newViewDate = this.moveMonth(this.viewDate, dir);
17244 newDate = new Date(this.date);
17245 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17246 newViewDate = new Date(this.viewDate);
17247 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17249 if (this.dateWithinRange(newDate)){
17250 this.date = newDate;
17251 this.viewDate = newViewDate;
17252 this.setValue(this.formatDate(this.date));
17254 e.preventDefault();
17255 dateChanged = true;
17259 this.setValue(this.formatDate(this.date));
17261 e.preventDefault();
17264 this.setValue(this.formatDate(this.date));
17278 onClick: function(e)
17280 e.stopPropagation();
17281 e.preventDefault();
17283 var target = e.getTarget();
17285 if(target.nodeName.toLowerCase() === 'i'){
17286 target = Roo.get(target).dom.parentNode;
17289 var nodeName = target.nodeName;
17290 var className = target.className;
17291 var html = target.innerHTML;
17292 //Roo.log(nodeName);
17294 switch(nodeName.toLowerCase()) {
17296 switch(className) {
17302 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17303 switch(this.viewMode){
17305 this.viewDate = this.moveMonth(this.viewDate, dir);
17309 this.viewDate = this.moveYear(this.viewDate, dir);
17315 var date = new Date();
17316 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17318 this.setValue(this.formatDate(this.date));
17325 if (className.indexOf('disabled') < 0) {
17326 this.viewDate.setUTCDate(1);
17327 if (className.indexOf('month') > -1) {
17328 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17330 var year = parseInt(html, 10) || 0;
17331 this.viewDate.setUTCFullYear(year);
17335 if(this.singleMode){
17336 this.setValue(this.formatDate(this.viewDate));
17347 //Roo.log(className);
17348 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17349 var day = parseInt(html, 10) || 1;
17350 var year = this.viewDate.getUTCFullYear(),
17351 month = this.viewDate.getUTCMonth();
17353 if (className.indexOf('old') > -1) {
17360 } else if (className.indexOf('new') > -1) {
17368 //Roo.log([year,month,day]);
17369 this.date = this.UTCDate(year, month, day,0,0,0,0);
17370 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17372 //Roo.log(this.formatDate(this.date));
17373 this.setValue(this.formatDate(this.date));
17380 setStartDate: function(startDate)
17382 this.startDate = startDate || -Infinity;
17383 if (this.startDate !== -Infinity) {
17384 this.startDate = this.parseDate(this.startDate);
17387 this.updateNavArrows();
17390 setEndDate: function(endDate)
17392 this.endDate = endDate || Infinity;
17393 if (this.endDate !== Infinity) {
17394 this.endDate = this.parseDate(this.endDate);
17397 this.updateNavArrows();
17400 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17402 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17403 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17404 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17406 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17407 return parseInt(d, 10);
17410 this.updateNavArrows();
17413 updateNavArrows: function()
17415 if(this.singleMode){
17419 var d = new Date(this.viewDate),
17420 year = d.getUTCFullYear(),
17421 month = d.getUTCMonth();
17423 Roo.each(this.picker().select('.prev', true).elements, function(v){
17425 switch (this.viewMode) {
17428 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17434 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17441 Roo.each(this.picker().select('.next', true).elements, function(v){
17443 switch (this.viewMode) {
17446 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17452 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17460 moveMonth: function(date, dir)
17465 var new_date = new Date(date.valueOf()),
17466 day = new_date.getUTCDate(),
17467 month = new_date.getUTCMonth(),
17468 mag = Math.abs(dir),
17470 dir = dir > 0 ? 1 : -1;
17473 // If going back one month, make sure month is not current month
17474 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17476 return new_date.getUTCMonth() == month;
17478 // If going forward one month, make sure month is as expected
17479 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17481 return new_date.getUTCMonth() != new_month;
17483 new_month = month + dir;
17484 new_date.setUTCMonth(new_month);
17485 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17486 if (new_month < 0 || new_month > 11) {
17487 new_month = (new_month + 12) % 12;
17490 // For magnitudes >1, move one month at a time...
17491 for (var i=0; i<mag; i++) {
17492 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17493 new_date = this.moveMonth(new_date, dir);
17495 // ...then reset the day, keeping it in the new month
17496 new_month = new_date.getUTCMonth();
17497 new_date.setUTCDate(day);
17499 return new_month != new_date.getUTCMonth();
17502 // Common date-resetting loop -- if date is beyond end of month, make it
17505 new_date.setUTCDate(--day);
17506 new_date.setUTCMonth(new_month);
17511 moveYear: function(date, dir)
17513 return this.moveMonth(date, dir*12);
17516 dateWithinRange: function(date)
17518 return date >= this.startDate && date <= this.endDate;
17524 this.picker().remove();
17529 Roo.apply(Roo.bootstrap.DateField, {
17540 html: '<i class="fa fa-arrow-left"/>'
17550 html: '<i class="fa fa-arrow-right"/>'
17592 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17593 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17594 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17595 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17596 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17609 navFnc: 'FullYear',
17614 navFnc: 'FullYear',
17619 Roo.apply(Roo.bootstrap.DateField, {
17623 cls: 'datepicker dropdown-menu roo-dynamic',
17627 cls: 'datepicker-days',
17631 cls: 'table-condensed',
17633 Roo.bootstrap.DateField.head,
17637 Roo.bootstrap.DateField.footer
17644 cls: 'datepicker-months',
17648 cls: 'table-condensed',
17650 Roo.bootstrap.DateField.head,
17651 Roo.bootstrap.DateField.content,
17652 Roo.bootstrap.DateField.footer
17659 cls: 'datepicker-years',
17663 cls: 'table-condensed',
17665 Roo.bootstrap.DateField.head,
17666 Roo.bootstrap.DateField.content,
17667 Roo.bootstrap.DateField.footer
17686 * @class Roo.bootstrap.TimeField
17687 * @extends Roo.bootstrap.Input
17688 * Bootstrap DateField class
17692 * Create a new TimeField
17693 * @param {Object} config The config object
17696 Roo.bootstrap.TimeField = function(config){
17697 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17701 * Fires when this field show.
17702 * @param {Roo.bootstrap.DateField} thisthis
17703 * @param {Mixed} date The date value
17708 * Fires when this field hide.
17709 * @param {Roo.bootstrap.DateField} this
17710 * @param {Mixed} date The date value
17715 * Fires when select a date.
17716 * @param {Roo.bootstrap.DateField} this
17717 * @param {Mixed} date The date value
17723 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17726 * @cfg {String} format
17727 * The default time format string which can be overriden for localization support. The format must be
17728 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17732 onRender: function(ct, position)
17735 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17737 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17739 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17741 this.pop = this.picker().select('>.datepicker-time',true).first();
17742 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17744 this.picker().on('mousedown', this.onMousedown, this);
17745 this.picker().on('click', this.onClick, this);
17747 this.picker().addClass('datepicker-dropdown');
17752 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17753 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17754 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17755 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17756 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17757 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17761 fireKey: function(e){
17762 if (!this.picker().isVisible()){
17763 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17769 e.preventDefault();
17777 this.onTogglePeriod();
17780 this.onIncrementMinutes();
17783 this.onDecrementMinutes();
17792 onClick: function(e) {
17793 e.stopPropagation();
17794 e.preventDefault();
17797 picker : function()
17799 return this.el.select('.datepicker', true).first();
17802 fillTime: function()
17804 var time = this.pop.select('tbody', true).first();
17806 time.dom.innerHTML = '';
17821 cls: 'hours-up glyphicon glyphicon-chevron-up'
17841 cls: 'minutes-up glyphicon glyphicon-chevron-up'
17862 cls: 'timepicker-hour',
17877 cls: 'timepicker-minute',
17892 cls: 'btn btn-primary period',
17914 cls: 'hours-down glyphicon glyphicon-chevron-down'
17934 cls: 'minutes-down glyphicon glyphicon-chevron-down'
17952 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17959 var hours = this.time.getHours();
17960 var minutes = this.time.getMinutes();
17973 hours = hours - 12;
17977 hours = '0' + hours;
17981 minutes = '0' + minutes;
17984 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17985 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17986 this.pop.select('button', true).first().dom.innerHTML = period;
17992 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17994 var cls = ['bottom'];
17996 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18003 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18008 this.picker().addClass(cls.join('-'));
18012 Roo.each(cls, function(c){
18014 _this.picker().setTop(_this.inputEl().getHeight());
18018 _this.picker().setTop(0 - _this.picker().getHeight());
18023 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18027 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18034 onFocus : function()
18036 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18040 onBlur : function()
18042 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18048 this.picker().show();
18053 this.fireEvent('show', this, this.date);
18058 this.picker().hide();
18061 this.fireEvent('hide', this, this.date);
18064 setTime : function()
18067 this.setValue(this.time.format(this.format));
18069 this.fireEvent('select', this, this.date);
18074 onMousedown: function(e){
18075 e.stopPropagation();
18076 e.preventDefault();
18079 onIncrementHours: function()
18081 Roo.log('onIncrementHours');
18082 this.time = this.time.add(Date.HOUR, 1);
18087 onDecrementHours: function()
18089 Roo.log('onDecrementHours');
18090 this.time = this.time.add(Date.HOUR, -1);
18094 onIncrementMinutes: function()
18096 Roo.log('onIncrementMinutes');
18097 this.time = this.time.add(Date.MINUTE, 1);
18101 onDecrementMinutes: function()
18103 Roo.log('onDecrementMinutes');
18104 this.time = this.time.add(Date.MINUTE, -1);
18108 onTogglePeriod: function()
18110 Roo.log('onTogglePeriod');
18111 this.time = this.time.add(Date.HOUR, 12);
18118 Roo.apply(Roo.bootstrap.TimeField, {
18148 cls: 'btn btn-info ok',
18160 Roo.apply(Roo.bootstrap.TimeField, {
18164 cls: 'datepicker dropdown-menu',
18168 cls: 'datepicker-time',
18172 cls: 'table-condensed',
18174 Roo.bootstrap.TimeField.content,
18175 Roo.bootstrap.TimeField.footer
18194 * @class Roo.bootstrap.MonthField
18195 * @extends Roo.bootstrap.Input
18196 * Bootstrap MonthField class
18198 * @cfg {String} language default en
18201 * Create a new MonthField
18202 * @param {Object} config The config object
18205 Roo.bootstrap.MonthField = function(config){
18206 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18211 * Fires when this field show.
18212 * @param {Roo.bootstrap.MonthField} this
18213 * @param {Mixed} date The date value
18218 * Fires when this field hide.
18219 * @param {Roo.bootstrap.MonthField} this
18220 * @param {Mixed} date The date value
18225 * Fires when select a date.
18226 * @param {Roo.bootstrap.MonthField} this
18227 * @param {String} oldvalue The old value
18228 * @param {String} newvalue The new value
18234 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
18236 onRender: function(ct, position)
18239 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18241 this.language = this.language || 'en';
18242 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18243 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18245 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18246 this.isInline = false;
18247 this.isInput = true;
18248 this.component = this.el.select('.add-on', true).first() || false;
18249 this.component = (this.component && this.component.length === 0) ? false : this.component;
18250 this.hasInput = this.component && this.inputEL().length;
18252 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18254 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18256 this.picker().on('mousedown', this.onMousedown, this);
18257 this.picker().on('click', this.onClick, this);
18259 this.picker().addClass('datepicker-dropdown');
18261 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18262 v.setStyle('width', '189px');
18269 if(this.isInline) {
18275 setValue: function(v, suppressEvent)
18277 var o = this.getValue();
18279 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18283 if(suppressEvent !== true){
18284 this.fireEvent('select', this, o, v);
18289 getValue: function()
18294 onClick: function(e)
18296 e.stopPropagation();
18297 e.preventDefault();
18299 var target = e.getTarget();
18301 if(target.nodeName.toLowerCase() === 'i'){
18302 target = Roo.get(target).dom.parentNode;
18305 var nodeName = target.nodeName;
18306 var className = target.className;
18307 var html = target.innerHTML;
18309 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18313 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18315 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18321 picker : function()
18323 return this.pickerEl;
18326 fillMonths: function()
18329 var months = this.picker().select('>.datepicker-months td', true).first();
18331 months.dom.innerHTML = '';
18337 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18340 months.createChild(month);
18349 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18350 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18353 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18354 e.removeClass('active');
18356 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18357 e.addClass('active');
18364 if(this.isInline) {
18368 this.picker().removeClass(['bottom', 'top']);
18370 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18372 * place to the top of element!
18376 this.picker().addClass('top');
18377 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18382 this.picker().addClass('bottom');
18384 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18387 onFocus : function()
18389 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18393 onBlur : function()
18395 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18397 var d = this.inputEl().getValue();
18406 this.picker().show();
18407 this.picker().select('>.datepicker-months', true).first().show();
18411 this.fireEvent('show', this, this.date);
18416 if(this.isInline) {
18419 this.picker().hide();
18420 this.fireEvent('hide', this, this.date);
18424 onMousedown: function(e)
18426 e.stopPropagation();
18427 e.preventDefault();
18432 Roo.bootstrap.MonthField.superclass.keyup.call(this);
18436 fireKey: function(e)
18438 if (!this.picker().isVisible()){
18439 if (e.keyCode == 27) {// allow escape to hide and re-show picker
18450 e.preventDefault();
18454 dir = e.keyCode == 37 ? -1 : 1;
18456 this.vIndex = this.vIndex + dir;
18458 if(this.vIndex < 0){
18462 if(this.vIndex > 11){
18466 if(isNaN(this.vIndex)){
18470 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18476 dir = e.keyCode == 38 ? -1 : 1;
18478 this.vIndex = this.vIndex + dir * 4;
18480 if(this.vIndex < 0){
18484 if(this.vIndex > 11){
18488 if(isNaN(this.vIndex)){
18492 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18497 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18498 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18502 e.preventDefault();
18505 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18506 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18522 this.picker().remove();
18527 Roo.apply(Roo.bootstrap.MonthField, {
18546 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18547 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18552 Roo.apply(Roo.bootstrap.MonthField, {
18556 cls: 'datepicker dropdown-menu roo-dynamic',
18560 cls: 'datepicker-months',
18564 cls: 'table-condensed',
18566 Roo.bootstrap.DateField.content
18586 * @class Roo.bootstrap.CheckBox
18587 * @extends Roo.bootstrap.Input
18588 * Bootstrap CheckBox class
18590 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18591 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18592 * @cfg {String} boxLabel The text that appears beside the checkbox
18593 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18594 * @cfg {Boolean} checked initnal the element
18595 * @cfg {Boolean} inline inline the element (default false)
18596 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18599 * Create a new CheckBox
18600 * @param {Object} config The config object
18603 Roo.bootstrap.CheckBox = function(config){
18604 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18609 * Fires when the element is checked or unchecked.
18610 * @param {Roo.bootstrap.CheckBox} this This input
18611 * @param {Boolean} checked The new checked value
18618 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18620 inputType: 'checkbox',
18628 getAutoCreate : function()
18630 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18636 cfg.cls = 'form-group ' + this.inputType; //input-group
18639 cfg.cls += ' ' + this.inputType + '-inline';
18645 type : this.inputType,
18646 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18647 cls : 'roo-' + this.inputType, //'form-box',
18648 placeholder : this.placeholder || ''
18652 if (this.weight) { // Validity check?
18653 cfg.cls += " " + this.inputType + "-" + this.weight;
18656 if (this.disabled) {
18657 input.disabled=true;
18661 input.checked = this.checked;
18665 input.name = this.name;
18669 input.cls += ' input-' + this.size;
18674 ['xs','sm','md','lg'].map(function(size){
18675 if (settings[size]) {
18676 cfg.cls += ' col-' + size + '-' + settings[size];
18680 var inputblock = input;
18682 if (this.before || this.after) {
18685 cls : 'input-group',
18690 inputblock.cn.push({
18692 cls : 'input-group-addon',
18697 inputblock.cn.push(input);
18700 inputblock.cn.push({
18702 cls : 'input-group-addon',
18709 if (align ==='left' && this.fieldLabel.length) {
18710 // Roo.log("left and has label");
18716 cls : 'control-label col-md-' + this.labelWidth,
18717 html : this.fieldLabel
18721 cls : "col-md-" + (12 - this.labelWidth),
18728 } else if ( this.fieldLabel.length) {
18729 // Roo.log(" label");
18733 tag: this.boxLabel ? 'span' : 'label',
18735 cls: 'control-label box-input-label',
18736 //cls : 'input-group-addon',
18737 html : this.fieldLabel
18747 // Roo.log(" no label && no align");
18748 cfg.cn = [ inputblock ] ;
18753 var boxLabelCfg = {
18755 //'for': id, // box label is handled by onclick - so no for...
18757 html: this.boxLabel
18761 boxLabelCfg.tooltip = this.tooltip;
18764 cfg.cn.push(boxLabelCfg);
18774 * return the real input element.
18776 inputEl: function ()
18778 return this.el.select('input.roo-' + this.inputType,true).first();
18781 labelEl: function()
18783 return this.el.select('label.control-label',true).first();
18785 /* depricated... */
18789 return this.labelEl();
18792 initEvents : function()
18794 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18796 this.inputEl().on('click', this.onClick, this);
18798 if (this.boxLabel) {
18799 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18802 this.startValue = this.getValue();
18805 Roo.bootstrap.CheckBox.register(this);
18809 onClick : function()
18811 this.setChecked(!this.checked);
18814 setChecked : function(state,suppressEvent)
18816 this.startValue = this.getValue();
18818 if(this.inputType == 'radio'){
18820 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18821 e.dom.checked = false;
18824 this.inputEl().dom.checked = true;
18826 this.inputEl().dom.value = this.inputValue;
18828 if(suppressEvent !== true){
18829 this.fireEvent('check', this, true);
18837 this.checked = state;
18839 this.inputEl().dom.checked = state;
18841 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18843 if(suppressEvent !== true){
18844 this.fireEvent('check', this, state);
18850 getValue : function()
18852 if(this.inputType == 'radio'){
18853 return this.getGroupValue();
18856 return this.inputEl().getValue();
18860 getGroupValue : function()
18862 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18866 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18869 setValue : function(v,suppressEvent)
18871 if(this.inputType == 'radio'){
18872 this.setGroupValue(v, suppressEvent);
18876 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18881 setGroupValue : function(v, suppressEvent)
18883 this.startValue = this.getValue();
18885 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18886 e.dom.checked = false;
18888 if(e.dom.value == v){
18889 e.dom.checked = true;
18893 if(suppressEvent !== true){
18894 this.fireEvent('check', this, true);
18902 validate : function()
18906 (this.inputType == 'radio' && this.validateRadio()) ||
18907 (this.inputType == 'checkbox' && this.validateCheckbox())
18913 this.markInvalid();
18917 validateRadio : function()
18921 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18922 if(!e.dom.checked){
18934 validateCheckbox : function()
18937 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18940 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18948 for(var i in group){
18953 r = (group[i].getValue() == group[i].inputValue) ? true : false;
18960 * Mark this field as valid
18962 markValid : function()
18964 if(this.allowBlank){
18970 this.fireEvent('valid', this);
18972 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18975 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18982 if(this.inputType == 'radio'){
18983 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18984 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18985 e.findParent('.form-group', false, true).addClass(_this.validClass);
18992 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18993 this.el.findParent('.form-group', false, true).addClass(this.validClass);
18997 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19003 for(var i in group){
19004 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19005 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19010 * Mark this field as invalid
19011 * @param {String} msg The validation message
19013 markInvalid : function(msg)
19015 if(this.allowBlank){
19021 this.fireEvent('invalid', this, msg);
19023 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19026 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19030 label.markInvalid();
19033 if(this.inputType == 'radio'){
19034 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19035 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19036 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19043 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19044 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19048 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19054 for(var i in group){
19055 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19056 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19063 Roo.apply(Roo.bootstrap.CheckBox, {
19068 * register a CheckBox Group
19069 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19071 register : function(checkbox)
19073 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19074 this.groups[checkbox.groupId] = {};
19077 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19081 this.groups[checkbox.groupId][checkbox.name] = checkbox;
19085 * fetch a CheckBox Group based on the group ID
19086 * @param {string} the group ID
19087 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19089 get: function(groupId) {
19090 if (typeof(this.groups[groupId]) == 'undefined') {
19094 return this.groups[groupId] ;
19106 *<div class="radio">
19108 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19109 Option one is this and that—be sure to include why it's great
19116 *<label class="radio-inline">fieldLabel</label>
19117 *<label class="radio-inline">
19118 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19126 * @class Roo.bootstrap.Radio
19127 * @extends Roo.bootstrap.CheckBox
19128 * Bootstrap Radio class
19131 * Create a new Radio
19132 * @param {Object} config The config object
19135 Roo.bootstrap.Radio = function(config){
19136 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19140 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
19142 inputType: 'radio',
19146 getAutoCreate : function()
19148 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19149 align = align || 'left'; // default...
19156 tag : this.inline ? 'span' : 'div',
19161 var inline = this.inline ? ' radio-inline' : '';
19165 // does not need for, as we wrap the input with it..
19167 cls : 'control-label box-label' + inline,
19170 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19174 //cls : 'control-label' + inline,
19175 html : this.fieldLabel,
19176 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19185 type : this.inputType,
19186 //value : (!this.checked) ? this.valueOff : this.inputValue,
19187 value : this.inputValue,
19189 placeholder : this.placeholder || '' // ?? needed????
19192 if (this.weight) { // Validity check?
19193 input.cls += " radio-" + this.weight;
19195 if (this.disabled) {
19196 input.disabled=true;
19200 input.checked = this.checked;
19204 input.name = this.name;
19208 input.cls += ' input-' + this.size;
19211 //?? can span's inline have a width??
19214 ['xs','sm','md','lg'].map(function(size){
19215 if (settings[size]) {
19216 cfg.cls += ' col-' + size + '-' + settings[size];
19220 var inputblock = input;
19222 if (this.before || this.after) {
19225 cls : 'input-group',
19230 inputblock.cn.push({
19232 cls : 'input-group-addon',
19236 inputblock.cn.push(input);
19238 inputblock.cn.push({
19240 cls : 'input-group-addon',
19248 if (this.fieldLabel && this.fieldLabel.length) {
19249 cfg.cn.push(fieldLabel);
19252 // normal bootstrap puts the input inside the label.
19253 // however with our styled version - it has to go after the input.
19255 //lbl.cn.push(inputblock);
19259 cls: 'radio' + inline,
19266 cfg.cn.push( lblwrap);
19271 html: this.boxLabel
19280 initEvents : function()
19282 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19284 this.inputEl().on('click', this.onClick, this);
19285 if (this.boxLabel) {
19286 //Roo.log('find label');
19287 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
19292 inputEl: function ()
19294 return this.el.select('input.roo-radio',true).first();
19296 onClick : function()
19299 this.setChecked(true);
19302 setChecked : function(state,suppressEvent)
19305 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19306 v.dom.checked = false;
19309 Roo.log(this.inputEl().dom);
19310 this.checked = state;
19311 this.inputEl().dom.checked = state;
19313 if(suppressEvent !== true){
19314 this.fireEvent('check', this, state);
19317 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19321 getGroupValue : function()
19324 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19325 if(v.dom.checked == true){
19326 value = v.dom.value;
19334 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
19335 * @return {Mixed} value The field value
19337 getValue : function(){
19338 return this.getGroupValue();
19344 //<script type="text/javascript">
19347 * Based Ext JS Library 1.1.1
19348 * Copyright(c) 2006-2007, Ext JS, LLC.
19354 * @class Roo.HtmlEditorCore
19355 * @extends Roo.Component
19356 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19358 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19361 Roo.HtmlEditorCore = function(config){
19364 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19369 * @event initialize
19370 * Fires when the editor is fully initialized (including the iframe)
19371 * @param {Roo.HtmlEditorCore} this
19376 * Fires when the editor is first receives the focus. Any insertion must wait
19377 * until after this event.
19378 * @param {Roo.HtmlEditorCore} this
19382 * @event beforesync
19383 * Fires before the textarea is updated with content from the editor iframe. Return false
19384 * to cancel the sync.
19385 * @param {Roo.HtmlEditorCore} this
19386 * @param {String} html
19390 * @event beforepush
19391 * Fires before the iframe editor is updated with content from the textarea. Return false
19392 * to cancel the push.
19393 * @param {Roo.HtmlEditorCore} this
19394 * @param {String} html
19399 * Fires when the textarea is updated with content from the editor iframe.
19400 * @param {Roo.HtmlEditorCore} this
19401 * @param {String} html
19406 * Fires when the iframe editor is updated with content from the textarea.
19407 * @param {Roo.HtmlEditorCore} this
19408 * @param {String} html
19413 * @event editorevent
19414 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19415 * @param {Roo.HtmlEditorCore} this
19421 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19423 // defaults : white / black...
19424 this.applyBlacklists();
19431 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
19435 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
19441 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
19446 * @cfg {Number} height (in pixels)
19450 * @cfg {Number} width (in pixels)
19455 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19458 stylesheets: false,
19463 // private properties
19464 validationEvent : false,
19466 initialized : false,
19468 sourceEditMode : false,
19469 onFocus : Roo.emptyFn,
19471 hideMode:'offsets',
19475 // blacklist + whitelisted elements..
19482 * Protected method that will not generally be called directly. It
19483 * is called when the editor initializes the iframe with HTML contents. Override this method if you
19484 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19486 getDocMarkup : function(){
19490 // inherit styels from page...??
19491 if (this.stylesheets === false) {
19493 Roo.get(document.head).select('style').each(function(node) {
19494 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19497 Roo.get(document.head).select('link').each(function(node) {
19498 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19501 } else if (!this.stylesheets.length) {
19503 st = '<style type="text/css">' +
19504 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19510 st += '<style type="text/css">' +
19511 'IMG { cursor: pointer } ' +
19515 return '<html><head>' + st +
19516 //<style type="text/css">' +
19517 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19519 ' </head><body class="roo-htmleditor-body"></body></html>';
19523 onRender : function(ct, position)
19526 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19527 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19530 this.el.dom.style.border = '0 none';
19531 this.el.dom.setAttribute('tabIndex', -1);
19532 this.el.addClass('x-hidden hide');
19536 if(Roo.isIE){ // fix IE 1px bogus margin
19537 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19541 this.frameId = Roo.id();
19545 var iframe = this.owner.wrap.createChild({
19547 cls: 'form-control', // bootstrap..
19549 name: this.frameId,
19550 frameBorder : 'no',
19551 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19556 this.iframe = iframe.dom;
19558 this.assignDocWin();
19560 this.doc.designMode = 'on';
19563 this.doc.write(this.getDocMarkup());
19567 var task = { // must defer to wait for browser to be ready
19569 //console.log("run task?" + this.doc.readyState);
19570 this.assignDocWin();
19571 if(this.doc.body || this.doc.readyState == 'complete'){
19573 this.doc.designMode="on";
19577 Roo.TaskMgr.stop(task);
19578 this.initEditor.defer(10, this);
19585 Roo.TaskMgr.start(task);
19590 onResize : function(w, h)
19592 Roo.log('resize: ' +w + ',' + h );
19593 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19597 if(typeof w == 'number'){
19599 this.iframe.style.width = w + 'px';
19601 if(typeof h == 'number'){
19603 this.iframe.style.height = h + 'px';
19605 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19612 * Toggles the editor between standard and source edit mode.
19613 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19615 toggleSourceEdit : function(sourceEditMode){
19617 this.sourceEditMode = sourceEditMode === true;
19619 if(this.sourceEditMode){
19621 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19624 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19625 //this.iframe.className = '';
19628 //this.setSize(this.owner.wrap.getSize());
19629 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19636 * Protected method that will not generally be called directly. If you need/want
19637 * custom HTML cleanup, this is the method you should override.
19638 * @param {String} html The HTML to be cleaned
19639 * return {String} The cleaned HTML
19641 cleanHtml : function(html){
19642 html = String(html);
19643 if(html.length > 5){
19644 if(Roo.isSafari){ // strip safari nonsense
19645 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19648 if(html == ' '){
19655 * HTML Editor -> Textarea
19656 * Protected method that will not generally be called directly. Syncs the contents
19657 * of the editor iframe with the textarea.
19659 syncValue : function(){
19660 if(this.initialized){
19661 var bd = (this.doc.body || this.doc.documentElement);
19662 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19663 var html = bd.innerHTML;
19665 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19666 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19668 html = '<div style="'+m[0]+'">' + html + '</div>';
19671 html = this.cleanHtml(html);
19672 // fix up the special chars.. normaly like back quotes in word...
19673 // however we do not want to do this with chinese..
19674 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19675 var cc = b.charCodeAt();
19677 (cc >= 0x4E00 && cc < 0xA000 ) ||
19678 (cc >= 0x3400 && cc < 0x4E00 ) ||
19679 (cc >= 0xf900 && cc < 0xfb00 )
19685 if(this.owner.fireEvent('beforesync', this, html) !== false){
19686 this.el.dom.value = html;
19687 this.owner.fireEvent('sync', this, html);
19693 * Protected method that will not generally be called directly. Pushes the value of the textarea
19694 * into the iframe editor.
19696 pushValue : function(){
19697 if(this.initialized){
19698 var v = this.el.dom.value.trim();
19700 // if(v.length < 1){
19704 if(this.owner.fireEvent('beforepush', this, v) !== false){
19705 var d = (this.doc.body || this.doc.documentElement);
19707 this.cleanUpPaste();
19708 this.el.dom.value = d.innerHTML;
19709 this.owner.fireEvent('push', this, v);
19715 deferFocus : function(){
19716 this.focus.defer(10, this);
19720 focus : function(){
19721 if(this.win && !this.sourceEditMode){
19728 assignDocWin: function()
19730 var iframe = this.iframe;
19733 this.doc = iframe.contentWindow.document;
19734 this.win = iframe.contentWindow;
19736 // if (!Roo.get(this.frameId)) {
19739 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19740 // this.win = Roo.get(this.frameId).dom.contentWindow;
19742 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19746 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19747 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19752 initEditor : function(){
19753 //console.log("INIT EDITOR");
19754 this.assignDocWin();
19758 this.doc.designMode="on";
19760 this.doc.write(this.getDocMarkup());
19763 var dbody = (this.doc.body || this.doc.documentElement);
19764 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19765 // this copies styles from the containing element into thsi one..
19766 // not sure why we need all of this..
19767 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19769 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19770 //ss['background-attachment'] = 'fixed'; // w3c
19771 dbody.bgProperties = 'fixed'; // ie
19772 //Roo.DomHelper.applyStyles(dbody, ss);
19773 Roo.EventManager.on(this.doc, {
19774 //'mousedown': this.onEditorEvent,
19775 'mouseup': this.onEditorEvent,
19776 'dblclick': this.onEditorEvent,
19777 'click': this.onEditorEvent,
19778 'keyup': this.onEditorEvent,
19783 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19785 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19786 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19788 this.initialized = true;
19790 this.owner.fireEvent('initialize', this);
19795 onDestroy : function(){
19801 //for (var i =0; i < this.toolbars.length;i++) {
19802 // // fixme - ask toolbars for heights?
19803 // this.toolbars[i].onDestroy();
19806 //this.wrap.dom.innerHTML = '';
19807 //this.wrap.remove();
19812 onFirstFocus : function(){
19814 this.assignDocWin();
19817 this.activated = true;
19820 if(Roo.isGecko){ // prevent silly gecko errors
19822 var s = this.win.getSelection();
19823 if(!s.focusNode || s.focusNode.nodeType != 3){
19824 var r = s.getRangeAt(0);
19825 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19830 this.execCmd('useCSS', true);
19831 this.execCmd('styleWithCSS', false);
19834 this.owner.fireEvent('activate', this);
19838 adjustFont: function(btn){
19839 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19840 //if(Roo.isSafari){ // safari
19843 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19844 if(Roo.isSafari){ // safari
19845 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19846 v = (v < 10) ? 10 : v;
19847 v = (v > 48) ? 48 : v;
19848 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19853 v = Math.max(1, v+adjust);
19855 this.execCmd('FontSize', v );
19858 onEditorEvent : function(e)
19860 this.owner.fireEvent('editorevent', this, e);
19861 // this.updateToolbar();
19862 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19865 insertTag : function(tg)
19867 // could be a bit smarter... -> wrap the current selected tRoo..
19868 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19870 range = this.createRange(this.getSelection());
19871 var wrappingNode = this.doc.createElement(tg.toLowerCase());
19872 wrappingNode.appendChild(range.extractContents());
19873 range.insertNode(wrappingNode);
19880 this.execCmd("formatblock", tg);
19884 insertText : function(txt)
19888 var range = this.createRange();
19889 range.deleteContents();
19890 //alert(Sender.getAttribute('label'));
19892 range.insertNode(this.doc.createTextNode(txt));
19898 * Executes a Midas editor command on the editor document and performs necessary focus and
19899 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19900 * @param {String} cmd The Midas command
19901 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19903 relayCmd : function(cmd, value){
19905 this.execCmd(cmd, value);
19906 this.owner.fireEvent('editorevent', this);
19907 //this.updateToolbar();
19908 this.owner.deferFocus();
19912 * Executes a Midas editor command directly on the editor document.
19913 * For visual commands, you should use {@link #relayCmd} instead.
19914 * <b>This should only be called after the editor is initialized.</b>
19915 * @param {String} cmd The Midas command
19916 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19918 execCmd : function(cmd, value){
19919 this.doc.execCommand(cmd, false, value === undefined ? null : value);
19926 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19928 * @param {String} text | dom node..
19930 insertAtCursor : function(text)
19935 if(!this.activated){
19941 var r = this.doc.selection.createRange();
19952 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19956 // from jquery ui (MIT licenced)
19958 var win = this.win;
19960 if (win.getSelection && win.getSelection().getRangeAt) {
19961 range = win.getSelection().getRangeAt(0);
19962 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19963 range.insertNode(node);
19964 } else if (win.document.selection && win.document.selection.createRange) {
19965 // no firefox support
19966 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19967 win.document.selection.createRange().pasteHTML(txt);
19969 // no firefox support
19970 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19971 this.execCmd('InsertHTML', txt);
19980 mozKeyPress : function(e){
19982 var c = e.getCharCode(), cmd;
19985 c = String.fromCharCode(c).toLowerCase();
19999 this.cleanUpPaste.defer(100, this);
20007 e.preventDefault();
20015 fixKeys : function(){ // load time branching for fastest keydown performance
20017 return function(e){
20018 var k = e.getKey(), r;
20021 r = this.doc.selection.createRange();
20024 r.pasteHTML('    ');
20031 r = this.doc.selection.createRange();
20033 var target = r.parentElement();
20034 if(!target || target.tagName.toLowerCase() != 'li'){
20036 r.pasteHTML('<br />');
20042 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20043 this.cleanUpPaste.defer(100, this);
20049 }else if(Roo.isOpera){
20050 return function(e){
20051 var k = e.getKey();
20055 this.execCmd('InsertHTML','    ');
20058 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20059 this.cleanUpPaste.defer(100, this);
20064 }else if(Roo.isSafari){
20065 return function(e){
20066 var k = e.getKey();
20070 this.execCmd('InsertText','\t');
20074 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20075 this.cleanUpPaste.defer(100, this);
20083 getAllAncestors: function()
20085 var p = this.getSelectedNode();
20088 a.push(p); // push blank onto stack..
20089 p = this.getParentElement();
20093 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20097 a.push(this.doc.body);
20101 lastSelNode : false,
20104 getSelection : function()
20106 this.assignDocWin();
20107 return Roo.isIE ? this.doc.selection : this.win.getSelection();
20110 getSelectedNode: function()
20112 // this may only work on Gecko!!!
20114 // should we cache this!!!!
20119 var range = this.createRange(this.getSelection()).cloneRange();
20122 var parent = range.parentElement();
20124 var testRange = range.duplicate();
20125 testRange.moveToElementText(parent);
20126 if (testRange.inRange(range)) {
20129 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20132 parent = parent.parentElement;
20137 // is ancestor a text element.
20138 var ac = range.commonAncestorContainer;
20139 if (ac.nodeType == 3) {
20140 ac = ac.parentNode;
20143 var ar = ac.childNodes;
20146 var other_nodes = [];
20147 var has_other_nodes = false;
20148 for (var i=0;i<ar.length;i++) {
20149 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
20152 // fullly contained node.
20154 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20159 // probably selected..
20160 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20161 other_nodes.push(ar[i]);
20165 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
20170 has_other_nodes = true;
20172 if (!nodes.length && other_nodes.length) {
20173 nodes= other_nodes;
20175 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20181 createRange: function(sel)
20183 // this has strange effects when using with
20184 // top toolbar - not sure if it's a great idea.
20185 //this.editor.contentWindow.focus();
20186 if (typeof sel != "undefined") {
20188 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20190 return this.doc.createRange();
20193 return this.doc.createRange();
20196 getParentElement: function()
20199 this.assignDocWin();
20200 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20202 var range = this.createRange(sel);
20205 var p = range.commonAncestorContainer;
20206 while (p.nodeType == 3) { // text node
20217 * Range intersection.. the hard stuff...
20221 * [ -- selected range --- ]
20225 * if end is before start or hits it. fail.
20226 * if start is after end or hits it fail.
20228 * if either hits (but other is outside. - then it's not
20234 // @see http://www.thismuchiknow.co.uk/?p=64.
20235 rangeIntersectsNode : function(range, node)
20237 var nodeRange = node.ownerDocument.createRange();
20239 nodeRange.selectNode(node);
20241 nodeRange.selectNodeContents(node);
20244 var rangeStartRange = range.cloneRange();
20245 rangeStartRange.collapse(true);
20247 var rangeEndRange = range.cloneRange();
20248 rangeEndRange.collapse(false);
20250 var nodeStartRange = nodeRange.cloneRange();
20251 nodeStartRange.collapse(true);
20253 var nodeEndRange = nodeRange.cloneRange();
20254 nodeEndRange.collapse(false);
20256 return rangeStartRange.compareBoundaryPoints(
20257 Range.START_TO_START, nodeEndRange) == -1 &&
20258 rangeEndRange.compareBoundaryPoints(
20259 Range.START_TO_START, nodeStartRange) == 1;
20263 rangeCompareNode : function(range, node)
20265 var nodeRange = node.ownerDocument.createRange();
20267 nodeRange.selectNode(node);
20269 nodeRange.selectNodeContents(node);
20273 range.collapse(true);
20275 nodeRange.collapse(true);
20277 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20278 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
20280 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20282 var nodeIsBefore = ss == 1;
20283 var nodeIsAfter = ee == -1;
20285 if (nodeIsBefore && nodeIsAfter) {
20288 if (!nodeIsBefore && nodeIsAfter) {
20289 return 1; //right trailed.
20292 if (nodeIsBefore && !nodeIsAfter) {
20293 return 2; // left trailed.
20299 // private? - in a new class?
20300 cleanUpPaste : function()
20302 // cleans up the whole document..
20303 Roo.log('cleanuppaste');
20305 this.cleanUpChildren(this.doc.body);
20306 var clean = this.cleanWordChars(this.doc.body.innerHTML);
20307 if (clean != this.doc.body.innerHTML) {
20308 this.doc.body.innerHTML = clean;
20313 cleanWordChars : function(input) {// change the chars to hex code
20314 var he = Roo.HtmlEditorCore;
20316 var output = input;
20317 Roo.each(he.swapCodes, function(sw) {
20318 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20320 output = output.replace(swapper, sw[1]);
20327 cleanUpChildren : function (n)
20329 if (!n.childNodes.length) {
20332 for (var i = n.childNodes.length-1; i > -1 ; i--) {
20333 this.cleanUpChild(n.childNodes[i]);
20340 cleanUpChild : function (node)
20343 //console.log(node);
20344 if (node.nodeName == "#text") {
20345 // clean up silly Windows -- stuff?
20348 if (node.nodeName == "#comment") {
20349 node.parentNode.removeChild(node);
20350 // clean up silly Windows -- stuff?
20353 var lcname = node.tagName.toLowerCase();
20354 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20355 // whitelist of tags..
20357 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20359 node.parentNode.removeChild(node);
20364 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20366 // remove <a name=....> as rendering on yahoo mailer is borked with this.
20367 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20369 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20370 // remove_keep_children = true;
20373 if (remove_keep_children) {
20374 this.cleanUpChildren(node);
20375 // inserts everything just before this node...
20376 while (node.childNodes.length) {
20377 var cn = node.childNodes[0];
20378 node.removeChild(cn);
20379 node.parentNode.insertBefore(cn, node);
20381 node.parentNode.removeChild(node);
20385 if (!node.attributes || !node.attributes.length) {
20386 this.cleanUpChildren(node);
20390 function cleanAttr(n,v)
20393 if (v.match(/^\./) || v.match(/^\//)) {
20396 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20399 if (v.match(/^#/)) {
20402 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20403 node.removeAttribute(n);
20407 var cwhite = this.cwhite;
20408 var cblack = this.cblack;
20410 function cleanStyle(n,v)
20412 if (v.match(/expression/)) { //XSS?? should we even bother..
20413 node.removeAttribute(n);
20417 var parts = v.split(/;/);
20420 Roo.each(parts, function(p) {
20421 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20425 var l = p.split(':').shift().replace(/\s+/g,'');
20426 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20428 if ( cwhite.length && cblack.indexOf(l) > -1) {
20429 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20430 //node.removeAttribute(n);
20434 // only allow 'c whitelisted system attributes'
20435 if ( cwhite.length && cwhite.indexOf(l) < 0) {
20436 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20437 //node.removeAttribute(n);
20447 if (clean.length) {
20448 node.setAttribute(n, clean.join(';'));
20450 node.removeAttribute(n);
20456 for (var i = node.attributes.length-1; i > -1 ; i--) {
20457 var a = node.attributes[i];
20460 if (a.name.toLowerCase().substr(0,2)=='on') {
20461 node.removeAttribute(a.name);
20464 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20465 node.removeAttribute(a.name);
20468 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20469 cleanAttr(a.name,a.value); // fixme..
20472 if (a.name == 'style') {
20473 cleanStyle(a.name,a.value);
20476 /// clean up MS crap..
20477 // tecnically this should be a list of valid class'es..
20480 if (a.name == 'class') {
20481 if (a.value.match(/^Mso/)) {
20482 node.className = '';
20485 if (a.value.match(/body/)) {
20486 node.className = '';
20497 this.cleanUpChildren(node);
20503 * Clean up MS wordisms...
20505 cleanWord : function(node)
20510 this.cleanWord(this.doc.body);
20513 if (node.nodeName == "#text") {
20514 // clean up silly Windows -- stuff?
20517 if (node.nodeName == "#comment") {
20518 node.parentNode.removeChild(node);
20519 // clean up silly Windows -- stuff?
20523 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20524 node.parentNode.removeChild(node);
20528 // remove - but keep children..
20529 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20530 while (node.childNodes.length) {
20531 var cn = node.childNodes[0];
20532 node.removeChild(cn);
20533 node.parentNode.insertBefore(cn, node);
20535 node.parentNode.removeChild(node);
20536 this.iterateChildren(node, this.cleanWord);
20540 if (node.className.length) {
20542 var cn = node.className.split(/\W+/);
20544 Roo.each(cn, function(cls) {
20545 if (cls.match(/Mso[a-zA-Z]+/)) {
20550 node.className = cna.length ? cna.join(' ') : '';
20552 node.removeAttribute("class");
20556 if (node.hasAttribute("lang")) {
20557 node.removeAttribute("lang");
20560 if (node.hasAttribute("style")) {
20562 var styles = node.getAttribute("style").split(";");
20564 Roo.each(styles, function(s) {
20565 if (!s.match(/:/)) {
20568 var kv = s.split(":");
20569 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20572 // what ever is left... we allow.
20575 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20576 if (!nstyle.length) {
20577 node.removeAttribute('style');
20580 this.iterateChildren(node, this.cleanWord);
20586 * iterateChildren of a Node, calling fn each time, using this as the scole..
20587 * @param {DomNode} node node to iterate children of.
20588 * @param {Function} fn method of this class to call on each item.
20590 iterateChildren : function(node, fn)
20592 if (!node.childNodes.length) {
20595 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20596 fn.call(this, node.childNodes[i])
20602 * cleanTableWidths.
20604 * Quite often pasting from word etc.. results in tables with column and widths.
20605 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20608 cleanTableWidths : function(node)
20613 this.cleanTableWidths(this.doc.body);
20618 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20621 Roo.log(node.tagName);
20622 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20623 this.iterateChildren(node, this.cleanTableWidths);
20626 if (node.hasAttribute('width')) {
20627 node.removeAttribute('width');
20631 if (node.hasAttribute("style")) {
20634 var styles = node.getAttribute("style").split(";");
20636 Roo.each(styles, function(s) {
20637 if (!s.match(/:/)) {
20640 var kv = s.split(":");
20641 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20644 // what ever is left... we allow.
20647 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20648 if (!nstyle.length) {
20649 node.removeAttribute('style');
20653 this.iterateChildren(node, this.cleanTableWidths);
20661 domToHTML : function(currentElement, depth, nopadtext) {
20663 depth = depth || 0;
20664 nopadtext = nopadtext || false;
20666 if (!currentElement) {
20667 return this.domToHTML(this.doc.body);
20670 //Roo.log(currentElement);
20672 var allText = false;
20673 var nodeName = currentElement.nodeName;
20674 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20676 if (nodeName == '#text') {
20678 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20683 if (nodeName != 'BODY') {
20686 // Prints the node tagName, such as <A>, <IMG>, etc
20689 for(i = 0; i < currentElement.attributes.length;i++) {
20691 var aname = currentElement.attributes.item(i).name;
20692 if (!currentElement.attributes.item(i).value.length) {
20695 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20698 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20707 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20710 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20715 // Traverse the tree
20717 var currentElementChild = currentElement.childNodes.item(i);
20718 var allText = true;
20719 var innerHTML = '';
20721 while (currentElementChild) {
20722 // Formatting code (indent the tree so it looks nice on the screen)
20723 var nopad = nopadtext;
20724 if (lastnode == 'SPAN') {
20728 if (currentElementChild.nodeName == '#text') {
20729 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20730 toadd = nopadtext ? toadd : toadd.trim();
20731 if (!nopad && toadd.length > 80) {
20732 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20734 innerHTML += toadd;
20737 currentElementChild = currentElement.childNodes.item(i);
20743 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20745 // Recursively traverse the tree structure of the child node
20746 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20747 lastnode = currentElementChild.nodeName;
20749 currentElementChild=currentElement.childNodes.item(i);
20755 // The remaining code is mostly for formatting the tree
20756 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20761 ret+= "</"+tagName+">";
20767 applyBlacklists : function()
20769 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20770 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20774 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20775 if (b.indexOf(tag) > -1) {
20778 this.white.push(tag);
20782 Roo.each(w, function(tag) {
20783 if (b.indexOf(tag) > -1) {
20786 if (this.white.indexOf(tag) > -1) {
20789 this.white.push(tag);
20794 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20795 if (w.indexOf(tag) > -1) {
20798 this.black.push(tag);
20802 Roo.each(b, function(tag) {
20803 if (w.indexOf(tag) > -1) {
20806 if (this.black.indexOf(tag) > -1) {
20809 this.black.push(tag);
20814 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20815 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20819 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20820 if (b.indexOf(tag) > -1) {
20823 this.cwhite.push(tag);
20827 Roo.each(w, function(tag) {
20828 if (b.indexOf(tag) > -1) {
20831 if (this.cwhite.indexOf(tag) > -1) {
20834 this.cwhite.push(tag);
20839 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20840 if (w.indexOf(tag) > -1) {
20843 this.cblack.push(tag);
20847 Roo.each(b, function(tag) {
20848 if (w.indexOf(tag) > -1) {
20851 if (this.cblack.indexOf(tag) > -1) {
20854 this.cblack.push(tag);
20859 setStylesheets : function(stylesheets)
20861 if(typeof(stylesheets) == 'string'){
20862 Roo.get(this.iframe.contentDocument.head).createChild({
20864 rel : 'stylesheet',
20873 Roo.each(stylesheets, function(s) {
20878 Roo.get(_this.iframe.contentDocument.head).createChild({
20880 rel : 'stylesheet',
20889 removeStylesheets : function()
20893 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20898 // hide stuff that is not compatible
20912 * @event specialkey
20916 * @cfg {String} fieldClass @hide
20919 * @cfg {String} focusClass @hide
20922 * @cfg {String} autoCreate @hide
20925 * @cfg {String} inputType @hide
20928 * @cfg {String} invalidClass @hide
20931 * @cfg {String} invalidText @hide
20934 * @cfg {String} msgFx @hide
20937 * @cfg {String} validateOnBlur @hide
20941 Roo.HtmlEditorCore.white = [
20942 'area', 'br', 'img', 'input', 'hr', 'wbr',
20944 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
20945 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
20946 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
20947 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
20948 'table', 'ul', 'xmp',
20950 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
20953 'dir', 'menu', 'ol', 'ul', 'dl',
20959 Roo.HtmlEditorCore.black = [
20960 // 'embed', 'object', // enable - backend responsiblity to clean thiese
20962 'base', 'basefont', 'bgsound', 'blink', 'body',
20963 'frame', 'frameset', 'head', 'html', 'ilayer',
20964 'iframe', 'layer', 'link', 'meta', 'object',
20965 'script', 'style' ,'title', 'xml' // clean later..
20967 Roo.HtmlEditorCore.clean = [
20968 'script', 'style', 'title', 'xml'
20970 Roo.HtmlEditorCore.remove = [
20975 Roo.HtmlEditorCore.ablack = [
20979 Roo.HtmlEditorCore.aclean = [
20980 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
20984 Roo.HtmlEditorCore.pwhite= [
20985 'http', 'https', 'mailto'
20988 // white listed style attributes.
20989 Roo.HtmlEditorCore.cwhite= [
20990 // 'text-align', /// default is to allow most things..
20996 // black listed style attributes.
20997 Roo.HtmlEditorCore.cblack= [
20998 // 'font-size' -- this can be set by the project
21002 Roo.HtmlEditorCore.swapCodes =[
21021 * @class Roo.bootstrap.HtmlEditor
21022 * @extends Roo.bootstrap.TextArea
21023 * Bootstrap HtmlEditor class
21026 * Create a new HtmlEditor
21027 * @param {Object} config The config object
21030 Roo.bootstrap.HtmlEditor = function(config){
21031 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21032 if (!this.toolbars) {
21033 this.toolbars = [];
21035 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21038 * @event initialize
21039 * Fires when the editor is fully initialized (including the iframe)
21040 * @param {HtmlEditor} this
21045 * Fires when the editor is first receives the focus. Any insertion must wait
21046 * until after this event.
21047 * @param {HtmlEditor} this
21051 * @event beforesync
21052 * Fires before the textarea is updated with content from the editor iframe. Return false
21053 * to cancel the sync.
21054 * @param {HtmlEditor} this
21055 * @param {String} html
21059 * @event beforepush
21060 * Fires before the iframe editor is updated with content from the textarea. Return false
21061 * to cancel the push.
21062 * @param {HtmlEditor} this
21063 * @param {String} html
21068 * Fires when the textarea is updated with content from the editor iframe.
21069 * @param {HtmlEditor} this
21070 * @param {String} html
21075 * Fires when the iframe editor is updated with content from the textarea.
21076 * @param {HtmlEditor} this
21077 * @param {String} html
21081 * @event editmodechange
21082 * Fires when the editor switches edit modes
21083 * @param {HtmlEditor} this
21084 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21086 editmodechange: true,
21088 * @event editorevent
21089 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21090 * @param {HtmlEditor} this
21094 * @event firstfocus
21095 * Fires when on first focus - needed by toolbars..
21096 * @param {HtmlEditor} this
21101 * Auto save the htmlEditor value as a file into Events
21102 * @param {HtmlEditor} this
21106 * @event savedpreview
21107 * preview the saved version of htmlEditor
21108 * @param {HtmlEditor} this
21115 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
21119 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21124 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21129 * @cfg {Number} height (in pixels)
21133 * @cfg {Number} width (in pixels)
21138 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21141 stylesheets: false,
21146 // private properties
21147 validationEvent : false,
21149 initialized : false,
21152 onFocus : Roo.emptyFn,
21154 hideMode:'offsets',
21157 tbContainer : false,
21159 toolbarContainer :function() {
21160 return this.wrap.select('.x-html-editor-tb',true).first();
21164 * Protected method that will not generally be called directly. It
21165 * is called when the editor creates its toolbar. Override this method if you need to
21166 * add custom toolbar buttons.
21167 * @param {HtmlEditor} editor
21169 createToolbar : function(){
21171 Roo.log("create toolbars");
21173 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21174 this.toolbars[0].render(this.toolbarContainer());
21178 // if (!editor.toolbars || !editor.toolbars.length) {
21179 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21182 // for (var i =0 ; i < editor.toolbars.length;i++) {
21183 // editor.toolbars[i] = Roo.factory(
21184 // typeof(editor.toolbars[i]) == 'string' ?
21185 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
21186 // Roo.bootstrap.HtmlEditor);
21187 // editor.toolbars[i].init(editor);
21193 onRender : function(ct, position)
21195 // Roo.log("Call onRender: " + this.xtype);
21197 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21199 this.wrap = this.inputEl().wrap({
21200 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21203 this.editorcore.onRender(ct, position);
21205 if (this.resizable) {
21206 this.resizeEl = new Roo.Resizable(this.wrap, {
21210 minHeight : this.height,
21211 height: this.height,
21212 handles : this.resizable,
21215 resize : function(r, w, h) {
21216 _t.onResize(w,h); // -something
21222 this.createToolbar(this);
21225 if(!this.width && this.resizable){
21226 this.setSize(this.wrap.getSize());
21228 if (this.resizeEl) {
21229 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21230 // should trigger onReize..
21236 onResize : function(w, h)
21238 Roo.log('resize: ' +w + ',' + h );
21239 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21243 if(this.inputEl() ){
21244 if(typeof w == 'number'){
21245 var aw = w - this.wrap.getFrameWidth('lr');
21246 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21249 if(typeof h == 'number'){
21250 var tbh = -11; // fixme it needs to tool bar size!
21251 for (var i =0; i < this.toolbars.length;i++) {
21252 // fixme - ask toolbars for heights?
21253 tbh += this.toolbars[i].el.getHeight();
21254 //if (this.toolbars[i].footer) {
21255 // tbh += this.toolbars[i].footer.el.getHeight();
21263 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21264 ah -= 5; // knock a few pixes off for look..
21265 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21269 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21270 this.editorcore.onResize(ew,eh);
21275 * Toggles the editor between standard and source edit mode.
21276 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21278 toggleSourceEdit : function(sourceEditMode)
21280 this.editorcore.toggleSourceEdit(sourceEditMode);
21282 if(this.editorcore.sourceEditMode){
21283 Roo.log('editor - showing textarea');
21286 // Roo.log(this.syncValue());
21288 this.inputEl().removeClass(['hide', 'x-hidden']);
21289 this.inputEl().dom.removeAttribute('tabIndex');
21290 this.inputEl().focus();
21292 Roo.log('editor - hiding textarea');
21294 // Roo.log(this.pushValue());
21297 this.inputEl().addClass(['hide', 'x-hidden']);
21298 this.inputEl().dom.setAttribute('tabIndex', -1);
21299 //this.deferFocus();
21302 if(this.resizable){
21303 this.setSize(this.wrap.getSize());
21306 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21309 // private (for BoxComponent)
21310 adjustSize : Roo.BoxComponent.prototype.adjustSize,
21312 // private (for BoxComponent)
21313 getResizeEl : function(){
21317 // private (for BoxComponent)
21318 getPositionEl : function(){
21323 initEvents : function(){
21324 this.originalValue = this.getValue();
21328 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21331 // markInvalid : Roo.emptyFn,
21333 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21336 // clearInvalid : Roo.emptyFn,
21338 setValue : function(v){
21339 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21340 this.editorcore.pushValue();
21345 deferFocus : function(){
21346 this.focus.defer(10, this);
21350 focus : function(){
21351 this.editorcore.focus();
21357 onDestroy : function(){
21363 for (var i =0; i < this.toolbars.length;i++) {
21364 // fixme - ask toolbars for heights?
21365 this.toolbars[i].onDestroy();
21368 this.wrap.dom.innerHTML = '';
21369 this.wrap.remove();
21374 onFirstFocus : function(){
21375 //Roo.log("onFirstFocus");
21376 this.editorcore.onFirstFocus();
21377 for (var i =0; i < this.toolbars.length;i++) {
21378 this.toolbars[i].onFirstFocus();
21384 syncValue : function()
21386 this.editorcore.syncValue();
21389 pushValue : function()
21391 this.editorcore.pushValue();
21395 // hide stuff that is not compatible
21409 * @event specialkey
21413 * @cfg {String} fieldClass @hide
21416 * @cfg {String} focusClass @hide
21419 * @cfg {String} autoCreate @hide
21422 * @cfg {String} inputType @hide
21425 * @cfg {String} invalidClass @hide
21428 * @cfg {String} invalidText @hide
21431 * @cfg {String} msgFx @hide
21434 * @cfg {String} validateOnBlur @hide
21443 Roo.namespace('Roo.bootstrap.htmleditor');
21445 * @class Roo.bootstrap.HtmlEditorToolbar1
21450 new Roo.bootstrap.HtmlEditor({
21453 new Roo.bootstrap.HtmlEditorToolbar1({
21454 disable : { fonts: 1 , format: 1, ..., ... , ...],
21460 * @cfg {Object} disable List of elements to disable..
21461 * @cfg {Array} btns List of additional buttons.
21465 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21468 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21471 Roo.apply(this, config);
21473 // default disabled, based on 'good practice'..
21474 this.disable = this.disable || {};
21475 Roo.applyIf(this.disable, {
21478 specialElements : true
21480 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21482 this.editor = config.editor;
21483 this.editorcore = config.editor.editorcore;
21485 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21487 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21488 // dont call parent... till later.
21490 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
21495 editorcore : false,
21500 "h1","h2","h3","h4","h5","h6",
21502 "abbr", "acronym", "address", "cite", "samp", "var",
21506 onRender : function(ct, position)
21508 // Roo.log("Call onRender: " + this.xtype);
21510 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21512 this.el.dom.style.marginBottom = '0';
21514 var editorcore = this.editorcore;
21515 var editor= this.editor;
21518 var btn = function(id,cmd , toggle, handler){
21520 var event = toggle ? 'toggle' : 'click';
21525 xns: Roo.bootstrap,
21528 enableToggle:toggle !== false,
21530 pressed : toggle ? false : null,
21533 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21534 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21543 xns: Roo.bootstrap,
21544 glyphicon : 'font',
21548 xns: Roo.bootstrap,
21552 Roo.each(this.formats, function(f) {
21553 style.menu.items.push({
21555 xns: Roo.bootstrap,
21556 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21561 editorcore.insertTag(this.tagname);
21568 children.push(style);
21571 btn('bold',false,true);
21572 btn('italic',false,true);
21573 btn('align-left', 'justifyleft',true);
21574 btn('align-center', 'justifycenter',true);
21575 btn('align-right' , 'justifyright',true);
21576 btn('link', false, false, function(btn) {
21577 //Roo.log("create link?");
21578 var url = prompt(this.createLinkText, this.defaultLinkValue);
21579 if(url && url != 'http:/'+'/'){
21580 this.editorcore.relayCmd('createlink', url);
21583 btn('list','insertunorderedlist',true);
21584 btn('pencil', false,true, function(btn){
21587 this.toggleSourceEdit(btn.pressed);
21593 xns: Roo.bootstrap,
21598 xns: Roo.bootstrap,
21603 cog.menu.items.push({
21605 xns: Roo.bootstrap,
21606 html : Clean styles,
21611 editorcore.insertTag(this.tagname);
21620 this.xtype = 'NavSimplebar';
21622 for(var i=0;i< children.length;i++) {
21624 this.buttons.add(this.addxtypeChild(children[i]));
21628 editor.on('editorevent', this.updateToolbar, this);
21630 onBtnClick : function(id)
21632 this.editorcore.relayCmd(id);
21633 this.editorcore.focus();
21637 * Protected method that will not generally be called directly. It triggers
21638 * a toolbar update by reading the markup state of the current selection in the editor.
21640 updateToolbar: function(){
21642 if(!this.editorcore.activated){
21643 this.editor.onFirstFocus(); // is this neeed?
21647 var btns = this.buttons;
21648 var doc = this.editorcore.doc;
21649 btns.get('bold').setActive(doc.queryCommandState('bold'));
21650 btns.get('italic').setActive(doc.queryCommandState('italic'));
21651 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21653 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21654 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21655 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21657 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21658 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21661 var ans = this.editorcore.getAllAncestors();
21662 if (this.formatCombo) {
21665 var store = this.formatCombo.store;
21666 this.formatCombo.setValue("");
21667 for (var i =0; i < ans.length;i++) {
21668 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21670 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21678 // hides menus... - so this cant be on a menu...
21679 Roo.bootstrap.MenuMgr.hideAll();
21681 Roo.bootstrap.MenuMgr.hideAll();
21682 //this.editorsyncValue();
21684 onFirstFocus: function() {
21685 this.buttons.each(function(item){
21689 toggleSourceEdit : function(sourceEditMode){
21692 if(sourceEditMode){
21693 Roo.log("disabling buttons");
21694 this.buttons.each( function(item){
21695 if(item.cmd != 'pencil'){
21701 Roo.log("enabling buttons");
21702 if(this.editorcore.initialized){
21703 this.buttons.each( function(item){
21709 Roo.log("calling toggole on editor");
21710 // tell the editor that it's been pressed..
21711 this.editor.toggleSourceEdit(sourceEditMode);
21721 * @class Roo.bootstrap.Table.AbstractSelectionModel
21722 * @extends Roo.util.Observable
21723 * Abstract base class for grid SelectionModels. It provides the interface that should be
21724 * implemented by descendant classes. This class should not be directly instantiated.
21727 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21728 this.locked = false;
21729 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21733 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21734 /** @ignore Called by the grid automatically. Do not call directly. */
21735 init : function(grid){
21741 * Locks the selections.
21744 this.locked = true;
21748 * Unlocks the selections.
21750 unlock : function(){
21751 this.locked = false;
21755 * Returns true if the selections are locked.
21756 * @return {Boolean}
21758 isLocked : function(){
21759 return this.locked;
21763 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21764 * @class Roo.bootstrap.Table.RowSelectionModel
21765 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21766 * It supports multiple selections and keyboard selection/navigation.
21768 * @param {Object} config
21771 Roo.bootstrap.Table.RowSelectionModel = function(config){
21772 Roo.apply(this, config);
21773 this.selections = new Roo.util.MixedCollection(false, function(o){
21778 this.lastActive = false;
21782 * @event selectionchange
21783 * Fires when the selection changes
21784 * @param {SelectionModel} this
21786 "selectionchange" : true,
21788 * @event afterselectionchange
21789 * Fires after the selection changes (eg. by key press or clicking)
21790 * @param {SelectionModel} this
21792 "afterselectionchange" : true,
21794 * @event beforerowselect
21795 * Fires when a row is selected being selected, return false to cancel.
21796 * @param {SelectionModel} this
21797 * @param {Number} rowIndex The selected index
21798 * @param {Boolean} keepExisting False if other selections will be cleared
21800 "beforerowselect" : true,
21803 * Fires when a row is selected.
21804 * @param {SelectionModel} this
21805 * @param {Number} rowIndex The selected index
21806 * @param {Roo.data.Record} r The record
21808 "rowselect" : true,
21810 * @event rowdeselect
21811 * Fires when a row is deselected.
21812 * @param {SelectionModel} this
21813 * @param {Number} rowIndex The selected index
21815 "rowdeselect" : true
21817 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21818 this.locked = false;
21821 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21823 * @cfg {Boolean} singleSelect
21824 * True to allow selection of only one row at a time (defaults to false)
21826 singleSelect : false,
21829 initEvents : function(){
21831 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21832 this.grid.on("mousedown", this.handleMouseDown, this);
21833 }else{ // allow click to work like normal
21834 this.grid.on("rowclick", this.handleDragableRowClick, this);
21837 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21838 "up" : function(e){
21840 this.selectPrevious(e.shiftKey);
21841 }else if(this.last !== false && this.lastActive !== false){
21842 var last = this.last;
21843 this.selectRange(this.last, this.lastActive-1);
21844 this.grid.getView().focusRow(this.lastActive);
21845 if(last !== false){
21849 this.selectFirstRow();
21851 this.fireEvent("afterselectionchange", this);
21853 "down" : function(e){
21855 this.selectNext(e.shiftKey);
21856 }else if(this.last !== false && this.lastActive !== false){
21857 var last = this.last;
21858 this.selectRange(this.last, this.lastActive+1);
21859 this.grid.getView().focusRow(this.lastActive);
21860 if(last !== false){
21864 this.selectFirstRow();
21866 this.fireEvent("afterselectionchange", this);
21871 var view = this.grid.view;
21872 view.on("refresh", this.onRefresh, this);
21873 view.on("rowupdated", this.onRowUpdated, this);
21874 view.on("rowremoved", this.onRemove, this);
21878 onRefresh : function(){
21879 var ds = this.grid.dataSource, i, v = this.grid.view;
21880 var s = this.selections;
21881 s.each(function(r){
21882 if((i = ds.indexOfId(r.id)) != -1){
21891 onRemove : function(v, index, r){
21892 this.selections.remove(r);
21896 onRowUpdated : function(v, index, r){
21897 if(this.isSelected(r)){
21898 v.onRowSelect(index);
21904 * @param {Array} records The records to select
21905 * @param {Boolean} keepExisting (optional) True to keep existing selections
21907 selectRecords : function(records, keepExisting){
21909 this.clearSelections();
21911 var ds = this.grid.dataSource;
21912 for(var i = 0, len = records.length; i < len; i++){
21913 this.selectRow(ds.indexOf(records[i]), true);
21918 * Gets the number of selected rows.
21921 getCount : function(){
21922 return this.selections.length;
21926 * Selects the first row in the grid.
21928 selectFirstRow : function(){
21933 * Select the last row.
21934 * @param {Boolean} keepExisting (optional) True to keep existing selections
21936 selectLastRow : function(keepExisting){
21937 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21941 * Selects the row immediately following the last selected row.
21942 * @param {Boolean} keepExisting (optional) True to keep existing selections
21944 selectNext : function(keepExisting){
21945 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21946 this.selectRow(this.last+1, keepExisting);
21947 this.grid.getView().focusRow(this.last);
21952 * Selects the row that precedes the last selected row.
21953 * @param {Boolean} keepExisting (optional) True to keep existing selections
21955 selectPrevious : function(keepExisting){
21957 this.selectRow(this.last-1, keepExisting);
21958 this.grid.getView().focusRow(this.last);
21963 * Returns the selected records
21964 * @return {Array} Array of selected records
21966 getSelections : function(){
21967 return [].concat(this.selections.items);
21971 * Returns the first selected record.
21974 getSelected : function(){
21975 return this.selections.itemAt(0);
21980 * Clears all selections.
21982 clearSelections : function(fast){
21987 var ds = this.grid.dataSource;
21988 var s = this.selections;
21989 s.each(function(r){
21990 this.deselectRow(ds.indexOfId(r.id));
21994 this.selections.clear();
22001 * Selects all rows.
22003 selectAll : function(){
22007 this.selections.clear();
22008 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
22009 this.selectRow(i, true);
22014 * Returns True if there is a selection.
22015 * @return {Boolean}
22017 hasSelection : function(){
22018 return this.selections.length > 0;
22022 * Returns True if the specified row is selected.
22023 * @param {Number/Record} record The record or index of the record to check
22024 * @return {Boolean}
22026 isSelected : function(index){
22027 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
22028 return (r && this.selections.key(r.id) ? true : false);
22032 * Returns True if the specified record id is selected.
22033 * @param {String} id The id of record to check
22034 * @return {Boolean}
22036 isIdSelected : function(id){
22037 return (this.selections.key(id) ? true : false);
22041 handleMouseDown : function(e, t){
22042 var view = this.grid.getView(), rowIndex;
22043 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
22046 if(e.shiftKey && this.last !== false){
22047 var last = this.last;
22048 this.selectRange(last, rowIndex, e.ctrlKey);
22049 this.last = last; // reset the last
22050 view.focusRow(rowIndex);
22052 var isSelected = this.isSelected(rowIndex);
22053 if(e.button !== 0 && isSelected){
22054 view.focusRow(rowIndex);
22055 }else if(e.ctrlKey && isSelected){
22056 this.deselectRow(rowIndex);
22057 }else if(!isSelected){
22058 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22059 view.focusRow(rowIndex);
22062 this.fireEvent("afterselectionchange", this);
22065 handleDragableRowClick : function(grid, rowIndex, e)
22067 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22068 this.selectRow(rowIndex, false);
22069 grid.view.focusRow(rowIndex);
22070 this.fireEvent("afterselectionchange", this);
22075 * Selects multiple rows.
22076 * @param {Array} rows Array of the indexes of the row to select
22077 * @param {Boolean} keepExisting (optional) True to keep existing selections
22079 selectRows : function(rows, keepExisting){
22081 this.clearSelections();
22083 for(var i = 0, len = rows.length; i < len; i++){
22084 this.selectRow(rows[i], true);
22089 * Selects a range of rows. All rows in between startRow and endRow are also selected.
22090 * @param {Number} startRow The index of the first row in the range
22091 * @param {Number} endRow The index of the last row in the range
22092 * @param {Boolean} keepExisting (optional) True to retain existing selections
22094 selectRange : function(startRow, endRow, keepExisting){
22099 this.clearSelections();
22101 if(startRow <= endRow){
22102 for(var i = startRow; i <= endRow; i++){
22103 this.selectRow(i, true);
22106 for(var i = startRow; i >= endRow; i--){
22107 this.selectRow(i, true);
22113 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22114 * @param {Number} startRow The index of the first row in the range
22115 * @param {Number} endRow The index of the last row in the range
22117 deselectRange : function(startRow, endRow, preventViewNotify){
22121 for(var i = startRow; i <= endRow; i++){
22122 this.deselectRow(i, preventViewNotify);
22128 * @param {Number} row The index of the row to select
22129 * @param {Boolean} keepExisting (optional) True to keep existing selections
22131 selectRow : function(index, keepExisting, preventViewNotify){
22132 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22135 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22136 if(!keepExisting || this.singleSelect){
22137 this.clearSelections();
22139 var r = this.grid.dataSource.getAt(index);
22140 this.selections.add(r);
22141 this.last = this.lastActive = index;
22142 if(!preventViewNotify){
22143 this.grid.getView().onRowSelect(index);
22145 this.fireEvent("rowselect", this, index, r);
22146 this.fireEvent("selectionchange", this);
22152 * @param {Number} row The index of the row to deselect
22154 deselectRow : function(index, preventViewNotify){
22158 if(this.last == index){
22161 if(this.lastActive == index){
22162 this.lastActive = false;
22164 var r = this.grid.dataSource.getAt(index);
22165 this.selections.remove(r);
22166 if(!preventViewNotify){
22167 this.grid.getView().onRowDeselect(index);
22169 this.fireEvent("rowdeselect", this, index);
22170 this.fireEvent("selectionchange", this);
22174 restoreLast : function(){
22176 this.last = this._last;
22181 acceptsNav : function(row, col, cm){
22182 return !cm.isHidden(col) && cm.isCellEditable(col, row);
22186 onEditorKey : function(field, e){
22187 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22192 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22194 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22196 }else if(k == e.ENTER && !e.ctrlKey){
22200 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22202 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22204 }else if(k == e.ESC){
22208 g.startEditing(newCell[0], newCell[1]);
22213 * Ext JS Library 1.1.1
22214 * Copyright(c) 2006-2007, Ext JS, LLC.
22216 * Originally Released Under LGPL - original licence link has changed is not relivant.
22219 * <script type="text/javascript">
22223 * @class Roo.bootstrap.PagingToolbar
22224 * @extends Roo.bootstrap.NavSimplebar
22225 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22227 * Create a new PagingToolbar
22228 * @param {Object} config The config object
22229 * @param {Roo.data.Store} store
22231 Roo.bootstrap.PagingToolbar = function(config)
22233 // old args format still supported... - xtype is prefered..
22234 // created from xtype...
22236 this.ds = config.dataSource;
22238 if (config.store && !this.ds) {
22239 this.store= Roo.factory(config.store, Roo.data);
22240 this.ds = this.store;
22241 this.ds.xmodule = this.xmodule || false;
22244 this.toolbarItems = [];
22245 if (config.items) {
22246 this.toolbarItems = config.items;
22249 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22254 this.bind(this.ds);
22257 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22261 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22263 * @cfg {Roo.data.Store} dataSource
22264 * The underlying data store providing the paged data
22267 * @cfg {String/HTMLElement/Element} container
22268 * container The id or element that will contain the toolbar
22271 * @cfg {Boolean} displayInfo
22272 * True to display the displayMsg (defaults to false)
22275 * @cfg {Number} pageSize
22276 * The number of records to display per page (defaults to 20)
22280 * @cfg {String} displayMsg
22281 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22283 displayMsg : 'Displaying {0} - {1} of {2}',
22285 * @cfg {String} emptyMsg
22286 * The message to display when no records are found (defaults to "No data to display")
22288 emptyMsg : 'No data to display',
22290 * Customizable piece of the default paging text (defaults to "Page")
22293 beforePageText : "Page",
22295 * Customizable piece of the default paging text (defaults to "of %0")
22298 afterPageText : "of {0}",
22300 * Customizable piece of the default paging text (defaults to "First Page")
22303 firstText : "First Page",
22305 * Customizable piece of the default paging text (defaults to "Previous Page")
22308 prevText : "Previous Page",
22310 * Customizable piece of the default paging text (defaults to "Next Page")
22313 nextText : "Next Page",
22315 * Customizable piece of the default paging text (defaults to "Last Page")
22318 lastText : "Last Page",
22320 * Customizable piece of the default paging text (defaults to "Refresh")
22323 refreshText : "Refresh",
22327 onRender : function(ct, position)
22329 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22330 this.navgroup.parentId = this.id;
22331 this.navgroup.onRender(this.el, null);
22332 // add the buttons to the navgroup
22334 if(this.displayInfo){
22335 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22336 this.displayEl = this.el.select('.x-paging-info', true).first();
22337 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22338 // this.displayEl = navel.el.select('span',true).first();
22344 Roo.each(_this.buttons, function(e){ // this might need to use render????
22345 Roo.factory(e).onRender(_this.el, null);
22349 Roo.each(_this.toolbarItems, function(e) {
22350 _this.navgroup.addItem(e);
22354 this.first = this.navgroup.addItem({
22355 tooltip: this.firstText,
22357 icon : 'fa fa-backward',
22359 preventDefault: true,
22360 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22363 this.prev = this.navgroup.addItem({
22364 tooltip: this.prevText,
22366 icon : 'fa fa-step-backward',
22368 preventDefault: true,
22369 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
22371 //this.addSeparator();
22374 var field = this.navgroup.addItem( {
22376 cls : 'x-paging-position',
22378 html : this.beforePageText +
22379 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22380 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
22383 this.field = field.el.select('input', true).first();
22384 this.field.on("keydown", this.onPagingKeydown, this);
22385 this.field.on("focus", function(){this.dom.select();});
22388 this.afterTextEl = field.el.select('.x-paging-after',true).first();
22389 //this.field.setHeight(18);
22390 //this.addSeparator();
22391 this.next = this.navgroup.addItem({
22392 tooltip: this.nextText,
22394 html : ' <i class="fa fa-step-forward">',
22396 preventDefault: true,
22397 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
22399 this.last = this.navgroup.addItem({
22400 tooltip: this.lastText,
22401 icon : 'fa fa-forward',
22404 preventDefault: true,
22405 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
22407 //this.addSeparator();
22408 this.loading = this.navgroup.addItem({
22409 tooltip: this.refreshText,
22410 icon: 'fa fa-refresh',
22411 preventDefault: true,
22412 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22418 updateInfo : function(){
22419 if(this.displayEl){
22420 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22421 var msg = count == 0 ?
22425 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
22427 this.displayEl.update(msg);
22432 onLoad : function(ds, r, o){
22433 this.cursor = o.params ? o.params.start : 0;
22434 var d = this.getPageData(),
22438 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22439 this.field.dom.value = ap;
22440 this.first.setDisabled(ap == 1);
22441 this.prev.setDisabled(ap == 1);
22442 this.next.setDisabled(ap == ps);
22443 this.last.setDisabled(ap == ps);
22444 this.loading.enable();
22449 getPageData : function(){
22450 var total = this.ds.getTotalCount();
22453 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22454 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22459 onLoadError : function(){
22460 this.loading.enable();
22464 onPagingKeydown : function(e){
22465 var k = e.getKey();
22466 var d = this.getPageData();
22468 var v = this.field.dom.value, pageNum;
22469 if(!v || isNaN(pageNum = parseInt(v, 10))){
22470 this.field.dom.value = d.activePage;
22473 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22474 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22477 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))
22479 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22480 this.field.dom.value = pageNum;
22481 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22484 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22486 var v = this.field.dom.value, pageNum;
22487 var increment = (e.shiftKey) ? 10 : 1;
22488 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22491 if(!v || isNaN(pageNum = parseInt(v, 10))) {
22492 this.field.dom.value = d.activePage;
22495 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22497 this.field.dom.value = parseInt(v, 10) + increment;
22498 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22499 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22506 beforeLoad : function(){
22508 this.loading.disable();
22513 onClick : function(which){
22522 ds.load({params:{start: 0, limit: this.pageSize}});
22525 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22528 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22531 var total = ds.getTotalCount();
22532 var extra = total % this.pageSize;
22533 var lastStart = extra ? (total - extra) : total-this.pageSize;
22534 ds.load({params:{start: lastStart, limit: this.pageSize}});
22537 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22543 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22544 * @param {Roo.data.Store} store The data store to unbind
22546 unbind : function(ds){
22547 ds.un("beforeload", this.beforeLoad, this);
22548 ds.un("load", this.onLoad, this);
22549 ds.un("loadexception", this.onLoadError, this);
22550 ds.un("remove", this.updateInfo, this);
22551 ds.un("add", this.updateInfo, this);
22552 this.ds = undefined;
22556 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22557 * @param {Roo.data.Store} store The data store to bind
22559 bind : function(ds){
22560 ds.on("beforeload", this.beforeLoad, this);
22561 ds.on("load", this.onLoad, this);
22562 ds.on("loadexception", this.onLoadError, this);
22563 ds.on("remove", this.updateInfo, this);
22564 ds.on("add", this.updateInfo, this);
22575 * @class Roo.bootstrap.MessageBar
22576 * @extends Roo.bootstrap.Component
22577 * Bootstrap MessageBar class
22578 * @cfg {String} html contents of the MessageBar
22579 * @cfg {String} weight (info | success | warning | danger) default info
22580 * @cfg {String} beforeClass insert the bar before the given class
22581 * @cfg {Boolean} closable (true | false) default false
22582 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22585 * Create a new Element
22586 * @param {Object} config The config object
22589 Roo.bootstrap.MessageBar = function(config){
22590 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22593 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22599 beforeClass: 'bootstrap-sticky-wrap',
22601 getAutoCreate : function(){
22605 cls: 'alert alert-dismissable alert-' + this.weight,
22610 html: this.html || ''
22616 cfg.cls += ' alert-messages-fixed';
22630 onRender : function(ct, position)
22632 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22635 var cfg = Roo.apply({}, this.getAutoCreate());
22639 cfg.cls += ' ' + this.cls;
22642 cfg.style = this.style;
22644 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22646 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22649 this.el.select('>button.close').on('click', this.hide, this);
22655 if (!this.rendered) {
22661 this.fireEvent('show', this);
22667 if (!this.rendered) {
22673 this.fireEvent('hide', this);
22676 update : function()
22678 // var e = this.el.dom.firstChild;
22680 // if(this.closable){
22681 // e = e.nextSibling;
22684 // e.data = this.html || '';
22686 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22702 * @class Roo.bootstrap.Graph
22703 * @extends Roo.bootstrap.Component
22704 * Bootstrap Graph class
22708 @cfg {String} graphtype bar | vbar | pie
22709 @cfg {number} g_x coodinator | centre x (pie)
22710 @cfg {number} g_y coodinator | centre y (pie)
22711 @cfg {number} g_r radius (pie)
22712 @cfg {number} g_height height of the chart (respected by all elements in the set)
22713 @cfg {number} g_width width of the chart (respected by all elements in the set)
22714 @cfg {Object} title The title of the chart
22717 -opts (object) options for the chart
22719 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22720 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22722 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.
22723 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22725 o stretch (boolean)
22727 -opts (object) options for the pie
22730 o startAngle (number)
22731 o endAngle (number)
22735 * Create a new Input
22736 * @param {Object} config The config object
22739 Roo.bootstrap.Graph = function(config){
22740 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22746 * The img click event for the img.
22747 * @param {Roo.EventObject} e
22753 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22764 //g_colors: this.colors,
22771 getAutoCreate : function(){
22782 onRender : function(ct,position){
22783 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22784 this.raphael = Raphael(this.el.dom);
22786 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22787 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22788 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22789 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22791 r.text(160, 10, "Single Series Chart").attr(txtattr);
22792 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22793 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22794 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22796 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22797 r.barchart(330, 10, 300, 220, data1);
22798 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22799 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22802 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22803 // r.barchart(30, 30, 560, 250, xdata, {
22804 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22805 // axis : "0 0 1 1",
22806 // axisxlabels : xdata
22807 // //yvalues : cols,
22810 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22812 // this.load(null,xdata,{
22813 // axis : "0 0 1 1",
22814 // axisxlabels : xdata
22819 load : function(graphtype,xdata,opts){
22820 this.raphael.clear();
22822 graphtype = this.graphtype;
22827 var r = this.raphael,
22828 fin = function () {
22829 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22831 fout = function () {
22832 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22834 pfin = function() {
22835 this.sector.stop();
22836 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22839 this.label[0].stop();
22840 this.label[0].attr({ r: 7.5 });
22841 this.label[1].attr({ "font-weight": 800 });
22844 pfout = function() {
22845 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22848 this.label[0].animate({ r: 5 }, 500, "bounce");
22849 this.label[1].attr({ "font-weight": 400 });
22855 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22858 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22861 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
22862 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22864 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22871 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22876 setTitle: function(o)
22881 initEvents: function() {
22884 this.el.on('click', this.onClick, this);
22888 onClick : function(e)
22890 Roo.log('img onclick');
22891 this.fireEvent('click', this, e);
22903 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22906 * @class Roo.bootstrap.dash.NumberBox
22907 * @extends Roo.bootstrap.Component
22908 * Bootstrap NumberBox class
22909 * @cfg {String} headline Box headline
22910 * @cfg {String} content Box content
22911 * @cfg {String} icon Box icon
22912 * @cfg {String} footer Footer text
22913 * @cfg {String} fhref Footer href
22916 * Create a new NumberBox
22917 * @param {Object} config The config object
22921 Roo.bootstrap.dash.NumberBox = function(config){
22922 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22926 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
22935 getAutoCreate : function(){
22939 cls : 'small-box ',
22947 cls : 'roo-headline',
22948 html : this.headline
22952 cls : 'roo-content',
22953 html : this.content
22967 cls : 'ion ' + this.icon
22976 cls : 'small-box-footer',
22977 href : this.fhref || '#',
22981 cfg.cn.push(footer);
22988 onRender : function(ct,position){
22989 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22996 setHeadline: function (value)
22998 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23001 setFooter: function (value, href)
23003 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23006 this.el.select('a.small-box-footer',true).first().attr('href', href);
23011 setContent: function (value)
23013 this.el.select('.roo-content',true).first().dom.innerHTML = value;
23016 initEvents: function()
23030 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23033 * @class Roo.bootstrap.dash.TabBox
23034 * @extends Roo.bootstrap.Component
23035 * Bootstrap TabBox class
23036 * @cfg {String} title Title of the TabBox
23037 * @cfg {String} icon Icon of the TabBox
23038 * @cfg {Boolean} showtabs (true|false) show the tabs default true
23039 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23042 * Create a new TabBox
23043 * @param {Object} config The config object
23047 Roo.bootstrap.dash.TabBox = function(config){
23048 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23053 * When a pane is added
23054 * @param {Roo.bootstrap.dash.TabPane} pane
23058 * @event activatepane
23059 * When a pane is activated
23060 * @param {Roo.bootstrap.dash.TabPane} pane
23062 "activatepane" : true
23070 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
23075 tabScrollable : false,
23077 getChildContainer : function()
23079 return this.el.select('.tab-content', true).first();
23082 getAutoCreate : function(){
23086 cls: 'pull-left header',
23094 cls: 'fa ' + this.icon
23100 cls: 'nav nav-tabs pull-right',
23106 if(this.tabScrollable){
23113 cls: 'nav nav-tabs pull-right',
23124 cls: 'nav-tabs-custom',
23129 cls: 'tab-content no-padding',
23137 initEvents : function()
23139 //Roo.log('add add pane handler');
23140 this.on('addpane', this.onAddPane, this);
23143 * Updates the box title
23144 * @param {String} html to set the title to.
23146 setTitle : function(value)
23148 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23150 onAddPane : function(pane)
23152 this.panes.push(pane);
23153 //Roo.log('addpane');
23155 // tabs are rendere left to right..
23156 if(!this.showtabs){
23160 var ctr = this.el.select('.nav-tabs', true).first();
23163 var existing = ctr.select('.nav-tab',true);
23164 var qty = existing.getCount();;
23167 var tab = ctr.createChild({
23169 cls : 'nav-tab' + (qty ? '' : ' active'),
23177 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23180 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23182 pane.el.addClass('active');
23187 onTabClick : function(ev,un,ob,pane)
23189 //Roo.log('tab - prev default');
23190 ev.preventDefault();
23193 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23194 pane.tab.addClass('active');
23195 //Roo.log(pane.title);
23196 this.getChildContainer().select('.tab-pane',true).removeClass('active');
23197 // technically we should have a deactivate event.. but maybe add later.
23198 // and it should not de-activate the selected tab...
23199 this.fireEvent('activatepane', pane);
23200 pane.el.addClass('active');
23201 pane.fireEvent('activate');
23206 getActivePane : function()
23209 Roo.each(this.panes, function(p) {
23210 if(p.el.hasClass('active')){
23231 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23233 * @class Roo.bootstrap.TabPane
23234 * @extends Roo.bootstrap.Component
23235 * Bootstrap TabPane class
23236 * @cfg {Boolean} active (false | true) Default false
23237 * @cfg {String} title title of panel
23241 * Create a new TabPane
23242 * @param {Object} config The config object
23245 Roo.bootstrap.dash.TabPane = function(config){
23246 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23252 * When a pane is activated
23253 * @param {Roo.bootstrap.dash.TabPane} pane
23260 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
23265 // the tabBox that this is attached to.
23268 getAutoCreate : function()
23276 cfg.cls += ' active';
23281 initEvents : function()
23283 //Roo.log('trigger add pane handler');
23284 this.parent().fireEvent('addpane', this)
23288 * Updates the tab title
23289 * @param {String} html to set the title to.
23291 setTitle: function(str)
23297 this.tab.select('a', true).first().dom.innerHTML = str;
23314 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23317 * @class Roo.bootstrap.menu.Menu
23318 * @extends Roo.bootstrap.Component
23319 * Bootstrap Menu class - container for Menu
23320 * @cfg {String} html Text of the menu
23321 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23322 * @cfg {String} icon Font awesome icon
23323 * @cfg {String} pos Menu align to (top | bottom) default bottom
23327 * Create a new Menu
23328 * @param {Object} config The config object
23332 Roo.bootstrap.menu.Menu = function(config){
23333 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23337 * @event beforeshow
23338 * Fires before this menu is displayed
23339 * @param {Roo.bootstrap.menu.Menu} this
23343 * @event beforehide
23344 * Fires before this menu is hidden
23345 * @param {Roo.bootstrap.menu.Menu} this
23350 * Fires after this menu is displayed
23351 * @param {Roo.bootstrap.menu.Menu} this
23356 * Fires after this menu is hidden
23357 * @param {Roo.bootstrap.menu.Menu} this
23362 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23363 * @param {Roo.bootstrap.menu.Menu} this
23364 * @param {Roo.EventObject} e
23371 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
23375 weight : 'default',
23380 getChildContainer : function() {
23381 if(this.isSubMenu){
23385 return this.el.select('ul.dropdown-menu', true).first();
23388 getAutoCreate : function()
23393 cls : 'roo-menu-text',
23401 cls : 'fa ' + this.icon
23412 cls : 'dropdown-button btn btn-' + this.weight,
23417 cls : 'dropdown-toggle btn btn-' + this.weight,
23427 cls : 'dropdown-menu'
23433 if(this.pos == 'top'){
23434 cfg.cls += ' dropup';
23437 if(this.isSubMenu){
23440 cls : 'dropdown-menu'
23447 onRender : function(ct, position)
23449 this.isSubMenu = ct.hasClass('dropdown-submenu');
23451 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23454 initEvents : function()
23456 if(this.isSubMenu){
23460 this.hidden = true;
23462 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23463 this.triggerEl.on('click', this.onTriggerPress, this);
23465 this.buttonEl = this.el.select('button.dropdown-button', true).first();
23466 this.buttonEl.on('click', this.onClick, this);
23472 if(this.isSubMenu){
23476 return this.el.select('ul.dropdown-menu', true).first();
23479 onClick : function(e)
23481 this.fireEvent("click", this, e);
23484 onTriggerPress : function(e)
23486 if (this.isVisible()) {
23493 isVisible : function(){
23494 return !this.hidden;
23499 this.fireEvent("beforeshow", this);
23501 this.hidden = false;
23502 this.el.addClass('open');
23504 Roo.get(document).on("mouseup", this.onMouseUp, this);
23506 this.fireEvent("show", this);
23513 this.fireEvent("beforehide", this);
23515 this.hidden = true;
23516 this.el.removeClass('open');
23518 Roo.get(document).un("mouseup", this.onMouseUp);
23520 this.fireEvent("hide", this);
23523 onMouseUp : function()
23537 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23540 * @class Roo.bootstrap.menu.Item
23541 * @extends Roo.bootstrap.Component
23542 * Bootstrap MenuItem class
23543 * @cfg {Boolean} submenu (true | false) default false
23544 * @cfg {String} html text of the item
23545 * @cfg {String} href the link
23546 * @cfg {Boolean} disable (true | false) default false
23547 * @cfg {Boolean} preventDefault (true | false) default true
23548 * @cfg {String} icon Font awesome icon
23549 * @cfg {String} pos Submenu align to (left | right) default right
23553 * Create a new Item
23554 * @param {Object} config The config object
23558 Roo.bootstrap.menu.Item = function(config){
23559 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23563 * Fires when the mouse is hovering over this menu
23564 * @param {Roo.bootstrap.menu.Item} this
23565 * @param {Roo.EventObject} e
23570 * Fires when the mouse exits this menu
23571 * @param {Roo.bootstrap.menu.Item} this
23572 * @param {Roo.EventObject} e
23578 * The raw click event for the entire grid.
23579 * @param {Roo.EventObject} e
23585 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23590 preventDefault: true,
23595 getAutoCreate : function()
23600 cls : 'roo-menu-item-text',
23608 cls : 'fa ' + this.icon
23617 href : this.href || '#',
23624 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23628 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23630 if(this.pos == 'left'){
23631 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23638 initEvents : function()
23640 this.el.on('mouseover', this.onMouseOver, this);
23641 this.el.on('mouseout', this.onMouseOut, this);
23643 this.el.select('a', true).first().on('click', this.onClick, this);
23647 onClick : function(e)
23649 if(this.preventDefault){
23650 e.preventDefault();
23653 this.fireEvent("click", this, e);
23656 onMouseOver : function(e)
23658 if(this.submenu && this.pos == 'left'){
23659 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23662 this.fireEvent("mouseover", this, e);
23665 onMouseOut : function(e)
23667 this.fireEvent("mouseout", this, e);
23679 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23682 * @class Roo.bootstrap.menu.Separator
23683 * @extends Roo.bootstrap.Component
23684 * Bootstrap Separator class
23687 * Create a new Separator
23688 * @param {Object} config The config object
23692 Roo.bootstrap.menu.Separator = function(config){
23693 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23696 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23698 getAutoCreate : function(){
23719 * @class Roo.bootstrap.Tooltip
23720 * Bootstrap Tooltip class
23721 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23722 * to determine which dom element triggers the tooltip.
23724 * It needs to add support for additional attributes like tooltip-position
23727 * Create a new Toolti
23728 * @param {Object} config The config object
23731 Roo.bootstrap.Tooltip = function(config){
23732 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23735 Roo.apply(Roo.bootstrap.Tooltip, {
23737 * @function init initialize tooltip monitoring.
23741 currentTip : false,
23742 currentRegion : false,
23748 Roo.get(document).on('mouseover', this.enter ,this);
23749 Roo.get(document).on('mouseout', this.leave, this);
23752 this.currentTip = new Roo.bootstrap.Tooltip();
23755 enter : function(ev)
23757 var dom = ev.getTarget();
23759 //Roo.log(['enter',dom]);
23760 var el = Roo.fly(dom);
23761 if (this.currentEl) {
23763 //Roo.log(this.currentEl);
23764 //Roo.log(this.currentEl.contains(dom));
23765 if (this.currentEl == el) {
23768 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23774 if (this.currentTip.el) {
23775 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23780 // you can not look for children, as if el is the body.. then everythign is the child..
23781 if (!el.attr('tooltip')) { //
23782 if (!el.select("[tooltip]").elements.length) {
23785 // is the mouse over this child...?
23786 bindEl = el.select("[tooltip]").first();
23787 var xy = ev.getXY();
23788 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23789 //Roo.log("not in region.");
23792 //Roo.log("child element over..");
23795 this.currentEl = bindEl;
23796 this.currentTip.bind(bindEl);
23797 this.currentRegion = Roo.lib.Region.getRegion(dom);
23798 this.currentTip.enter();
23801 leave : function(ev)
23803 var dom = ev.getTarget();
23804 //Roo.log(['leave',dom]);
23805 if (!this.currentEl) {
23810 if (dom != this.currentEl.dom) {
23813 var xy = ev.getXY();
23814 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23817 // only activate leave if mouse cursor is outside... bounding box..
23822 if (this.currentTip) {
23823 this.currentTip.leave();
23825 //Roo.log('clear currentEl');
23826 this.currentEl = false;
23831 'left' : ['r-l', [-2,0], 'right'],
23832 'right' : ['l-r', [2,0], 'left'],
23833 'bottom' : ['t-b', [0,2], 'top'],
23834 'top' : [ 'b-t', [0,-2], 'bottom']
23840 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
23845 delay : null, // can be { show : 300 , hide: 500}
23849 hoverState : null, //???
23851 placement : 'bottom',
23853 getAutoCreate : function(){
23860 cls : 'tooltip-arrow'
23863 cls : 'tooltip-inner'
23870 bind : function(el)
23876 enter : function () {
23878 if (this.timeout != null) {
23879 clearTimeout(this.timeout);
23882 this.hoverState = 'in';
23883 //Roo.log("enter - show");
23884 if (!this.delay || !this.delay.show) {
23889 this.timeout = setTimeout(function () {
23890 if (_t.hoverState == 'in') {
23893 }, this.delay.show);
23897 clearTimeout(this.timeout);
23899 this.hoverState = 'out';
23900 if (!this.delay || !this.delay.hide) {
23906 this.timeout = setTimeout(function () {
23907 //Roo.log("leave - timeout");
23909 if (_t.hoverState == 'out') {
23911 Roo.bootstrap.Tooltip.currentEl = false;
23919 this.render(document.body);
23922 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23924 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23926 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23928 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23930 var placement = typeof this.placement == 'function' ?
23931 this.placement.call(this, this.el, on_el) :
23934 var autoToken = /\s?auto?\s?/i;
23935 var autoPlace = autoToken.test(placement);
23937 placement = placement.replace(autoToken, '') || 'top';
23941 //this.el.setXY([0,0]);
23943 //this.el.dom.style.display='block';
23945 //this.el.appendTo(on_el);
23947 var p = this.getPosition();
23948 var box = this.el.getBox();
23954 var align = Roo.bootstrap.Tooltip.alignment[placement];
23956 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23958 if(placement == 'top' || placement == 'bottom'){
23960 placement = 'right';
23963 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23964 placement = 'left';
23968 align = Roo.bootstrap.Tooltip.alignment[placement];
23970 this.el.alignTo(this.bindEl, align[0],align[1]);
23971 //var arrow = this.el.select('.arrow',true).first();
23972 //arrow.set(align[2],
23974 this.el.addClass(placement);
23976 this.el.addClass('in fade');
23978 this.hoverState = null;
23980 if (this.el.hasClass('fade')) {
23991 //this.el.setXY([0,0]);
23992 this.el.removeClass('in');
24008 * @class Roo.bootstrap.LocationPicker
24009 * @extends Roo.bootstrap.Component
24010 * Bootstrap LocationPicker class
24011 * @cfg {Number} latitude Position when init default 0
24012 * @cfg {Number} longitude Position when init default 0
24013 * @cfg {Number} zoom default 15
24014 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24015 * @cfg {Boolean} mapTypeControl default false
24016 * @cfg {Boolean} disableDoubleClickZoom default false
24017 * @cfg {Boolean} scrollwheel default true
24018 * @cfg {Boolean} streetViewControl default false
24019 * @cfg {Number} radius default 0
24020 * @cfg {String} locationName
24021 * @cfg {Boolean} draggable default true
24022 * @cfg {Boolean} enableAutocomplete default false
24023 * @cfg {Boolean} enableReverseGeocode default true
24024 * @cfg {String} markerTitle
24027 * Create a new LocationPicker
24028 * @param {Object} config The config object
24032 Roo.bootstrap.LocationPicker = function(config){
24034 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24039 * Fires when the picker initialized.
24040 * @param {Roo.bootstrap.LocationPicker} this
24041 * @param {Google Location} location
24045 * @event positionchanged
24046 * Fires when the picker position changed.
24047 * @param {Roo.bootstrap.LocationPicker} this
24048 * @param {Google Location} location
24050 positionchanged : true,
24053 * Fires when the map resize.
24054 * @param {Roo.bootstrap.LocationPicker} this
24059 * Fires when the map show.
24060 * @param {Roo.bootstrap.LocationPicker} this
24065 * Fires when the map hide.
24066 * @param {Roo.bootstrap.LocationPicker} this
24071 * Fires when click the map.
24072 * @param {Roo.bootstrap.LocationPicker} this
24073 * @param {Map event} e
24077 * @event mapRightClick
24078 * Fires when right click the map.
24079 * @param {Roo.bootstrap.LocationPicker} this
24080 * @param {Map event} e
24082 mapRightClick : true,
24084 * @event markerClick
24085 * Fires when click the marker.
24086 * @param {Roo.bootstrap.LocationPicker} this
24087 * @param {Map event} e
24089 markerClick : true,
24091 * @event markerRightClick
24092 * Fires when right click the marker.
24093 * @param {Roo.bootstrap.LocationPicker} this
24094 * @param {Map event} e
24096 markerRightClick : true,
24098 * @event OverlayViewDraw
24099 * Fires when OverlayView Draw
24100 * @param {Roo.bootstrap.LocationPicker} this
24102 OverlayViewDraw : true,
24104 * @event OverlayViewOnAdd
24105 * Fires when OverlayView Draw
24106 * @param {Roo.bootstrap.LocationPicker} this
24108 OverlayViewOnAdd : true,
24110 * @event OverlayViewOnRemove
24111 * Fires when OverlayView Draw
24112 * @param {Roo.bootstrap.LocationPicker} this
24114 OverlayViewOnRemove : true,
24116 * @event OverlayViewShow
24117 * Fires when OverlayView Draw
24118 * @param {Roo.bootstrap.LocationPicker} this
24119 * @param {Pixel} cpx
24121 OverlayViewShow : true,
24123 * @event OverlayViewHide
24124 * Fires when OverlayView Draw
24125 * @param {Roo.bootstrap.LocationPicker} this
24127 OverlayViewHide : true,
24129 * @event loadexception
24130 * Fires when load google lib failed.
24131 * @param {Roo.bootstrap.LocationPicker} this
24133 loadexception : true
24138 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
24140 gMapContext: false,
24146 mapTypeControl: false,
24147 disableDoubleClickZoom: false,
24149 streetViewControl: false,
24153 enableAutocomplete: false,
24154 enableReverseGeocode: true,
24157 getAutoCreate: function()
24162 cls: 'roo-location-picker'
24168 initEvents: function(ct, position)
24170 if(!this.el.getWidth() || this.isApplied()){
24174 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24179 initial: function()
24181 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24182 this.fireEvent('loadexception', this);
24186 if(!this.mapTypeId){
24187 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24190 this.gMapContext = this.GMapContext();
24192 this.initOverlayView();
24194 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24198 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24199 _this.setPosition(_this.gMapContext.marker.position);
24202 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24203 _this.fireEvent('mapClick', this, event);
24207 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24208 _this.fireEvent('mapRightClick', this, event);
24212 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24213 _this.fireEvent('markerClick', this, event);
24217 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24218 _this.fireEvent('markerRightClick', this, event);
24222 this.setPosition(this.gMapContext.location);
24224 this.fireEvent('initial', this, this.gMapContext.location);
24227 initOverlayView: function()
24231 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24235 _this.fireEvent('OverlayViewDraw', _this);
24240 _this.fireEvent('OverlayViewOnAdd', _this);
24243 onRemove: function()
24245 _this.fireEvent('OverlayViewOnRemove', _this);
24248 show: function(cpx)
24250 _this.fireEvent('OverlayViewShow', _this, cpx);
24255 _this.fireEvent('OverlayViewHide', _this);
24261 fromLatLngToContainerPixel: function(event)
24263 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24266 isApplied: function()
24268 return this.getGmapContext() == false ? false : true;
24271 getGmapContext: function()
24273 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24276 GMapContext: function()
24278 var position = new google.maps.LatLng(this.latitude, this.longitude);
24280 var _map = new google.maps.Map(this.el.dom, {
24283 mapTypeId: this.mapTypeId,
24284 mapTypeControl: this.mapTypeControl,
24285 disableDoubleClickZoom: this.disableDoubleClickZoom,
24286 scrollwheel: this.scrollwheel,
24287 streetViewControl: this.streetViewControl,
24288 locationName: this.locationName,
24289 draggable: this.draggable,
24290 enableAutocomplete: this.enableAutocomplete,
24291 enableReverseGeocode: this.enableReverseGeocode
24294 var _marker = new google.maps.Marker({
24295 position: position,
24297 title: this.markerTitle,
24298 draggable: this.draggable
24305 location: position,
24306 radius: this.radius,
24307 locationName: this.locationName,
24308 addressComponents: {
24309 formatted_address: null,
24310 addressLine1: null,
24311 addressLine2: null,
24313 streetNumber: null,
24317 stateOrProvince: null
24320 domContainer: this.el.dom,
24321 geodecoder: new google.maps.Geocoder()
24325 drawCircle: function(center, radius, options)
24327 if (this.gMapContext.circle != null) {
24328 this.gMapContext.circle.setMap(null);
24332 options = Roo.apply({}, options, {
24333 strokeColor: "#0000FF",
24334 strokeOpacity: .35,
24336 fillColor: "#0000FF",
24340 options.map = this.gMapContext.map;
24341 options.radius = radius;
24342 options.center = center;
24343 this.gMapContext.circle = new google.maps.Circle(options);
24344 return this.gMapContext.circle;
24350 setPosition: function(location)
24352 this.gMapContext.location = location;
24353 this.gMapContext.marker.setPosition(location);
24354 this.gMapContext.map.panTo(location);
24355 this.drawCircle(location, this.gMapContext.radius, {});
24359 if (this.gMapContext.settings.enableReverseGeocode) {
24360 this.gMapContext.geodecoder.geocode({
24361 latLng: this.gMapContext.location
24362 }, function(results, status) {
24364 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24365 _this.gMapContext.locationName = results[0].formatted_address;
24366 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24368 _this.fireEvent('positionchanged', this, location);
24375 this.fireEvent('positionchanged', this, location);
24380 google.maps.event.trigger(this.gMapContext.map, "resize");
24382 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24384 this.fireEvent('resize', this);
24387 setPositionByLatLng: function(latitude, longitude)
24389 this.setPosition(new google.maps.LatLng(latitude, longitude));
24392 getCurrentPosition: function()
24395 latitude: this.gMapContext.location.lat(),
24396 longitude: this.gMapContext.location.lng()
24400 getAddressName: function()
24402 return this.gMapContext.locationName;
24405 getAddressComponents: function()
24407 return this.gMapContext.addressComponents;
24410 address_component_from_google_geocode: function(address_components)
24414 for (var i = 0; i < address_components.length; i++) {
24415 var component = address_components[i];
24416 if (component.types.indexOf("postal_code") >= 0) {
24417 result.postalCode = component.short_name;
24418 } else if (component.types.indexOf("street_number") >= 0) {
24419 result.streetNumber = component.short_name;
24420 } else if (component.types.indexOf("route") >= 0) {
24421 result.streetName = component.short_name;
24422 } else if (component.types.indexOf("neighborhood") >= 0) {
24423 result.city = component.short_name;
24424 } else if (component.types.indexOf("locality") >= 0) {
24425 result.city = component.short_name;
24426 } else if (component.types.indexOf("sublocality") >= 0) {
24427 result.district = component.short_name;
24428 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24429 result.stateOrProvince = component.short_name;
24430 } else if (component.types.indexOf("country") >= 0) {
24431 result.country = component.short_name;
24435 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24436 result.addressLine2 = "";
24440 setZoomLevel: function(zoom)
24442 this.gMapContext.map.setZoom(zoom);
24455 this.fireEvent('show', this);
24466 this.fireEvent('hide', this);
24471 Roo.apply(Roo.bootstrap.LocationPicker, {
24473 OverlayView : function(map, options)
24475 options = options || {};
24489 * @class Roo.bootstrap.Alert
24490 * @extends Roo.bootstrap.Component
24491 * Bootstrap Alert class
24492 * @cfg {String} title The title of alert
24493 * @cfg {String} html The content of alert
24494 * @cfg {String} weight ( success | info | warning | danger )
24495 * @cfg {String} faicon font-awesomeicon
24498 * Create a new alert
24499 * @param {Object} config The config object
24503 Roo.bootstrap.Alert = function(config){
24504 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24508 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
24515 getAutoCreate : function()
24524 cls : 'roo-alert-icon'
24529 cls : 'roo-alert-title',
24534 cls : 'roo-alert-text',
24541 cfg.cn[0].cls += ' fa ' + this.faicon;
24545 cfg.cls += ' alert-' + this.weight;
24551 initEvents: function()
24553 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24556 setTitle : function(str)
24558 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24561 setText : function(str)
24563 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24566 setWeight : function(weight)
24569 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24572 this.weight = weight;
24574 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24577 setIcon : function(icon)
24580 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24583 this.faicon = icon;
24585 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24606 * @class Roo.bootstrap.UploadCropbox
24607 * @extends Roo.bootstrap.Component
24608 * Bootstrap UploadCropbox class
24609 * @cfg {String} emptyText show when image has been loaded
24610 * @cfg {String} rotateNotify show when image too small to rotate
24611 * @cfg {Number} errorTimeout default 3000
24612 * @cfg {Number} minWidth default 300
24613 * @cfg {Number} minHeight default 300
24614 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24615 * @cfg {Boolean} isDocument (true|false) default false
24616 * @cfg {String} url action url
24617 * @cfg {String} paramName default 'imageUpload'
24618 * @cfg {String} method default POST
24619 * @cfg {Boolean} loadMask (true|false) default true
24620 * @cfg {Boolean} loadingText default 'Loading...'
24623 * Create a new UploadCropbox
24624 * @param {Object} config The config object
24627 Roo.bootstrap.UploadCropbox = function(config){
24628 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24632 * @event beforeselectfile
24633 * Fire before select file
24634 * @param {Roo.bootstrap.UploadCropbox} this
24636 "beforeselectfile" : true,
24639 * Fire after initEvent
24640 * @param {Roo.bootstrap.UploadCropbox} this
24645 * Fire after initEvent
24646 * @param {Roo.bootstrap.UploadCropbox} this
24647 * @param {String} data
24652 * Fire when preparing the file data
24653 * @param {Roo.bootstrap.UploadCropbox} this
24654 * @param {Object} file
24659 * Fire when get exception
24660 * @param {Roo.bootstrap.UploadCropbox} this
24661 * @param {XMLHttpRequest} xhr
24663 "exception" : true,
24665 * @event beforeloadcanvas
24666 * Fire before load the canvas
24667 * @param {Roo.bootstrap.UploadCropbox} this
24668 * @param {String} src
24670 "beforeloadcanvas" : true,
24673 * Fire when trash image
24674 * @param {Roo.bootstrap.UploadCropbox} this
24679 * Fire when download the image
24680 * @param {Roo.bootstrap.UploadCropbox} this
24684 * @event footerbuttonclick
24685 * Fire when footerbuttonclick
24686 * @param {Roo.bootstrap.UploadCropbox} this
24687 * @param {String} type
24689 "footerbuttonclick" : true,
24693 * @param {Roo.bootstrap.UploadCropbox} this
24698 * Fire when rotate the image
24699 * @param {Roo.bootstrap.UploadCropbox} this
24700 * @param {String} pos
24705 * Fire when inspect the file
24706 * @param {Roo.bootstrap.UploadCropbox} this
24707 * @param {Object} file
24712 * Fire when xhr upload the file
24713 * @param {Roo.bootstrap.UploadCropbox} this
24714 * @param {Object} data
24719 * Fire when arrange the file data
24720 * @param {Roo.bootstrap.UploadCropbox} this
24721 * @param {Object} formData
24726 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24729 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24731 emptyText : 'Click to upload image',
24732 rotateNotify : 'Image is too small to rotate',
24733 errorTimeout : 3000,
24747 cropType : 'image/jpeg',
24749 canvasLoaded : false,
24750 isDocument : false,
24752 paramName : 'imageUpload',
24754 loadingText : 'Loading...',
24757 getAutoCreate : function()
24761 cls : 'roo-upload-cropbox',
24765 cls : 'roo-upload-cropbox-selector',
24770 cls : 'roo-upload-cropbox-body',
24771 style : 'cursor:pointer',
24775 cls : 'roo-upload-cropbox-preview'
24779 cls : 'roo-upload-cropbox-thumb'
24783 cls : 'roo-upload-cropbox-empty-notify',
24784 html : this.emptyText
24788 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24789 html : this.rotateNotify
24795 cls : 'roo-upload-cropbox-footer',
24798 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24808 onRender : function(ct, position)
24810 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24812 if (this.buttons.length) {
24814 Roo.each(this.buttons, function(bb) {
24816 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24818 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24824 this.maskEl = this.el;
24828 initEvents : function()
24830 this.urlAPI = (window.createObjectURL && window) ||
24831 (window.URL && URL.revokeObjectURL && URL) ||
24832 (window.webkitURL && webkitURL);
24834 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24835 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24837 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24838 this.selectorEl.hide();
24840 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24841 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24843 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24844 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24845 this.thumbEl.hide();
24847 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24848 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24850 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24851 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24852 this.errorEl.hide();
24854 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24855 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24856 this.footerEl.hide();
24858 this.setThumbBoxSize();
24864 this.fireEvent('initial', this);
24871 window.addEventListener("resize", function() { _this.resize(); } );
24873 this.bodyEl.on('click', this.beforeSelectFile, this);
24876 this.bodyEl.on('touchstart', this.onTouchStart, this);
24877 this.bodyEl.on('touchmove', this.onTouchMove, this);
24878 this.bodyEl.on('touchend', this.onTouchEnd, this);
24882 this.bodyEl.on('mousedown', this.onMouseDown, this);
24883 this.bodyEl.on('mousemove', this.onMouseMove, this);
24884 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24885 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24886 Roo.get(document).on('mouseup', this.onMouseUp, this);
24889 this.selectorEl.on('change', this.onFileSelected, this);
24895 this.baseScale = 1;
24897 this.baseRotate = 1;
24898 this.dragable = false;
24899 this.pinching = false;
24902 this.cropData = false;
24903 this.notifyEl.dom.innerHTML = this.emptyText;
24905 this.selectorEl.dom.value = '';
24909 resize : function()
24911 if(this.fireEvent('resize', this) != false){
24912 this.setThumbBoxPosition();
24913 this.setCanvasPosition();
24917 onFooterButtonClick : function(e, el, o, type)
24920 case 'rotate-left' :
24921 this.onRotateLeft(e);
24923 case 'rotate-right' :
24924 this.onRotateRight(e);
24927 this.beforeSelectFile(e);
24942 this.fireEvent('footerbuttonclick', this, type);
24945 beforeSelectFile : function(e)
24947 e.preventDefault();
24949 if(this.fireEvent('beforeselectfile', this) != false){
24950 this.selectorEl.dom.click();
24954 onFileSelected : function(e)
24956 e.preventDefault();
24958 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24962 var file = this.selectorEl.dom.files[0];
24964 if(this.fireEvent('inspect', this, file) != false){
24965 this.prepare(file);
24970 trash : function(e)
24972 this.fireEvent('trash', this);
24975 download : function(e)
24977 this.fireEvent('download', this);
24980 loadCanvas : function(src)
24982 if(this.fireEvent('beforeloadcanvas', this, src) != false){
24986 this.imageEl = document.createElement('img');
24990 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24992 this.imageEl.src = src;
24996 onLoadCanvas : function()
24998 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24999 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25001 this.bodyEl.un('click', this.beforeSelectFile, this);
25003 this.notifyEl.hide();
25004 this.thumbEl.show();
25005 this.footerEl.show();
25007 this.baseRotateLevel();
25009 if(this.isDocument){
25010 this.setThumbBoxSize();
25013 this.setThumbBoxPosition();
25015 this.baseScaleLevel();
25021 this.canvasLoaded = true;
25024 this.maskEl.unmask();
25029 setCanvasPosition : function()
25031 if(!this.canvasEl){
25035 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25036 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25038 this.previewEl.setLeft(pw);
25039 this.previewEl.setTop(ph);
25043 onMouseDown : function(e)
25047 this.dragable = true;
25048 this.pinching = false;
25050 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25051 this.dragable = false;
25055 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25056 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25060 onMouseMove : function(e)
25064 if(!this.canvasLoaded){
25068 if (!this.dragable){
25072 var minX = Math.ceil(this.thumbEl.getLeft(true));
25073 var minY = Math.ceil(this.thumbEl.getTop(true));
25075 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25076 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25078 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25079 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25081 x = x - this.mouseX;
25082 y = y - this.mouseY;
25084 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25085 var bgY = Math.ceil(y + this.previewEl.getTop(true));
25087 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25088 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25090 this.previewEl.setLeft(bgX);
25091 this.previewEl.setTop(bgY);
25093 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25094 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25097 onMouseUp : function(e)
25101 this.dragable = false;
25104 onMouseWheel : function(e)
25108 this.startScale = this.scale;
25110 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25112 if(!this.zoomable()){
25113 this.scale = this.startScale;
25122 zoomable : function()
25124 var minScale = this.thumbEl.getWidth() / this.minWidth;
25126 if(this.minWidth < this.minHeight){
25127 minScale = this.thumbEl.getHeight() / this.minHeight;
25130 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25131 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25135 (this.rotate == 0 || this.rotate == 180) &&
25137 width > this.imageEl.OriginWidth ||
25138 height > this.imageEl.OriginHeight ||
25139 (width < this.minWidth && height < this.minHeight)
25147 (this.rotate == 90 || this.rotate == 270) &&
25149 width > this.imageEl.OriginWidth ||
25150 height > this.imageEl.OriginHeight ||
25151 (width < this.minHeight && height < this.minWidth)
25158 !this.isDocument &&
25159 (this.rotate == 0 || this.rotate == 180) &&
25161 width < this.minWidth ||
25162 width > this.imageEl.OriginWidth ||
25163 height < this.minHeight ||
25164 height > this.imageEl.OriginHeight
25171 !this.isDocument &&
25172 (this.rotate == 90 || this.rotate == 270) &&
25174 width < this.minHeight ||
25175 width > this.imageEl.OriginWidth ||
25176 height < this.minWidth ||
25177 height > this.imageEl.OriginHeight
25187 onRotateLeft : function(e)
25189 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25191 var minScale = this.thumbEl.getWidth() / this.minWidth;
25193 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25194 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25196 this.startScale = this.scale;
25198 while (this.getScaleLevel() < minScale){
25200 this.scale = this.scale + 1;
25202 if(!this.zoomable()){
25207 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25208 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25213 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25220 this.scale = this.startScale;
25222 this.onRotateFail();
25227 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25229 if(this.isDocument){
25230 this.setThumbBoxSize();
25231 this.setThumbBoxPosition();
25232 this.setCanvasPosition();
25237 this.fireEvent('rotate', this, 'left');
25241 onRotateRight : function(e)
25243 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25245 var minScale = this.thumbEl.getWidth() / this.minWidth;
25247 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25248 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25250 this.startScale = this.scale;
25252 while (this.getScaleLevel() < minScale){
25254 this.scale = this.scale + 1;
25256 if(!this.zoomable()){
25261 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25262 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25267 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25274 this.scale = this.startScale;
25276 this.onRotateFail();
25281 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25283 if(this.isDocument){
25284 this.setThumbBoxSize();
25285 this.setThumbBoxPosition();
25286 this.setCanvasPosition();
25291 this.fireEvent('rotate', this, 'right');
25294 onRotateFail : function()
25296 this.errorEl.show(true);
25300 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25305 this.previewEl.dom.innerHTML = '';
25307 var canvasEl = document.createElement("canvas");
25309 var contextEl = canvasEl.getContext("2d");
25311 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25312 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25313 var center = this.imageEl.OriginWidth / 2;
25315 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25316 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25317 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25318 center = this.imageEl.OriginHeight / 2;
25321 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25323 contextEl.translate(center, center);
25324 contextEl.rotate(this.rotate * Math.PI / 180);
25326 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25328 this.canvasEl = document.createElement("canvas");
25330 this.contextEl = this.canvasEl.getContext("2d");
25332 switch (this.rotate) {
25335 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25336 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25338 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25343 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25344 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25346 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25347 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);
25351 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25356 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25357 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25359 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25360 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);
25364 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);
25369 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25370 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25372 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25373 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25377 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);
25384 this.previewEl.appendChild(this.canvasEl);
25386 this.setCanvasPosition();
25391 if(!this.canvasLoaded){
25395 var imageCanvas = document.createElement("canvas");
25397 var imageContext = imageCanvas.getContext("2d");
25399 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25400 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25402 var center = imageCanvas.width / 2;
25404 imageContext.translate(center, center);
25406 imageContext.rotate(this.rotate * Math.PI / 180);
25408 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25410 var canvas = document.createElement("canvas");
25412 var context = canvas.getContext("2d");
25414 canvas.width = this.minWidth;
25415 canvas.height = this.minHeight;
25417 switch (this.rotate) {
25420 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25421 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25423 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25424 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25426 var targetWidth = this.minWidth - 2 * x;
25427 var targetHeight = this.minHeight - 2 * y;
25431 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25432 scale = targetWidth / width;
25435 if(x > 0 && y == 0){
25436 scale = targetHeight / height;
25439 if(x > 0 && y > 0){
25440 scale = targetWidth / width;
25442 if(width < height){
25443 scale = targetHeight / height;
25447 context.scale(scale, scale);
25449 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25450 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25452 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25453 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25455 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25460 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25461 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25463 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25464 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25466 var targetWidth = this.minWidth - 2 * x;
25467 var targetHeight = this.minHeight - 2 * y;
25471 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25472 scale = targetWidth / width;
25475 if(x > 0 && y == 0){
25476 scale = targetHeight / height;
25479 if(x > 0 && y > 0){
25480 scale = targetWidth / width;
25482 if(width < height){
25483 scale = targetHeight / height;
25487 context.scale(scale, scale);
25489 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25490 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25492 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25493 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25495 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25497 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25502 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25503 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25505 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25506 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25508 var targetWidth = this.minWidth - 2 * x;
25509 var targetHeight = this.minHeight - 2 * y;
25513 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25514 scale = targetWidth / width;
25517 if(x > 0 && y == 0){
25518 scale = targetHeight / height;
25521 if(x > 0 && y > 0){
25522 scale = targetWidth / width;
25524 if(width < height){
25525 scale = targetHeight / height;
25529 context.scale(scale, scale);
25531 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25532 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25534 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25535 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25537 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25538 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25540 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25545 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25546 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25548 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25549 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25551 var targetWidth = this.minWidth - 2 * x;
25552 var targetHeight = this.minHeight - 2 * y;
25556 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25557 scale = targetWidth / width;
25560 if(x > 0 && y == 0){
25561 scale = targetHeight / height;
25564 if(x > 0 && y > 0){
25565 scale = targetWidth / width;
25567 if(width < height){
25568 scale = targetHeight / height;
25572 context.scale(scale, scale);
25574 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25575 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25577 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25578 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25580 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25582 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25589 this.cropData = canvas.toDataURL(this.cropType);
25591 if(this.fireEvent('crop', this, this.cropData) !== false){
25592 this.process(this.file, this.cropData);
25599 setThumbBoxSize : function()
25603 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25604 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25605 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25607 this.minWidth = width;
25608 this.minHeight = height;
25610 if(this.rotate == 90 || this.rotate == 270){
25611 this.minWidth = height;
25612 this.minHeight = width;
25617 width = Math.ceil(this.minWidth * height / this.minHeight);
25619 if(this.minWidth > this.minHeight){
25621 height = Math.ceil(this.minHeight * width / this.minWidth);
25624 this.thumbEl.setStyle({
25625 width : width + 'px',
25626 height : height + 'px'
25633 setThumbBoxPosition : function()
25635 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25636 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25638 this.thumbEl.setLeft(x);
25639 this.thumbEl.setTop(y);
25643 baseRotateLevel : function()
25645 this.baseRotate = 1;
25648 typeof(this.exif) != 'undefined' &&
25649 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25650 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25652 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25655 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25659 baseScaleLevel : function()
25663 if(this.isDocument){
25665 if(this.baseRotate == 6 || this.baseRotate == 8){
25667 height = this.thumbEl.getHeight();
25668 this.baseScale = height / this.imageEl.OriginWidth;
25670 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25671 width = this.thumbEl.getWidth();
25672 this.baseScale = width / this.imageEl.OriginHeight;
25678 height = this.thumbEl.getHeight();
25679 this.baseScale = height / this.imageEl.OriginHeight;
25681 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25682 width = this.thumbEl.getWidth();
25683 this.baseScale = width / this.imageEl.OriginWidth;
25689 if(this.baseRotate == 6 || this.baseRotate == 8){
25691 width = this.thumbEl.getHeight();
25692 this.baseScale = width / this.imageEl.OriginHeight;
25694 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25695 height = this.thumbEl.getWidth();
25696 this.baseScale = height / this.imageEl.OriginHeight;
25699 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25700 height = this.thumbEl.getWidth();
25701 this.baseScale = height / this.imageEl.OriginHeight;
25703 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25704 width = this.thumbEl.getHeight();
25705 this.baseScale = width / this.imageEl.OriginWidth;
25712 width = this.thumbEl.getWidth();
25713 this.baseScale = width / this.imageEl.OriginWidth;
25715 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25716 height = this.thumbEl.getHeight();
25717 this.baseScale = height / this.imageEl.OriginHeight;
25720 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25722 height = this.thumbEl.getHeight();
25723 this.baseScale = height / this.imageEl.OriginHeight;
25725 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25726 width = this.thumbEl.getWidth();
25727 this.baseScale = width / this.imageEl.OriginWidth;
25735 getScaleLevel : function()
25737 return this.baseScale * Math.pow(1.1, this.scale);
25740 onTouchStart : function(e)
25742 if(!this.canvasLoaded){
25743 this.beforeSelectFile(e);
25747 var touches = e.browserEvent.touches;
25753 if(touches.length == 1){
25754 this.onMouseDown(e);
25758 if(touches.length != 2){
25764 for(var i = 0, finger; finger = touches[i]; i++){
25765 coords.push(finger.pageX, finger.pageY);
25768 var x = Math.pow(coords[0] - coords[2], 2);
25769 var y = Math.pow(coords[1] - coords[3], 2);
25771 this.startDistance = Math.sqrt(x + y);
25773 this.startScale = this.scale;
25775 this.pinching = true;
25776 this.dragable = false;
25780 onTouchMove : function(e)
25782 if(!this.pinching && !this.dragable){
25786 var touches = e.browserEvent.touches;
25793 this.onMouseMove(e);
25799 for(var i = 0, finger; finger = touches[i]; i++){
25800 coords.push(finger.pageX, finger.pageY);
25803 var x = Math.pow(coords[0] - coords[2], 2);
25804 var y = Math.pow(coords[1] - coords[3], 2);
25806 this.endDistance = Math.sqrt(x + y);
25808 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25810 if(!this.zoomable()){
25811 this.scale = this.startScale;
25819 onTouchEnd : function(e)
25821 this.pinching = false;
25822 this.dragable = false;
25826 process : function(file, crop)
25829 this.maskEl.mask(this.loadingText);
25832 this.xhr = new XMLHttpRequest();
25834 file.xhr = this.xhr;
25836 this.xhr.open(this.method, this.url, true);
25839 "Accept": "application/json",
25840 "Cache-Control": "no-cache",
25841 "X-Requested-With": "XMLHttpRequest"
25844 for (var headerName in headers) {
25845 var headerValue = headers[headerName];
25847 this.xhr.setRequestHeader(headerName, headerValue);
25853 this.xhr.onload = function()
25855 _this.xhrOnLoad(_this.xhr);
25858 this.xhr.onerror = function()
25860 _this.xhrOnError(_this.xhr);
25863 var formData = new FormData();
25865 formData.append('returnHTML', 'NO');
25868 formData.append('crop', crop);
25871 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25872 formData.append(this.paramName, file, file.name);
25875 if(typeof(file.filename) != 'undefined'){
25876 formData.append('filename', file.filename);
25879 if(typeof(file.mimetype) != 'undefined'){
25880 formData.append('mimetype', file.mimetype);
25883 if(this.fireEvent('arrange', this, formData) != false){
25884 this.xhr.send(formData);
25888 xhrOnLoad : function(xhr)
25891 this.maskEl.unmask();
25894 if (xhr.readyState !== 4) {
25895 this.fireEvent('exception', this, xhr);
25899 var response = Roo.decode(xhr.responseText);
25901 if(!response.success){
25902 this.fireEvent('exception', this, xhr);
25906 var response = Roo.decode(xhr.responseText);
25908 this.fireEvent('upload', this, response);
25912 xhrOnError : function()
25915 this.maskEl.unmask();
25918 Roo.log('xhr on error');
25920 var response = Roo.decode(xhr.responseText);
25926 prepare : function(file)
25929 this.maskEl.mask(this.loadingText);
25935 if(typeof(file) === 'string'){
25936 this.loadCanvas(file);
25940 if(!file || !this.urlAPI){
25945 this.cropType = file.type;
25949 if(this.fireEvent('prepare', this, this.file) != false){
25951 var reader = new FileReader();
25953 reader.onload = function (e) {
25954 if (e.target.error) {
25955 Roo.log(e.target.error);
25959 var buffer = e.target.result,
25960 dataView = new DataView(buffer),
25962 maxOffset = dataView.byteLength - 4,
25966 if (dataView.getUint16(0) === 0xffd8) {
25967 while (offset < maxOffset) {
25968 markerBytes = dataView.getUint16(offset);
25970 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25971 markerLength = dataView.getUint16(offset + 2) + 2;
25972 if (offset + markerLength > dataView.byteLength) {
25973 Roo.log('Invalid meta data: Invalid segment size.');
25977 if(markerBytes == 0xffe1){
25978 _this.parseExifData(
25985 offset += markerLength;
25995 var url = _this.urlAPI.createObjectURL(_this.file);
25997 _this.loadCanvas(url);
26002 reader.readAsArrayBuffer(this.file);
26008 parseExifData : function(dataView, offset, length)
26010 var tiffOffset = offset + 10,
26014 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26015 // No Exif data, might be XMP data instead
26019 // Check for the ASCII code for "Exif" (0x45786966):
26020 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26021 // No Exif data, might be XMP data instead
26024 if (tiffOffset + 8 > dataView.byteLength) {
26025 Roo.log('Invalid Exif data: Invalid segment size.');
26028 // Check for the two null bytes:
26029 if (dataView.getUint16(offset + 8) !== 0x0000) {
26030 Roo.log('Invalid Exif data: Missing byte alignment offset.');
26033 // Check the byte alignment:
26034 switch (dataView.getUint16(tiffOffset)) {
26036 littleEndian = true;
26039 littleEndian = false;
26042 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26045 // Check for the TIFF tag marker (0x002A):
26046 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26047 Roo.log('Invalid Exif data: Missing TIFF marker.');
26050 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26051 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26053 this.parseExifTags(
26056 tiffOffset + dirOffset,
26061 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26066 if (dirOffset + 6 > dataView.byteLength) {
26067 Roo.log('Invalid Exif data: Invalid directory offset.');
26070 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26071 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26072 if (dirEndOffset + 4 > dataView.byteLength) {
26073 Roo.log('Invalid Exif data: Invalid directory size.');
26076 for (i = 0; i < tagsNumber; i += 1) {
26080 dirOffset + 2 + 12 * i, // tag offset
26084 // Return the offset to the next directory:
26085 return dataView.getUint32(dirEndOffset, littleEndian);
26088 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
26090 var tag = dataView.getUint16(offset, littleEndian);
26092 this.exif[tag] = this.getExifValue(
26096 dataView.getUint16(offset + 2, littleEndian), // tag type
26097 dataView.getUint32(offset + 4, littleEndian), // tag length
26102 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26104 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26113 Roo.log('Invalid Exif data: Invalid tag type.');
26117 tagSize = tagType.size * length;
26118 // Determine if the value is contained in the dataOffset bytes,
26119 // or if the value at the dataOffset is a pointer to the actual data:
26120 dataOffset = tagSize > 4 ?
26121 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26122 if (dataOffset + tagSize > dataView.byteLength) {
26123 Roo.log('Invalid Exif data: Invalid data offset.');
26126 if (length === 1) {
26127 return tagType.getValue(dataView, dataOffset, littleEndian);
26130 for (i = 0; i < length; i += 1) {
26131 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26134 if (tagType.ascii) {
26136 // Concatenate the chars:
26137 for (i = 0; i < values.length; i += 1) {
26139 // Ignore the terminating NULL byte(s):
26140 if (c === '\u0000') {
26152 Roo.apply(Roo.bootstrap.UploadCropbox, {
26154 'Orientation': 0x0112
26158 1: 0, //'top-left',
26160 3: 180, //'bottom-right',
26161 // 4: 'bottom-left',
26163 6: 90, //'right-top',
26164 // 7: 'right-bottom',
26165 8: 270 //'left-bottom'
26169 // byte, 8-bit unsigned int:
26171 getValue: function (dataView, dataOffset) {
26172 return dataView.getUint8(dataOffset);
26176 // ascii, 8-bit byte:
26178 getValue: function (dataView, dataOffset) {
26179 return String.fromCharCode(dataView.getUint8(dataOffset));
26184 // short, 16 bit int:
26186 getValue: function (dataView, dataOffset, littleEndian) {
26187 return dataView.getUint16(dataOffset, littleEndian);
26191 // long, 32 bit int:
26193 getValue: function (dataView, dataOffset, littleEndian) {
26194 return dataView.getUint32(dataOffset, littleEndian);
26198 // rational = two long values, first is numerator, second is denominator:
26200 getValue: function (dataView, dataOffset, littleEndian) {
26201 return dataView.getUint32(dataOffset, littleEndian) /
26202 dataView.getUint32(dataOffset + 4, littleEndian);
26206 // slong, 32 bit signed int:
26208 getValue: function (dataView, dataOffset, littleEndian) {
26209 return dataView.getInt32(dataOffset, littleEndian);
26213 // srational, two slongs, first is numerator, second is denominator:
26215 getValue: function (dataView, dataOffset, littleEndian) {
26216 return dataView.getInt32(dataOffset, littleEndian) /
26217 dataView.getInt32(dataOffset + 4, littleEndian);
26227 cls : 'btn-group roo-upload-cropbox-rotate-left',
26228 action : 'rotate-left',
26232 cls : 'btn btn-default',
26233 html : '<i class="fa fa-undo"></i>'
26239 cls : 'btn-group roo-upload-cropbox-picture',
26240 action : 'picture',
26244 cls : 'btn btn-default',
26245 html : '<i class="fa fa-picture-o"></i>'
26251 cls : 'btn-group roo-upload-cropbox-rotate-right',
26252 action : 'rotate-right',
26256 cls : 'btn btn-default',
26257 html : '<i class="fa fa-repeat"></i>'
26265 cls : 'btn-group roo-upload-cropbox-rotate-left',
26266 action : 'rotate-left',
26270 cls : 'btn btn-default',
26271 html : '<i class="fa fa-undo"></i>'
26277 cls : 'btn-group roo-upload-cropbox-download',
26278 action : 'download',
26282 cls : 'btn btn-default',
26283 html : '<i class="fa fa-download"></i>'
26289 cls : 'btn-group roo-upload-cropbox-crop',
26294 cls : 'btn btn-default',
26295 html : '<i class="fa fa-crop"></i>'
26301 cls : 'btn-group roo-upload-cropbox-trash',
26306 cls : 'btn btn-default',
26307 html : '<i class="fa fa-trash"></i>'
26313 cls : 'btn-group roo-upload-cropbox-rotate-right',
26314 action : 'rotate-right',
26318 cls : 'btn btn-default',
26319 html : '<i class="fa fa-repeat"></i>'
26327 cls : 'btn-group roo-upload-cropbox-rotate-left',
26328 action : 'rotate-left',
26332 cls : 'btn btn-default',
26333 html : '<i class="fa fa-undo"></i>'
26339 cls : 'btn-group roo-upload-cropbox-rotate-right',
26340 action : 'rotate-right',
26344 cls : 'btn btn-default',
26345 html : '<i class="fa fa-repeat"></i>'
26358 * @class Roo.bootstrap.DocumentManager
26359 * @extends Roo.bootstrap.Component
26360 * Bootstrap DocumentManager class
26361 * @cfg {String} paramName default 'imageUpload'
26362 * @cfg {String} method default POST
26363 * @cfg {String} url action url
26364 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26365 * @cfg {Boolean} multiple multiple upload default true
26366 * @cfg {Number} thumbSize default 300
26367 * @cfg {String} fieldLabel
26368 * @cfg {Number} labelWidth default 4
26369 * @cfg {String} labelAlign (left|top) default left
26370 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26373 * Create a new DocumentManager
26374 * @param {Object} config The config object
26377 Roo.bootstrap.DocumentManager = function(config){
26378 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26383 * Fire when initial the DocumentManager
26384 * @param {Roo.bootstrap.DocumentManager} this
26389 * inspect selected file
26390 * @param {Roo.bootstrap.DocumentManager} this
26391 * @param {File} file
26396 * Fire when xhr load exception
26397 * @param {Roo.bootstrap.DocumentManager} this
26398 * @param {XMLHttpRequest} xhr
26400 "exception" : true,
26403 * prepare the form data
26404 * @param {Roo.bootstrap.DocumentManager} this
26405 * @param {Object} formData
26410 * Fire when remove the file
26411 * @param {Roo.bootstrap.DocumentManager} this
26412 * @param {Object} file
26417 * Fire after refresh the file
26418 * @param {Roo.bootstrap.DocumentManager} this
26423 * Fire after click the image
26424 * @param {Roo.bootstrap.DocumentManager} this
26425 * @param {Object} file
26430 * Fire when upload a image and editable set to true
26431 * @param {Roo.bootstrap.DocumentManager} this
26432 * @param {Object} file
26436 * @event beforeselectfile
26437 * Fire before select file
26438 * @param {Roo.bootstrap.DocumentManager} this
26440 "beforeselectfile" : true,
26443 * Fire before process file
26444 * @param {Roo.bootstrap.DocumentManager} this
26445 * @param {Object} file
26452 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
26461 paramName : 'imageUpload',
26464 labelAlign : 'left',
26471 getAutoCreate : function()
26473 var managerWidget = {
26475 cls : 'roo-document-manager',
26479 cls : 'roo-document-manager-selector',
26484 cls : 'roo-document-manager-uploader',
26488 cls : 'roo-document-manager-upload-btn',
26489 html : '<i class="fa fa-plus"></i>'
26500 cls : 'column col-md-12',
26505 if(this.fieldLabel.length){
26510 cls : 'column col-md-12',
26511 html : this.fieldLabel
26515 cls : 'column col-md-12',
26520 if(this.labelAlign == 'left'){
26524 cls : 'column col-md-' + this.labelWidth,
26525 html : this.fieldLabel
26529 cls : 'column col-md-' + (12 - this.labelWidth),
26539 cls : 'row clearfix',
26547 initEvents : function()
26549 this.managerEl = this.el.select('.roo-document-manager', true).first();
26550 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26552 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26553 this.selectorEl.hide();
26556 this.selectorEl.attr('multiple', 'multiple');
26559 this.selectorEl.on('change', this.onFileSelected, this);
26561 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26562 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26564 this.uploader.on('click', this.onUploaderClick, this);
26566 this.renderProgressDialog();
26570 window.addEventListener("resize", function() { _this.refresh(); } );
26572 this.fireEvent('initial', this);
26575 renderProgressDialog : function()
26579 this.progressDialog = new Roo.bootstrap.Modal({
26580 cls : 'roo-document-manager-progress-dialog',
26581 allow_close : false,
26591 btnclick : function() {
26592 _this.uploadCancel();
26598 this.progressDialog.render(Roo.get(document.body));
26600 this.progress = new Roo.bootstrap.Progress({
26601 cls : 'roo-document-manager-progress',
26606 this.progress.render(this.progressDialog.getChildContainer());
26608 this.progressBar = new Roo.bootstrap.ProgressBar({
26609 cls : 'roo-document-manager-progress-bar',
26612 aria_valuemax : 12,
26616 this.progressBar.render(this.progress.getChildContainer());
26619 onUploaderClick : function(e)
26621 e.preventDefault();
26623 if(this.fireEvent('beforeselectfile', this) != false){
26624 this.selectorEl.dom.click();
26629 onFileSelected : function(e)
26631 e.preventDefault();
26633 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26637 Roo.each(this.selectorEl.dom.files, function(file){
26638 if(this.fireEvent('inspect', this, file) != false){
26639 this.files.push(file);
26649 this.selectorEl.dom.value = '';
26651 if(!this.files.length){
26655 if(this.boxes > 0 && this.files.length > this.boxes){
26656 this.files = this.files.slice(0, this.boxes);
26659 this.uploader.show();
26661 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26662 this.uploader.hide();
26671 Roo.each(this.files, function(file){
26673 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26674 var f = this.renderPreview(file);
26679 if(file.type.indexOf('image') != -1){
26680 this.delegates.push(
26682 _this.process(file);
26683 }).createDelegate(this)
26691 _this.process(file);
26692 }).createDelegate(this)
26697 this.files = files;
26699 this.delegates = this.delegates.concat(docs);
26701 if(!this.delegates.length){
26706 this.progressBar.aria_valuemax = this.delegates.length;
26713 arrange : function()
26715 if(!this.delegates.length){
26716 this.progressDialog.hide();
26721 var delegate = this.delegates.shift();
26723 this.progressDialog.show();
26725 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26727 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26732 refresh : function()
26734 this.uploader.show();
26736 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26737 this.uploader.hide();
26740 Roo.isTouch ? this.closable(false) : this.closable(true);
26742 this.fireEvent('refresh', this);
26745 onRemove : function(e, el, o)
26747 e.preventDefault();
26749 this.fireEvent('remove', this, o);
26753 remove : function(o)
26757 Roo.each(this.files, function(file){
26758 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26767 this.files = files;
26774 Roo.each(this.files, function(file){
26779 file.target.remove();
26788 onClick : function(e, el, o)
26790 e.preventDefault();
26792 this.fireEvent('click', this, o);
26796 closable : function(closable)
26798 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26800 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26812 xhrOnLoad : function(xhr)
26814 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26818 if (xhr.readyState !== 4) {
26820 this.fireEvent('exception', this, xhr);
26824 var response = Roo.decode(xhr.responseText);
26826 if(!response.success){
26828 this.fireEvent('exception', this, xhr);
26832 var file = this.renderPreview(response.data);
26834 this.files.push(file);
26840 xhrOnError : function(xhr)
26842 Roo.log('xhr on error');
26844 var response = Roo.decode(xhr.responseText);
26851 process : function(file)
26853 if(this.fireEvent('process', this, file) !== false){
26854 if(this.editable && file.type.indexOf('image') != -1){
26855 this.fireEvent('edit', this, file);
26859 this.uploadStart(file, false);
26866 uploadStart : function(file, crop)
26868 this.xhr = new XMLHttpRequest();
26870 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26875 file.xhr = this.xhr;
26877 this.managerEl.createChild({
26879 cls : 'roo-document-manager-loading',
26883 tooltip : file.name,
26884 cls : 'roo-document-manager-thumb',
26885 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26891 this.xhr.open(this.method, this.url, true);
26894 "Accept": "application/json",
26895 "Cache-Control": "no-cache",
26896 "X-Requested-With": "XMLHttpRequest"
26899 for (var headerName in headers) {
26900 var headerValue = headers[headerName];
26902 this.xhr.setRequestHeader(headerName, headerValue);
26908 this.xhr.onload = function()
26910 _this.xhrOnLoad(_this.xhr);
26913 this.xhr.onerror = function()
26915 _this.xhrOnError(_this.xhr);
26918 var formData = new FormData();
26920 formData.append('returnHTML', 'NO');
26923 formData.append('crop', crop);
26926 formData.append(this.paramName, file, file.name);
26928 if(this.fireEvent('prepare', this, formData) != false){
26929 this.xhr.send(formData);
26933 uploadCancel : function()
26940 this.delegates = [];
26942 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26949 renderPreview : function(file)
26951 if(typeof(file.target) != 'undefined' && file.target){
26955 var previewEl = this.managerEl.createChild({
26957 cls : 'roo-document-manager-preview',
26961 tooltip : file.filename,
26962 cls : 'roo-document-manager-thumb',
26963 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26968 html : '<i class="fa fa-times-circle"></i>'
26973 var close = previewEl.select('button.close', true).first();
26975 close.on('click', this.onRemove, this, file);
26977 file.target = previewEl;
26979 var image = previewEl.select('img', true).first();
26983 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26985 image.on('click', this.onClick, this, file);
26991 onPreviewLoad : function(file, image)
26993 if(typeof(file.target) == 'undefined' || !file.target){
26997 var width = image.dom.naturalWidth || image.dom.width;
26998 var height = image.dom.naturalHeight || image.dom.height;
27000 if(width > height){
27001 file.target.addClass('wide');
27005 file.target.addClass('tall');
27010 uploadFromSource : function(file, crop)
27012 this.xhr = new XMLHttpRequest();
27014 this.managerEl.createChild({
27016 cls : 'roo-document-manager-loading',
27020 tooltip : file.name,
27021 cls : 'roo-document-manager-thumb',
27022 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27028 this.xhr.open(this.method, this.url, true);
27031 "Accept": "application/json",
27032 "Cache-Control": "no-cache",
27033 "X-Requested-With": "XMLHttpRequest"
27036 for (var headerName in headers) {
27037 var headerValue = headers[headerName];
27039 this.xhr.setRequestHeader(headerName, headerValue);
27045 this.xhr.onload = function()
27047 _this.xhrOnLoad(_this.xhr);
27050 this.xhr.onerror = function()
27052 _this.xhrOnError(_this.xhr);
27055 var formData = new FormData();
27057 formData.append('returnHTML', 'NO');
27059 formData.append('crop', crop);
27061 if(typeof(file.filename) != 'undefined'){
27062 formData.append('filename', file.filename);
27065 if(typeof(file.mimetype) != 'undefined'){
27066 formData.append('mimetype', file.mimetype);
27069 if(this.fireEvent('prepare', this, formData) != false){
27070 this.xhr.send(formData);
27080 * @class Roo.bootstrap.DocumentViewer
27081 * @extends Roo.bootstrap.Component
27082 * Bootstrap DocumentViewer class
27085 * Create a new DocumentViewer
27086 * @param {Object} config The config object
27089 Roo.bootstrap.DocumentViewer = function(config){
27090 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27095 * Fire after initEvent
27096 * @param {Roo.bootstrap.DocumentViewer} this
27102 * @param {Roo.bootstrap.DocumentViewer} this
27107 * Fire after trash button
27108 * @param {Roo.bootstrap.DocumentViewer} this
27115 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
27117 getAutoCreate : function()
27121 cls : 'roo-document-viewer',
27125 cls : 'roo-document-viewer-body',
27129 cls : 'roo-document-viewer-thumb',
27133 cls : 'roo-document-viewer-image'
27141 cls : 'roo-document-viewer-footer',
27144 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27152 cls : 'btn btn-default roo-document-viewer-trash',
27153 html : '<i class="fa fa-trash"></i>'
27166 initEvents : function()
27169 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27170 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27172 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27173 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27175 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27176 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27178 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27179 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27181 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27182 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27184 this.bodyEl.on('click', this.onClick, this);
27186 this.trashBtn.on('click', this.onTrash, this);
27190 initial : function()
27192 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27195 this.fireEvent('initial', this);
27199 onClick : function(e)
27201 e.preventDefault();
27203 this.fireEvent('click', this);
27206 onTrash : function(e)
27208 e.preventDefault();
27210 this.fireEvent('trash', this);
27222 * @class Roo.bootstrap.NavProgressBar
27223 * @extends Roo.bootstrap.Component
27224 * Bootstrap NavProgressBar class
27227 * Create a new nav progress bar
27228 * @param {Object} config The config object
27231 Roo.bootstrap.NavProgressBar = function(config){
27232 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27234 this.bullets = this.bullets || [];
27236 // Roo.bootstrap.NavProgressBar.register(this);
27240 * Fires when the active item changes
27241 * @param {Roo.bootstrap.NavProgressBar} this
27242 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27243 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
27250 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
27255 getAutoCreate : function()
27257 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27261 cls : 'roo-navigation-bar-group',
27265 cls : 'roo-navigation-top-bar'
27269 cls : 'roo-navigation-bullets-bar',
27273 cls : 'roo-navigation-bar'
27280 cls : 'roo-navigation-bottom-bar'
27290 initEvents: function()
27295 onRender : function(ct, position)
27297 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27299 if(this.bullets.length){
27300 Roo.each(this.bullets, function(b){
27309 addItem : function(cfg)
27311 var item = new Roo.bootstrap.NavProgressItem(cfg);
27313 item.parentId = this.id;
27314 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27317 var top = new Roo.bootstrap.Element({
27319 cls : 'roo-navigation-bar-text'
27322 var bottom = new Roo.bootstrap.Element({
27324 cls : 'roo-navigation-bar-text'
27327 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27328 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27330 var topText = new Roo.bootstrap.Element({
27332 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27335 var bottomText = new Roo.bootstrap.Element({
27337 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27340 topText.onRender(top.el, null);
27341 bottomText.onRender(bottom.el, null);
27344 item.bottomEl = bottom;
27347 this.barItems.push(item);
27352 getActive : function()
27354 var active = false;
27356 Roo.each(this.barItems, function(v){
27358 if (!v.isActive()) {
27370 setActiveItem : function(item)
27374 Roo.each(this.barItems, function(v){
27375 if (v.rid == item.rid) {
27379 if (v.isActive()) {
27380 v.setActive(false);
27385 item.setActive(true);
27387 this.fireEvent('changed', this, item, prev);
27390 getBarItem: function(rid)
27394 Roo.each(this.barItems, function(e) {
27395 if (e.rid != rid) {
27406 indexOfItem : function(item)
27410 Roo.each(this.barItems, function(v, i){
27412 if (v.rid != item.rid) {
27423 setActiveNext : function()
27425 var i = this.indexOfItem(this.getActive());
27427 if (i > this.barItems.length) {
27431 this.setActiveItem(this.barItems[i+1]);
27434 setActivePrev : function()
27436 var i = this.indexOfItem(this.getActive());
27442 this.setActiveItem(this.barItems[i-1]);
27445 format : function()
27447 if(!this.barItems.length){
27451 var width = 100 / this.barItems.length;
27453 Roo.each(this.barItems, function(i){
27454 i.el.setStyle('width', width + '%');
27455 i.topEl.el.setStyle('width', width + '%');
27456 i.bottomEl.el.setStyle('width', width + '%');
27465 * Nav Progress Item
27470 * @class Roo.bootstrap.NavProgressItem
27471 * @extends Roo.bootstrap.Component
27472 * Bootstrap NavProgressItem class
27473 * @cfg {String} rid the reference id
27474 * @cfg {Boolean} active (true|false) Is item active default false
27475 * @cfg {Boolean} disabled (true|false) Is item active default false
27476 * @cfg {String} html
27477 * @cfg {String} position (top|bottom) text position default bottom
27478 * @cfg {String} icon show icon instead of number
27481 * Create a new NavProgressItem
27482 * @param {Object} config The config object
27484 Roo.bootstrap.NavProgressItem = function(config){
27485 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27490 * The raw click event for the entire grid.
27491 * @param {Roo.bootstrap.NavProgressItem} this
27492 * @param {Roo.EventObject} e
27499 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
27505 position : 'bottom',
27508 getAutoCreate : function()
27510 var iconCls = 'roo-navigation-bar-item-icon';
27512 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27516 cls: 'roo-navigation-bar-item',
27526 cfg.cls += ' active';
27529 cfg.cls += ' disabled';
27535 disable : function()
27537 this.setDisabled(true);
27540 enable : function()
27542 this.setDisabled(false);
27545 initEvents: function()
27547 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27549 this.iconEl.on('click', this.onClick, this);
27552 onClick : function(e)
27554 e.preventDefault();
27560 if(this.fireEvent('click', this, e) === false){
27564 this.parent().setActiveItem(this);
27567 isActive: function ()
27569 return this.active;
27572 setActive : function(state)
27574 if(this.active == state){
27578 this.active = state;
27581 this.el.addClass('active');
27585 this.el.removeClass('active');
27590 setDisabled : function(state)
27592 if(this.disabled == state){
27596 this.disabled = state;
27599 this.el.addClass('disabled');
27603 this.el.removeClass('disabled');
27606 tooltipEl : function()
27608 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27621 * @class Roo.bootstrap.FieldLabel
27622 * @extends Roo.bootstrap.Component
27623 * Bootstrap FieldLabel class
27624 * @cfg {String} html contents of the element
27625 * @cfg {String} tag tag of the element default label
27626 * @cfg {String} cls class of the element
27627 * @cfg {String} target label target
27628 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27629 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27630 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27631 * @cfg {String} iconTooltip default "This field is required"
27634 * Create a new FieldLabel
27635 * @param {Object} config The config object
27638 Roo.bootstrap.FieldLabel = function(config){
27639 Roo.bootstrap.Element.superclass.constructor.call(this, config);
27644 * Fires after the field has been marked as invalid.
27645 * @param {Roo.form.FieldLabel} this
27646 * @param {String} msg The validation message
27651 * Fires after the field has been validated with no errors.
27652 * @param {Roo.form.FieldLabel} this
27658 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
27665 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27666 validClass : 'text-success fa fa-lg fa-check',
27667 iconTooltip : 'This field is required',
27669 getAutoCreate : function(){
27673 cls : 'roo-bootstrap-field-label ' + this.cls,
27679 tooltip : this.iconTooltip
27691 initEvents: function()
27693 Roo.bootstrap.Element.superclass.initEvents.call(this);
27695 this.iconEl = this.el.select('i', true).first();
27697 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27699 Roo.bootstrap.FieldLabel.register(this);
27703 * Mark this field as valid
27705 markValid : function()
27707 this.iconEl.show();
27709 this.iconEl.removeClass(this.invalidClass);
27711 this.iconEl.addClass(this.validClass);
27713 this.fireEvent('valid', this);
27717 * Mark this field as invalid
27718 * @param {String} msg The validation message
27720 markInvalid : function(msg)
27722 this.iconEl.show();
27724 this.iconEl.removeClass(this.validClass);
27726 this.iconEl.addClass(this.invalidClass);
27728 this.fireEvent('invalid', this, msg);
27734 Roo.apply(Roo.bootstrap.FieldLabel, {
27739 * register a FieldLabel Group
27740 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27742 register : function(label)
27744 if(this.groups.hasOwnProperty(label.target)){
27748 this.groups[label.target] = label;
27752 * fetch a FieldLabel Group based on the target
27753 * @param {string} target
27754 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27756 get: function(target) {
27757 if (typeof(this.groups[target]) == 'undefined') {
27761 return this.groups[target] ;
27770 * page DateSplitField.
27776 * @class Roo.bootstrap.DateSplitField
27777 * @extends Roo.bootstrap.Component
27778 * Bootstrap DateSplitField class
27779 * @cfg {string} fieldLabel - the label associated
27780 * @cfg {Number} labelWidth set the width of label (0-12)
27781 * @cfg {String} labelAlign (top|left)
27782 * @cfg {Boolean} dayAllowBlank (true|false) default false
27783 * @cfg {Boolean} monthAllowBlank (true|false) default false
27784 * @cfg {Boolean} yearAllowBlank (true|false) default false
27785 * @cfg {string} dayPlaceholder
27786 * @cfg {string} monthPlaceholder
27787 * @cfg {string} yearPlaceholder
27788 * @cfg {string} dayFormat default 'd'
27789 * @cfg {string} monthFormat default 'm'
27790 * @cfg {string} yearFormat default 'Y'
27794 * Create a new DateSplitField
27795 * @param {Object} config The config object
27798 Roo.bootstrap.DateSplitField = function(config){
27799 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27805 * getting the data of years
27806 * @param {Roo.bootstrap.DateSplitField} this
27807 * @param {Object} years
27812 * getting the data of days
27813 * @param {Roo.bootstrap.DateSplitField} this
27814 * @param {Object} days
27819 * Fires after the field has been marked as invalid.
27820 * @param {Roo.form.Field} this
27821 * @param {String} msg The validation message
27826 * Fires after the field has been validated with no errors.
27827 * @param {Roo.form.Field} this
27833 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
27836 labelAlign : 'top',
27838 dayAllowBlank : false,
27839 monthAllowBlank : false,
27840 yearAllowBlank : false,
27841 dayPlaceholder : '',
27842 monthPlaceholder : '',
27843 yearPlaceholder : '',
27847 isFormField : true,
27849 getAutoCreate : function()
27853 cls : 'row roo-date-split-field-group',
27858 cls : 'form-hidden-field roo-date-split-field-group-value',
27864 if(this.fieldLabel){
27867 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27871 html : this.fieldLabel
27877 Roo.each(['day', 'month', 'year'], function(t){
27880 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27887 inputEl: function ()
27889 return this.el.select('.roo-date-split-field-group-value', true).first();
27892 onRender : function(ct, position)
27896 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27898 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27900 this.dayField = new Roo.bootstrap.ComboBox({
27901 allowBlank : this.dayAllowBlank,
27902 alwaysQuery : true,
27903 displayField : 'value',
27906 forceSelection : true,
27908 placeholder : this.dayPlaceholder,
27909 selectOnFocus : true,
27910 tpl : '<div class="select2-result"><b>{value}</b></div>',
27911 triggerAction : 'all',
27913 valueField : 'value',
27914 store : new Roo.data.SimpleStore({
27915 data : (function() {
27917 _this.fireEvent('days', _this, days);
27920 fields : [ 'value' ]
27923 select : function (_self, record, index)
27925 _this.setValue(_this.getValue());
27930 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27932 this.monthField = new Roo.bootstrap.MonthField({
27933 after : '<i class=\"fa fa-calendar\"></i>',
27934 allowBlank : this.monthAllowBlank,
27935 placeholder : this.monthPlaceholder,
27938 render : function (_self)
27940 this.el.select('span.input-group-addon', true).first().on('click', function(e){
27941 e.preventDefault();
27945 select : function (_self, oldvalue, newvalue)
27947 _this.setValue(_this.getValue());
27952 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27954 this.yearField = new Roo.bootstrap.ComboBox({
27955 allowBlank : this.yearAllowBlank,
27956 alwaysQuery : true,
27957 displayField : 'value',
27960 forceSelection : true,
27962 placeholder : this.yearPlaceholder,
27963 selectOnFocus : true,
27964 tpl : '<div class="select2-result"><b>{value}</b></div>',
27965 triggerAction : 'all',
27967 valueField : 'value',
27968 store : new Roo.data.SimpleStore({
27969 data : (function() {
27971 _this.fireEvent('years', _this, years);
27974 fields : [ 'value' ]
27977 select : function (_self, record, index)
27979 _this.setValue(_this.getValue());
27984 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
27987 setValue : function(v, format)
27989 this.inputEl.dom.value = v;
27991 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
27993 var d = Date.parseDate(v, f);
28000 this.setDay(d.format(this.dayFormat));
28001 this.setMonth(d.format(this.monthFormat));
28002 this.setYear(d.format(this.yearFormat));
28009 setDay : function(v)
28011 this.dayField.setValue(v);
28012 this.inputEl.dom.value = this.getValue();
28017 setMonth : function(v)
28019 this.monthField.setValue(v, true);
28020 this.inputEl.dom.value = this.getValue();
28025 setYear : function(v)
28027 this.yearField.setValue(v);
28028 this.inputEl.dom.value = this.getValue();
28033 getDay : function()
28035 return this.dayField.getValue();
28038 getMonth : function()
28040 return this.monthField.getValue();
28043 getYear : function()
28045 return this.yearField.getValue();
28048 getValue : function()
28050 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28052 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28062 this.inputEl.dom.value = '';
28067 validate : function()
28069 var d = this.dayField.validate();
28070 var m = this.monthField.validate();
28071 var y = this.yearField.validate();
28076 (!this.dayAllowBlank && !d) ||
28077 (!this.monthAllowBlank && !m) ||
28078 (!this.yearAllowBlank && !y)
28083 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28092 this.markInvalid();
28097 markValid : function()
28100 var label = this.el.select('label', true).first();
28101 var icon = this.el.select('i.fa-star', true).first();
28107 this.fireEvent('valid', this);
28111 * Mark this field as invalid
28112 * @param {String} msg The validation message
28114 markInvalid : function(msg)
28117 var label = this.el.select('label', true).first();
28118 var icon = this.el.select('i.fa-star', true).first();
28120 if(label && !icon){
28121 this.el.select('.roo-date-split-field-label', true).createChild({
28123 cls : 'text-danger fa fa-lg fa-star',
28124 tooltip : 'This field is required',
28125 style : 'margin-right:5px;'
28129 this.fireEvent('invalid', this, msg);
28132 clearInvalid : function()
28134 var label = this.el.select('label', true).first();
28135 var icon = this.el.select('i.fa-star', true).first();
28141 this.fireEvent('valid', this);
28144 getName: function()