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");
1748 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1758 function onBeforeCheck(mi, state){
1760 var g = groups[mi.group];
1761 for(var i = 0, l = g.length; i < l; i++){
1763 g[i].setChecked(false);
1772 * Hides all menus that are currently visible
1774 hideAll : function(){
1779 register : function(menu){
1783 menus[menu.id] = menu;
1784 menu.on("beforehide", onBeforeHide);
1785 menu.on("hide", onHide);
1786 menu.on("beforeshow", onBeforeShow);
1787 menu.on("show", onShow);
1789 if(g && menu.events["checkchange"]){
1793 groups[g].push(menu);
1794 menu.on("checkchange", onCheck);
1799 * Returns a {@link Roo.menu.Menu} object
1800 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1801 * be used to generate and return a new Menu instance.
1803 get : function(menu){
1804 if(typeof menu == "string"){ // menu id
1806 }else if(menu.events){ // menu instance
1809 /*else if(typeof menu.length == 'number'){ // array of menu items?
1810 return new Roo.bootstrap.Menu({items:menu});
1811 }else{ // otherwise, must be a config
1812 return new Roo.bootstrap.Menu(menu);
1819 unregister : function(menu){
1820 delete menus[menu.id];
1821 menu.un("beforehide", onBeforeHide);
1822 menu.un("hide", onHide);
1823 menu.un("beforeshow", onBeforeShow);
1824 menu.un("show", onShow);
1826 if(g && menu.events["checkchange"]){
1827 groups[g].remove(menu);
1828 menu.un("checkchange", onCheck);
1833 registerCheckable : function(menuItem){
1834 var g = menuItem.group;
1839 groups[g].push(menuItem);
1840 menuItem.on("beforecheckchange", onBeforeCheck);
1845 unregisterCheckable : function(menuItem){
1846 var g = menuItem.group;
1848 groups[g].remove(menuItem);
1849 menuItem.un("beforecheckchange", onBeforeCheck);
1861 * @class Roo.bootstrap.Menu
1862 * @extends Roo.bootstrap.Component
1863 * Bootstrap Menu class - container for MenuItems
1864 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1868 * @param {Object} config The config object
1872 Roo.bootstrap.Menu = function(config){
1873 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1874 if (this.registerMenu) {
1875 Roo.bootstrap.MenuMgr.register(this);
1880 * Fires before this menu is displayed
1881 * @param {Roo.menu.Menu} this
1886 * Fires before this menu is hidden
1887 * @param {Roo.menu.Menu} this
1892 * Fires after this menu is displayed
1893 * @param {Roo.menu.Menu} this
1898 * Fires after this menu is hidden
1899 * @param {Roo.menu.Menu} this
1904 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1905 * @param {Roo.menu.Menu} this
1906 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1907 * @param {Roo.EventObject} e
1912 * Fires when the mouse is hovering over this menu
1913 * @param {Roo.menu.Menu} this
1914 * @param {Roo.EventObject} e
1915 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1920 * Fires when the mouse exits this menu
1921 * @param {Roo.menu.Menu} this
1922 * @param {Roo.EventObject} e
1923 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1928 * Fires when a menu item contained in this menu is clicked
1929 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1930 * @param {Roo.EventObject} e
1934 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1937 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1941 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1944 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1946 registerMenu : true,
1948 menuItems :false, // stores the menu items..
1954 getChildContainer : function() {
1958 getAutoCreate : function(){
1960 //if (['right'].indexOf(this.align)!==-1) {
1961 // cfg.cn[1].cls += ' pull-right'
1967 cls : 'dropdown-menu' ,
1968 style : 'z-index:1000'
1972 if (this.type === 'submenu') {
1973 cfg.cls = 'submenu active';
1975 if (this.type === 'treeview') {
1976 cfg.cls = 'treeview-menu';
1981 initEvents : function() {
1983 // Roo.log("ADD event");
1984 // Roo.log(this.triggerEl.dom);
1985 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1987 this.triggerEl.addClass('dropdown-toggle');
1990 this.el.on('touchstart' , this.onTouch, this);
1992 this.el.on('click' , this.onClick, this);
1994 this.el.on("mouseover", this.onMouseOver, this);
1995 this.el.on("mouseout", this.onMouseOut, this);
1999 findTargetItem : function(e)
2001 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2005 //Roo.log(t); Roo.log(t.id);
2007 //Roo.log(this.menuitems);
2008 return this.menuitems.get(t.id);
2010 //return this.items.get(t.menuItemId);
2016 onTouch : function(e)
2018 //e.stopEvent(); this make the user popdown broken
2022 onClick : function(e)
2024 Roo.log("menu.onClick");
2025 var t = this.findTargetItem(e);
2026 if(!t || t.isContainer){
2031 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2032 if(t == this.activeItem && t.shouldDeactivate(e)){
2033 this.activeItem.deactivate();
2034 delete this.activeItem;
2038 this.setActiveItem(t, true);
2046 Roo.log('pass click event');
2050 this.fireEvent("click", this, t, e);
2054 onMouseOver : function(e){
2055 var t = this.findTargetItem(e);
2058 // if(t.canActivate && !t.disabled){
2059 // this.setActiveItem(t, true);
2063 this.fireEvent("mouseover", this, e, t);
2065 isVisible : function(){
2066 return !this.hidden;
2068 onMouseOut : function(e){
2069 var t = this.findTargetItem(e);
2072 // if(t == this.activeItem && t.shouldDeactivate(e)){
2073 // this.activeItem.deactivate();
2074 // delete this.activeItem;
2077 this.fireEvent("mouseout", this, e, t);
2082 * Displays this menu relative to another element
2083 * @param {String/HTMLElement/Roo.Element} element The element to align to
2084 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2085 * the element (defaults to this.defaultAlign)
2086 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2088 show : function(el, pos, parentMenu){
2089 this.parentMenu = parentMenu;
2093 this.fireEvent("beforeshow", this);
2094 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2097 * Displays this menu at a specific xy position
2098 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2099 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2101 showAt : function(xy, parentMenu, /* private: */_e){
2102 this.parentMenu = parentMenu;
2107 this.fireEvent("beforeshow", this);
2108 //xy = this.el.adjustForConstraints(xy);
2112 this.hideMenuItems();
2113 this.hidden = false;
2114 this.triggerEl.addClass('open');
2116 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2117 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2122 this.fireEvent("show", this);
2128 this.doFocus.defer(50, this);
2132 doFocus : function(){
2134 this.focusEl.focus();
2139 * Hides this menu and optionally all parent menus
2140 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2142 hide : function(deep){
2144 this.hideMenuItems();
2145 if(this.el && this.isVisible()){
2146 this.fireEvent("beforehide", this);
2147 if(this.activeItem){
2148 this.activeItem.deactivate();
2149 this.activeItem = null;
2151 this.triggerEl.removeClass('open');;
2153 this.fireEvent("hide", this);
2155 if(deep === true && this.parentMenu){
2156 this.parentMenu.hide(true);
2160 onTriggerPress : function(e)
2163 Roo.log('trigger press');
2164 //Roo.log(e.getTarget());
2165 // Roo.log(this.triggerEl.dom);
2166 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2170 if (this.isVisible()) {
2175 this.show(this.triggerEl, false, false);
2184 hideMenuItems : function()
2186 //$(backdrop).remove()
2187 Roo.select('.open',true).each(function(aa) {
2189 aa.removeClass('open');
2190 //var parent = getParent($(this))
2191 //var relatedTarget = { relatedTarget: this }
2193 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2194 //if (e.isDefaultPrevented()) return
2195 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2198 addxtypeChild : function (tree, cntr) {
2199 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2201 this.menuitems.add(comp);
2222 * @class Roo.bootstrap.MenuItem
2223 * @extends Roo.bootstrap.Component
2224 * Bootstrap MenuItem class
2225 * @cfg {String} html the menu label
2226 * @cfg {String} href the link
2227 * @cfg {Boolean} preventDefault (true | false) default true
2228 * @cfg {Boolean} isContainer (true | false) default false
2232 * Create a new MenuItem
2233 * @param {Object} config The config object
2237 Roo.bootstrap.MenuItem = function(config){
2238 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2243 * The raw click event for the entire grid.
2244 * @param {Roo.bootstrap.MenuItem} this
2245 * @param {Roo.EventObject} e
2251 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2255 preventDefault: true,
2256 isContainer : false,
2258 getAutoCreate : function(){
2260 if(this.isContainer){
2263 cls: 'dropdown-menu-item'
2269 cls: 'dropdown-menu-item',
2278 if (this.parent().type == 'treeview') {
2279 cfg.cls = 'treeview-menu';
2282 cfg.cn[0].href = this.href || cfg.cn[0].href ;
2283 cfg.cn[0].html = this.html || cfg.cn[0].html ;
2287 initEvents: function() {
2289 //this.el.select('a').on('click', this.onClick, this);
2292 onClick : function(e)
2294 Roo.log('item on click ');
2295 //if(this.preventDefault){
2296 // e.preventDefault();
2298 //this.parent().hideMenuItems();
2300 this.fireEvent('click', this, e);
2319 * @class Roo.bootstrap.MenuSeparator
2320 * @extends Roo.bootstrap.Component
2321 * Bootstrap MenuSeparator class
2324 * Create a new MenuItem
2325 * @param {Object} config The config object
2329 Roo.bootstrap.MenuSeparator = function(config){
2330 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2333 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2335 getAutoCreate : function(){
2354 * @class Roo.bootstrap.Modal
2355 * @extends Roo.bootstrap.Component
2356 * Bootstrap Modal class
2357 * @cfg {String} title Title of dialog
2358 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2359 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2360 * @cfg {Boolean} specificTitle default false
2361 * @cfg {Array} buttons Array of buttons or standard button set..
2362 * @cfg {String} buttonPosition (left|right|center) default right
2363 * @cfg {Boolean} animate default true
2364 * @cfg {Boolean} allow_close default true
2367 * Create a new Modal Dialog
2368 * @param {Object} config The config object
2371 Roo.bootstrap.Modal = function(config){
2372 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2377 * The raw btnclick event for the button
2378 * @param {Roo.EventObject} e
2382 this.buttons = this.buttons || [];
2385 this.tmpl = Roo.factory(this.tmpl);
2390 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2392 title : 'test dialog',
2402 specificTitle: false,
2404 buttonPosition: 'right',
2418 onRender : function(ct, position)
2420 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2423 var cfg = Roo.apply({}, this.getAutoCreate());
2426 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2428 //if (!cfg.name.length) {
2432 cfg.cls += ' ' + this.cls;
2435 cfg.style = this.style;
2437 this.el = Roo.get(document.body).createChild(cfg, position);
2439 //var type = this.el.dom.type;
2442 if(this.tabIndex !== undefined){
2443 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2447 this.bodyEl = this.el.select('.modal-body',true).first();
2448 this.closeEl = this.el.select('.modal-header .close', true).first();
2449 this.footerEl = this.el.select('.modal-footer',true).first();
2450 this.titleEl = this.el.select('.modal-title',true).first();
2454 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2455 this.maskEl.enableDisplayMode("block");
2457 //this.el.addClass("x-dlg-modal");
2459 if (this.buttons.length) {
2460 Roo.each(this.buttons, function(bb) {
2461 var b = Roo.apply({}, bb);
2462 b.xns = b.xns || Roo.bootstrap;
2463 b.xtype = b.xtype || 'Button';
2464 if (typeof(b.listeners) == 'undefined') {
2465 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2468 var btn = Roo.factory(b);
2470 btn.onRender(this.el.select('.modal-footer div').first());
2474 // render the children.
2477 if(typeof(this.items) != 'undefined'){
2478 var items = this.items;
2481 for(var i =0;i < items.length;i++) {
2482 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2486 this.items = nitems;
2488 // where are these used - they used to be body/close/footer
2492 //this.el.addClass([this.fieldClass, this.cls]);
2496 getAutoCreate : function(){
2501 html : this.html || ''
2506 cls : 'modal-title',
2510 if(this.specificTitle){
2516 if (this.allow_close) {
2527 style : 'display: none',
2530 cls: "modal-dialog",
2533 cls : "modal-content",
2536 cls : 'modal-header',
2541 cls : 'modal-footer',
2545 cls: 'btn-' + this.buttonPosition
2562 modal.cls += ' fade';
2568 getChildContainer : function() {
2573 getButtonContainer : function() {
2574 return this.el.select('.modal-footer div',true).first();
2577 initEvents : function()
2579 if (this.allow_close) {
2580 this.closeEl.on('click', this.hide, this);
2585 window.addEventListener("resize", function() { _this.resize(); } );
2591 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2596 if (!this.rendered) {
2600 this.el.setStyle('display', 'block');
2602 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2605 this.el.addClass('in');
2608 this.el.addClass('in');
2612 // not sure how we can show data in here..
2614 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2617 Roo.get(document.body).addClass("x-body-masked");
2618 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2620 this.el.setStyle('zIndex', '10001');
2622 this.fireEvent('show', this);
2630 Roo.get(document.body).removeClass("x-body-masked");
2631 this.el.removeClass('in');
2632 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2634 if(this.animate){ // why
2636 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2638 this.el.setStyle('display', 'none');
2641 this.fireEvent('hide', this);
2644 addButton : function(str, cb)
2648 var b = Roo.apply({}, { html : str } );
2649 b.xns = b.xns || Roo.bootstrap;
2650 b.xtype = b.xtype || 'Button';
2651 if (typeof(b.listeners) == 'undefined') {
2652 b.listeners = { click : cb.createDelegate(this) };
2655 var btn = Roo.factory(b);
2657 btn.onRender(this.el.select('.modal-footer div').first());
2663 setDefaultButton : function(btn)
2665 //this.el.select('.modal-footer').()
2667 resizeTo: function(w,h)
2671 setContentSize : function(w, h)
2675 onButtonClick: function(btn,e)
2678 this.fireEvent('btnclick', btn.name, e);
2681 * Set the title of the Dialog
2682 * @param {String} str new Title
2684 setTitle: function(str) {
2685 this.titleEl.dom.innerHTML = str;
2688 * Set the body of the Dialog
2689 * @param {String} str new Title
2691 setBody: function(str) {
2692 this.bodyEl.dom.innerHTML = str;
2695 * Set the body of the Dialog using the template
2696 * @param {Obj} data - apply this data to the template and replace the body contents.
2698 applyBody: function(obj)
2701 Roo.log("Error - using apply Body without a template");
2704 this.tmpl.overwrite(this.bodyEl, obj);
2710 Roo.apply(Roo.bootstrap.Modal, {
2712 * Button config that displays a single OK button
2721 * Button config that displays Yes and No buttons
2737 * Button config that displays OK and Cancel buttons
2752 * Button config that displays Yes, No and Cancel buttons
2775 * messagebox - can be used as a replace
2779 * @class Roo.MessageBox
2780 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2784 Roo.Msg.alert('Status', 'Changes saved successfully.');
2786 // Prompt for user data:
2787 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2789 // process text value...
2793 // Show a dialog using config options:
2795 title:'Save Changes?',
2796 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2797 buttons: Roo.Msg.YESNOCANCEL,
2804 Roo.bootstrap.MessageBox = function(){
2805 var dlg, opt, mask, waitTimer;
2806 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2807 var buttons, activeTextEl, bwidth;
2811 var handleButton = function(button){
2813 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2817 var handleHide = function(){
2819 dlg.el.removeClass(opt.cls);
2822 // Roo.TaskMgr.stop(waitTimer);
2823 // waitTimer = null;
2828 var updateButtons = function(b){
2831 buttons["ok"].hide();
2832 buttons["cancel"].hide();
2833 buttons["yes"].hide();
2834 buttons["no"].hide();
2835 //dlg.footer.dom.style.display = 'none';
2838 dlg.footerEl.dom.style.display = '';
2839 for(var k in buttons){
2840 if(typeof buttons[k] != "function"){
2843 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2844 width += buttons[k].el.getWidth()+15;
2854 var handleEsc = function(d, k, e){
2855 if(opt && opt.closable !== false){
2865 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2866 * @return {Roo.BasicDialog} The BasicDialog element
2868 getDialog : function(){
2870 dlg = new Roo.bootstrap.Modal( {
2873 //constraintoviewport:false,
2875 //collapsible : false,
2880 //buttonAlign:"center",
2881 closeClick : function(){
2882 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2885 handleButton("cancel");
2890 dlg.on("hide", handleHide);
2892 //dlg.addKeyListener(27, handleEsc);
2894 this.buttons = buttons;
2895 var bt = this.buttonText;
2896 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2897 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2898 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2899 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2901 bodyEl = dlg.bodyEl.createChild({
2903 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2904 '<textarea class="roo-mb-textarea"></textarea>' +
2905 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2907 msgEl = bodyEl.dom.firstChild;
2908 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2909 textboxEl.enableDisplayMode();
2910 textboxEl.addKeyListener([10,13], function(){
2911 if(dlg.isVisible() && opt && opt.buttons){
2914 }else if(opt.buttons.yes){
2915 handleButton("yes");
2919 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2920 textareaEl.enableDisplayMode();
2921 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2922 progressEl.enableDisplayMode();
2923 var pf = progressEl.dom.firstChild;
2925 pp = Roo.get(pf.firstChild);
2926 pp.setHeight(pf.offsetHeight);
2934 * Updates the message box body text
2935 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2936 * the XHTML-compliant non-breaking space character '&#160;')
2937 * @return {Roo.MessageBox} This message box
2939 updateText : function(text){
2940 if(!dlg.isVisible() && !opt.width){
2941 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2943 msgEl.innerHTML = text || ' ';
2945 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2946 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2948 Math.min(opt.width || cw , this.maxWidth),
2949 Math.max(opt.minWidth || this.minWidth, bwidth)
2952 activeTextEl.setWidth(w);
2954 if(dlg.isVisible()){
2955 dlg.fixedcenter = false;
2957 // to big, make it scroll. = But as usual stupid IE does not support
2960 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2961 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2962 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2964 bodyEl.dom.style.height = '';
2965 bodyEl.dom.style.overflowY = '';
2968 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2970 bodyEl.dom.style.overflowX = '';
2973 dlg.setContentSize(w, bodyEl.getHeight());
2974 if(dlg.isVisible()){
2975 dlg.fixedcenter = true;
2981 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
2982 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2983 * @param {Number} value Any number between 0 and 1 (e.g., .5)
2984 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2985 * @return {Roo.MessageBox} This message box
2987 updateProgress : function(value, text){
2989 this.updateText(text);
2991 if (pp) { // weird bug on my firefox - for some reason this is not defined
2992 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2998 * Returns true if the message box is currently displayed
2999 * @return {Boolean} True if the message box is visible, else false
3001 isVisible : function(){
3002 return dlg && dlg.isVisible();
3006 * Hides the message box if it is displayed
3009 if(this.isVisible()){
3015 * Displays a new message box, or reinitializes an existing message box, based on the config options
3016 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3017 * The following config object properties are supported:
3019 Property Type Description
3020 ---------- --------------- ------------------------------------------------------------------------------------
3021 animEl String/Element An id or Element from which the message box should animate as it opens and
3022 closes (defaults to undefined)
3023 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3024 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3025 closable Boolean False to hide the top-right close button (defaults to true). Note that
3026 progress and wait dialogs will ignore this property and always hide the
3027 close button as they can only be closed programmatically.
3028 cls String A custom CSS class to apply to the message box element
3029 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3030 displayed (defaults to 75)
3031 fn Function A callback function to execute after closing the dialog. The arguments to the
3032 function will be btn (the name of the button that was clicked, if applicable,
3033 e.g. "ok"), and text (the value of the active text field, if applicable).
3034 Progress and wait dialogs will ignore this option since they do not respond to
3035 user actions and can only be closed programmatically, so any required function
3036 should be called by the same code after it closes the dialog.
3037 icon String A CSS class that provides a background image to be used as an icon for
3038 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3039 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3040 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3041 modal Boolean False to allow user interaction with the page while the message box is
3042 displayed (defaults to true)
3043 msg String A string that will replace the existing message box body text (defaults
3044 to the XHTML-compliant non-breaking space character ' ')
3045 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3046 progress Boolean True to display a progress bar (defaults to false)
3047 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3048 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3049 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3050 title String The title text
3051 value String The string value to set into the active textbox element if displayed
3052 wait Boolean True to display a progress bar (defaults to false)
3053 width Number The width of the dialog in pixels
3060 msg: 'Please enter your address:',
3062 buttons: Roo.MessageBox.OKCANCEL,
3065 animEl: 'addAddressBtn'
3068 * @param {Object} config Configuration options
3069 * @return {Roo.MessageBox} This message box
3071 show : function(options)
3074 // this causes nightmares if you show one dialog after another
3075 // especially on callbacks..
3077 if(this.isVisible()){
3080 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3081 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3082 Roo.log("New Dialog Message:" + options.msg )
3083 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3084 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3087 var d = this.getDialog();
3089 d.setTitle(opt.title || " ");
3090 d.closeEl.setDisplayed(opt.closable !== false);
3091 activeTextEl = textboxEl;
3092 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3097 textareaEl.setHeight(typeof opt.multiline == "number" ?
3098 opt.multiline : this.defaultTextHeight);
3099 activeTextEl = textareaEl;
3108 progressEl.setDisplayed(opt.progress === true);
3109 this.updateProgress(0);
3110 activeTextEl.dom.value = opt.value || "";
3112 dlg.setDefaultButton(activeTextEl);
3114 var bs = opt.buttons;
3118 }else if(bs && bs.yes){
3119 db = buttons["yes"];
3121 dlg.setDefaultButton(db);
3123 bwidth = updateButtons(opt.buttons);
3124 this.updateText(opt.msg);
3126 d.el.addClass(opt.cls);
3128 d.proxyDrag = opt.proxyDrag === true;
3129 d.modal = opt.modal !== false;
3130 d.mask = opt.modal !== false ? mask : false;
3132 // force it to the end of the z-index stack so it gets a cursor in FF
3133 document.body.appendChild(dlg.el.dom);
3134 d.animateTarget = null;
3135 d.show(options.animEl);
3141 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3142 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3143 * and closing the message box when the process is complete.
3144 * @param {String} title The title bar text
3145 * @param {String} msg The message box body text
3146 * @return {Roo.MessageBox} This message box
3148 progress : function(title, msg){
3155 minWidth: this.minProgressWidth,
3162 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3163 * If a callback function is passed it will be called after the user clicks the button, and the
3164 * id of the button that was clicked will be passed as the only parameter to the callback
3165 * (could also be the top-right close button).
3166 * @param {String} title The title bar text
3167 * @param {String} msg The message box body text
3168 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3169 * @param {Object} scope (optional) The scope of the callback function
3170 * @return {Roo.MessageBox} This message box
3172 alert : function(title, msg, fn, scope){
3185 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3186 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3187 * You are responsible for closing the message box when the process is complete.
3188 * @param {String} msg The message box body text
3189 * @param {String} title (optional) The title bar text
3190 * @return {Roo.MessageBox} This message box
3192 wait : function(msg, title){
3203 waitTimer = Roo.TaskMgr.start({
3205 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3213 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3214 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3215 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3216 * @param {String} title The title bar text
3217 * @param {String} msg The message box body text
3218 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3219 * @param {Object} scope (optional) The scope of the callback function
3220 * @return {Roo.MessageBox} This message box
3222 confirm : function(title, msg, fn, scope){
3226 buttons: this.YESNO,
3235 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3236 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3237 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3238 * (could also be the top-right close button) and the text that was entered will be passed as the two
3239 * parameters to the callback.
3240 * @param {String} title The title bar text
3241 * @param {String} msg The message box body text
3242 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3243 * @param {Object} scope (optional) The scope of the callback function
3244 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3245 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3246 * @return {Roo.MessageBox} This message box
3248 prompt : function(title, msg, fn, scope, multiline){
3252 buttons: this.OKCANCEL,
3257 multiline: multiline,
3264 * Button config that displays a single OK button
3269 * Button config that displays Yes and No buttons
3272 YESNO : {yes:true, no:true},
3274 * Button config that displays OK and Cancel buttons
3277 OKCANCEL : {ok:true, cancel:true},
3279 * Button config that displays Yes, No and Cancel buttons
3282 YESNOCANCEL : {yes:true, no:true, cancel:true},
3285 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3288 defaultTextHeight : 75,
3290 * The maximum width in pixels of the message box (defaults to 600)
3295 * The minimum width in pixels of the message box (defaults to 100)
3300 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3301 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3304 minProgressWidth : 250,
3306 * An object containing the default button text strings that can be overriden for localized language support.
3307 * Supported properties are: ok, cancel, yes and no.
3308 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3321 * Shorthand for {@link Roo.MessageBox}
3323 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3324 Roo.Msg = Roo.Msg || Roo.MessageBox;
3333 * @class Roo.bootstrap.Navbar
3334 * @extends Roo.bootstrap.Component
3335 * Bootstrap Navbar class
3338 * Create a new Navbar
3339 * @param {Object} config The config object
3343 Roo.bootstrap.Navbar = function(config){
3344 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3348 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3357 getAutoCreate : function(){
3360 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3364 initEvents :function ()
3366 //Roo.log(this.el.select('.navbar-toggle',true));
3367 this.el.select('.navbar-toggle',true).on('click', function() {
3368 // Roo.log('click');
3369 this.el.select('.navbar-collapse',true).toggleClass('in');
3377 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3379 var size = this.el.getSize();
3380 this.maskEl.setSize(size.width, size.height);
3381 this.maskEl.enableDisplayMode("block");
3390 getChildContainer : function()
3392 if (this.el.select('.collapse').getCount()) {
3393 return this.el.select('.collapse',true).first();
3426 * @class Roo.bootstrap.NavSimplebar
3427 * @extends Roo.bootstrap.Navbar
3428 * Bootstrap Sidebar class
3430 * @cfg {Boolean} inverse is inverted color
3432 * @cfg {String} type (nav | pills | tabs)
3433 * @cfg {Boolean} arrangement stacked | justified
3434 * @cfg {String} align (left | right) alignment
3436 * @cfg {Boolean} main (true|false) main nav bar? default false
3437 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3439 * @cfg {String} tag (header|footer|nav|div) default is nav
3445 * Create a new Sidebar
3446 * @param {Object} config The config object
3450 Roo.bootstrap.NavSimplebar = function(config){
3451 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3454 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3470 getAutoCreate : function(){
3474 tag : this.tag || 'div',
3487 this.type = this.type || 'nav';
3488 if (['tabs','pills'].indexOf(this.type)!==-1) {
3489 cfg.cn[0].cls += ' nav-' + this.type
3493 if (this.type!=='nav') {
3494 Roo.log('nav type must be nav/tabs/pills')
3496 cfg.cn[0].cls += ' navbar-nav'
3502 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3503 cfg.cn[0].cls += ' nav-' + this.arrangement;
3507 if (this.align === 'right') {
3508 cfg.cn[0].cls += ' navbar-right';
3512 cfg.cls += ' navbar-inverse';
3539 * @class Roo.bootstrap.NavHeaderbar
3540 * @extends Roo.bootstrap.NavSimplebar
3541 * Bootstrap Sidebar class
3543 * @cfg {String} brand what is brand
3544 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3545 * @cfg {String} brand_href href of the brand
3546 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3547 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3548 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3549 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3552 * Create a new Sidebar
3553 * @param {Object} config The config object
3557 Roo.bootstrap.NavHeaderbar = function(config){
3558 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3562 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3569 desktopCenter : false,
3572 getAutoCreate : function(){
3575 tag: this.nav || 'nav',
3582 if (this.desktopCenter) {
3583 cn.push({cls : 'container', cn : []});
3590 cls: 'navbar-header',
3595 cls: 'navbar-toggle',
3596 'data-toggle': 'collapse',
3601 html: 'Toggle navigation'
3623 cls: 'collapse navbar-collapse',
3627 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3629 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3630 cfg.cls += ' navbar-' + this.position;
3632 // tag can override this..
3634 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3637 if (this.brand !== '') {
3640 href: this.brand_href ? this.brand_href : '#',
3641 cls: 'navbar-brand',
3649 cfg.cls += ' main-nav';
3657 getHeaderChildContainer : function()
3659 if (this.el.select('.navbar-header').getCount()) {
3660 return this.el.select('.navbar-header',true).first();
3663 return this.getChildContainer();
3667 initEvents : function()
3669 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3671 if (this.autohide) {
3676 Roo.get(document).on('scroll',function(e) {
3677 var ns = Roo.get(document).getScroll().top;
3678 var os = prevScroll;
3682 ft.removeClass('slideDown');
3683 ft.addClass('slideUp');
3686 ft.removeClass('slideUp');
3687 ft.addClass('slideDown');
3708 * @class Roo.bootstrap.NavSidebar
3709 * @extends Roo.bootstrap.Navbar
3710 * Bootstrap Sidebar class
3713 * Create a new Sidebar
3714 * @param {Object} config The config object
3718 Roo.bootstrap.NavSidebar = function(config){
3719 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3722 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3724 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3726 getAutoCreate : function(){
3731 cls: 'sidebar sidebar-nav'
3753 * @class Roo.bootstrap.NavGroup
3754 * @extends Roo.bootstrap.Component
3755 * Bootstrap NavGroup class
3756 * @cfg {String} align (left|right)
3757 * @cfg {Boolean} inverse
3758 * @cfg {String} type (nav|pills|tab) default nav
3759 * @cfg {String} navId - reference Id for navbar.
3763 * Create a new nav group
3764 * @param {Object} config The config object
3767 Roo.bootstrap.NavGroup = function(config){
3768 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3771 Roo.bootstrap.NavGroup.register(this);
3775 * Fires when the active item changes
3776 * @param {Roo.bootstrap.NavGroup} this
3777 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3778 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3785 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3796 getAutoCreate : function()
3798 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3805 if (['tabs','pills'].indexOf(this.type)!==-1) {
3806 cfg.cls += ' nav-' + this.type
3808 if (this.type!=='nav') {
3809 Roo.log('nav type must be nav/tabs/pills')
3811 cfg.cls += ' navbar-nav'
3814 if (this.parent().sidebar) {
3817 cls: 'dashboard-menu sidebar-menu'
3823 if (this.form === true) {
3829 if (this.align === 'right') {
3830 cfg.cls += ' navbar-right';
3832 cfg.cls += ' navbar-left';
3836 if (this.align === 'right') {
3837 cfg.cls += ' navbar-right';
3841 cfg.cls += ' navbar-inverse';
3849 * sets the active Navigation item
3850 * @param {Roo.bootstrap.NavItem} the new current navitem
3852 setActiveItem : function(item)
3855 Roo.each(this.navItems, function(v){
3860 v.setActive(false, true);
3867 item.setActive(true, true);
3868 this.fireEvent('changed', this, item, prev);
3873 * gets the active Navigation item
3874 * @return {Roo.bootstrap.NavItem} the current navitem
3876 getActive : function()
3880 Roo.each(this.navItems, function(v){
3891 indexOfNav : function()
3895 Roo.each(this.navItems, function(v,i){
3906 * adds a Navigation item
3907 * @param {Roo.bootstrap.NavItem} the navitem to add
3909 addItem : function(cfg)
3911 var cn = new Roo.bootstrap.NavItem(cfg);
3913 cn.parentId = this.id;
3914 cn.onRender(this.el, null);
3918 * register a Navigation item
3919 * @param {Roo.bootstrap.NavItem} the navitem to add
3921 register : function(item)
3923 this.navItems.push( item);
3924 item.navId = this.navId;
3929 * clear all the Navigation item
3932 clearAll : function()
3935 this.el.dom.innerHTML = '';
3938 getNavItem: function(tabId)
3941 Roo.each(this.navItems, function(e) {
3942 if (e.tabId == tabId) {
3952 setActiveNext : function()
3954 var i = this.indexOfNav(this.getActive());
3955 if (i > this.navItems.length) {
3958 this.setActiveItem(this.navItems[i+1]);
3960 setActivePrev : function()
3962 var i = this.indexOfNav(this.getActive());
3966 this.setActiveItem(this.navItems[i-1]);
3968 clearWasActive : function(except) {
3969 Roo.each(this.navItems, function(e) {
3970 if (e.tabId != except.tabId && e.was_active) {
3971 e.was_active = false;
3978 getWasActive : function ()
3981 Roo.each(this.navItems, function(e) {
3996 Roo.apply(Roo.bootstrap.NavGroup, {
4000 * register a Navigation Group
4001 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4003 register : function(navgrp)
4005 this.groups[navgrp.navId] = navgrp;
4009 * fetch a Navigation Group based on the navigation ID
4010 * @param {string} the navgroup to add
4011 * @returns {Roo.bootstrap.NavGroup} the navgroup
4013 get: function(navId) {
4014 if (typeof(this.groups[navId]) == 'undefined') {
4016 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4018 return this.groups[navId] ;
4033 * @class Roo.bootstrap.NavItem
4034 * @extends Roo.bootstrap.Component
4035 * Bootstrap Navbar.NavItem class
4036 * @cfg {String} href link to
4037 * @cfg {String} html content of button
4038 * @cfg {String} badge text inside badge
4039 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4040 * @cfg {String} glyphicon name of glyphicon
4041 * @cfg {String} icon name of font awesome icon
4042 * @cfg {Boolean} active Is item active
4043 * @cfg {Boolean} disabled Is item disabled
4045 * @cfg {Boolean} preventDefault (true | false) default false
4046 * @cfg {String} tabId the tab that this item activates.
4047 * @cfg {String} tagtype (a|span) render as a href or span?
4048 * @cfg {Boolean} animateRef (true|false) link to element default false
4051 * Create a new Navbar Item
4052 * @param {Object} config The config object
4054 Roo.bootstrap.NavItem = function(config){
4055 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4060 * The raw click event for the entire grid.
4061 * @param {Roo.EventObject} e
4066 * Fires when the active item active state changes
4067 * @param {Roo.bootstrap.NavItem} this
4068 * @param {boolean} state the new state
4074 * Fires when scroll to element
4075 * @param {Roo.bootstrap.NavItem} this
4076 * @param {Object} options
4077 * @param {Roo.EventObject} e
4085 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4093 preventDefault : false,
4100 getAutoCreate : function(){
4109 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4111 if (this.disabled) {
4112 cfg.cls += ' disabled';
4115 if (this.href || this.html || this.glyphicon || this.icon) {
4119 href : this.href || "#",
4120 html: this.html || ''
4125 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4128 if(this.glyphicon) {
4129 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4134 cfg.cn[0].html += " <span class='caret'></span>";
4138 if (this.badge !== '') {
4140 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4148 initEvents: function()
4150 if (typeof (this.menu) != 'undefined') {
4151 this.menu.parentType = this.xtype;
4152 this.menu.triggerEl = this.el;
4153 this.menu = this.addxtype(Roo.apply({}, this.menu));
4156 this.el.select('a',true).on('click', this.onClick, this);
4158 if(this.tagtype == 'span'){
4159 this.el.select('span',true).on('click', this.onClick, this);
4162 // at this point parent should be available..
4163 this.parent().register(this);
4166 onClick : function(e)
4169 this.preventDefault ||
4176 if (this.disabled) {
4180 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4181 if (tg && tg.transition) {
4182 Roo.log("waiting for the transitionend");
4188 //Roo.log("fire event clicked");
4189 if(this.fireEvent('click', this, e) === false){
4193 if(this.tagtype == 'span'){
4197 //Roo.log(this.href);
4198 var ael = this.el.select('a',true).first();
4201 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4202 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4203 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4204 return; // ignore... - it's a 'hash' to another page.
4208 this.scrollToElement(e);
4212 var p = this.parent();
4214 if (['tabs','pills'].indexOf(p.type)!==-1) {
4215 if (typeof(p.setActiveItem) !== 'undefined') {
4216 p.setActiveItem(this);
4220 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4221 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4222 // remove the collapsed menu expand...
4223 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4227 isActive: function () {
4230 setActive : function(state, fire, is_was_active)
4232 if (this.active && !state && this.navId) {
4233 this.was_active = true;
4234 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4236 nv.clearWasActive(this);
4240 this.active = state;
4243 this.el.removeClass('active');
4244 } else if (!this.el.hasClass('active')) {
4245 this.el.addClass('active');
4248 this.fireEvent('changed', this, state);
4251 // show a panel if it's registered and related..
4253 if (!this.navId || !this.tabId || !state || is_was_active) {
4257 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4261 var pan = tg.getPanelByName(this.tabId);
4265 // if we can not flip to new panel - go back to old nav highlight..
4266 if (false == tg.showPanel(pan)) {
4267 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4269 var onav = nv.getWasActive();
4271 onav.setActive(true, false, true);
4280 // this should not be here...
4281 setDisabled : function(state)
4283 this.disabled = state;
4285 this.el.removeClass('disabled');
4286 } else if (!this.el.hasClass('disabled')) {
4287 this.el.addClass('disabled');
4293 * Fetch the element to display the tooltip on.
4294 * @return {Roo.Element} defaults to this.el
4296 tooltipEl : function()
4298 return this.el.select('' + this.tagtype + '', true).first();
4301 scrollToElement : function(e)
4303 var c = document.body;
4306 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4308 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4309 c = document.documentElement;
4312 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4318 var o = target.calcOffsetsTo(c);
4325 this.fireEvent('scrollto', this, options, e);
4327 Roo.get(c).scrollTo('top', options.value, true);
4340 * <span> icon </span>
4341 * <span> text </span>
4342 * <span>badge </span>
4346 * @class Roo.bootstrap.NavSidebarItem
4347 * @extends Roo.bootstrap.NavItem
4348 * Bootstrap Navbar.NavSidebarItem class
4349 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4351 * Create a new Navbar Button
4352 * @param {Object} config The config object
4354 Roo.bootstrap.NavSidebarItem = function(config){
4355 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4360 * The raw click event for the entire grid.
4361 * @param {Roo.EventObject} e
4366 * Fires when the active item active state changes
4367 * @param {Roo.bootstrap.NavSidebarItem} this
4368 * @param {boolean} state the new state
4376 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4378 badgeWeight : 'default',
4380 getAutoCreate : function(){
4385 href : this.href || '#',
4397 html : this.html || ''
4402 cfg.cls += ' active';
4405 if (this.disabled) {
4406 cfg.cls += ' disabled';
4410 if (this.glyphicon || this.icon) {
4411 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4412 a.cn.push({ tag : 'i', cls : c }) ;
4417 if (this.badge !== '') {
4419 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4423 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4424 a.cls += 'dropdown-toggle treeview' ;
4435 initEvents : function()
4437 this.el.on('click', this.onClick, this);
4440 if(this.badge !== ''){
4442 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4447 onClick : function(e)
4454 if(this.preventDefault){
4458 this.fireEvent('click', this);
4461 disable : function()
4463 this.setDisabled(true);
4468 this.setDisabled(false);
4471 setDisabled : function(state)
4473 if(this.disabled == state){
4477 this.disabled = state;
4480 this.el.addClass('disabled');
4484 this.el.removeClass('disabled');
4489 setActive : function(state)
4491 if(this.active == state){
4495 this.active = state;
4498 this.el.addClass('active');
4502 this.el.removeClass('active');
4507 isActive: function ()
4512 setBadge : function(str)
4518 this.badgeEl.dom.innerHTML = str;
4535 * @class Roo.bootstrap.Row
4536 * @extends Roo.bootstrap.Component
4537 * Bootstrap Row class (contains columns...)
4541 * @param {Object} config The config object
4544 Roo.bootstrap.Row = function(config){
4545 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4548 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4550 getAutoCreate : function(){
4569 * @class Roo.bootstrap.Element
4570 * @extends Roo.bootstrap.Component
4571 * Bootstrap Element class
4572 * @cfg {String} html contents of the element
4573 * @cfg {String} tag tag of the element
4574 * @cfg {String} cls class of the element
4575 * @cfg {Boolean} preventDefault (true|false) default false
4576 * @cfg {Boolean} clickable (true|false) default false
4579 * Create a new Element
4580 * @param {Object} config The config object
4583 Roo.bootstrap.Element = function(config){
4584 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4590 * When a element is chick
4591 * @param {Roo.bootstrap.Element} this
4592 * @param {Roo.EventObject} e
4598 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4603 preventDefault: false,
4606 getAutoCreate : function(){
4617 initEvents: function()
4619 Roo.bootstrap.Element.superclass.initEvents.call(this);
4622 this.el.on('click', this.onClick, this);
4627 onClick : function(e)
4629 if(this.preventDefault){
4633 this.fireEvent('click', this, e);
4636 getValue : function()
4638 return this.el.dom.innerHTML;
4641 setValue : function(value)
4643 this.el.dom.innerHTML = value;
4658 * @class Roo.bootstrap.Pagination
4659 * @extends Roo.bootstrap.Component
4660 * Bootstrap Pagination class
4661 * @cfg {String} size xs | sm | md | lg
4662 * @cfg {Boolean} inverse false | true
4665 * Create a new Pagination
4666 * @param {Object} config The config object
4669 Roo.bootstrap.Pagination = function(config){
4670 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4673 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4679 getAutoCreate : function(){
4685 cfg.cls += ' inverse';
4691 cfg.cls += " " + this.cls;
4709 * @class Roo.bootstrap.PaginationItem
4710 * @extends Roo.bootstrap.Component
4711 * Bootstrap PaginationItem class
4712 * @cfg {String} html text
4713 * @cfg {String} href the link
4714 * @cfg {Boolean} preventDefault (true | false) default true
4715 * @cfg {Boolean} active (true | false) default false
4716 * @cfg {Boolean} disabled default false
4720 * Create a new PaginationItem
4721 * @param {Object} config The config object
4725 Roo.bootstrap.PaginationItem = function(config){
4726 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4731 * The raw click event for the entire grid.
4732 * @param {Roo.EventObject} e
4738 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4742 preventDefault: true,
4747 getAutoCreate : function(){
4753 href : this.href ? this.href : '#',
4754 html : this.html ? this.html : ''
4764 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4768 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4774 initEvents: function() {
4776 this.el.on('click', this.onClick, this);
4779 onClick : function(e)
4781 Roo.log('PaginationItem on click ');
4782 if(this.preventDefault){
4790 this.fireEvent('click', this, e);
4806 * @class Roo.bootstrap.Slider
4807 * @extends Roo.bootstrap.Component
4808 * Bootstrap Slider class
4811 * Create a new Slider
4812 * @param {Object} config The config object
4815 Roo.bootstrap.Slider = function(config){
4816 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4819 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4821 getAutoCreate : function(){
4825 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4829 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4841 * Ext JS Library 1.1.1
4842 * Copyright(c) 2006-2007, Ext JS, LLC.
4844 * Originally Released Under LGPL - original licence link has changed is not relivant.
4847 * <script type="text/javascript">
4852 * @class Roo.grid.ColumnModel
4853 * @extends Roo.util.Observable
4854 * This is the default implementation of a ColumnModel used by the Grid. It defines
4855 * the columns in the grid.
4858 var colModel = new Roo.grid.ColumnModel([
4859 {header: "Ticker", width: 60, sortable: true, locked: true},
4860 {header: "Company Name", width: 150, sortable: true},
4861 {header: "Market Cap.", width: 100, sortable: true},
4862 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4863 {header: "Employees", width: 100, sortable: true, resizable: false}
4868 * The config options listed for this class are options which may appear in each
4869 * individual column definition.
4870 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4872 * @param {Object} config An Array of column config objects. See this class's
4873 * config objects for details.
4875 Roo.grid.ColumnModel = function(config){
4877 * The config passed into the constructor
4879 this.config = config;
4882 // if no id, create one
4883 // if the column does not have a dataIndex mapping,
4884 // map it to the order it is in the config
4885 for(var i = 0, len = config.length; i < len; i++){
4887 if(typeof c.dataIndex == "undefined"){
4890 if(typeof c.renderer == "string"){
4891 c.renderer = Roo.util.Format[c.renderer];
4893 if(typeof c.id == "undefined"){
4896 if(c.editor && c.editor.xtype){
4897 c.editor = Roo.factory(c.editor, Roo.grid);
4899 if(c.editor && c.editor.isFormField){
4900 c.editor = new Roo.grid.GridEditor(c.editor);
4902 this.lookup[c.id] = c;
4906 * The width of columns which have no width specified (defaults to 100)
4909 this.defaultWidth = 100;
4912 * Default sortable of columns which have no sortable specified (defaults to false)
4915 this.defaultSortable = false;
4919 * @event widthchange
4920 * Fires when the width of a column changes.
4921 * @param {ColumnModel} this
4922 * @param {Number} columnIndex The column index
4923 * @param {Number} newWidth The new width
4925 "widthchange": true,
4927 * @event headerchange
4928 * Fires when the text of a header changes.
4929 * @param {ColumnModel} this
4930 * @param {Number} columnIndex The column index
4931 * @param {Number} newText The new header text
4933 "headerchange": true,
4935 * @event hiddenchange
4936 * Fires when a column is hidden or "unhidden".
4937 * @param {ColumnModel} this
4938 * @param {Number} columnIndex The column index
4939 * @param {Boolean} hidden true if hidden, false otherwise
4941 "hiddenchange": true,
4943 * @event columnmoved
4944 * Fires when a column is moved.
4945 * @param {ColumnModel} this
4946 * @param {Number} oldIndex
4947 * @param {Number} newIndex
4949 "columnmoved" : true,
4951 * @event columlockchange
4952 * Fires when a column's locked state is changed
4953 * @param {ColumnModel} this
4954 * @param {Number} colIndex
4955 * @param {Boolean} locked true if locked
4957 "columnlockchange" : true
4959 Roo.grid.ColumnModel.superclass.constructor.call(this);
4961 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4963 * @cfg {String} header The header text to display in the Grid view.
4966 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4967 * {@link Roo.data.Record} definition from which to draw the column's value. If not
4968 * specified, the column's index is used as an index into the Record's data Array.
4971 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4972 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4975 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4976 * Defaults to the value of the {@link #defaultSortable} property.
4977 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4980 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
4983 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
4986 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4989 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4992 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4993 * given the cell's data value. See {@link #setRenderer}. If not specified, the
4994 * default renderer uses the raw data value. If an object is returned (bootstrap only)
4995 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4998 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5001 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5004 * @cfg {String} cursor (Optional)
5007 * @cfg {String} tooltip (Optional)
5010 * @cfg {Number} xs (Optional)
5013 * @cfg {Number} sm (Optional)
5016 * @cfg {Number} md (Optional)
5019 * @cfg {Number} lg (Optional)
5022 * Returns the id of the column at the specified index.
5023 * @param {Number} index The column index
5024 * @return {String} the id
5026 getColumnId : function(index){
5027 return this.config[index].id;
5031 * Returns the column for a specified id.
5032 * @param {String} id The column id
5033 * @return {Object} the column
5035 getColumnById : function(id){
5036 return this.lookup[id];
5041 * Returns the column for a specified dataIndex.
5042 * @param {String} dataIndex The column dataIndex
5043 * @return {Object|Boolean} the column or false if not found
5045 getColumnByDataIndex: function(dataIndex){
5046 var index = this.findColumnIndex(dataIndex);
5047 return index > -1 ? this.config[index] : false;
5051 * Returns the index for a specified column id.
5052 * @param {String} id The column id
5053 * @return {Number} the index, or -1 if not found
5055 getIndexById : function(id){
5056 for(var i = 0, len = this.config.length; i < len; i++){
5057 if(this.config[i].id == id){
5065 * Returns the index for a specified column dataIndex.
5066 * @param {String} dataIndex The column dataIndex
5067 * @return {Number} the index, or -1 if not found
5070 findColumnIndex : function(dataIndex){
5071 for(var i = 0, len = this.config.length; i < len; i++){
5072 if(this.config[i].dataIndex == dataIndex){
5080 moveColumn : function(oldIndex, newIndex){
5081 var c = this.config[oldIndex];
5082 this.config.splice(oldIndex, 1);
5083 this.config.splice(newIndex, 0, c);
5084 this.dataMap = null;
5085 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5088 isLocked : function(colIndex){
5089 return this.config[colIndex].locked === true;
5092 setLocked : function(colIndex, value, suppressEvent){
5093 if(this.isLocked(colIndex) == value){
5096 this.config[colIndex].locked = value;
5098 this.fireEvent("columnlockchange", this, colIndex, value);
5102 getTotalLockedWidth : function(){
5104 for(var i = 0; i < this.config.length; i++){
5105 if(this.isLocked(i) && !this.isHidden(i)){
5106 this.totalWidth += this.getColumnWidth(i);
5112 getLockedCount : function(){
5113 for(var i = 0, len = this.config.length; i < len; i++){
5114 if(!this.isLocked(i)){
5119 return this.config.length;
5123 * Returns the number of columns.
5126 getColumnCount : function(visibleOnly){
5127 if(visibleOnly === true){
5129 for(var i = 0, len = this.config.length; i < len; i++){
5130 if(!this.isHidden(i)){
5136 return this.config.length;
5140 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5141 * @param {Function} fn
5142 * @param {Object} scope (optional)
5143 * @return {Array} result
5145 getColumnsBy : function(fn, scope){
5147 for(var i = 0, len = this.config.length; i < len; i++){
5148 var c = this.config[i];
5149 if(fn.call(scope||this, c, i) === true){
5157 * Returns true if the specified column is sortable.
5158 * @param {Number} col The column index
5161 isSortable : function(col){
5162 if(typeof this.config[col].sortable == "undefined"){
5163 return this.defaultSortable;
5165 return this.config[col].sortable;
5169 * Returns the rendering (formatting) function defined for the column.
5170 * @param {Number} col The column index.
5171 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5173 getRenderer : function(col){
5174 if(!this.config[col].renderer){
5175 return Roo.grid.ColumnModel.defaultRenderer;
5177 return this.config[col].renderer;
5181 * Sets the rendering (formatting) function for a column.
5182 * @param {Number} col The column index
5183 * @param {Function} fn The function to use to process the cell's raw data
5184 * to return HTML markup for the grid view. The render function is called with
5185 * the following parameters:<ul>
5186 * <li>Data value.</li>
5187 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5188 * <li>css A CSS style string to apply to the table cell.</li>
5189 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5190 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5191 * <li>Row index</li>
5192 * <li>Column index</li>
5193 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5195 setRenderer : function(col, fn){
5196 this.config[col].renderer = fn;
5200 * Returns the width for the specified column.
5201 * @param {Number} col The column index
5204 getColumnWidth : function(col){
5205 return this.config[col].width * 1 || this.defaultWidth;
5209 * Sets the width for a column.
5210 * @param {Number} col The column index
5211 * @param {Number} width The new width
5213 setColumnWidth : function(col, width, suppressEvent){
5214 this.config[col].width = width;
5215 this.totalWidth = null;
5217 this.fireEvent("widthchange", this, col, width);
5222 * Returns the total width of all columns.
5223 * @param {Boolean} includeHidden True to include hidden column widths
5226 getTotalWidth : function(includeHidden){
5227 if(!this.totalWidth){
5228 this.totalWidth = 0;
5229 for(var i = 0, len = this.config.length; i < len; i++){
5230 if(includeHidden || !this.isHidden(i)){
5231 this.totalWidth += this.getColumnWidth(i);
5235 return this.totalWidth;
5239 * Returns the header for the specified column.
5240 * @param {Number} col The column index
5243 getColumnHeader : function(col){
5244 return this.config[col].header;
5248 * Sets the header for a column.
5249 * @param {Number} col The column index
5250 * @param {String} header The new header
5252 setColumnHeader : function(col, header){
5253 this.config[col].header = header;
5254 this.fireEvent("headerchange", this, col, header);
5258 * Returns the tooltip for the specified column.
5259 * @param {Number} col The column index
5262 getColumnTooltip : function(col){
5263 return this.config[col].tooltip;
5266 * Sets the tooltip for a column.
5267 * @param {Number} col The column index
5268 * @param {String} tooltip The new tooltip
5270 setColumnTooltip : function(col, tooltip){
5271 this.config[col].tooltip = tooltip;
5275 * Returns the dataIndex for the specified column.
5276 * @param {Number} col The column index
5279 getDataIndex : function(col){
5280 return this.config[col].dataIndex;
5284 * Sets the dataIndex for a column.
5285 * @param {Number} col The column index
5286 * @param {Number} dataIndex The new dataIndex
5288 setDataIndex : function(col, dataIndex){
5289 this.config[col].dataIndex = dataIndex;
5295 * Returns true if the cell is editable.
5296 * @param {Number} colIndex The column index
5297 * @param {Number} rowIndex The row index - this is nto actually used..?
5300 isCellEditable : function(colIndex, rowIndex){
5301 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5305 * Returns the editor defined for the cell/column.
5306 * return false or null to disable editing.
5307 * @param {Number} colIndex The column index
5308 * @param {Number} rowIndex The row index
5311 getCellEditor : function(colIndex, rowIndex){
5312 return this.config[colIndex].editor;
5316 * Sets if a column is editable.
5317 * @param {Number} col The column index
5318 * @param {Boolean} editable True if the column is editable
5320 setEditable : function(col, editable){
5321 this.config[col].editable = editable;
5326 * Returns true if the column is hidden.
5327 * @param {Number} colIndex The column index
5330 isHidden : function(colIndex){
5331 return this.config[colIndex].hidden;
5336 * Returns true if the column width cannot be changed
5338 isFixed : function(colIndex){
5339 return this.config[colIndex].fixed;
5343 * Returns true if the column can be resized
5346 isResizable : function(colIndex){
5347 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5350 * Sets if a column is hidden.
5351 * @param {Number} colIndex The column index
5352 * @param {Boolean} hidden True if the column is hidden
5354 setHidden : function(colIndex, hidden){
5355 this.config[colIndex].hidden = hidden;
5356 this.totalWidth = null;
5357 this.fireEvent("hiddenchange", this, colIndex, hidden);
5361 * Sets the editor for a column.
5362 * @param {Number} col The column index
5363 * @param {Object} editor The editor object
5365 setEditor : function(col, editor){
5366 this.config[col].editor = editor;
5370 Roo.grid.ColumnModel.defaultRenderer = function(value){
5371 if(typeof value == "string" && value.length < 1){
5377 // Alias for backwards compatibility
5378 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5381 * Ext JS Library 1.1.1
5382 * Copyright(c) 2006-2007, Ext JS, LLC.
5384 * Originally Released Under LGPL - original licence link has changed is not relivant.
5387 * <script type="text/javascript">
5391 * @class Roo.LoadMask
5392 * A simple utility class for generically masking elements while loading data. If the element being masked has
5393 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5394 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5395 * element's UpdateManager load indicator and will be destroyed after the initial load.
5397 * Create a new LoadMask
5398 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5399 * @param {Object} config The config object
5401 Roo.LoadMask = function(el, config){
5402 this.el = Roo.get(el);
5403 Roo.apply(this, config);
5405 this.store.on('beforeload', this.onBeforeLoad, this);
5406 this.store.on('load', this.onLoad, this);
5407 this.store.on('loadexception', this.onLoadException, this);
5408 this.removeMask = false;
5410 var um = this.el.getUpdateManager();
5411 um.showLoadIndicator = false; // disable the default indicator
5412 um.on('beforeupdate', this.onBeforeLoad, this);
5413 um.on('update', this.onLoad, this);
5414 um.on('failure', this.onLoad, this);
5415 this.removeMask = true;
5419 Roo.LoadMask.prototype = {
5421 * @cfg {Boolean} removeMask
5422 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5423 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5427 * The text to display in a centered loading message box (defaults to 'Loading...')
5431 * @cfg {String} msgCls
5432 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5434 msgCls : 'x-mask-loading',
5437 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5443 * Disables the mask to prevent it from being displayed
5445 disable : function(){
5446 this.disabled = true;
5450 * Enables the mask so that it can be displayed
5452 enable : function(){
5453 this.disabled = false;
5456 onLoadException : function()
5460 if (typeof(arguments[3]) != 'undefined') {
5461 Roo.MessageBox.alert("Error loading",arguments[3]);
5465 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5466 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5475 this.el.unmask(this.removeMask);
5480 this.el.unmask(this.removeMask);
5484 onBeforeLoad : function(){
5486 this.el.mask(this.msg, this.msgCls);
5491 destroy : function(){
5493 this.store.un('beforeload', this.onBeforeLoad, this);
5494 this.store.un('load', this.onLoad, this);
5495 this.store.un('loadexception', this.onLoadException, this);
5497 var um = this.el.getUpdateManager();
5498 um.un('beforeupdate', this.onBeforeLoad, this);
5499 um.un('update', this.onLoad, this);
5500 um.un('failure', this.onLoad, this);
5511 * @class Roo.bootstrap.Table
5512 * @extends Roo.bootstrap.Component
5513 * Bootstrap Table class
5514 * @cfg {String} cls table class
5515 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5516 * @cfg {String} bgcolor Specifies the background color for a table
5517 * @cfg {Number} border Specifies whether the table cells should have borders or not
5518 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5519 * @cfg {Number} cellspacing Specifies the space between cells
5520 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5521 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5522 * @cfg {String} sortable Specifies that the table should be sortable
5523 * @cfg {String} summary Specifies a summary of the content of a table
5524 * @cfg {Number} width Specifies the width of a table
5525 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5527 * @cfg {boolean} striped Should the rows be alternative striped
5528 * @cfg {boolean} bordered Add borders to the table
5529 * @cfg {boolean} hover Add hover highlighting
5530 * @cfg {boolean} condensed Format condensed
5531 * @cfg {boolean} responsive Format condensed
5532 * @cfg {Boolean} loadMask (true|false) default false
5533 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5534 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5535 * @cfg {Boolean} rowSelection (true|false) default false
5536 * @cfg {Boolean} cellSelection (true|false) default false
5537 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5541 * Create a new Table
5542 * @param {Object} config The config object
5545 Roo.bootstrap.Table = function(config){
5546 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5549 this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5550 this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5551 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5552 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5556 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5557 this.sm = this.selModel;
5558 this.sm.xmodule = this.xmodule || false;
5560 if (this.cm && typeof(this.cm.config) == 'undefined') {
5561 this.colModel = new Roo.grid.ColumnModel(this.cm);
5562 this.cm = this.colModel;
5563 this.cm.xmodule = this.xmodule || false;
5566 this.store= Roo.factory(this.store, Roo.data);
5567 this.ds = this.store;
5568 this.ds.xmodule = this.xmodule || false;
5571 if (this.footer && this.store) {
5572 this.footer.dataSource = this.ds;
5573 this.footer = Roo.factory(this.footer);
5580 * Fires when a cell is clicked
5581 * @param {Roo.bootstrap.Table} this
5582 * @param {Roo.Element} el
5583 * @param {Number} rowIndex
5584 * @param {Number} columnIndex
5585 * @param {Roo.EventObject} e
5589 * @event celldblclick
5590 * Fires when a cell is double clicked
5591 * @param {Roo.bootstrap.Table} this
5592 * @param {Roo.Element} el
5593 * @param {Number} rowIndex
5594 * @param {Number} columnIndex
5595 * @param {Roo.EventObject} e
5597 "celldblclick" : true,
5600 * Fires when a row is clicked
5601 * @param {Roo.bootstrap.Table} this
5602 * @param {Roo.Element} el
5603 * @param {Number} rowIndex
5604 * @param {Roo.EventObject} e
5608 * @event rowdblclick
5609 * Fires when a row is double clicked
5610 * @param {Roo.bootstrap.Table} this
5611 * @param {Roo.Element} el
5612 * @param {Number} rowIndex
5613 * @param {Roo.EventObject} e
5615 "rowdblclick" : true,
5618 * Fires when a mouseover occur
5619 * @param {Roo.bootstrap.Table} this
5620 * @param {Roo.Element} el
5621 * @param {Number} rowIndex
5622 * @param {Number} columnIndex
5623 * @param {Roo.EventObject} e
5628 * Fires when a mouseout occur
5629 * @param {Roo.bootstrap.Table} this
5630 * @param {Roo.Element} el
5631 * @param {Number} rowIndex
5632 * @param {Number} columnIndex
5633 * @param {Roo.EventObject} e
5638 * Fires when a row is rendered, so you can change add a style to it.
5639 * @param {Roo.bootstrap.Table} this
5640 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5644 * @event rowsrendered
5645 * Fires when all the rows have been rendered
5646 * @param {Roo.bootstrap.Table} this
5648 'rowsrendered' : true
5653 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5678 rowSelection : false,
5679 cellSelection : false,
5682 // Roo.Element - the tbody
5685 getAutoCreate : function(){
5686 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5695 cfg.cls += ' table-striped';
5699 cfg.cls += ' table-hover';
5701 if (this.bordered) {
5702 cfg.cls += ' table-bordered';
5704 if (this.condensed) {
5705 cfg.cls += ' table-condensed';
5707 if (this.responsive) {
5708 cfg.cls += ' table-responsive';
5712 cfg.cls+= ' ' +this.cls;
5715 // this lot should be simplifed...
5718 cfg.align=this.align;
5721 cfg.bgcolor=this.bgcolor;
5724 cfg.border=this.border;
5726 if (this.cellpadding) {
5727 cfg.cellpadding=this.cellpadding;
5729 if (this.cellspacing) {
5730 cfg.cellspacing=this.cellspacing;
5733 cfg.frame=this.frame;
5736 cfg.rules=this.rules;
5738 if (this.sortable) {
5739 cfg.sortable=this.sortable;
5742 cfg.summary=this.summary;
5745 cfg.width=this.width;
5748 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5751 if(this.store || this.cm){
5752 if(this.headerShow){
5753 cfg.cn.push(this.renderHeader());
5756 cfg.cn.push(this.renderBody());
5758 if(this.footerShow){
5759 cfg.cn.push(this.renderFooter());
5762 cfg.cls+= ' TableGrid';
5765 return { cn : [ cfg ] };
5768 initEvents : function()
5770 if(!this.store || !this.cm){
5774 //Roo.log('initEvents with ds!!!!');
5776 this.mainBody = this.el.select('tbody', true).first();
5781 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5782 e.on('click', _this.sort, _this);
5785 this.el.on("click", this.onClick, this);
5786 this.el.on("dblclick", this.onDblClick, this);
5788 // why is this done????? = it breaks dialogs??
5789 //this.parent().el.setStyle('position', 'relative');
5793 this.footer.parentId = this.id;
5794 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5797 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5799 this.store.on('load', this.onLoad, this);
5800 this.store.on('beforeload', this.onBeforeLoad, this);
5801 this.store.on('update', this.onUpdate, this);
5802 this.store.on('add', this.onAdd, this);
5806 onMouseover : function(e, el)
5808 var cell = Roo.get(el);
5814 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5815 cell = cell.findParent('td', false, true);
5818 var row = cell.findParent('tr', false, true);
5819 var cellIndex = cell.dom.cellIndex;
5820 var rowIndex = row.dom.rowIndex - 1; // start from 0
5822 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5826 onMouseout : function(e, el)
5828 var cell = Roo.get(el);
5834 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5835 cell = cell.findParent('td', false, true);
5838 var row = cell.findParent('tr', false, true);
5839 var cellIndex = cell.dom.cellIndex;
5840 var rowIndex = row.dom.rowIndex - 1; // start from 0
5842 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5846 onClick : function(e, el)
5848 var cell = Roo.get(el);
5850 if(!cell || (!this.cellSelection && !this.rowSelection)){
5854 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5855 cell = cell.findParent('td', false, true);
5858 if(!cell || typeof(cell) == 'undefined'){
5862 var row = cell.findParent('tr', false, true);
5864 if(!row || typeof(row) == 'undefined'){
5868 var cellIndex = cell.dom.cellIndex;
5869 var rowIndex = this.getRowIndex(row);
5871 // why??? - should these not be based on SelectionModel?
5872 if(this.cellSelection){
5873 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5876 if(this.rowSelection){
5877 this.fireEvent('rowclick', this, row, rowIndex, e);
5883 onDblClick : function(e,el)
5885 var cell = Roo.get(el);
5887 if(!cell || (!this.CellSelection && !this.RowSelection)){
5891 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5892 cell = cell.findParent('td', false, true);
5895 if(!cell || typeof(cell) == 'undefined'){
5899 var row = cell.findParent('tr', false, true);
5901 if(!row || typeof(row) == 'undefined'){
5905 var cellIndex = cell.dom.cellIndex;
5906 var rowIndex = this.getRowIndex(row);
5908 if(this.CellSelection){
5909 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5912 if(this.RowSelection){
5913 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5917 sort : function(e,el)
5919 var col = Roo.get(el);
5921 if(!col.hasClass('sortable')){
5925 var sort = col.attr('sort');
5928 if(col.hasClass('glyphicon-arrow-up')){
5932 this.store.sortInfo = {field : sort, direction : dir};
5935 Roo.log("calling footer first");
5936 this.footer.onClick('first');
5939 this.store.load({ params : { start : 0 } });
5943 renderHeader : function()
5952 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5954 var config = cm.config[i];
5959 html: cm.getColumnHeader(i)
5964 if(typeof(config.lgHeader) != 'undefined'){
5965 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5968 if(typeof(config.mdHeader) != 'undefined'){
5969 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5972 if(typeof(config.smHeader) != 'undefined'){
5973 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5976 if(typeof(config.xsHeader) != 'undefined'){
5977 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5984 if(typeof(config.tooltip) != 'undefined'){
5985 c.tooltip = config.tooltip;
5988 if(typeof(config.colspan) != 'undefined'){
5989 c.colspan = config.colspan;
5992 if(typeof(config.hidden) != 'undefined' && config.hidden){
5993 c.style += ' display:none;';
5996 if(typeof(config.dataIndex) != 'undefined'){
5997 c.sort = config.dataIndex;
6000 if(typeof(config.sortable) != 'undefined' && config.sortable){
6004 if(typeof(config.align) != 'undefined' && config.align.length){
6005 c.style += ' text-align:' + config.align + ';';
6008 if(typeof(config.width) != 'undefined'){
6009 c.style += ' width:' + config.width + 'px;';
6012 if(typeof(config.cls) != 'undefined'){
6013 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6016 ['xs','sm','md','lg'].map(function(size){
6018 if(typeof(config[size]) == 'undefined'){
6022 if (!config[size]) { // 0 = hidden
6023 c.cls += ' hidden-' + size;
6027 c.cls += ' col-' + size + '-' + config[size];
6037 renderBody : function()
6047 colspan : this.cm.getColumnCount()
6057 renderFooter : function()
6067 colspan : this.cm.getColumnCount()
6081 // Roo.log('ds onload');
6086 var ds = this.store;
6088 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6089 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6091 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6092 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6095 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6096 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6100 var tbody = this.mainBody;
6102 if(ds.getCount() > 0){
6103 ds.data.each(function(d,rowIndex){
6104 var row = this.renderRow(cm, ds, rowIndex);
6106 tbody.createChild(row);
6110 if(row.cellObjects.length){
6111 Roo.each(row.cellObjects, function(r){
6112 _this.renderCellObject(r);
6119 Roo.each(this.el.select('tbody td', true).elements, function(e){
6120 e.on('mouseover', _this.onMouseover, _this);
6123 Roo.each(this.el.select('tbody td', true).elements, function(e){
6124 e.on('mouseout', _this.onMouseout, _this);
6126 this.fireEvent('rowsrendered', this);
6127 //if(this.loadMask){
6128 // this.maskEl.hide();
6133 onUpdate : function(ds,record)
6135 this.refreshRow(record);
6138 onRemove : function(ds, record, index, isUpdate){
6139 if(isUpdate !== true){
6140 this.fireEvent("beforerowremoved", this, index, record);
6142 var bt = this.mainBody.dom;
6144 var rows = this.el.select('tbody > tr', true).elements;
6146 if(typeof(rows[index]) != 'undefined'){
6147 bt.removeChild(rows[index].dom);
6150 // if(bt.rows[index]){
6151 // bt.removeChild(bt.rows[index]);
6154 if(isUpdate !== true){
6155 //this.stripeRows(index);
6156 //this.syncRowHeights(index, index);
6158 this.fireEvent("rowremoved", this, index, record);
6162 onAdd : function(ds, records, rowIndex)
6164 //Roo.log('on Add called');
6165 // - note this does not handle multiple adding very well..
6166 var bt = this.mainBody.dom;
6167 for (var i =0 ; i < records.length;i++) {
6168 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6169 //Roo.log(records[i]);
6170 //Roo.log(this.store.getAt(rowIndex+i));
6171 this.insertRow(this.store, rowIndex + i, false);
6178 refreshRow : function(record){
6179 var ds = this.store, index;
6180 if(typeof record == 'number'){
6182 record = ds.getAt(index);
6184 index = ds.indexOf(record);
6186 this.insertRow(ds, index, true);
6187 this.onRemove(ds, record, index+1, true);
6188 //this.syncRowHeights(index, index);
6190 this.fireEvent("rowupdated", this, index, record);
6193 insertRow : function(dm, rowIndex, isUpdate){
6196 this.fireEvent("beforerowsinserted", this, rowIndex);
6198 //var s = this.getScrollState();
6199 var row = this.renderRow(this.cm, this.store, rowIndex);
6200 // insert before rowIndex..
6201 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6205 if(row.cellObjects.length){
6206 Roo.each(row.cellObjects, function(r){
6207 _this.renderCellObject(r);
6212 this.fireEvent("rowsinserted", this, rowIndex);
6213 //this.syncRowHeights(firstRow, lastRow);
6214 //this.stripeRows(firstRow);
6221 getRowDom : function(rowIndex)
6223 var rows = this.el.select('tbody > tr', true).elements;
6225 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6228 // returns the object tree for a tr..
6231 renderRow : function(cm, ds, rowIndex)
6234 var d = ds.getAt(rowIndex);
6241 var cellObjects = [];
6243 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6244 var config = cm.config[i];
6246 var renderer = cm.getRenderer(i);
6250 if(typeof(renderer) !== 'undefined'){
6251 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6253 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6254 // and are rendered into the cells after the row is rendered - using the id for the element.
6256 if(typeof(value) === 'object'){
6266 rowIndex : rowIndex,
6271 this.fireEvent('rowclass', this, rowcfg);
6275 cls : rowcfg.rowClass,
6277 html: (typeof(value) === 'object') ? '' : value
6284 if(typeof(config.colspan) != 'undefined'){
6285 td.colspan = config.colspan;
6288 if(typeof(config.hidden) != 'undefined' && config.hidden){
6289 td.style += ' display:none;';
6292 if(typeof(config.align) != 'undefined' && config.align.length){
6293 td.style += ' text-align:' + config.align + ';';
6296 if(typeof(config.width) != 'undefined'){
6297 td.style += ' width:' + config.width + 'px;';
6300 if(typeof(config.cursor) != 'undefined'){
6301 td.style += ' cursor:' + config.cursor + ';';
6304 if(typeof(config.cls) != 'undefined'){
6305 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6308 ['xs','sm','md','lg'].map(function(size){
6310 if(typeof(config[size]) == 'undefined'){
6314 if (!config[size]) { // 0 = hidden
6315 td.cls += ' hidden-' + size;
6319 td.cls += ' col-' + size + '-' + config[size];
6327 row.cellObjects = cellObjects;
6335 onBeforeLoad : function()
6337 //Roo.log('ds onBeforeLoad');
6341 //if(this.loadMask){
6342 // this.maskEl.show();
6350 this.el.select('tbody', true).first().dom.innerHTML = '';
6353 * Show or hide a row.
6354 * @param {Number} rowIndex to show or hide
6355 * @param {Boolean} state hide
6357 setRowVisibility : function(rowIndex, state)
6359 var bt = this.mainBody.dom;
6361 var rows = this.el.select('tbody > tr', true).elements;
6363 if(typeof(rows[rowIndex]) == 'undefined'){
6366 rows[rowIndex].dom.style.display = state ? '' : 'none';
6370 getSelectionModel : function(){
6372 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6374 return this.selModel;
6377 * Render the Roo.bootstrap object from renderder
6379 renderCellObject : function(r)
6383 var t = r.cfg.render(r.container);
6386 Roo.each(r.cfg.cn, function(c){
6388 container: t.getChildContainer(),
6391 _this.renderCellObject(child);
6396 getRowIndex : function(row)
6400 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6423 * @class Roo.bootstrap.TableCell
6424 * @extends Roo.bootstrap.Component
6425 * Bootstrap TableCell class
6426 * @cfg {String} html cell contain text
6427 * @cfg {String} cls cell class
6428 * @cfg {String} tag cell tag (td|th) default td
6429 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6430 * @cfg {String} align Aligns the content in a cell
6431 * @cfg {String} axis Categorizes cells
6432 * @cfg {String} bgcolor Specifies the background color of a cell
6433 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6434 * @cfg {Number} colspan Specifies the number of columns a cell should span
6435 * @cfg {String} headers Specifies one or more header cells a cell is related to
6436 * @cfg {Number} height Sets the height of a cell
6437 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6438 * @cfg {Number} rowspan Sets the number of rows a cell should span
6439 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6440 * @cfg {String} valign Vertical aligns the content in a cell
6441 * @cfg {Number} width Specifies the width of a cell
6444 * Create a new TableCell
6445 * @param {Object} config The config object
6448 Roo.bootstrap.TableCell = function(config){
6449 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6452 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6472 getAutoCreate : function(){
6473 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6493 cfg.align=this.align
6499 cfg.bgcolor=this.bgcolor
6502 cfg.charoff=this.charoff
6505 cfg.colspan=this.colspan
6508 cfg.headers=this.headers
6511 cfg.height=this.height
6514 cfg.nowrap=this.nowrap
6517 cfg.rowspan=this.rowspan
6520 cfg.scope=this.scope
6523 cfg.valign=this.valign
6526 cfg.width=this.width
6545 * @class Roo.bootstrap.TableRow
6546 * @extends Roo.bootstrap.Component
6547 * Bootstrap TableRow class
6548 * @cfg {String} cls row class
6549 * @cfg {String} align Aligns the content in a table row
6550 * @cfg {String} bgcolor Specifies a background color for a table row
6551 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6552 * @cfg {String} valign Vertical aligns the content in a table row
6555 * Create a new TableRow
6556 * @param {Object} config The config object
6559 Roo.bootstrap.TableRow = function(config){
6560 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6563 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6571 getAutoCreate : function(){
6572 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6582 cfg.align = this.align;
6585 cfg.bgcolor = this.bgcolor;
6588 cfg.charoff = this.charoff;
6591 cfg.valign = this.valign;
6609 * @class Roo.bootstrap.TableBody
6610 * @extends Roo.bootstrap.Component
6611 * Bootstrap TableBody class
6612 * @cfg {String} cls element class
6613 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6614 * @cfg {String} align Aligns the content inside the element
6615 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6616 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6619 * Create a new TableBody
6620 * @param {Object} config The config object
6623 Roo.bootstrap.TableBody = function(config){
6624 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6627 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6635 getAutoCreate : function(){
6636 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6650 cfg.align = this.align;
6653 cfg.charoff = this.charoff;
6656 cfg.valign = this.valign;
6663 // initEvents : function()
6670 // this.store = Roo.factory(this.store, Roo.data);
6671 // this.store.on('load', this.onLoad, this);
6673 // this.store.load();
6677 // onLoad: function ()
6679 // this.fireEvent('load', this);
6689 * Ext JS Library 1.1.1
6690 * Copyright(c) 2006-2007, Ext JS, LLC.
6692 * Originally Released Under LGPL - original licence link has changed is not relivant.
6695 * <script type="text/javascript">
6698 // as we use this in bootstrap.
6699 Roo.namespace('Roo.form');
6701 * @class Roo.form.Action
6702 * Internal Class used to handle form actions
6704 * @param {Roo.form.BasicForm} el The form element or its id
6705 * @param {Object} config Configuration options
6710 // define the action interface
6711 Roo.form.Action = function(form, options){
6713 this.options = options || {};
6716 * Client Validation Failed
6719 Roo.form.Action.CLIENT_INVALID = 'client';
6721 * Server Validation Failed
6724 Roo.form.Action.SERVER_INVALID = 'server';
6726 * Connect to Server Failed
6729 Roo.form.Action.CONNECT_FAILURE = 'connect';
6731 * Reading Data from Server Failed
6734 Roo.form.Action.LOAD_FAILURE = 'load';
6736 Roo.form.Action.prototype = {
6738 failureType : undefined,
6739 response : undefined,
6743 run : function(options){
6748 success : function(response){
6753 handleResponse : function(response){
6757 // default connection failure
6758 failure : function(response){
6760 this.response = response;
6761 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6762 this.form.afterAction(this, false);
6765 processResponse : function(response){
6766 this.response = response;
6767 if(!response.responseText){
6770 this.result = this.handleResponse(response);
6774 // utility functions used internally
6775 getUrl : function(appendParams){
6776 var url = this.options.url || this.form.url || this.form.el.dom.action;
6778 var p = this.getParams();
6780 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6786 getMethod : function(){
6787 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6790 getParams : function(){
6791 var bp = this.form.baseParams;
6792 var p = this.options.params;
6794 if(typeof p == "object"){
6795 p = Roo.urlEncode(Roo.applyIf(p, bp));
6796 }else if(typeof p == 'string' && bp){
6797 p += '&' + Roo.urlEncode(bp);
6800 p = Roo.urlEncode(bp);
6805 createCallback : function(){
6807 success: this.success,
6808 failure: this.failure,
6810 timeout: (this.form.timeout*1000),
6811 upload: this.form.fileUpload ? this.success : undefined
6816 Roo.form.Action.Submit = function(form, options){
6817 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6820 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6823 haveProgress : false,
6824 uploadComplete : false,
6826 // uploadProgress indicator.
6827 uploadProgress : function()
6829 if (!this.form.progressUrl) {
6833 if (!this.haveProgress) {
6834 Roo.MessageBox.progress("Uploading", "Uploading");
6836 if (this.uploadComplete) {
6837 Roo.MessageBox.hide();
6841 this.haveProgress = true;
6843 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6845 var c = new Roo.data.Connection();
6847 url : this.form.progressUrl,
6852 success : function(req){
6853 //console.log(data);
6857 rdata = Roo.decode(req.responseText)
6859 Roo.log("Invalid data from server..");
6863 if (!rdata || !rdata.success) {
6865 Roo.MessageBox.alert(Roo.encode(rdata));
6868 var data = rdata.data;
6870 if (this.uploadComplete) {
6871 Roo.MessageBox.hide();
6876 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6877 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6880 this.uploadProgress.defer(2000,this);
6883 failure: function(data) {
6884 Roo.log('progress url failed ');
6895 // run get Values on the form, so it syncs any secondary forms.
6896 this.form.getValues();
6898 var o = this.options;
6899 var method = this.getMethod();
6900 var isPost = method == 'POST';
6901 if(o.clientValidation === false || this.form.isValid()){
6903 if (this.form.progressUrl) {
6904 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6905 (new Date() * 1) + '' + Math.random());
6910 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6911 form:this.form.el.dom,
6912 url:this.getUrl(!isPost),
6914 params:isPost ? this.getParams() : null,
6915 isUpload: this.form.fileUpload
6918 this.uploadProgress();
6920 }else if (o.clientValidation !== false){ // client validation failed
6921 this.failureType = Roo.form.Action.CLIENT_INVALID;
6922 this.form.afterAction(this, false);
6926 success : function(response)
6928 this.uploadComplete= true;
6929 if (this.haveProgress) {
6930 Roo.MessageBox.hide();
6934 var result = this.processResponse(response);
6935 if(result === true || result.success){
6936 this.form.afterAction(this, true);
6940 this.form.markInvalid(result.errors);
6941 this.failureType = Roo.form.Action.SERVER_INVALID;
6943 this.form.afterAction(this, false);
6945 failure : function(response)
6947 this.uploadComplete= true;
6948 if (this.haveProgress) {
6949 Roo.MessageBox.hide();
6952 this.response = response;
6953 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6954 this.form.afterAction(this, false);
6957 handleResponse : function(response){
6958 if(this.form.errorReader){
6959 var rs = this.form.errorReader.read(response);
6962 for(var i = 0, len = rs.records.length; i < len; i++) {
6963 var r = rs.records[i];
6967 if(errors.length < 1){
6971 success : rs.success,
6977 ret = Roo.decode(response.responseText);
6981 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6991 Roo.form.Action.Load = function(form, options){
6992 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6993 this.reader = this.form.reader;
6996 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7001 Roo.Ajax.request(Roo.apply(
7002 this.createCallback(), {
7003 method:this.getMethod(),
7004 url:this.getUrl(false),
7005 params:this.getParams()
7009 success : function(response){
7011 var result = this.processResponse(response);
7012 if(result === true || !result.success || !result.data){
7013 this.failureType = Roo.form.Action.LOAD_FAILURE;
7014 this.form.afterAction(this, false);
7017 this.form.clearInvalid();
7018 this.form.setValues(result.data);
7019 this.form.afterAction(this, true);
7022 handleResponse : function(response){
7023 if(this.form.reader){
7024 var rs = this.form.reader.read(response);
7025 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7027 success : rs.success,
7031 return Roo.decode(response.responseText);
7035 Roo.form.Action.ACTION_TYPES = {
7036 'load' : Roo.form.Action.Load,
7037 'submit' : Roo.form.Action.Submit
7046 * @class Roo.bootstrap.Form
7047 * @extends Roo.bootstrap.Component
7048 * Bootstrap Form class
7049 * @cfg {String} method GET | POST (default POST)
7050 * @cfg {String} labelAlign top | left (default top)
7051 * @cfg {String} align left | right - for navbars
7052 * @cfg {Boolean} loadMask load mask when submit (default true)
7057 * @param {Object} config The config object
7061 Roo.bootstrap.Form = function(config){
7062 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7065 * @event clientvalidation
7066 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7067 * @param {Form} this
7068 * @param {Boolean} valid true if the form has passed client-side validation
7070 clientvalidation: true,
7072 * @event beforeaction
7073 * Fires before any action is performed. Return false to cancel the action.
7074 * @param {Form} this
7075 * @param {Action} action The action to be performed
7079 * @event actionfailed
7080 * Fires when an action fails.
7081 * @param {Form} this
7082 * @param {Action} action The action that failed
7084 actionfailed : true,
7086 * @event actioncomplete
7087 * Fires when an action is completed.
7088 * @param {Form} this
7089 * @param {Action} action The action that completed
7091 actioncomplete : true
7096 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7099 * @cfg {String} method
7100 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7105 * The URL to use for form actions if one isn't supplied in the action options.
7108 * @cfg {Boolean} fileUpload
7109 * Set to true if this form is a file upload.
7113 * @cfg {Object} baseParams
7114 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7118 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7122 * @cfg {Sting} align (left|right) for navbar forms
7127 activeAction : null,
7130 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7131 * element by passing it or its id or mask the form itself by passing in true.
7134 waitMsgTarget : false,
7138 getAutoCreate : function(){
7142 method : this.method || 'POST',
7143 id : this.id || Roo.id(),
7146 if (this.parent().xtype.match(/^Nav/)) {
7147 cfg.cls = 'navbar-form navbar-' + this.align;
7151 if (this.labelAlign == 'left' ) {
7152 cfg.cls += ' form-horizontal';
7158 initEvents : function()
7160 this.el.on('submit', this.onSubmit, this);
7161 // this was added as random key presses on the form where triggering form submit.
7162 this.el.on('keypress', function(e) {
7163 if (e.getCharCode() != 13) {
7166 // we might need to allow it for textareas.. and some other items.
7167 // check e.getTarget().
7169 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7173 Roo.log("keypress blocked");
7181 onSubmit : function(e){
7186 * Returns true if client-side validation on the form is successful.
7189 isValid : function(){
7190 var items = this.getItems();
7192 items.each(function(f){
7201 * Returns true if any fields in this form have changed since their original load.
7204 isDirty : function(){
7206 var items = this.getItems();
7207 items.each(function(f){
7217 * Performs a predefined action (submit or load) or custom actions you define on this form.
7218 * @param {String} actionName The name of the action type
7219 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7220 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7221 * accept other config options):
7223 Property Type Description
7224 ---------------- --------------- ----------------------------------------------------------------------------------
7225 url String The url for the action (defaults to the form's url)
7226 method String The form method to use (defaults to the form's method, or POST if not defined)
7227 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7228 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7229 validate the form on the client (defaults to false)
7231 * @return {BasicForm} this
7233 doAction : function(action, options){
7234 if(typeof action == 'string'){
7235 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7237 if(this.fireEvent('beforeaction', this, action) !== false){
7238 this.beforeAction(action);
7239 action.run.defer(100, action);
7245 beforeAction : function(action){
7246 var o = action.options;
7249 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7251 // not really supported yet.. ??
7253 //if(this.waitMsgTarget === true){
7254 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7255 //}else if(this.waitMsgTarget){
7256 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7257 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7259 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7265 afterAction : function(action, success){
7266 this.activeAction = null;
7267 var o = action.options;
7269 //if(this.waitMsgTarget === true){
7271 //}else if(this.waitMsgTarget){
7272 // this.waitMsgTarget.unmask();
7274 // Roo.MessageBox.updateProgress(1);
7275 // Roo.MessageBox.hide();
7282 Roo.callback(o.success, o.scope, [this, action]);
7283 this.fireEvent('actioncomplete', this, action);
7287 // failure condition..
7288 // we have a scenario where updates need confirming.
7289 // eg. if a locking scenario exists..
7290 // we look for { errors : { needs_confirm : true }} in the response.
7292 (typeof(action.result) != 'undefined') &&
7293 (typeof(action.result.errors) != 'undefined') &&
7294 (typeof(action.result.errors.needs_confirm) != 'undefined')
7297 Roo.log("not supported yet");
7300 Roo.MessageBox.confirm(
7301 "Change requires confirmation",
7302 action.result.errorMsg,
7307 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7317 Roo.callback(o.failure, o.scope, [this, action]);
7318 // show an error message if no failed handler is set..
7319 if (!this.hasListener('actionfailed')) {
7320 Roo.log("need to add dialog support");
7322 Roo.MessageBox.alert("Error",
7323 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7324 action.result.errorMsg :
7325 "Saving Failed, please check your entries or try again"
7330 this.fireEvent('actionfailed', this, action);
7335 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7336 * @param {String} id The value to search for
7339 findField : function(id){
7340 var items = this.getItems();
7341 var field = items.get(id);
7343 items.each(function(f){
7344 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7351 return field || null;
7354 * Mark fields in this form invalid in bulk.
7355 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7356 * @return {BasicForm} this
7358 markInvalid : function(errors){
7359 if(errors instanceof Array){
7360 for(var i = 0, len = errors.length; i < len; i++){
7361 var fieldError = errors[i];
7362 var f = this.findField(fieldError.id);
7364 f.markInvalid(fieldError.msg);
7370 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7371 field.markInvalid(errors[id]);
7375 //Roo.each(this.childForms || [], function (f) {
7376 // f.markInvalid(errors);
7383 * Set values for fields in this form in bulk.
7384 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7385 * @return {BasicForm} this
7387 setValues : function(values){
7388 if(values instanceof Array){ // array of objects
7389 for(var i = 0, len = values.length; i < len; i++){
7391 var f = this.findField(v.id);
7393 f.setValue(v.value);
7394 if(this.trackResetOnLoad){
7395 f.originalValue = f.getValue();
7399 }else{ // object hash
7402 if(typeof values[id] != 'function' && (field = this.findField(id))){
7404 if (field.setFromData &&
7406 field.displayField &&
7407 // combos' with local stores can
7408 // be queried via setValue()
7409 // to set their value..
7410 (field.store && !field.store.isLocal)
7414 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7415 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7416 field.setFromData(sd);
7419 field.setValue(values[id]);
7423 if(this.trackResetOnLoad){
7424 field.originalValue = field.getValue();
7430 //Roo.each(this.childForms || [], function (f) {
7431 // f.setValues(values);
7438 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7439 * they are returned as an array.
7440 * @param {Boolean} asString
7443 getValues : function(asString){
7444 //if (this.childForms) {
7445 // copy values from the child forms
7446 // Roo.each(this.childForms, function (f) {
7447 // this.setValues(f.getValues());
7453 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7454 if(asString === true){
7457 return Roo.urlDecode(fs);
7461 * Returns the fields in this form as an object with key/value pairs.
7462 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7465 getFieldValues : function(with_hidden)
7467 var items = this.getItems();
7469 items.each(function(f){
7473 var v = f.getValue();
7474 if (f.inputType =='radio') {
7475 if (typeof(ret[f.getName()]) == 'undefined') {
7476 ret[f.getName()] = ''; // empty..
7479 if (!f.el.dom.checked) {
7487 // not sure if this supported any more..
7488 if ((typeof(v) == 'object') && f.getRawValue) {
7489 v = f.getRawValue() ; // dates..
7491 // combo boxes where name != hiddenName...
7492 if (f.name != f.getName()) {
7493 ret[f.name] = f.getRawValue();
7495 ret[f.getName()] = v;
7502 * Clears all invalid messages in this form.
7503 * @return {BasicForm} this
7505 clearInvalid : function(){
7506 var items = this.getItems();
7508 items.each(function(f){
7519 * @return {BasicForm} this
7522 var items = this.getItems();
7523 items.each(function(f){
7527 Roo.each(this.childForms || [], function (f) {
7534 getItems : function()
7536 var r=new Roo.util.MixedCollection(false, function(o){
7537 return o.id || (o.id = Roo.id());
7539 var iter = function(el) {
7546 Roo.each(el.items,function(e) {
7566 * Ext JS Library 1.1.1
7567 * Copyright(c) 2006-2007, Ext JS, LLC.
7569 * Originally Released Under LGPL - original licence link has changed is not relivant.
7572 * <script type="text/javascript">
7575 * @class Roo.form.VTypes
7576 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7579 Roo.form.VTypes = function(){
7580 // closure these in so they are only created once.
7581 var alpha = /^[a-zA-Z_]+$/;
7582 var alphanum = /^[a-zA-Z0-9_]+$/;
7583 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7584 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7586 // All these messages and functions are configurable
7589 * The function used to validate email addresses
7590 * @param {String} value The email address
7592 'email' : function(v){
7593 return email.test(v);
7596 * The error text to display when the email validation function returns false
7599 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7601 * The keystroke filter mask to be applied on email input
7604 'emailMask' : /[a-z0-9_\.\-@]/i,
7607 * The function used to validate URLs
7608 * @param {String} value The URL
7610 'url' : function(v){
7614 * The error text to display when the url validation function returns false
7617 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7620 * The function used to validate alpha values
7621 * @param {String} value The value
7623 'alpha' : function(v){
7624 return alpha.test(v);
7627 * The error text to display when the alpha validation function returns false
7630 'alphaText' : 'This field should only contain letters and _',
7632 * The keystroke filter mask to be applied on alpha input
7635 'alphaMask' : /[a-z_]/i,
7638 * The function used to validate alphanumeric values
7639 * @param {String} value The value
7641 'alphanum' : function(v){
7642 return alphanum.test(v);
7645 * The error text to display when the alphanumeric validation function returns false
7648 'alphanumText' : 'This field should only contain letters, numbers and _',
7650 * The keystroke filter mask to be applied on alphanumeric input
7653 'alphanumMask' : /[a-z0-9_]/i
7663 * @class Roo.bootstrap.Input
7664 * @extends Roo.bootstrap.Component
7665 * Bootstrap Input class
7666 * @cfg {Boolean} disabled is it disabled
7667 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7668 * @cfg {String} name name of the input
7669 * @cfg {string} fieldLabel - the label associated
7670 * @cfg {string} placeholder - placeholder to put in text.
7671 * @cfg {string} before - input group add on before
7672 * @cfg {string} after - input group add on after
7673 * @cfg {string} size - (lg|sm) or leave empty..
7674 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7675 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7676 * @cfg {Number} md colspan out of 12 for computer-sized screens
7677 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7678 * @cfg {string} value default value of the input
7679 * @cfg {Number} labelWidth set the width of label (0-12)
7680 * @cfg {String} labelAlign (top|left)
7681 * @cfg {Boolean} readOnly Specifies that the field should be read-only
7682 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7684 * @cfg {String} align (left|center|right) Default left
7685 * @cfg {Boolean} forceFeedback (true|false) Default false
7691 * Create a new Input
7692 * @param {Object} config The config object
7695 Roo.bootstrap.Input = function(config){
7696 Roo.bootstrap.Input.superclass.constructor.call(this, config);
7701 * Fires when this field receives input focus.
7702 * @param {Roo.form.Field} this
7707 * Fires when this field loses input focus.
7708 * @param {Roo.form.Field} this
7713 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
7714 * {@link Roo.EventObject#getKey} to determine which key was pressed.
7715 * @param {Roo.form.Field} this
7716 * @param {Roo.EventObject} e The event object
7721 * Fires just before the field blurs if the field value has changed.
7722 * @param {Roo.form.Field} this
7723 * @param {Mixed} newValue The new value
7724 * @param {Mixed} oldValue The original value
7729 * Fires after the field has been marked as invalid.
7730 * @param {Roo.form.Field} this
7731 * @param {String} msg The validation message
7736 * Fires after the field has been validated with no errors.
7737 * @param {Roo.form.Field} this
7742 * Fires after the key up
7743 * @param {Roo.form.Field} this
7744 * @param {Roo.EventObject} e The event Object
7750 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
7752 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7753 automatic validation (defaults to "keyup").
7755 validationEvent : "keyup",
7757 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7759 validateOnBlur : true,
7761 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7763 validationDelay : 250,
7765 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7767 focusClass : "x-form-focus", // not needed???
7771 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7773 invalidClass : "has-warning",
7776 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7778 validClass : "has-success",
7781 * @cfg {Boolean} hasFeedback (true|false) default true
7786 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7788 invalidFeedbackClass : "glyphicon-warning-sign",
7791 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7793 validFeedbackClass : "glyphicon-ok",
7796 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7798 selectOnFocus : false,
7801 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7805 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7810 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7812 disableKeyFilter : false,
7815 * @cfg {Boolean} disabled True to disable the field (defaults to false).
7819 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7823 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7825 blankText : "This field is required",
7828 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7832 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7834 maxLength : Number.MAX_VALUE,
7836 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7838 minLengthText : "The minimum length for this field is {0}",
7840 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7842 maxLengthText : "The maximum length for this field is {0}",
7846 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7847 * If available, this function will be called only after the basic validators all return true, and will be passed the
7848 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7852 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7853 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7854 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
7858 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7862 autocomplete: false,
7881 formatedValue : false,
7882 forceFeedback : false,
7884 parentLabelAlign : function()
7887 while (parent.parent()) {
7888 parent = parent.parent();
7889 if (typeof(parent.labelAlign) !='undefined') {
7890 return parent.labelAlign;
7897 getAutoCreate : function(){
7899 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7905 if(this.inputType != 'hidden'){
7906 cfg.cls = 'form-group' //input-group
7912 type : this.inputType,
7914 cls : 'form-control',
7915 placeholder : this.placeholder || '',
7916 autocomplete : this.autocomplete || 'new-password'
7921 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7924 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7925 input.maxLength = this.maxLength;
7928 if (this.disabled) {
7929 input.disabled=true;
7932 if (this.readOnly) {
7933 input.readonly=true;
7937 input.name = this.name;
7940 input.cls += ' input-' + this.size;
7943 ['xs','sm','md','lg'].map(function(size){
7944 if (settings[size]) {
7945 cfg.cls += ' col-' + size + '-' + settings[size];
7949 var inputblock = input;
7953 cls: 'glyphicon form-control-feedback'
7956 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7959 cls : 'has-feedback',
7967 if (this.before || this.after) {
7970 cls : 'input-group',
7974 if (this.before && typeof(this.before) == 'string') {
7976 inputblock.cn.push({
7978 cls : 'roo-input-before input-group-addon',
7982 if (this.before && typeof(this.before) == 'object') {
7983 this.before = Roo.factory(this.before);
7985 inputblock.cn.push({
7987 cls : 'roo-input-before input-group-' +
7988 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7992 inputblock.cn.push(input);
7994 if (this.after && typeof(this.after) == 'string') {
7995 inputblock.cn.push({
7997 cls : 'roo-input-after input-group-addon',
8001 if (this.after && typeof(this.after) == 'object') {
8002 this.after = Roo.factory(this.after);
8004 inputblock.cn.push({
8006 cls : 'roo-input-after input-group-' +
8007 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8011 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8012 inputblock.cls += ' has-feedback';
8013 inputblock.cn.push(feedback);
8017 if (align ==='left' && this.fieldLabel.length) {
8024 cls : 'control-label col-sm-' + this.labelWidth,
8025 html : this.fieldLabel
8029 cls : "col-sm-" + (12 - this.labelWidth),
8036 } else if ( this.fieldLabel.length) {
8042 //cls : 'input-group-addon',
8043 html : this.fieldLabel
8062 if (this.parentType === 'Navbar' && this.parent().bar) {
8063 cfg.cls += ' navbar-form';
8070 * return the real input element.
8072 inputEl: function ()
8074 return this.el.select('input.form-control',true).first();
8077 tooltipEl : function()
8079 return this.inputEl();
8082 setDisabled : function(v)
8084 var i = this.inputEl().dom;
8086 i.removeAttribute('disabled');
8090 i.setAttribute('disabled','true');
8092 initEvents : function()
8095 this.inputEl().on("keydown" , this.fireKey, this);
8096 this.inputEl().on("focus", this.onFocus, this);
8097 this.inputEl().on("blur", this.onBlur, this);
8099 this.inputEl().relayEvent('keyup', this);
8101 // reference to original value for reset
8102 this.originalValue = this.getValue();
8103 //Roo.form.TextField.superclass.initEvents.call(this);
8104 if(this.validationEvent == 'keyup'){
8105 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8106 this.inputEl().on('keyup', this.filterValidation, this);
8108 else if(this.validationEvent !== false){
8109 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8112 if(this.selectOnFocus){
8113 this.on("focus", this.preFocus, this);
8116 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8117 this.inputEl().on("keypress", this.filterKeys, this);
8120 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8121 this.el.on("click", this.autoSize, this);
8124 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8125 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8128 if (typeof(this.before) == 'object') {
8129 this.before.render(this.el.select('.roo-input-before',true).first());
8131 if (typeof(this.after) == 'object') {
8132 this.after.render(this.el.select('.roo-input-after',true).first());
8137 filterValidation : function(e){
8138 if(!e.isNavKeyPress()){
8139 this.validationTask.delay(this.validationDelay);
8143 * Validates the field value
8144 * @return {Boolean} True if the value is valid, else false
8146 validate : function(){
8147 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8148 if(this.disabled || this.validateValue(this.getRawValue())){
8159 * Validates a value according to the field's validation rules and marks the field as invalid
8160 * if the validation fails
8161 * @param {Mixed} value The value to validate
8162 * @return {Boolean} True if the value is valid, else false
8164 validateValue : function(value){
8165 if(value.length < 1) { // if it's blank
8166 if(this.allowBlank){
8172 if(value.length < this.minLength){
8175 if(value.length > this.maxLength){
8179 var vt = Roo.form.VTypes;
8180 if(!vt[this.vtype](value, this)){
8184 if(typeof this.validator == "function"){
8185 var msg = this.validator(value);
8191 if(this.regex && !this.regex.test(value)){
8201 fireKey : function(e){
8202 //Roo.log('field ' + e.getKey());
8203 if(e.isNavKeyPress()){
8204 this.fireEvent("specialkey", this, e);
8207 focus : function (selectText){
8209 this.inputEl().focus();
8210 if(selectText === true){
8211 this.inputEl().dom.select();
8217 onFocus : function(){
8218 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8219 // this.el.addClass(this.focusClass);
8222 this.hasFocus = true;
8223 this.startValue = this.getValue();
8224 this.fireEvent("focus", this);
8228 beforeBlur : Roo.emptyFn,
8232 onBlur : function(){
8234 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8235 //this.el.removeClass(this.focusClass);
8237 this.hasFocus = false;
8238 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8241 var v = this.getValue();
8242 if(String(v) !== String(this.startValue)){
8243 this.fireEvent('change', this, v, this.startValue);
8245 this.fireEvent("blur", this);
8249 * Resets the current field value to the originally loaded value and clears any validation messages
8252 this.setValue(this.originalValue);
8256 * Returns the name of the field
8257 * @return {Mixed} name The name field
8259 getName: function(){
8263 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8264 * @return {Mixed} value The field value
8266 getValue : function(){
8268 var v = this.inputEl().getValue();
8273 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8274 * @return {Mixed} value The field value
8276 getRawValue : function(){
8277 var v = this.inputEl().getValue();
8283 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8284 * @param {Mixed} value The value to set
8286 setRawValue : function(v){
8287 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8290 selectText : function(start, end){
8291 var v = this.getRawValue();
8293 start = start === undefined ? 0 : start;
8294 end = end === undefined ? v.length : end;
8295 var d = this.inputEl().dom;
8296 if(d.setSelectionRange){
8297 d.setSelectionRange(start, end);
8298 }else if(d.createTextRange){
8299 var range = d.createTextRange();
8300 range.moveStart("character", start);
8301 range.moveEnd("character", v.length-end);
8308 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8309 * @param {Mixed} value The value to set
8311 setValue : function(v){
8314 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8320 processValue : function(value){
8321 if(this.stripCharsRe){
8322 var newValue = value.replace(this.stripCharsRe, '');
8323 if(newValue !== value){
8324 this.setRawValue(newValue);
8331 preFocus : function(){
8333 if(this.selectOnFocus){
8334 this.inputEl().dom.select();
8337 filterKeys : function(e){
8339 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8342 var c = e.getCharCode(), cc = String.fromCharCode(c);
8343 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8346 if(!this.maskRe.test(cc)){
8351 * Clear any invalid styles/messages for this field
8353 clearInvalid : function(){
8355 if(!this.el || this.preventMark){ // not rendered
8359 var label = this.el.select('label', true).first();
8360 var icon = this.el.select('i.fa-star', true).first();
8366 this.el.removeClass(this.invalidClass);
8368 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8370 var feedback = this.el.select('.form-control-feedback', true).first();
8373 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8378 this.fireEvent('valid', this);
8382 * Mark this field as valid
8384 markValid : function()
8386 if(!this.el || this.preventMark){ // not rendered
8390 this.el.removeClass([this.invalidClass, this.validClass]);
8392 var feedback = this.el.select('.form-control-feedback', true).first();
8395 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8398 if(this.disabled || this.allowBlank){
8402 var formGroup = this.el.findParent('.form-group', false, true);
8406 var label = formGroup.select('label', true).first();
8407 var icon = formGroup.select('i.fa-star', true).first();
8414 this.el.addClass(this.validClass);
8416 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8418 var feedback = this.el.select('.form-control-feedback', true).first();
8421 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8422 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8427 this.fireEvent('valid', this);
8431 * Mark this field as invalid
8432 * @param {String} msg The validation message
8434 markInvalid : function(msg)
8436 if(!this.el || this.preventMark){ // not rendered
8440 this.el.removeClass([this.invalidClass, this.validClass]);
8442 var feedback = this.el.select('.form-control-feedback', true).first();
8445 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8448 if(this.disabled || this.allowBlank){
8452 var formGroup = this.el.findParent('.form-group', false, true);
8455 var label = formGroup.select('label', true).first();
8456 var icon = formGroup.select('i.fa-star', true).first();
8458 if(!this.getValue().length && label && !icon){
8459 this.el.findParent('.form-group', false, true).createChild({
8461 cls : 'text-danger fa fa-lg fa-star',
8462 tooltip : 'This field is required',
8463 style : 'margin-right:5px;'
8469 this.el.addClass(this.invalidClass);
8471 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8473 var feedback = this.el.select('.form-control-feedback', true).first();
8476 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8478 if(this.getValue().length || this.forceFeedback){
8479 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8486 this.fireEvent('invalid', this, msg);
8489 SafariOnKeyDown : function(event)
8491 // this is a workaround for a password hang bug on chrome/ webkit.
8493 var isSelectAll = false;
8495 if(this.inputEl().dom.selectionEnd > 0){
8496 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8498 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8499 event.preventDefault();
8504 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8506 event.preventDefault();
8507 // this is very hacky as keydown always get's upper case.
8509 var cc = String.fromCharCode(event.getCharCode());
8510 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8514 adjustWidth : function(tag, w){
8515 tag = tag.toLowerCase();
8516 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8517 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8521 if(tag == 'textarea'){
8524 }else if(Roo.isOpera){
8528 if(tag == 'textarea'){
8547 * @class Roo.bootstrap.TextArea
8548 * @extends Roo.bootstrap.Input
8549 * Bootstrap TextArea class
8550 * @cfg {Number} cols Specifies the visible width of a text area
8551 * @cfg {Number} rows Specifies the visible number of lines in a text area
8552 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8553 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8554 * @cfg {string} html text
8557 * Create a new TextArea
8558 * @param {Object} config The config object
8561 Roo.bootstrap.TextArea = function(config){
8562 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8566 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8576 getAutoCreate : function(){
8578 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8589 value : this.value || '',
8590 html: this.html || '',
8591 cls : 'form-control',
8592 placeholder : this.placeholder || ''
8596 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8597 input.maxLength = this.maxLength;
8601 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8605 input.cols = this.cols;
8608 if (this.readOnly) {
8609 input.readonly = true;
8613 input.name = this.name;
8617 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8621 ['xs','sm','md','lg'].map(function(size){
8622 if (settings[size]) {
8623 cfg.cls += ' col-' + size + '-' + settings[size];
8627 var inputblock = input;
8629 if(this.hasFeedback && !this.allowBlank){
8633 cls: 'glyphicon form-control-feedback'
8637 cls : 'has-feedback',
8646 if (this.before || this.after) {
8649 cls : 'input-group',
8653 inputblock.cn.push({
8655 cls : 'input-group-addon',
8660 inputblock.cn.push(input);
8662 if(this.hasFeedback && !this.allowBlank){
8663 inputblock.cls += ' has-feedback';
8664 inputblock.cn.push(feedback);
8668 inputblock.cn.push({
8670 cls : 'input-group-addon',
8677 if (align ==='left' && this.fieldLabel.length) {
8678 // Roo.log("left and has label");
8684 cls : 'control-label col-sm-' + this.labelWidth,
8685 html : this.fieldLabel
8689 cls : "col-sm-" + (12 - this.labelWidth),
8696 } else if ( this.fieldLabel.length) {
8697 // Roo.log(" label");
8702 //cls : 'input-group-addon',
8703 html : this.fieldLabel
8713 // Roo.log(" no label && no align");
8723 if (this.disabled) {
8724 input.disabled=true;
8731 * return the real textarea element.
8733 inputEl: function ()
8735 return this.el.select('textarea.form-control',true).first();
8739 * Clear any invalid styles/messages for this field
8741 clearInvalid : function()
8744 if(!this.el || this.preventMark){ // not rendered
8748 var label = this.el.select('label', true).first();
8749 var icon = this.el.select('i.fa-star', true).first();
8755 this.el.removeClass(this.invalidClass);
8757 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8759 var feedback = this.el.select('.form-control-feedback', true).first();
8762 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8767 this.fireEvent('valid', this);
8771 * Mark this field as valid
8773 markValid : function()
8775 if(!this.el || this.preventMark){ // not rendered
8779 this.el.removeClass([this.invalidClass, this.validClass]);
8781 var feedback = this.el.select('.form-control-feedback', true).first();
8784 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8787 if(this.disabled || this.allowBlank){
8791 var label = this.el.select('label', true).first();
8792 var icon = this.el.select('i.fa-star', true).first();
8798 this.el.addClass(this.validClass);
8800 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8802 var feedback = this.el.select('.form-control-feedback', true).first();
8805 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8806 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8811 this.fireEvent('valid', this);
8815 * Mark this field as invalid
8816 * @param {String} msg The validation message
8818 markInvalid : function(msg)
8820 if(!this.el || this.preventMark){ // not rendered
8824 this.el.removeClass([this.invalidClass, this.validClass]);
8826 var feedback = this.el.select('.form-control-feedback', true).first();
8829 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8832 if(this.disabled || this.allowBlank){
8836 var label = this.el.select('label', true).first();
8837 var icon = this.el.select('i.fa-star', true).first();
8839 if(!this.getValue().length && label && !icon){
8840 this.el.createChild({
8842 cls : 'text-danger fa fa-lg fa-star',
8843 tooltip : 'This field is required',
8844 style : 'margin-right:5px;'
8848 this.el.addClass(this.invalidClass);
8850 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
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]);
8857 if(this.getValue().length || this.forceFeedback){
8858 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8865 this.fireEvent('invalid', this, msg);
8873 * trigger field - base class for combo..
8878 * @class Roo.bootstrap.TriggerField
8879 * @extends Roo.bootstrap.Input
8880 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8881 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8882 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8883 * for which you can provide a custom implementation. For example:
8885 var trigger = new Roo.bootstrap.TriggerField();
8886 trigger.onTriggerClick = myTriggerFn;
8887 trigger.applyTo('my-field');
8890 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8891 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8892 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
8893 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8894 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8897 * Create a new TriggerField.
8898 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8899 * to the base TextField)
8901 Roo.bootstrap.TriggerField = function(config){
8902 this.mimicing = false;
8903 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8906 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
8908 * @cfg {String} triggerClass A CSS class to apply to the trigger
8911 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8916 * @cfg {Boolean} removable (true|false) special filter default false
8920 /** @cfg {Boolean} grow @hide */
8921 /** @cfg {Number} growMin @hide */
8922 /** @cfg {Number} growMax @hide */
8928 autoSize: Roo.emptyFn,
8935 actionMode : 'wrap',
8940 getAutoCreate : function(){
8942 var align = this.labelAlign || this.parentLabelAlign();
8947 cls: 'form-group' //input-group
8954 type : this.inputType,
8955 cls : 'form-control',
8956 autocomplete: 'new-password',
8957 placeholder : this.placeholder || ''
8961 input.name = this.name;
8964 input.cls += ' input-' + this.size;
8967 if (this.disabled) {
8968 input.disabled=true;
8971 var inputblock = input;
8973 if(this.hasFeedback && !this.allowBlank){
8977 cls: 'glyphicon form-control-feedback'
8980 if(this.removable && !this.editable && !this.tickable){
8982 cls : 'has-feedback',
8988 cls : 'roo-combo-removable-btn close'
8995 cls : 'has-feedback',
9004 if(this.removable && !this.editable && !this.tickable){
9006 cls : 'roo-removable',
9012 cls : 'roo-combo-removable-btn close'
9019 if (this.before || this.after) {
9022 cls : 'input-group',
9026 inputblock.cn.push({
9028 cls : 'input-group-addon',
9033 inputblock.cn.push(input);
9035 if(this.hasFeedback && !this.allowBlank){
9036 inputblock.cls += ' has-feedback';
9037 inputblock.cn.push(feedback);
9041 inputblock.cn.push({
9043 cls : 'input-group-addon',
9056 cls: 'form-hidden-field'
9070 cls: 'form-hidden-field'
9074 cls: 'select2-choices',
9078 cls: 'select2-search-field',
9091 cls: 'select2-container input-group',
9096 // cls: 'typeahead typeahead-long dropdown-menu',
9097 // style: 'display:none'
9102 if(!this.multiple && this.showToggleBtn){
9108 if (this.caret != false) {
9111 cls: 'fa fa-' + this.caret
9118 cls : 'input-group-addon btn dropdown-toggle',
9123 cls: 'combobox-clear',
9137 combobox.cls += ' select2-container-multi';
9140 if (align ==='left' && this.fieldLabel.length) {
9142 // Roo.log("left and has label");
9148 cls : 'control-label col-sm-' + this.labelWidth,
9149 html : this.fieldLabel
9153 cls : "col-sm-" + (12 - this.labelWidth),
9160 } else if ( this.fieldLabel.length) {
9161 // Roo.log(" label");
9166 //cls : 'input-group-addon',
9167 html : this.fieldLabel
9177 // Roo.log(" no label && no align");
9184 ['xs','sm','md','lg'].map(function(size){
9185 if (settings[size]) {
9186 cfg.cls += ' col-' + size + '-' + settings[size];
9197 onResize : function(w, h){
9198 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9199 // if(typeof w == 'number'){
9200 // var x = w - this.trigger.getWidth();
9201 // this.inputEl().setWidth(this.adjustWidth('input', x));
9202 // this.trigger.setStyle('left', x+'px');
9207 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9210 getResizeEl : function(){
9211 return this.inputEl();
9215 getPositionEl : function(){
9216 return this.inputEl();
9220 alignErrorIcon : function(){
9221 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9225 initEvents : function(){
9229 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9230 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9231 if(!this.multiple && this.showToggleBtn){
9232 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9233 if(this.hideTrigger){
9234 this.trigger.setDisplayed(false);
9236 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9240 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9243 if(this.removable && !this.editable && !this.tickable){
9244 var close = this.closeTriggerEl();
9247 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9248 close.on('click', this.removeBtnClick, this, close);
9252 //this.trigger.addClassOnOver('x-form-trigger-over');
9253 //this.trigger.addClassOnClick('x-form-trigger-click');
9256 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9260 closeTriggerEl : function()
9262 var close = this.el.select('.roo-combo-removable-btn', true).first();
9263 return close ? close : false;
9266 removeBtnClick : function(e, h, el)
9270 if(this.fireEvent("remove", this) !== false){
9275 createList : function()
9277 this.list = Roo.get(document.body).createChild({
9279 cls: 'typeahead typeahead-long dropdown-menu',
9280 style: 'display:none'
9283 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9288 initTrigger : function(){
9293 onDestroy : function(){
9295 this.trigger.removeAllListeners();
9296 // this.trigger.remove();
9299 // this.wrap.remove();
9301 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9305 onFocus : function(){
9306 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9309 this.wrap.addClass('x-trigger-wrap-focus');
9310 this.mimicing = true;
9311 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9312 if(this.monitorTab){
9313 this.el.on("keydown", this.checkTab, this);
9320 checkTab : function(e){
9321 if(e.getKey() == e.TAB){
9327 onBlur : function(){
9332 mimicBlur : function(e, t){
9334 if(!this.wrap.contains(t) && this.validateBlur()){
9341 triggerBlur : function(){
9342 this.mimicing = false;
9343 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9344 if(this.monitorTab){
9345 this.el.un("keydown", this.checkTab, this);
9347 //this.wrap.removeClass('x-trigger-wrap-focus');
9348 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9352 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9353 validateBlur : function(e, t){
9358 onDisable : function(){
9359 this.inputEl().dom.disabled = true;
9360 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9362 // this.wrap.addClass('x-item-disabled');
9367 onEnable : function(){
9368 this.inputEl().dom.disabled = false;
9369 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9371 // this.el.removeClass('x-item-disabled');
9376 onShow : function(){
9377 var ae = this.getActionEl();
9380 ae.dom.style.display = '';
9381 ae.dom.style.visibility = 'visible';
9387 onHide : function(){
9388 var ae = this.getActionEl();
9389 ae.dom.style.display = 'none';
9393 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9394 * by an implementing function.
9396 * @param {EventObject} e
9398 onTriggerClick : Roo.emptyFn
9402 * Ext JS Library 1.1.1
9403 * Copyright(c) 2006-2007, Ext JS, LLC.
9405 * Originally Released Under LGPL - original licence link has changed is not relivant.
9408 * <script type="text/javascript">
9413 * @class Roo.data.SortTypes
9415 * Defines the default sorting (casting?) comparison functions used when sorting data.
9417 Roo.data.SortTypes = {
9419 * Default sort that does nothing
9420 * @param {Mixed} s The value being converted
9421 * @return {Mixed} The comparison value
9428 * The regular expression used to strip tags
9432 stripTagsRE : /<\/?[^>]+>/gi,
9435 * Strips all HTML tags to sort on text only
9436 * @param {Mixed} s The value being converted
9437 * @return {String} The comparison value
9439 asText : function(s){
9440 return String(s).replace(this.stripTagsRE, "");
9444 * Strips all HTML tags to sort on text only - Case insensitive
9445 * @param {Mixed} s The value being converted
9446 * @return {String} The comparison value
9448 asUCText : function(s){
9449 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9453 * Case insensitive string
9454 * @param {Mixed} s The value being converted
9455 * @return {String} The comparison value
9457 asUCString : function(s) {
9458 return String(s).toUpperCase();
9463 * @param {Mixed} s The value being converted
9464 * @return {Number} The comparison value
9466 asDate : function(s) {
9470 if(s instanceof Date){
9473 return Date.parse(String(s));
9478 * @param {Mixed} s The value being converted
9479 * @return {Float} The comparison value
9481 asFloat : function(s) {
9482 var val = parseFloat(String(s).replace(/,/g, ""));
9491 * @param {Mixed} s The value being converted
9492 * @return {Number} The comparison value
9494 asInt : function(s) {
9495 var val = parseInt(String(s).replace(/,/g, ""));
9503 * Ext JS Library 1.1.1
9504 * Copyright(c) 2006-2007, Ext JS, LLC.
9506 * Originally Released Under LGPL - original licence link has changed is not relivant.
9509 * <script type="text/javascript">
9513 * @class Roo.data.Record
9514 * Instances of this class encapsulate both record <em>definition</em> information, and record
9515 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9516 * to access Records cached in an {@link Roo.data.Store} object.<br>
9518 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9519 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9522 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9524 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9525 * {@link #create}. The parameters are the same.
9526 * @param {Array} data An associative Array of data values keyed by the field name.
9527 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9528 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9529 * not specified an integer id is generated.
9531 Roo.data.Record = function(data, id){
9532 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9537 * Generate a constructor for a specific record layout.
9538 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9539 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9540 * Each field definition object may contain the following properties: <ul>
9541 * <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,
9542 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9543 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9544 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9545 * is being used, then this is a string containing the javascript expression to reference the data relative to
9546 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9547 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9548 * this may be omitted.</p></li>
9549 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9550 * <ul><li>auto (Default, implies no conversion)</li>
9555 * <li>date</li></ul></p></li>
9556 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9557 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9558 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9559 * by the Reader into an object that will be stored in the Record. It is passed the
9560 * following parameters:<ul>
9561 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9563 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9565 * <br>usage:<br><pre><code>
9566 var TopicRecord = Roo.data.Record.create(
9567 {name: 'title', mapping: 'topic_title'},
9568 {name: 'author', mapping: 'username'},
9569 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9570 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9571 {name: 'lastPoster', mapping: 'user2'},
9572 {name: 'excerpt', mapping: 'post_text'}
9575 var myNewRecord = new TopicRecord({
9576 title: 'Do my job please',
9579 lastPost: new Date(),
9580 lastPoster: 'Animal',
9581 excerpt: 'No way dude!'
9583 myStore.add(myNewRecord);
9588 Roo.data.Record.create = function(o){
9590 f.superclass.constructor.apply(this, arguments);
9592 Roo.extend(f, Roo.data.Record);
9593 var p = f.prototype;
9594 p.fields = new Roo.util.MixedCollection(false, function(field){
9597 for(var i = 0, len = o.length; i < len; i++){
9598 p.fields.add(new Roo.data.Field(o[i]));
9600 f.getField = function(name){
9601 return p.fields.get(name);
9606 Roo.data.Record.AUTO_ID = 1000;
9607 Roo.data.Record.EDIT = 'edit';
9608 Roo.data.Record.REJECT = 'reject';
9609 Roo.data.Record.COMMIT = 'commit';
9611 Roo.data.Record.prototype = {
9613 * Readonly flag - true if this record has been modified.
9622 join : function(store){
9627 * Set the named field to the specified value.
9628 * @param {String} name The name of the field to set.
9629 * @param {Object} value The value to set the field to.
9631 set : function(name, value){
9632 if(this.data[name] == value){
9639 if(typeof this.modified[name] == 'undefined'){
9640 this.modified[name] = this.data[name];
9642 this.data[name] = value;
9643 if(!this.editing && this.store){
9644 this.store.afterEdit(this);
9649 * Get the value of the named field.
9650 * @param {String} name The name of the field to get the value of.
9651 * @return {Object} The value of the field.
9653 get : function(name){
9654 return this.data[name];
9658 beginEdit : function(){
9659 this.editing = true;
9664 cancelEdit : function(){
9665 this.editing = false;
9666 delete this.modified;
9670 endEdit : function(){
9671 this.editing = false;
9672 if(this.dirty && this.store){
9673 this.store.afterEdit(this);
9678 * Usually called by the {@link Roo.data.Store} which owns the Record.
9679 * Rejects all changes made to the Record since either creation, or the last commit operation.
9680 * Modified fields are reverted to their original values.
9682 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9683 * of reject operations.
9685 reject : function(){
9686 var m = this.modified;
9688 if(typeof m[n] != "function"){
9689 this.data[n] = m[n];
9693 delete this.modified;
9694 this.editing = false;
9696 this.store.afterReject(this);
9701 * Usually called by the {@link Roo.data.Store} which owns the Record.
9702 * Commits all changes made to the Record since either creation, or the last commit operation.
9704 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9705 * of commit operations.
9707 commit : function(){
9709 delete this.modified;
9710 this.editing = false;
9712 this.store.afterCommit(this);
9717 hasError : function(){
9718 return this.error != null;
9722 clearError : function(){
9727 * Creates a copy of this record.
9728 * @param {String} id (optional) A new record id if you don't want to use this record's id
9731 copy : function(newId) {
9732 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9736 * Ext JS Library 1.1.1
9737 * Copyright(c) 2006-2007, Ext JS, LLC.
9739 * Originally Released Under LGPL - original licence link has changed is not relivant.
9742 * <script type="text/javascript">
9748 * @class Roo.data.Store
9749 * @extends Roo.util.Observable
9750 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9751 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9753 * 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
9754 * has no knowledge of the format of the data returned by the Proxy.<br>
9756 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9757 * instances from the data object. These records are cached and made available through accessor functions.
9759 * Creates a new Store.
9760 * @param {Object} config A config object containing the objects needed for the Store to access data,
9761 * and read the data into Records.
9763 Roo.data.Store = function(config){
9764 this.data = new Roo.util.MixedCollection(false);
9765 this.data.getKey = function(o){
9768 this.baseParams = {};
9775 "multisort" : "_multisort"
9778 if(config && config.data){
9779 this.inlineData = config.data;
9783 Roo.apply(this, config);
9785 if(this.reader){ // reader passed
9786 this.reader = Roo.factory(this.reader, Roo.data);
9787 this.reader.xmodule = this.xmodule || false;
9788 if(!this.recordType){
9789 this.recordType = this.reader.recordType;
9791 if(this.reader.onMetaChange){
9792 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9796 if(this.recordType){
9797 this.fields = this.recordType.prototype.fields;
9803 * @event datachanged
9804 * Fires when the data cache has changed, and a widget which is using this Store
9805 * as a Record cache should refresh its view.
9806 * @param {Store} this
9811 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9812 * @param {Store} this
9813 * @param {Object} meta The JSON metadata
9818 * Fires when Records have been added to the Store
9819 * @param {Store} this
9820 * @param {Roo.data.Record[]} records The array of Records added
9821 * @param {Number} index The index at which the record(s) were added
9826 * Fires when a Record has been removed from the Store
9827 * @param {Store} this
9828 * @param {Roo.data.Record} record The Record that was removed
9829 * @param {Number} index The index at which the record was removed
9834 * Fires when a Record has been updated
9835 * @param {Store} this
9836 * @param {Roo.data.Record} record The Record that was updated
9837 * @param {String} operation The update operation being performed. Value may be one of:
9839 Roo.data.Record.EDIT
9840 Roo.data.Record.REJECT
9841 Roo.data.Record.COMMIT
9847 * Fires when the data cache has been cleared.
9848 * @param {Store} this
9853 * Fires before a request is made for a new data object. If the beforeload handler returns false
9854 * the load action will be canceled.
9855 * @param {Store} this
9856 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9860 * @event beforeloadadd
9861 * Fires after a new set of Records has been loaded.
9862 * @param {Store} this
9863 * @param {Roo.data.Record[]} records The Records that were loaded
9864 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9866 beforeloadadd : true,
9869 * Fires after a new set of Records has been loaded, before they are added to the store.
9870 * @param {Store} this
9871 * @param {Roo.data.Record[]} records The Records that were loaded
9872 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9873 * @params {Object} return from reader
9877 * @event loadexception
9878 * Fires if an exception occurs in the Proxy during loading.
9879 * Called with the signature of the Proxy's "loadexception" event.
9880 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9883 * @param {Object} return from JsonData.reader() - success, totalRecords, records
9884 * @param {Object} load options
9885 * @param {Object} jsonData from your request (normally this contains the Exception)
9887 loadexception : true
9891 this.proxy = Roo.factory(this.proxy, Roo.data);
9892 this.proxy.xmodule = this.xmodule || false;
9893 this.relayEvents(this.proxy, ["loadexception"]);
9895 this.sortToggle = {};
9896 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9898 Roo.data.Store.superclass.constructor.call(this);
9900 if(this.inlineData){
9901 this.loadData(this.inlineData);
9902 delete this.inlineData;
9906 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9908 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
9909 * without a remote query - used by combo/forms at present.
9913 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9916 * @cfg {Array} data Inline data to be loaded when the store is initialized.
9919 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9920 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9923 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9924 * on any HTTP request
9927 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9930 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9934 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9935 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9940 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9941 * loaded or when a record is removed. (defaults to false).
9943 pruneModifiedRecords : false,
9949 * Add Records to the Store and fires the add event.
9950 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9952 add : function(records){
9953 records = [].concat(records);
9954 for(var i = 0, len = records.length; i < len; i++){
9955 records[i].join(this);
9957 var index = this.data.length;
9958 this.data.addAll(records);
9959 this.fireEvent("add", this, records, index);
9963 * Remove a Record from the Store and fires the remove event.
9964 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9966 remove : function(record){
9967 var index = this.data.indexOf(record);
9968 this.data.removeAt(index);
9969 if(this.pruneModifiedRecords){
9970 this.modified.remove(record);
9972 this.fireEvent("remove", this, record, index);
9976 * Remove all Records from the Store and fires the clear event.
9978 removeAll : function(){
9980 if(this.pruneModifiedRecords){
9983 this.fireEvent("clear", this);
9987 * Inserts Records to the Store at the given index and fires the add event.
9988 * @param {Number} index The start index at which to insert the passed Records.
9989 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9991 insert : function(index, records){
9992 records = [].concat(records);
9993 for(var i = 0, len = records.length; i < len; i++){
9994 this.data.insert(index, records[i]);
9995 records[i].join(this);
9997 this.fireEvent("add", this, records, index);
10001 * Get the index within the cache of the passed Record.
10002 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10003 * @return {Number} The index of the passed Record. Returns -1 if not found.
10005 indexOf : function(record){
10006 return this.data.indexOf(record);
10010 * Get the index within the cache of the Record with the passed id.
10011 * @param {String} id The id of the Record to find.
10012 * @return {Number} The index of the Record. Returns -1 if not found.
10014 indexOfId : function(id){
10015 return this.data.indexOfKey(id);
10019 * Get the Record with the specified id.
10020 * @param {String} id The id of the Record to find.
10021 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10023 getById : function(id){
10024 return this.data.key(id);
10028 * Get the Record at the specified index.
10029 * @param {Number} index The index of the Record to find.
10030 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10032 getAt : function(index){
10033 return this.data.itemAt(index);
10037 * Returns a range of Records between specified indices.
10038 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10039 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10040 * @return {Roo.data.Record[]} An array of Records
10042 getRange : function(start, end){
10043 return this.data.getRange(start, end);
10047 storeOptions : function(o){
10048 o = Roo.apply({}, o);
10051 this.lastOptions = o;
10055 * Loads the Record cache from the configured Proxy using the configured Reader.
10057 * If using remote paging, then the first load call must specify the <em>start</em>
10058 * and <em>limit</em> properties in the options.params property to establish the initial
10059 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10061 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10062 * and this call will return before the new data has been loaded. Perform any post-processing
10063 * in a callback function, or in a "load" event handler.</strong>
10065 * @param {Object} options An object containing properties which control loading options:<ul>
10066 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10067 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10068 * passed the following arguments:<ul>
10069 * <li>r : Roo.data.Record[]</li>
10070 * <li>options: Options object from the load call</li>
10071 * <li>success: Boolean success indicator</li></ul></li>
10072 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10073 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10076 load : function(options){
10077 options = options || {};
10078 if(this.fireEvent("beforeload", this, options) !== false){
10079 this.storeOptions(options);
10080 var p = Roo.apply(options.params || {}, this.baseParams);
10081 // if meta was not loaded from remote source.. try requesting it.
10082 if (!this.reader.metaFromRemote) {
10083 p._requestMeta = 1;
10085 if(this.sortInfo && this.remoteSort){
10086 var pn = this.paramNames;
10087 p[pn["sort"]] = this.sortInfo.field;
10088 p[pn["dir"]] = this.sortInfo.direction;
10090 if (this.multiSort) {
10091 var pn = this.paramNames;
10092 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10095 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10100 * Reloads the Record cache from the configured Proxy using the configured Reader and
10101 * the options from the last load operation performed.
10102 * @param {Object} options (optional) An object containing properties which may override the options
10103 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10104 * the most recently used options are reused).
10106 reload : function(options){
10107 this.load(Roo.applyIf(options||{}, this.lastOptions));
10111 // Called as a callback by the Reader during a load operation.
10112 loadRecords : function(o, options, success){
10113 if(!o || success === false){
10114 if(success !== false){
10115 this.fireEvent("load", this, [], options, o);
10117 if(options.callback){
10118 options.callback.call(options.scope || this, [], options, false);
10122 // if data returned failure - throw an exception.
10123 if (o.success === false) {
10124 // show a message if no listener is registered.
10125 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10126 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10128 // loadmask wil be hooked into this..
10129 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10132 var r = o.records, t = o.totalRecords || r.length;
10134 this.fireEvent("beforeloadadd", this, r, options, o);
10136 if(!options || options.add !== true){
10137 if(this.pruneModifiedRecords){
10138 this.modified = [];
10140 for(var i = 0, len = r.length; i < len; i++){
10144 this.data = this.snapshot;
10145 delete this.snapshot;
10148 this.data.addAll(r);
10149 this.totalLength = t;
10151 this.fireEvent("datachanged", this);
10153 this.totalLength = Math.max(t, this.data.length+r.length);
10156 this.fireEvent("load", this, r, options, o);
10157 if(options.callback){
10158 options.callback.call(options.scope || this, r, options, true);
10164 * Loads data from a passed data block. A Reader which understands the format of the data
10165 * must have been configured in the constructor.
10166 * @param {Object} data The data block from which to read the Records. The format of the data expected
10167 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10168 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10170 loadData : function(o, append){
10171 var r = this.reader.readRecords(o);
10172 this.loadRecords(r, {add: append}, true);
10176 * Gets the number of cached records.
10178 * <em>If using paging, this may not be the total size of the dataset. If the data object
10179 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10180 * the data set size</em>
10182 getCount : function(){
10183 return this.data.length || 0;
10187 * Gets the total number of records in the dataset as returned by the server.
10189 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10190 * the dataset size</em>
10192 getTotalCount : function(){
10193 return this.totalLength || 0;
10197 * Returns the sort state of the Store as an object with two properties:
10199 field {String} The name of the field by which the Records are sorted
10200 direction {String} The sort order, "ASC" or "DESC"
10203 getSortState : function(){
10204 return this.sortInfo;
10208 applySort : function(){
10209 if(this.sortInfo && !this.remoteSort){
10210 var s = this.sortInfo, f = s.field;
10211 var st = this.fields.get(f).sortType;
10212 var fn = function(r1, r2){
10213 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10214 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10216 this.data.sort(s.direction, fn);
10217 if(this.snapshot && this.snapshot != this.data){
10218 this.snapshot.sort(s.direction, fn);
10224 * Sets the default sort column and order to be used by the next load operation.
10225 * @param {String} fieldName The name of the field to sort by.
10226 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10228 setDefaultSort : function(field, dir){
10229 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10233 * Sort the Records.
10234 * If remote sorting is used, the sort is performed on the server, and the cache is
10235 * reloaded. If local sorting is used, the cache is sorted internally.
10236 * @param {String} fieldName The name of the field to sort by.
10237 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10239 sort : function(fieldName, dir){
10240 var f = this.fields.get(fieldName);
10242 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10244 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10245 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10250 this.sortToggle[f.name] = dir;
10251 this.sortInfo = {field: f.name, direction: dir};
10252 if(!this.remoteSort){
10254 this.fireEvent("datachanged", this);
10256 this.load(this.lastOptions);
10261 * Calls the specified function for each of the Records in the cache.
10262 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10263 * Returning <em>false</em> aborts and exits the iteration.
10264 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10266 each : function(fn, scope){
10267 this.data.each(fn, scope);
10271 * Gets all records modified since the last commit. Modified records are persisted across load operations
10272 * (e.g., during paging).
10273 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10275 getModifiedRecords : function(){
10276 return this.modified;
10280 createFilterFn : function(property, value, anyMatch){
10281 if(!value.exec){ // not a regex
10282 value = String(value);
10283 if(value.length == 0){
10286 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10288 return function(r){
10289 return value.test(r.data[property]);
10294 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10295 * @param {String} property A field on your records
10296 * @param {Number} start The record index to start at (defaults to 0)
10297 * @param {Number} end The last record index to include (defaults to length - 1)
10298 * @return {Number} The sum
10300 sum : function(property, start, end){
10301 var rs = this.data.items, v = 0;
10302 start = start || 0;
10303 end = (end || end === 0) ? end : rs.length-1;
10305 for(var i = start; i <= end; i++){
10306 v += (rs[i].data[property] || 0);
10312 * Filter the records by a specified property.
10313 * @param {String} field A field on your records
10314 * @param {String/RegExp} value Either a string that the field
10315 * should start with or a RegExp to test against the field
10316 * @param {Boolean} anyMatch True to match any part not just the beginning
10318 filter : function(property, value, anyMatch){
10319 var fn = this.createFilterFn(property, value, anyMatch);
10320 return fn ? this.filterBy(fn) : this.clearFilter();
10324 * Filter by a function. The specified function will be called with each
10325 * record in this data source. If the function returns true the record is included,
10326 * otherwise it is filtered.
10327 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10328 * @param {Object} scope (optional) The scope of the function (defaults to this)
10330 filterBy : function(fn, scope){
10331 this.snapshot = this.snapshot || this.data;
10332 this.data = this.queryBy(fn, scope||this);
10333 this.fireEvent("datachanged", this);
10337 * Query the records by a specified property.
10338 * @param {String} field A field on your records
10339 * @param {String/RegExp} value Either a string that the field
10340 * should start with or a RegExp to test against the field
10341 * @param {Boolean} anyMatch True to match any part not just the beginning
10342 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10344 query : function(property, value, anyMatch){
10345 var fn = this.createFilterFn(property, value, anyMatch);
10346 return fn ? this.queryBy(fn) : this.data.clone();
10350 * Query by a function. The specified function will be called with each
10351 * record in this data source. If the function returns true the record is included
10353 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10354 * @param {Object} scope (optional) The scope of the function (defaults to this)
10355 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10357 queryBy : function(fn, scope){
10358 var data = this.snapshot || this.data;
10359 return data.filterBy(fn, scope||this);
10363 * Collects unique values for a particular dataIndex from this store.
10364 * @param {String} dataIndex The property to collect
10365 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10366 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10367 * @return {Array} An array of the unique values
10369 collect : function(dataIndex, allowNull, bypassFilter){
10370 var d = (bypassFilter === true && this.snapshot) ?
10371 this.snapshot.items : this.data.items;
10372 var v, sv, r = [], l = {};
10373 for(var i = 0, len = d.length; i < len; i++){
10374 v = d[i].data[dataIndex];
10376 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10385 * Revert to a view of the Record cache with no filtering applied.
10386 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10388 clearFilter : function(suppressEvent){
10389 if(this.snapshot && this.snapshot != this.data){
10390 this.data = this.snapshot;
10391 delete this.snapshot;
10392 if(suppressEvent !== true){
10393 this.fireEvent("datachanged", this);
10399 afterEdit : function(record){
10400 if(this.modified.indexOf(record) == -1){
10401 this.modified.push(record);
10403 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10407 afterReject : function(record){
10408 this.modified.remove(record);
10409 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10413 afterCommit : function(record){
10414 this.modified.remove(record);
10415 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10419 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10420 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10422 commitChanges : function(){
10423 var m = this.modified.slice(0);
10424 this.modified = [];
10425 for(var i = 0, len = m.length; i < len; i++){
10431 * Cancel outstanding changes on all changed records.
10433 rejectChanges : function(){
10434 var m = this.modified.slice(0);
10435 this.modified = [];
10436 for(var i = 0, len = m.length; i < len; i++){
10441 onMetaChange : function(meta, rtype, o){
10442 this.recordType = rtype;
10443 this.fields = rtype.prototype.fields;
10444 delete this.snapshot;
10445 this.sortInfo = meta.sortInfo || this.sortInfo;
10446 this.modified = [];
10447 this.fireEvent('metachange', this, this.reader.meta);
10450 moveIndex : function(data, type)
10452 var index = this.indexOf(data);
10454 var newIndex = index + type;
10458 this.insert(newIndex, data);
10463 * Ext JS Library 1.1.1
10464 * Copyright(c) 2006-2007, Ext JS, LLC.
10466 * Originally Released Under LGPL - original licence link has changed is not relivant.
10469 * <script type="text/javascript">
10473 * @class Roo.data.SimpleStore
10474 * @extends Roo.data.Store
10475 * Small helper class to make creating Stores from Array data easier.
10476 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10477 * @cfg {Array} fields An array of field definition objects, or field name strings.
10478 * @cfg {Array} data The multi-dimensional array of data
10480 * @param {Object} config
10482 Roo.data.SimpleStore = function(config){
10483 Roo.data.SimpleStore.superclass.constructor.call(this, {
10485 reader: new Roo.data.ArrayReader({
10488 Roo.data.Record.create(config.fields)
10490 proxy : new Roo.data.MemoryProxy(config.data)
10494 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10496 * Ext JS Library 1.1.1
10497 * Copyright(c) 2006-2007, Ext JS, LLC.
10499 * Originally Released Under LGPL - original licence link has changed is not relivant.
10502 * <script type="text/javascript">
10507 * @extends Roo.data.Store
10508 * @class Roo.data.JsonStore
10509 * Small helper class to make creating Stores for JSON data easier. <br/>
10511 var store = new Roo.data.JsonStore({
10512 url: 'get-images.php',
10514 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10517 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10518 * JsonReader and HttpProxy (unless inline data is provided).</b>
10519 * @cfg {Array} fields An array of field definition objects, or field name strings.
10521 * @param {Object} config
10523 Roo.data.JsonStore = function(c){
10524 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10525 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10526 reader: new Roo.data.JsonReader(c, c.fields)
10529 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10531 * Ext JS Library 1.1.1
10532 * Copyright(c) 2006-2007, Ext JS, LLC.
10534 * Originally Released Under LGPL - original licence link has changed is not relivant.
10537 * <script type="text/javascript">
10541 Roo.data.Field = function(config){
10542 if(typeof config == "string"){
10543 config = {name: config};
10545 Roo.apply(this, config);
10548 this.type = "auto";
10551 var st = Roo.data.SortTypes;
10552 // named sortTypes are supported, here we look them up
10553 if(typeof this.sortType == "string"){
10554 this.sortType = st[this.sortType];
10557 // set default sortType for strings and dates
10558 if(!this.sortType){
10561 this.sortType = st.asUCString;
10564 this.sortType = st.asDate;
10567 this.sortType = st.none;
10572 var stripRe = /[\$,%]/g;
10574 // prebuilt conversion function for this field, instead of
10575 // switching every time we're reading a value
10577 var cv, dateFormat = this.dateFormat;
10582 cv = function(v){ return v; };
10585 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10589 return v !== undefined && v !== null && v !== '' ?
10590 parseInt(String(v).replace(stripRe, ""), 10) : '';
10595 return v !== undefined && v !== null && v !== '' ?
10596 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10601 cv = function(v){ return v === true || v === "true" || v == 1; };
10608 if(v instanceof Date){
10612 if(dateFormat == "timestamp"){
10613 return new Date(v*1000);
10615 return Date.parseDate(v, dateFormat);
10617 var parsed = Date.parse(v);
10618 return parsed ? new Date(parsed) : null;
10627 Roo.data.Field.prototype = {
10635 * Ext JS Library 1.1.1
10636 * Copyright(c) 2006-2007, Ext JS, LLC.
10638 * Originally Released Under LGPL - original licence link has changed is not relivant.
10641 * <script type="text/javascript">
10644 // Base class for reading structured data from a data source. This class is intended to be
10645 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10648 * @class Roo.data.DataReader
10649 * Base class for reading structured data from a data source. This class is intended to be
10650 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10653 Roo.data.DataReader = function(meta, recordType){
10657 this.recordType = recordType instanceof Array ?
10658 Roo.data.Record.create(recordType) : recordType;
10661 Roo.data.DataReader.prototype = {
10663 * Create an empty record
10664 * @param {Object} data (optional) - overlay some values
10665 * @return {Roo.data.Record} record created.
10667 newRow : function(d) {
10669 this.recordType.prototype.fields.each(function(c) {
10671 case 'int' : da[c.name] = 0; break;
10672 case 'date' : da[c.name] = new Date(); break;
10673 case 'float' : da[c.name] = 0.0; break;
10674 case 'boolean' : da[c.name] = false; break;
10675 default : da[c.name] = ""; break;
10679 return new this.recordType(Roo.apply(da, d));
10684 * Ext JS Library 1.1.1
10685 * Copyright(c) 2006-2007, Ext JS, LLC.
10687 * Originally Released Under LGPL - original licence link has changed is not relivant.
10690 * <script type="text/javascript">
10694 * @class Roo.data.DataProxy
10695 * @extends Roo.data.Observable
10696 * This class is an abstract base class for implementations which provide retrieval of
10697 * unformatted data objects.<br>
10699 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10700 * (of the appropriate type which knows how to parse the data object) to provide a block of
10701 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10703 * Custom implementations must implement the load method as described in
10704 * {@link Roo.data.HttpProxy#load}.
10706 Roo.data.DataProxy = function(){
10709 * @event beforeload
10710 * Fires before a network request is made to retrieve a data object.
10711 * @param {Object} This DataProxy object.
10712 * @param {Object} params The params parameter to the load function.
10717 * Fires before the load method's callback is called.
10718 * @param {Object} This DataProxy object.
10719 * @param {Object} o The data object.
10720 * @param {Object} arg The callback argument object passed to the load function.
10724 * @event loadexception
10725 * Fires if an Exception occurs during data retrieval.
10726 * @param {Object} This DataProxy object.
10727 * @param {Object} o The data object.
10728 * @param {Object} arg The callback argument object passed to the load function.
10729 * @param {Object} e The Exception.
10731 loadexception : true
10733 Roo.data.DataProxy.superclass.constructor.call(this);
10736 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10739 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10743 * Ext JS Library 1.1.1
10744 * Copyright(c) 2006-2007, Ext JS, LLC.
10746 * Originally Released Under LGPL - original licence link has changed is not relivant.
10749 * <script type="text/javascript">
10752 * @class Roo.data.MemoryProxy
10753 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10754 * to the Reader when its load method is called.
10756 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10758 Roo.data.MemoryProxy = function(data){
10762 Roo.data.MemoryProxy.superclass.constructor.call(this);
10766 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10768 * Load data from the requested source (in this case an in-memory
10769 * data object passed to the constructor), read the data object into
10770 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10771 * process that block using the passed callback.
10772 * @param {Object} params This parameter is not used by the MemoryProxy class.
10773 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10774 * object into a block of Roo.data.Records.
10775 * @param {Function} callback The function into which to pass the block of Roo.data.records.
10776 * The function must be passed <ul>
10777 * <li>The Record block object</li>
10778 * <li>The "arg" argument from the load function</li>
10779 * <li>A boolean success indicator</li>
10781 * @param {Object} scope The scope in which to call the callback
10782 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10784 load : function(params, reader, callback, scope, arg){
10785 params = params || {};
10788 result = reader.readRecords(this.data);
10790 this.fireEvent("loadexception", this, arg, null, e);
10791 callback.call(scope, null, arg, false);
10794 callback.call(scope, result, arg, true);
10798 update : function(params, records){
10803 * Ext JS Library 1.1.1
10804 * Copyright(c) 2006-2007, Ext JS, LLC.
10806 * Originally Released Under LGPL - original licence link has changed is not relivant.
10809 * <script type="text/javascript">
10812 * @class Roo.data.HttpProxy
10813 * @extends Roo.data.DataProxy
10814 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10815 * configured to reference a certain URL.<br><br>
10817 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10818 * from which the running page was served.<br><br>
10820 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10822 * Be aware that to enable the browser to parse an XML document, the server must set
10823 * the Content-Type header in the HTTP response to "text/xml".
10825 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10826 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
10827 * will be used to make the request.
10829 Roo.data.HttpProxy = function(conn){
10830 Roo.data.HttpProxy.superclass.constructor.call(this);
10831 // is conn a conn config or a real conn?
10833 this.useAjax = !conn || !conn.events;
10837 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10838 // thse are take from connection...
10841 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10844 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10845 * extra parameters to each request made by this object. (defaults to undefined)
10848 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10849 * to each request made by this object. (defaults to undefined)
10852 * @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)
10855 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10858 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10864 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10868 * Return the {@link Roo.data.Connection} object being used by this Proxy.
10869 * @return {Connection} The Connection object. This object may be used to subscribe to events on
10870 * a finer-grained basis than the DataProxy events.
10872 getConnection : function(){
10873 return this.useAjax ? Roo.Ajax : this.conn;
10877 * Load data from the configured {@link Roo.data.Connection}, read the data object into
10878 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10879 * process that block using the passed callback.
10880 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10881 * for the request to the remote server.
10882 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10883 * object into a block of Roo.data.Records.
10884 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10885 * The function must be passed <ul>
10886 * <li>The Record block object</li>
10887 * <li>The "arg" argument from the load function</li>
10888 * <li>A boolean success indicator</li>
10890 * @param {Object} scope The scope in which to call the callback
10891 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10893 load : function(params, reader, callback, scope, arg){
10894 if(this.fireEvent("beforeload", this, params) !== false){
10896 params : params || {},
10898 callback : callback,
10903 callback : this.loadResponse,
10907 Roo.applyIf(o, this.conn);
10908 if(this.activeRequest){
10909 Roo.Ajax.abort(this.activeRequest);
10911 this.activeRequest = Roo.Ajax.request(o);
10913 this.conn.request(o);
10916 callback.call(scope||this, null, arg, false);
10921 loadResponse : function(o, success, response){
10922 delete this.activeRequest;
10924 this.fireEvent("loadexception", this, o, response);
10925 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10930 result = o.reader.read(response);
10932 this.fireEvent("loadexception", this, o, response, e);
10933 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10937 this.fireEvent("load", this, o, o.request.arg);
10938 o.request.callback.call(o.request.scope, result, o.request.arg, true);
10942 update : function(dataSet){
10947 updateResponse : function(dataSet){
10952 * Ext JS Library 1.1.1
10953 * Copyright(c) 2006-2007, Ext JS, LLC.
10955 * Originally Released Under LGPL - original licence link has changed is not relivant.
10958 * <script type="text/javascript">
10962 * @class Roo.data.ScriptTagProxy
10963 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10964 * other than the originating domain of the running page.<br><br>
10966 * <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
10967 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10969 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10970 * source code that is used as the source inside a <script> tag.<br><br>
10972 * In order for the browser to process the returned data, the server must wrap the data object
10973 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10974 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10975 * depending on whether the callback name was passed:
10978 boolean scriptTag = false;
10979 String cb = request.getParameter("callback");
10982 response.setContentType("text/javascript");
10984 response.setContentType("application/x-json");
10986 Writer out = response.getWriter();
10988 out.write(cb + "(");
10990 out.print(dataBlock.toJsonString());
10997 * @param {Object} config A configuration object.
10999 Roo.data.ScriptTagProxy = function(config){
11000 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11001 Roo.apply(this, config);
11002 this.head = document.getElementsByTagName("head")[0];
11005 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11007 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11009 * @cfg {String} url The URL from which to request the data object.
11012 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11016 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11017 * the server the name of the callback function set up by the load call to process the returned data object.
11018 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11019 * javascript output which calls this named function passing the data object as its only parameter.
11021 callbackParam : "callback",
11023 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11024 * name to the request.
11029 * Load data from the configured URL, read the data object into
11030 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11031 * process that block using the passed callback.
11032 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11033 * for the request to the remote server.
11034 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11035 * object into a block of Roo.data.Records.
11036 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11037 * The function must be passed <ul>
11038 * <li>The Record block object</li>
11039 * <li>The "arg" argument from the load function</li>
11040 * <li>A boolean success indicator</li>
11042 * @param {Object} scope The scope in which to call the callback
11043 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11045 load : function(params, reader, callback, scope, arg){
11046 if(this.fireEvent("beforeload", this, params) !== false){
11048 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11050 var url = this.url;
11051 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11053 url += "&_dc=" + (new Date().getTime());
11055 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11058 cb : "stcCallback"+transId,
11059 scriptId : "stcScript"+transId,
11063 callback : callback,
11069 window[trans.cb] = function(o){
11070 conn.handleResponse(o, trans);
11073 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11075 if(this.autoAbort !== false){
11079 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11081 var script = document.createElement("script");
11082 script.setAttribute("src", url);
11083 script.setAttribute("type", "text/javascript");
11084 script.setAttribute("id", trans.scriptId);
11085 this.head.appendChild(script);
11087 this.trans = trans;
11089 callback.call(scope||this, null, arg, false);
11094 isLoading : function(){
11095 return this.trans ? true : false;
11099 * Abort the current server request.
11101 abort : function(){
11102 if(this.isLoading()){
11103 this.destroyTrans(this.trans);
11108 destroyTrans : function(trans, isLoaded){
11109 this.head.removeChild(document.getElementById(trans.scriptId));
11110 clearTimeout(trans.timeoutId);
11112 window[trans.cb] = undefined;
11114 delete window[trans.cb];
11117 // if hasn't been loaded, wait for load to remove it to prevent script error
11118 window[trans.cb] = function(){
11119 window[trans.cb] = undefined;
11121 delete window[trans.cb];
11128 handleResponse : function(o, trans){
11129 this.trans = false;
11130 this.destroyTrans(trans, true);
11133 result = trans.reader.readRecords(o);
11135 this.fireEvent("loadexception", this, o, trans.arg, e);
11136 trans.callback.call(trans.scope||window, null, trans.arg, false);
11139 this.fireEvent("load", this, o, trans.arg);
11140 trans.callback.call(trans.scope||window, result, trans.arg, true);
11144 handleFailure : function(trans){
11145 this.trans = false;
11146 this.destroyTrans(trans, false);
11147 this.fireEvent("loadexception", this, null, trans.arg);
11148 trans.callback.call(trans.scope||window, null, trans.arg, false);
11152 * Ext JS Library 1.1.1
11153 * Copyright(c) 2006-2007, Ext JS, LLC.
11155 * Originally Released Under LGPL - original licence link has changed is not relivant.
11158 * <script type="text/javascript">
11162 * @class Roo.data.JsonReader
11163 * @extends Roo.data.DataReader
11164 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11165 * based on mappings in a provided Roo.data.Record constructor.
11167 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11168 * in the reply previously.
11173 var RecordDef = Roo.data.Record.create([
11174 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11175 {name: 'occupation'} // This field will use "occupation" as the mapping.
11177 var myReader = new Roo.data.JsonReader({
11178 totalProperty: "results", // The property which contains the total dataset size (optional)
11179 root: "rows", // The property which contains an Array of row objects
11180 id: "id" // The property within each row object that provides an ID for the record (optional)
11184 * This would consume a JSON file like this:
11186 { 'results': 2, 'rows': [
11187 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11188 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11191 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11192 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11193 * paged from the remote server.
11194 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11195 * @cfg {String} root name of the property which contains the Array of row objects.
11196 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11197 * @cfg {Array} fields Array of field definition objects
11199 * Create a new JsonReader
11200 * @param {Object} meta Metadata configuration options
11201 * @param {Object} recordType Either an Array of field definition objects,
11202 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11204 Roo.data.JsonReader = function(meta, recordType){
11207 // set some defaults:
11208 Roo.applyIf(meta, {
11209 totalProperty: 'total',
11210 successProperty : 'success',
11215 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11217 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11220 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11221 * Used by Store query builder to append _requestMeta to params.
11224 metaFromRemote : false,
11226 * This method is only used by a DataProxy which has retrieved data from a remote server.
11227 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11228 * @return {Object} data A data block which is used by an Roo.data.Store object as
11229 * a cache of Roo.data.Records.
11231 read : function(response){
11232 var json = response.responseText;
11234 var o = /* eval:var:o */ eval("("+json+")");
11236 throw {message: "JsonReader.read: Json object not found"};
11242 this.metaFromRemote = true;
11243 this.meta = o.metaData;
11244 this.recordType = Roo.data.Record.create(o.metaData.fields);
11245 this.onMetaChange(this.meta, this.recordType, o);
11247 return this.readRecords(o);
11250 // private function a store will implement
11251 onMetaChange : function(meta, recordType, o){
11258 simpleAccess: function(obj, subsc) {
11265 getJsonAccessor: function(){
11267 return function(expr) {
11269 return(re.test(expr))
11270 ? new Function("obj", "return obj." + expr)
11275 return Roo.emptyFn;
11280 * Create a data block containing Roo.data.Records from an XML document.
11281 * @param {Object} o An object which contains an Array of row objects in the property specified
11282 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11283 * which contains the total size of the dataset.
11284 * @return {Object} data A data block which is used by an Roo.data.Store object as
11285 * a cache of Roo.data.Records.
11287 readRecords : function(o){
11289 * After any data loads, the raw JSON data is available for further custom processing.
11293 var s = this.meta, Record = this.recordType,
11294 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11296 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11298 if(s.totalProperty) {
11299 this.getTotal = this.getJsonAccessor(s.totalProperty);
11301 if(s.successProperty) {
11302 this.getSuccess = this.getJsonAccessor(s.successProperty);
11304 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11306 var g = this.getJsonAccessor(s.id);
11307 this.getId = function(rec) {
11309 return (r === undefined || r === "") ? null : r;
11312 this.getId = function(){return null;};
11315 for(var jj = 0; jj < fl; jj++){
11317 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11318 this.ef[jj] = this.getJsonAccessor(map);
11322 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11323 if(s.totalProperty){
11324 var vt = parseInt(this.getTotal(o), 10);
11329 if(s.successProperty){
11330 var vs = this.getSuccess(o);
11331 if(vs === false || vs === 'false'){
11336 for(var i = 0; i < c; i++){
11339 var id = this.getId(n);
11340 for(var j = 0; j < fl; j++){
11342 var v = this.ef[j](n);
11344 Roo.log('missing convert for ' + f.name);
11348 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11350 var record = new Record(values, id);
11352 records[i] = record;
11358 totalRecords : totalRecords
11363 * Ext JS Library 1.1.1
11364 * Copyright(c) 2006-2007, Ext JS, LLC.
11366 * Originally Released Under LGPL - original licence link has changed is not relivant.
11369 * <script type="text/javascript">
11373 * @class Roo.data.ArrayReader
11374 * @extends Roo.data.DataReader
11375 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11376 * Each element of that Array represents a row of data fields. The
11377 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11378 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11382 var RecordDef = Roo.data.Record.create([
11383 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11384 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11386 var myReader = new Roo.data.ArrayReader({
11387 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11391 * This would consume an Array like this:
11393 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11395 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11397 * Create a new JsonReader
11398 * @param {Object} meta Metadata configuration options.
11399 * @param {Object} recordType Either an Array of field definition objects
11400 * as specified to {@link Roo.data.Record#create},
11401 * or an {@link Roo.data.Record} object
11402 * created using {@link Roo.data.Record#create}.
11404 Roo.data.ArrayReader = function(meta, recordType){
11405 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11408 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11410 * Create a data block containing Roo.data.Records from an XML document.
11411 * @param {Object} o An Array of row objects which represents the dataset.
11412 * @return {Object} data A data block which is used by an Roo.data.Store object as
11413 * a cache of Roo.data.Records.
11415 readRecords : function(o){
11416 var sid = this.meta ? this.meta.id : null;
11417 var recordType = this.recordType, fields = recordType.prototype.fields;
11420 for(var i = 0; i < root.length; i++){
11423 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11424 for(var j = 0, jlen = fields.length; j < jlen; j++){
11425 var f = fields.items[j];
11426 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11427 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11429 values[f.name] = v;
11431 var record = new recordType(values, id);
11433 records[records.length] = record;
11437 totalRecords : records.length
11446 * @class Roo.bootstrap.ComboBox
11447 * @extends Roo.bootstrap.TriggerField
11448 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11449 * @cfg {Boolean} append (true|false) default false
11450 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11451 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11452 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11453 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11454 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11455 * @cfg {Boolean} animate default true
11456 * @cfg {Boolean} emptyResultText only for touch device
11457 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11459 * Create a new ComboBox.
11460 * @param {Object} config Configuration options
11462 Roo.bootstrap.ComboBox = function(config){
11463 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11467 * Fires when the dropdown list is expanded
11468 * @param {Roo.bootstrap.ComboBox} combo This combo box
11473 * Fires when the dropdown list is collapsed
11474 * @param {Roo.bootstrap.ComboBox} combo This combo box
11478 * @event beforeselect
11479 * Fires before a list item is selected. Return false to cancel the selection.
11480 * @param {Roo.bootstrap.ComboBox} combo This combo box
11481 * @param {Roo.data.Record} record The data record returned from the underlying store
11482 * @param {Number} index The index of the selected item in the dropdown list
11484 'beforeselect' : true,
11487 * Fires when a list item is selected
11488 * @param {Roo.bootstrap.ComboBox} combo This combo box
11489 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11490 * @param {Number} index The index of the selected item in the dropdown list
11494 * @event beforequery
11495 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11496 * The event object passed has these properties:
11497 * @param {Roo.bootstrap.ComboBox} combo This combo box
11498 * @param {String} query The query
11499 * @param {Boolean} forceAll true to force "all" query
11500 * @param {Boolean} cancel true to cancel the query
11501 * @param {Object} e The query event object
11503 'beforequery': true,
11506 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11507 * @param {Roo.bootstrap.ComboBox} combo This combo box
11512 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11513 * @param {Roo.bootstrap.ComboBox} combo This combo box
11514 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11519 * Fires when the remove value from the combobox array
11520 * @param {Roo.bootstrap.ComboBox} combo This combo box
11524 * @event specialfilter
11525 * Fires when specialfilter
11526 * @param {Roo.bootstrap.ComboBox} combo This combo box
11528 'specialfilter' : true,
11531 * Fires when tick the element
11532 * @param {Roo.bootstrap.ComboBox} combo This combo box
11536 * @event touchviewdisplay
11537 * Fires when touch view require special display (default is using displayField)
11538 * @param {Roo.bootstrap.ComboBox} combo This combo box
11539 * @param {Object} cfg set html .
11541 'touchviewdisplay' : true
11546 this.tickItems = [];
11548 this.selectedIndex = -1;
11549 if(this.mode == 'local'){
11550 if(config.queryDelay === undefined){
11551 this.queryDelay = 10;
11553 if(config.minChars === undefined){
11559 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11562 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11563 * rendering into an Roo.Editor, defaults to false)
11566 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11567 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11570 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11573 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11574 * the dropdown list (defaults to undefined, with no header element)
11578 * @cfg {String/Roo.Template} tpl The template to use to render the output
11582 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11584 listWidth: undefined,
11586 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11587 * mode = 'remote' or 'text' if mode = 'local')
11589 displayField: undefined,
11592 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11593 * mode = 'remote' or 'value' if mode = 'local').
11594 * Note: use of a valueField requires the user make a selection
11595 * in order for a value to be mapped.
11597 valueField: undefined,
11601 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11602 * field's data value (defaults to the underlying DOM element's name)
11604 hiddenName: undefined,
11606 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11610 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11612 selectedClass: 'active',
11615 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11619 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11620 * anchor positions (defaults to 'tl-bl')
11622 listAlign: 'tl-bl?',
11624 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11628 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11629 * query specified by the allQuery config option (defaults to 'query')
11631 triggerAction: 'query',
11633 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11634 * (defaults to 4, does not apply if editable = false)
11638 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11639 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11643 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11644 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11648 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11649 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11653 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11654 * when editable = true (defaults to false)
11656 selectOnFocus:false,
11658 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11660 queryParam: 'query',
11662 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
11663 * when mode = 'remote' (defaults to 'Loading...')
11665 loadingText: 'Loading...',
11667 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11671 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11675 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11676 * traditional select (defaults to true)
11680 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11684 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11688 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11689 * listWidth has a higher value)
11693 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11694 * allow the user to set arbitrary text into the field (defaults to false)
11696 forceSelection:false,
11698 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11699 * if typeAhead = true (defaults to 250)
11701 typeAheadDelay : 250,
11703 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11704 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11706 valueNotFoundText : undefined,
11708 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11710 blockFocus : false,
11713 * @cfg {Boolean} disableClear Disable showing of clear button.
11715 disableClear : false,
11717 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
11719 alwaysQuery : false,
11722 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
11727 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11729 invalidClass : "has-warning",
11732 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11734 validClass : "has-success",
11737 * @cfg {Boolean} specialFilter (true|false) special filter default false
11739 specialFilter : false,
11742 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11744 mobileTouchView : true,
11756 btnPosition : 'right',
11757 triggerList : true,
11758 showToggleBtn : true,
11760 emptyResultText: 'Empty',
11761 triggerText : 'Select',
11763 // element that contains real text value.. (when hidden is used..)
11765 getAutoCreate : function()
11773 if(Roo.isTouch && this.mobileTouchView){
11774 cfg = this.getAutoCreateTouchView();
11781 if(!this.tickable){
11782 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11787 * ComboBox with tickable selections
11790 var align = this.labelAlign || this.parentLabelAlign();
11793 cls : 'form-group roo-combobox-tickable' //input-group
11798 cls : 'tickable-buttons',
11803 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11804 html : this.triggerText
11810 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11817 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11824 buttons.cn.unshift({
11826 cls: 'select2-search-field-input'
11832 Roo.each(buttons.cn, function(c){
11834 c.cls += ' btn-' + _this.size;
11837 if (_this.disabled) {
11848 cls: 'form-hidden-field'
11852 cls: 'select2-choices',
11856 cls: 'select2-search-field',
11868 cls: 'select2-container input-group select2-container-multi',
11873 // cls: 'typeahead typeahead-long dropdown-menu',
11874 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
11879 if(this.hasFeedback && !this.allowBlank){
11883 cls: 'glyphicon form-control-feedback'
11886 combobox.cn.push(feedback);
11889 if (align ==='left' && this.fieldLabel.length) {
11891 // Roo.log("left and has label");
11897 cls : 'control-label col-sm-' + this.labelWidth,
11898 html : this.fieldLabel
11902 cls : "col-sm-" + (12 - this.labelWidth),
11909 } else if ( this.fieldLabel.length) {
11910 // Roo.log(" label");
11915 //cls : 'input-group-addon',
11916 html : this.fieldLabel
11926 // Roo.log(" no label && no align");
11933 ['xs','sm','md','lg'].map(function(size){
11934 if (settings[size]) {
11935 cfg.cls += ' col-' + size + '-' + settings[size];
11943 _initEventsCalled : false,
11946 initEvents: function()
11949 if (this._initEventsCalled) { // as we call render... prevent looping...
11952 this._initEventsCalled = true;
11955 throw "can not find store for combo";
11958 this.store = Roo.factory(this.store, Roo.data);
11960 // if we are building from html. then this element is so complex, that we can not really
11961 // use the rendered HTML.
11962 // so we have to trash and replace the previous code.
11963 if (Roo.XComponent.build_from_html) {
11965 // remove this element....
11966 var e = this.el.dom, k=0;
11967 while (e ) { e = e.previousSibling; ++k;}
11972 this.rendered = false;
11974 this.render(this.parent().getChildContainer(true), k);
11985 if(Roo.isTouch && this.mobileTouchView){
11986 this.initTouchView();
11991 this.initTickableEvents();
11995 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11997 if(this.hiddenName){
11999 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12001 this.hiddenField.dom.value =
12002 this.hiddenValue !== undefined ? this.hiddenValue :
12003 this.value !== undefined ? this.value : '';
12005 // prevent input submission
12006 this.el.dom.removeAttribute('name');
12007 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12012 // this.el.dom.setAttribute('autocomplete', 'off');
12015 var cls = 'x-combo-list';
12017 //this.list = new Roo.Layer({
12018 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12024 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12025 _this.list.setWidth(lw);
12028 this.list.on('mouseover', this.onViewOver, this);
12029 this.list.on('mousemove', this.onViewMove, this);
12031 this.list.on('scroll', this.onViewScroll, this);
12034 this.list.swallowEvent('mousewheel');
12035 this.assetHeight = 0;
12038 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12039 this.assetHeight += this.header.getHeight();
12042 this.innerList = this.list.createChild({cls:cls+'-inner'});
12043 this.innerList.on('mouseover', this.onViewOver, this);
12044 this.innerList.on('mousemove', this.onViewMove, this);
12045 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12047 if(this.allowBlank && !this.pageSize && !this.disableClear){
12048 this.footer = this.list.createChild({cls:cls+'-ft'});
12049 this.pageTb = new Roo.Toolbar(this.footer);
12053 this.footer = this.list.createChild({cls:cls+'-ft'});
12054 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12055 {pageSize: this.pageSize});
12059 if (this.pageTb && this.allowBlank && !this.disableClear) {
12061 this.pageTb.add(new Roo.Toolbar.Fill(), {
12062 cls: 'x-btn-icon x-btn-clear',
12064 handler: function()
12067 _this.clearValue();
12068 _this.onSelect(false, -1);
12073 this.assetHeight += this.footer.getHeight();
12078 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12081 this.view = new Roo.View(this.list, this.tpl, {
12082 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12084 //this.view.wrapEl.setDisplayed(false);
12085 this.view.on('click', this.onViewClick, this);
12089 this.store.on('beforeload', this.onBeforeLoad, this);
12090 this.store.on('load', this.onLoad, this);
12091 this.store.on('loadexception', this.onLoadException, this);
12093 if(this.resizable){
12094 this.resizer = new Roo.Resizable(this.list, {
12095 pinned:true, handles:'se'
12097 this.resizer.on('resize', function(r, w, h){
12098 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12099 this.listWidth = w;
12100 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12101 this.restrictHeight();
12103 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12106 if(!this.editable){
12107 this.editable = true;
12108 this.setEditable(false);
12113 if (typeof(this.events.add.listeners) != 'undefined') {
12115 this.addicon = this.wrap.createChild(
12116 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12118 this.addicon.on('click', function(e) {
12119 this.fireEvent('add', this);
12122 if (typeof(this.events.edit.listeners) != 'undefined') {
12124 this.editicon = this.wrap.createChild(
12125 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12126 if (this.addicon) {
12127 this.editicon.setStyle('margin-left', '40px');
12129 this.editicon.on('click', function(e) {
12131 // we fire even if inothing is selected..
12132 this.fireEvent('edit', this, this.lastData );
12138 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12139 "up" : function(e){
12140 this.inKeyMode = true;
12144 "down" : function(e){
12145 if(!this.isExpanded()){
12146 this.onTriggerClick();
12148 this.inKeyMode = true;
12153 "enter" : function(e){
12154 // this.onViewClick();
12158 if(this.fireEvent("specialkey", this, e)){
12159 this.onViewClick(false);
12165 "esc" : function(e){
12169 "tab" : function(e){
12172 if(this.fireEvent("specialkey", this, e)){
12173 this.onViewClick(false);
12181 doRelay : function(foo, bar, hname){
12182 if(hname == 'down' || this.scope.isExpanded()){
12183 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12192 this.queryDelay = Math.max(this.queryDelay || 10,
12193 this.mode == 'local' ? 10 : 250);
12196 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12198 if(this.typeAhead){
12199 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12201 if(this.editable !== false){
12202 this.inputEl().on("keyup", this.onKeyUp, this);
12204 if(this.forceSelection){
12205 this.inputEl().on('blur', this.doForce, this);
12209 this.choices = this.el.select('ul.select2-choices', true).first();
12210 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12214 initTickableEvents: function()
12218 if(this.hiddenName){
12220 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12222 this.hiddenField.dom.value =
12223 this.hiddenValue !== undefined ? this.hiddenValue :
12224 this.value !== undefined ? this.value : '';
12226 // prevent input submission
12227 this.el.dom.removeAttribute('name');
12228 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12233 // this.list = this.el.select('ul.dropdown-menu',true).first();
12235 this.choices = this.el.select('ul.select2-choices', true).first();
12236 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12237 if(this.triggerList){
12238 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12241 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12242 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12244 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12245 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12247 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12248 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12250 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12251 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12252 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12255 this.cancelBtn.hide();
12260 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12261 _this.list.setWidth(lw);
12264 this.list.on('mouseover', this.onViewOver, this);
12265 this.list.on('mousemove', this.onViewMove, this);
12267 this.list.on('scroll', this.onViewScroll, this);
12270 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>';
12273 this.view = new Roo.View(this.list, this.tpl, {
12274 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12277 //this.view.wrapEl.setDisplayed(false);
12278 this.view.on('click', this.onViewClick, this);
12282 this.store.on('beforeload', this.onBeforeLoad, this);
12283 this.store.on('load', this.onLoad, this);
12284 this.store.on('loadexception', this.onLoadException, this);
12287 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12288 "up" : function(e){
12289 this.inKeyMode = true;
12293 "down" : function(e){
12294 this.inKeyMode = true;
12298 "enter" : function(e){
12299 if(this.fireEvent("specialkey", this, e)){
12300 this.onViewClick(false);
12306 "esc" : function(e){
12307 this.onTickableFooterButtonClick(e, false, false);
12310 "tab" : function(e){
12311 this.fireEvent("specialkey", this, e);
12313 this.onTickableFooterButtonClick(e, false, false);
12320 doRelay : function(e, fn, key){
12321 if(this.scope.isExpanded()){
12322 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12331 this.queryDelay = Math.max(this.queryDelay || 10,
12332 this.mode == 'local' ? 10 : 250);
12335 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12337 if(this.typeAhead){
12338 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12341 if(this.editable !== false){
12342 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12347 onDestroy : function(){
12349 this.view.setStore(null);
12350 this.view.el.removeAllListeners();
12351 this.view.el.remove();
12352 this.view.purgeListeners();
12355 this.list.dom.innerHTML = '';
12359 this.store.un('beforeload', this.onBeforeLoad, this);
12360 this.store.un('load', this.onLoad, this);
12361 this.store.un('loadexception', this.onLoadException, this);
12363 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12367 fireKey : function(e){
12368 if(e.isNavKeyPress() && !this.list.isVisible()){
12369 this.fireEvent("specialkey", this, e);
12374 onResize: function(w, h){
12375 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12377 // if(typeof w != 'number'){
12378 // // we do not handle it!?!?
12381 // var tw = this.trigger.getWidth();
12382 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12383 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12385 // this.inputEl().setWidth( this.adjustWidth('input', x));
12387 // //this.trigger.setStyle('left', x+'px');
12389 // if(this.list && this.listWidth === undefined){
12390 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12391 // this.list.setWidth(lw);
12392 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12400 * Allow or prevent the user from directly editing the field text. If false is passed,
12401 * the user will only be able to select from the items defined in the dropdown list. This method
12402 * is the runtime equivalent of setting the 'editable' config option at config time.
12403 * @param {Boolean} value True to allow the user to directly edit the field text
12405 setEditable : function(value){
12406 if(value == this.editable){
12409 this.editable = value;
12411 this.inputEl().dom.setAttribute('readOnly', true);
12412 this.inputEl().on('mousedown', this.onTriggerClick, this);
12413 this.inputEl().addClass('x-combo-noedit');
12415 this.inputEl().dom.setAttribute('readOnly', false);
12416 this.inputEl().un('mousedown', this.onTriggerClick, this);
12417 this.inputEl().removeClass('x-combo-noedit');
12423 onBeforeLoad : function(combo,opts){
12424 if(!this.hasFocus){
12428 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12430 this.restrictHeight();
12431 this.selectedIndex = -1;
12435 onLoad : function(){
12437 this.hasQuery = false;
12439 if(!this.hasFocus){
12443 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12444 this.loading.hide();
12447 if(this.store.getCount() > 0){
12449 this.restrictHeight();
12450 if(this.lastQuery == this.allQuery){
12451 if(this.editable && !this.tickable){
12452 this.inputEl().dom.select();
12456 !this.selectByValue(this.value, true) &&
12459 !this.store.lastOptions ||
12460 typeof(this.store.lastOptions.add) == 'undefined' ||
12461 this.store.lastOptions.add != true
12464 this.select(0, true);
12467 if(this.autoFocus){
12470 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12471 this.taTask.delay(this.typeAheadDelay);
12475 this.onEmptyResults();
12481 onLoadException : function()
12483 this.hasQuery = false;
12485 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12486 this.loading.hide();
12489 if(this.tickable && this.editable){
12494 // only causes errors at present
12495 //Roo.log(this.store.reader.jsonData);
12496 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12498 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12504 onTypeAhead : function(){
12505 if(this.store.getCount() > 0){
12506 var r = this.store.getAt(0);
12507 var newValue = r.data[this.displayField];
12508 var len = newValue.length;
12509 var selStart = this.getRawValue().length;
12511 if(selStart != len){
12512 this.setRawValue(newValue);
12513 this.selectText(selStart, newValue.length);
12519 onSelect : function(record, index){
12521 if(this.fireEvent('beforeselect', this, record, index) !== false){
12523 this.setFromData(index > -1 ? record.data : false);
12526 this.fireEvent('select', this, record, index);
12531 * Returns the currently selected field value or empty string if no value is set.
12532 * @return {String} value The selected value
12534 getValue : function(){
12537 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12540 if(this.valueField){
12541 return typeof this.value != 'undefined' ? this.value : '';
12543 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12548 * Clears any text/value currently set in the field
12550 clearValue : function(){
12551 if(this.hiddenField){
12552 this.hiddenField.dom.value = '';
12555 this.setRawValue('');
12556 this.lastSelectionText = '';
12557 this.lastData = false;
12559 var close = this.closeTriggerEl();
12568 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12569 * will be displayed in the field. If the value does not match the data value of an existing item,
12570 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12571 * Otherwise the field will be blank (although the value will still be set).
12572 * @param {String} value The value to match
12574 setValue : function(v){
12581 if(this.valueField){
12582 var r = this.findRecord(this.valueField, v);
12584 text = r.data[this.displayField];
12585 }else if(this.valueNotFoundText !== undefined){
12586 text = this.valueNotFoundText;
12589 this.lastSelectionText = text;
12590 if(this.hiddenField){
12591 this.hiddenField.dom.value = v;
12593 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12596 var close = this.closeTriggerEl();
12599 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12603 * @property {Object} the last set data for the element
12608 * Sets the value of the field based on a object which is related to the record format for the store.
12609 * @param {Object} value the value to set as. or false on reset?
12611 setFromData : function(o){
12618 var dv = ''; // display value
12619 var vv = ''; // value value..
12621 if (this.displayField) {
12622 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12624 // this is an error condition!!!
12625 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12628 if(this.valueField){
12629 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12632 var close = this.closeTriggerEl();
12635 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12638 if(this.hiddenField){
12639 this.hiddenField.dom.value = vv;
12641 this.lastSelectionText = dv;
12642 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12646 // no hidden field.. - we store the value in 'value', but still display
12647 // display field!!!!
12648 this.lastSelectionText = dv;
12649 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12656 reset : function(){
12657 // overridden so that last data is reset..
12664 this.setValue(this.originalValue);
12665 this.clearInvalid();
12666 this.lastData = false;
12668 this.view.clearSelections();
12672 findRecord : function(prop, value){
12674 if(this.store.getCount() > 0){
12675 this.store.each(function(r){
12676 if(r.data[prop] == value){
12686 getName: function()
12688 // returns hidden if it's set..
12689 if (!this.rendered) {return ''};
12690 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
12694 onViewMove : function(e, t){
12695 this.inKeyMode = false;
12699 onViewOver : function(e, t){
12700 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12703 var item = this.view.findItemFromChild(t);
12706 var index = this.view.indexOf(item);
12707 this.select(index, false);
12712 onViewClick : function(view, doFocus, el, e)
12714 var index = this.view.getSelectedIndexes()[0];
12716 var r = this.store.getAt(index);
12720 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12727 Roo.each(this.tickItems, function(v,k){
12729 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12731 _this.tickItems.splice(k, 1);
12733 if(typeof(e) == 'undefined' && view == false){
12734 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12746 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
12747 this.tickItems.push(r.data);
12750 if(typeof(e) == 'undefined' && view == false){
12751 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12758 this.onSelect(r, index);
12760 if(doFocus !== false && !this.blockFocus){
12761 this.inputEl().focus();
12766 restrictHeight : function(){
12767 //this.innerList.dom.style.height = '';
12768 //var inner = this.innerList.dom;
12769 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12770 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12771 //this.list.beginUpdate();
12772 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12773 this.list.alignTo(this.inputEl(), this.listAlign);
12774 this.list.alignTo(this.inputEl(), this.listAlign);
12775 //this.list.endUpdate();
12779 onEmptyResults : function(){
12781 if(this.tickable && this.editable){
12782 this.restrictHeight();
12790 * Returns true if the dropdown list is expanded, else false.
12792 isExpanded : function(){
12793 return this.list.isVisible();
12797 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12798 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12799 * @param {String} value The data value of the item to select
12800 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12801 * selected item if it is not currently in view (defaults to true)
12802 * @return {Boolean} True if the value matched an item in the list, else false
12804 selectByValue : function(v, scrollIntoView){
12805 if(v !== undefined && v !== null){
12806 var r = this.findRecord(this.valueField || this.displayField, v);
12808 this.select(this.store.indexOf(r), scrollIntoView);
12816 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12817 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12818 * @param {Number} index The zero-based index of the list item to select
12819 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12820 * selected item if it is not currently in view (defaults to true)
12822 select : function(index, scrollIntoView){
12823 this.selectedIndex = index;
12824 this.view.select(index);
12825 if(scrollIntoView !== false){
12826 var el = this.view.getNode(index);
12828 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12831 this.list.scrollChildIntoView(el, false);
12837 selectNext : function(){
12838 var ct = this.store.getCount();
12840 if(this.selectedIndex == -1){
12842 }else if(this.selectedIndex < ct-1){
12843 this.select(this.selectedIndex+1);
12849 selectPrev : function(){
12850 var ct = this.store.getCount();
12852 if(this.selectedIndex == -1){
12854 }else if(this.selectedIndex != 0){
12855 this.select(this.selectedIndex-1);
12861 onKeyUp : function(e){
12862 if(this.editable !== false && !e.isSpecialKey()){
12863 this.lastKey = e.getKey();
12864 this.dqTask.delay(this.queryDelay);
12869 validateBlur : function(){
12870 return !this.list || !this.list.isVisible();
12874 initQuery : function(){
12876 var v = this.getRawValue();
12878 if(this.tickable && this.editable){
12879 v = this.tickableInputEl().getValue();
12886 doForce : function(){
12887 if(this.inputEl().dom.value.length > 0){
12888 this.inputEl().dom.value =
12889 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12895 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
12896 * query allowing the query action to be canceled if needed.
12897 * @param {String} query The SQL query to execute
12898 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12899 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
12900 * saved in the current store (defaults to false)
12902 doQuery : function(q, forceAll){
12904 if(q === undefined || q === null){
12909 forceAll: forceAll,
12913 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12918 forceAll = qe.forceAll;
12919 if(forceAll === true || (q.length >= this.minChars)){
12921 this.hasQuery = true;
12923 if(this.lastQuery != q || this.alwaysQuery){
12924 this.lastQuery = q;
12925 if(this.mode == 'local'){
12926 this.selectedIndex = -1;
12928 this.store.clearFilter();
12931 if(this.specialFilter){
12932 this.fireEvent('specialfilter', this);
12937 this.store.filter(this.displayField, q);
12940 this.store.fireEvent("datachanged", this.store);
12947 this.store.baseParams[this.queryParam] = q;
12949 var options = {params : this.getParams(q)};
12952 options.add = true;
12953 options.params.start = this.page * this.pageSize;
12956 this.store.load(options);
12959 * this code will make the page width larger, at the beginning, the list not align correctly,
12960 * we should expand the list on onLoad
12961 * so command out it
12966 this.selectedIndex = -1;
12971 this.loadNext = false;
12975 getParams : function(q){
12977 //p[this.queryParam] = q;
12981 p.limit = this.pageSize;
12987 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12989 collapse : function(){
12990 if(!this.isExpanded()){
12997 this.hasFocus = false;
12999 this.cancelBtn.hide();
13000 this.trigger.show();
13003 this.tickableInputEl().dom.value = '';
13004 this.tickableInputEl().blur();
13009 Roo.get(document).un('mousedown', this.collapseIf, this);
13010 Roo.get(document).un('mousewheel', this.collapseIf, this);
13011 if (!this.editable) {
13012 Roo.get(document).un('keydown', this.listKeyPress, this);
13014 this.fireEvent('collapse', this);
13018 collapseIf : function(e){
13019 var in_combo = e.within(this.el);
13020 var in_list = e.within(this.list);
13021 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13023 if (in_combo || in_list || is_list) {
13024 //e.stopPropagation();
13029 this.onTickableFooterButtonClick(e, false, false);
13037 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13039 expand : function(){
13041 if(this.isExpanded() || !this.hasFocus){
13045 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13046 this.list.setWidth(lw);
13053 this.restrictHeight();
13057 this.tickItems = Roo.apply([], this.item);
13060 this.cancelBtn.show();
13061 this.trigger.hide();
13064 this.tickableInputEl().focus();
13069 Roo.get(document).on('mousedown', this.collapseIf, this);
13070 Roo.get(document).on('mousewheel', this.collapseIf, this);
13071 if (!this.editable) {
13072 Roo.get(document).on('keydown', this.listKeyPress, this);
13075 this.fireEvent('expand', this);
13079 // Implements the default empty TriggerField.onTriggerClick function
13080 onTriggerClick : function(e)
13082 Roo.log('trigger click');
13084 if(this.disabled || !this.triggerList){
13089 this.loadNext = false;
13091 if(this.isExpanded()){
13093 if (!this.blockFocus) {
13094 this.inputEl().focus();
13098 this.hasFocus = true;
13099 if(this.triggerAction == 'all') {
13100 this.doQuery(this.allQuery, true);
13102 this.doQuery(this.getRawValue());
13104 if (!this.blockFocus) {
13105 this.inputEl().focus();
13110 onTickableTriggerClick : function(e)
13117 this.loadNext = false;
13118 this.hasFocus = true;
13120 if(this.triggerAction == 'all') {
13121 this.doQuery(this.allQuery, true);
13123 this.doQuery(this.getRawValue());
13127 onSearchFieldClick : function(e)
13129 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13130 this.onTickableFooterButtonClick(e, false, false);
13134 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13139 this.loadNext = false;
13140 this.hasFocus = true;
13142 if(this.triggerAction == 'all') {
13143 this.doQuery(this.allQuery, true);
13145 this.doQuery(this.getRawValue());
13149 listKeyPress : function(e)
13151 //Roo.log('listkeypress');
13152 // scroll to first matching element based on key pres..
13153 if (e.isSpecialKey()) {
13156 var k = String.fromCharCode(e.getKey()).toUpperCase();
13159 var csel = this.view.getSelectedNodes();
13160 var cselitem = false;
13162 var ix = this.view.indexOf(csel[0]);
13163 cselitem = this.store.getAt(ix);
13164 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13170 this.store.each(function(v) {
13172 // start at existing selection.
13173 if (cselitem.id == v.id) {
13179 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13180 match = this.store.indexOf(v);
13186 if (match === false) {
13187 return true; // no more action?
13190 this.view.select(match);
13191 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13192 sn.scrollIntoView(sn.dom.parentNode, false);
13195 onViewScroll : function(e, t){
13197 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){
13201 this.hasQuery = true;
13203 this.loading = this.list.select('.loading', true).first();
13205 if(this.loading === null){
13206 this.list.createChild({
13208 cls: 'loading select2-more-results select2-active',
13209 html: 'Loading more results...'
13212 this.loading = this.list.select('.loading', true).first();
13214 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13216 this.loading.hide();
13219 this.loading.show();
13224 this.loadNext = true;
13226 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13231 addItem : function(o)
13233 var dv = ''; // display value
13235 if (this.displayField) {
13236 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13238 // this is an error condition!!!
13239 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13246 var choice = this.choices.createChild({
13248 cls: 'select2-search-choice',
13257 cls: 'select2-search-choice-close',
13262 }, this.searchField);
13264 var close = choice.select('a.select2-search-choice-close', true).first();
13266 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13274 this.inputEl().dom.value = '';
13279 onRemoveItem : function(e, _self, o)
13281 e.preventDefault();
13283 this.lastItem = Roo.apply([], this.item);
13285 var index = this.item.indexOf(o.data) * 1;
13288 Roo.log('not this item?!');
13292 this.item.splice(index, 1);
13297 this.fireEvent('remove', this, e);
13303 syncValue : function()
13305 if(!this.item.length){
13312 Roo.each(this.item, function(i){
13313 if(_this.valueField){
13314 value.push(i[_this.valueField]);
13321 this.value = value.join(',');
13323 if(this.hiddenField){
13324 this.hiddenField.dom.value = this.value;
13327 this.store.fireEvent("datachanged", this.store);
13330 clearItem : function()
13332 if(!this.multiple){
13338 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13346 if(this.tickable && !Roo.isTouch){
13347 this.view.refresh();
13351 inputEl: function ()
13353 if(Roo.isTouch && this.mobileTouchView){
13354 return this.el.select('input.form-control',true).first();
13358 return this.searchField;
13361 return this.el.select('input.form-control',true).first();
13365 onTickableFooterButtonClick : function(e, btn, el)
13367 e.preventDefault();
13369 this.lastItem = Roo.apply([], this.item);
13371 if(btn && btn.name == 'cancel'){
13372 this.tickItems = Roo.apply([], this.item);
13381 Roo.each(this.tickItems, function(o){
13389 validate : function()
13391 var v = this.getRawValue();
13394 v = this.getValue();
13397 if(this.disabled || this.allowBlank || v.length){
13402 this.markInvalid();
13406 tickableInputEl : function()
13408 if(!this.tickable || !this.editable){
13409 return this.inputEl();
13412 return this.inputEl().select('.select2-search-field-input', true).first();
13416 getAutoCreateTouchView : function()
13421 cls: 'form-group' //input-group
13427 type : this.inputType,
13428 cls : 'form-control x-combo-noedit',
13429 autocomplete: 'new-password',
13430 placeholder : this.placeholder || '',
13435 input.name = this.name;
13439 input.cls += ' input-' + this.size;
13442 if (this.disabled) {
13443 input.disabled = true;
13454 inputblock.cls += ' input-group';
13456 inputblock.cn.unshift({
13458 cls : 'input-group-addon',
13463 if(this.removable && !this.multiple){
13464 inputblock.cls += ' roo-removable';
13466 inputblock.cn.push({
13469 cls : 'roo-combo-removable-btn close'
13473 if(this.hasFeedback && !this.allowBlank){
13475 inputblock.cls += ' has-feedback';
13477 inputblock.cn.push({
13479 cls: 'glyphicon form-control-feedback'
13486 inputblock.cls += (this.before) ? '' : ' input-group';
13488 inputblock.cn.push({
13490 cls : 'input-group-addon',
13501 cls: 'form-hidden-field'
13515 cls: 'form-hidden-field'
13519 cls: 'select2-choices',
13523 cls: 'select2-search-field',
13536 cls: 'select2-container input-group',
13543 combobox.cls += ' select2-container-multi';
13546 var align = this.labelAlign || this.parentLabelAlign();
13550 if(this.fieldLabel.length){
13552 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13553 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13558 cls : 'control-label ' + lw,
13559 html : this.fieldLabel
13571 var settings = this;
13573 ['xs','sm','md','lg'].map(function(size){
13574 if (settings[size]) {
13575 cfg.cls += ' col-' + size + '-' + settings[size];
13582 initTouchView : function()
13584 this.renderTouchView();
13586 this.touchViewEl.on('scroll', function(){
13587 this.el.dom.scrollTop = 0;
13590 this.originalValue = this.getValue();
13592 this.inputEl().on("click", this.showTouchView, this);
13594 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13595 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13597 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13599 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13600 this.store.on('load', this.onTouchViewLoad, this);
13601 this.store.on('loadexception', this.onTouchViewLoadException, this);
13603 if(this.hiddenName){
13605 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13607 this.hiddenField.dom.value =
13608 this.hiddenValue !== undefined ? this.hiddenValue :
13609 this.value !== undefined ? this.value : '';
13611 this.el.dom.removeAttribute('name');
13612 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13616 this.choices = this.el.select('ul.select2-choices', true).first();
13617 this.searchField = this.el.select('ul li.select2-search-field', true).first();
13620 if(this.removable && !this.multiple){
13621 var close = this.closeTriggerEl();
13623 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13624 close.on('click', this.removeBtnClick, this, close);
13628 * fix the bug in Safari iOS8
13630 this.inputEl().on("focus", function(e){
13631 document.activeElement.blur();
13639 renderTouchView : function()
13641 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13642 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13644 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13645 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13647 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13648 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13649 this.touchViewBodyEl.setStyle('overflow', 'auto');
13651 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13652 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13654 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13655 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13659 showTouchView : function()
13665 this.touchViewHeaderEl.hide();
13667 if(this.fieldLabel.length){
13668 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13669 this.touchViewHeaderEl.show();
13672 this.touchViewEl.show();
13674 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13675 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13677 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13679 if(this.fieldLabel.length){
13680 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13683 this.touchViewBodyEl.setHeight(bodyHeight);
13687 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13689 this.touchViewEl.addClass('in');
13692 this.doTouchViewQuery();
13696 hideTouchView : function()
13698 this.touchViewEl.removeClass('in');
13702 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13704 this.touchViewEl.setStyle('display', 'none');
13709 setTouchViewValue : function()
13716 Roo.each(this.tickItems, function(o){
13721 this.hideTouchView();
13724 doTouchViewQuery : function()
13733 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13737 if(!this.alwaysQuery || this.mode == 'local'){
13738 this.onTouchViewLoad();
13745 onTouchViewBeforeLoad : function(combo,opts)
13751 onTouchViewLoad : function()
13753 if(this.store.getCount() < 1){
13754 this.onTouchViewEmptyResults();
13758 this.clearTouchView();
13760 var rawValue = this.getRawValue();
13762 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13764 this.tickItems = [];
13766 this.store.data.each(function(d, rowIndex){
13767 var row = this.touchViewListGroup.createChild(template);
13769 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13772 html : d.data[this.displayField]
13775 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
13776 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
13780 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13781 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13784 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13785 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13786 this.tickItems.push(d.data);
13789 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13793 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13795 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13797 if(this.fieldLabel.length){
13798 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13801 var listHeight = this.touchViewListGroup.getHeight();
13805 if(firstChecked && listHeight > bodyHeight){
13806 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13811 onTouchViewLoadException : function()
13813 this.hideTouchView();
13816 onTouchViewEmptyResults : function()
13818 this.clearTouchView();
13820 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13822 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13826 clearTouchView : function()
13828 this.touchViewListGroup.dom.innerHTML = '';
13831 onTouchViewClick : function(e, el, o)
13833 e.preventDefault();
13836 var rowIndex = o.rowIndex;
13838 var r = this.store.getAt(rowIndex);
13840 if(!this.multiple){
13841 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13842 c.dom.removeAttribute('checked');
13845 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13847 this.setFromData(r.data);
13849 var close = this.closeTriggerEl();
13855 this.hideTouchView();
13857 this.fireEvent('select', this, r, rowIndex);
13862 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13863 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13864 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13868 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13869 this.addItem(r.data);
13870 this.tickItems.push(r.data);
13876 * @cfg {Boolean} grow
13880 * @cfg {Number} growMin
13884 * @cfg {Number} growMax
13893 Roo.apply(Roo.bootstrap.ComboBox, {
13897 cls: 'modal-header',
13919 cls: 'list-group-item',
13923 cls: 'roo-combobox-list-group-item-value'
13927 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13941 listItemCheckbox : {
13943 cls: 'list-group-item',
13947 cls: 'roo-combobox-list-group-item-value'
13951 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13967 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13972 cls: 'modal-footer',
13980 cls: 'col-xs-6 text-left',
13983 cls: 'btn btn-danger roo-touch-view-cancel',
13989 cls: 'col-xs-6 text-right',
13992 cls: 'btn btn-success roo-touch-view-ok',
14003 Roo.apply(Roo.bootstrap.ComboBox, {
14005 touchViewTemplate : {
14007 cls: 'modal fade roo-combobox-touch-view',
14011 cls: 'modal-dialog',
14012 style : 'position:fixed', // we have to fix position....
14016 cls: 'modal-content',
14018 Roo.bootstrap.ComboBox.header,
14019 Roo.bootstrap.ComboBox.body,
14020 Roo.bootstrap.ComboBox.footer
14029 * Ext JS Library 1.1.1
14030 * Copyright(c) 2006-2007, Ext JS, LLC.
14032 * Originally Released Under LGPL - original licence link has changed is not relivant.
14035 * <script type="text/javascript">
14040 * @extends Roo.util.Observable
14041 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14042 * This class also supports single and multi selection modes. <br>
14043 * Create a data model bound view:
14045 var store = new Roo.data.Store(...);
14047 var view = new Roo.View({
14049 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14051 singleSelect: true,
14052 selectedClass: "ydataview-selected",
14056 // listen for node click?
14057 view.on("click", function(vw, index, node, e){
14058 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14062 dataModel.load("foobar.xml");
14064 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14066 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14067 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14069 * Note: old style constructor is still suported (container, template, config)
14072 * Create a new View
14073 * @param {Object} config The config object
14076 Roo.View = function(config, depreciated_tpl, depreciated_config){
14078 this.parent = false;
14080 if (typeof(depreciated_tpl) == 'undefined') {
14081 // new way.. - universal constructor.
14082 Roo.apply(this, config);
14083 this.el = Roo.get(this.el);
14086 this.el = Roo.get(config);
14087 this.tpl = depreciated_tpl;
14088 Roo.apply(this, depreciated_config);
14090 this.wrapEl = this.el.wrap().wrap();
14091 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14094 if(typeof(this.tpl) == "string"){
14095 this.tpl = new Roo.Template(this.tpl);
14097 // support xtype ctors..
14098 this.tpl = new Roo.factory(this.tpl, Roo);
14102 this.tpl.compile();
14107 * @event beforeclick
14108 * Fires before a click is processed. Returns false to cancel the default action.
14109 * @param {Roo.View} this
14110 * @param {Number} index The index of the target node
14111 * @param {HTMLElement} node The target node
14112 * @param {Roo.EventObject} e The raw event object
14114 "beforeclick" : true,
14117 * Fires when a template node is clicked.
14118 * @param {Roo.View} this
14119 * @param {Number} index The index of the target node
14120 * @param {HTMLElement} node The target node
14121 * @param {Roo.EventObject} e The raw event object
14126 * Fires when a template node is double clicked.
14127 * @param {Roo.View} this
14128 * @param {Number} index The index of the target node
14129 * @param {HTMLElement} node The target node
14130 * @param {Roo.EventObject} e The raw event object
14134 * @event contextmenu
14135 * Fires when a template node is right clicked.
14136 * @param {Roo.View} this
14137 * @param {Number} index The index of the target node
14138 * @param {HTMLElement} node The target node
14139 * @param {Roo.EventObject} e The raw event object
14141 "contextmenu" : true,
14143 * @event selectionchange
14144 * Fires when the selected nodes change.
14145 * @param {Roo.View} this
14146 * @param {Array} selections Array of the selected nodes
14148 "selectionchange" : true,
14151 * @event beforeselect
14152 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14153 * @param {Roo.View} this
14154 * @param {HTMLElement} node The node to be selected
14155 * @param {Array} selections Array of currently selected nodes
14157 "beforeselect" : true,
14159 * @event preparedata
14160 * Fires on every row to render, to allow you to change the data.
14161 * @param {Roo.View} this
14162 * @param {Object} data to be rendered (change this)
14164 "preparedata" : true
14172 "click": this.onClick,
14173 "dblclick": this.onDblClick,
14174 "contextmenu": this.onContextMenu,
14178 this.selections = [];
14180 this.cmp = new Roo.CompositeElementLite([]);
14182 this.store = Roo.factory(this.store, Roo.data);
14183 this.setStore(this.store, true);
14186 if ( this.footer && this.footer.xtype) {
14188 var fctr = this.wrapEl.appendChild(document.createElement("div"));
14190 this.footer.dataSource = this.store;
14191 this.footer.container = fctr;
14192 this.footer = Roo.factory(this.footer, Roo);
14193 fctr.insertFirst(this.el);
14195 // this is a bit insane - as the paging toolbar seems to detach the el..
14196 // dom.parentNode.parentNode.parentNode
14197 // they get detached?
14201 Roo.View.superclass.constructor.call(this);
14206 Roo.extend(Roo.View, Roo.util.Observable, {
14209 * @cfg {Roo.data.Store} store Data store to load data from.
14214 * @cfg {String|Roo.Element} el The container element.
14219 * @cfg {String|Roo.Template} tpl The template used by this View
14223 * @cfg {String} dataName the named area of the template to use as the data area
14224 * Works with domtemplates roo-name="name"
14228 * @cfg {String} selectedClass The css class to add to selected nodes
14230 selectedClass : "x-view-selected",
14232 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14237 * @cfg {String} text to display on mask (default Loading)
14241 * @cfg {Boolean} multiSelect Allow multiple selection
14243 multiSelect : false,
14245 * @cfg {Boolean} singleSelect Allow single selection
14247 singleSelect: false,
14250 * @cfg {Boolean} toggleSelect - selecting
14252 toggleSelect : false,
14255 * @cfg {Boolean} tickable - selecting
14260 * Returns the element this view is bound to.
14261 * @return {Roo.Element}
14263 getEl : function(){
14264 return this.wrapEl;
14270 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14272 refresh : function(){
14273 //Roo.log('refresh');
14276 // if we are using something like 'domtemplate', then
14277 // the what gets used is:
14278 // t.applySubtemplate(NAME, data, wrapping data..)
14279 // the outer template then get' applied with
14280 // the store 'extra data'
14281 // and the body get's added to the
14282 // roo-name="data" node?
14283 // <span class='roo-tpl-{name}'></span> ?????
14287 this.clearSelections();
14288 this.el.update("");
14290 var records = this.store.getRange();
14291 if(records.length < 1) {
14293 // is this valid?? = should it render a template??
14295 this.el.update(this.emptyText);
14299 if (this.dataName) {
14300 this.el.update(t.apply(this.store.meta)); //????
14301 el = this.el.child('.roo-tpl-' + this.dataName);
14304 for(var i = 0, len = records.length; i < len; i++){
14305 var data = this.prepareData(records[i].data, i, records[i]);
14306 this.fireEvent("preparedata", this, data, i, records[i]);
14308 var d = Roo.apply({}, data);
14311 Roo.apply(d, {'roo-id' : Roo.id()});
14315 Roo.each(this.parent.item, function(item){
14316 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14319 Roo.apply(d, {'roo-data-checked' : 'checked'});
14323 html[html.length] = Roo.util.Format.trim(
14325 t.applySubtemplate(this.dataName, d, this.store.meta) :
14332 el.update(html.join(""));
14333 this.nodes = el.dom.childNodes;
14334 this.updateIndexes(0);
14339 * Function to override to reformat the data that is sent to
14340 * the template for each node.
14341 * DEPRICATED - use the preparedata event handler.
14342 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14343 * a JSON object for an UpdateManager bound view).
14345 prepareData : function(data, index, record)
14347 this.fireEvent("preparedata", this, data, index, record);
14351 onUpdate : function(ds, record){
14352 // Roo.log('on update');
14353 this.clearSelections();
14354 var index = this.store.indexOf(record);
14355 var n = this.nodes[index];
14356 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14357 n.parentNode.removeChild(n);
14358 this.updateIndexes(index, index);
14364 onAdd : function(ds, records, index)
14366 //Roo.log(['on Add', ds, records, index] );
14367 this.clearSelections();
14368 if(this.nodes.length == 0){
14372 var n = this.nodes[index];
14373 for(var i = 0, len = records.length; i < len; i++){
14374 var d = this.prepareData(records[i].data, i, records[i]);
14376 this.tpl.insertBefore(n, d);
14379 this.tpl.append(this.el, d);
14382 this.updateIndexes(index);
14385 onRemove : function(ds, record, index){
14386 // Roo.log('onRemove');
14387 this.clearSelections();
14388 var el = this.dataName ?
14389 this.el.child('.roo-tpl-' + this.dataName) :
14392 el.dom.removeChild(this.nodes[index]);
14393 this.updateIndexes(index);
14397 * Refresh an individual node.
14398 * @param {Number} index
14400 refreshNode : function(index){
14401 this.onUpdate(this.store, this.store.getAt(index));
14404 updateIndexes : function(startIndex, endIndex){
14405 var ns = this.nodes;
14406 startIndex = startIndex || 0;
14407 endIndex = endIndex || ns.length - 1;
14408 for(var i = startIndex; i <= endIndex; i++){
14409 ns[i].nodeIndex = i;
14414 * Changes the data store this view uses and refresh the view.
14415 * @param {Store} store
14417 setStore : function(store, initial){
14418 if(!initial && this.store){
14419 this.store.un("datachanged", this.refresh);
14420 this.store.un("add", this.onAdd);
14421 this.store.un("remove", this.onRemove);
14422 this.store.un("update", this.onUpdate);
14423 this.store.un("clear", this.refresh);
14424 this.store.un("beforeload", this.onBeforeLoad);
14425 this.store.un("load", this.onLoad);
14426 this.store.un("loadexception", this.onLoad);
14430 store.on("datachanged", this.refresh, this);
14431 store.on("add", this.onAdd, this);
14432 store.on("remove", this.onRemove, this);
14433 store.on("update", this.onUpdate, this);
14434 store.on("clear", this.refresh, this);
14435 store.on("beforeload", this.onBeforeLoad, this);
14436 store.on("load", this.onLoad, this);
14437 store.on("loadexception", this.onLoad, this);
14445 * onbeforeLoad - masks the loading area.
14448 onBeforeLoad : function(store,opts)
14450 //Roo.log('onBeforeLoad');
14452 this.el.update("");
14454 this.el.mask(this.mask ? this.mask : "Loading" );
14456 onLoad : function ()
14463 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14464 * @param {HTMLElement} node
14465 * @return {HTMLElement} The template node
14467 findItemFromChild : function(node){
14468 var el = this.dataName ?
14469 this.el.child('.roo-tpl-' + this.dataName,true) :
14472 if(!node || node.parentNode == el){
14475 var p = node.parentNode;
14476 while(p && p != el){
14477 if(p.parentNode == el){
14486 onClick : function(e){
14487 var item = this.findItemFromChild(e.getTarget());
14489 var index = this.indexOf(item);
14490 if(this.onItemClick(item, index, e) !== false){
14491 this.fireEvent("click", this, index, item, e);
14494 this.clearSelections();
14499 onContextMenu : function(e){
14500 var item = this.findItemFromChild(e.getTarget());
14502 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14507 onDblClick : function(e){
14508 var item = this.findItemFromChild(e.getTarget());
14510 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14514 onItemClick : function(item, index, e)
14516 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14519 if (this.toggleSelect) {
14520 var m = this.isSelected(item) ? 'unselect' : 'select';
14523 _t[m](item, true, false);
14526 if(this.multiSelect || this.singleSelect){
14527 if(this.multiSelect && e.shiftKey && this.lastSelection){
14528 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14530 this.select(item, this.multiSelect && e.ctrlKey);
14531 this.lastSelection = item;
14534 if(!this.tickable){
14535 e.preventDefault();
14543 * Get the number of selected nodes.
14546 getSelectionCount : function(){
14547 return this.selections.length;
14551 * Get the currently selected nodes.
14552 * @return {Array} An array of HTMLElements
14554 getSelectedNodes : function(){
14555 return this.selections;
14559 * Get the indexes of the selected nodes.
14562 getSelectedIndexes : function(){
14563 var indexes = [], s = this.selections;
14564 for(var i = 0, len = s.length; i < len; i++){
14565 indexes.push(s[i].nodeIndex);
14571 * Clear all selections
14572 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14574 clearSelections : function(suppressEvent){
14575 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14576 this.cmp.elements = this.selections;
14577 this.cmp.removeClass(this.selectedClass);
14578 this.selections = [];
14579 if(!suppressEvent){
14580 this.fireEvent("selectionchange", this, this.selections);
14586 * Returns true if the passed node is selected
14587 * @param {HTMLElement/Number} node The node or node index
14588 * @return {Boolean}
14590 isSelected : function(node){
14591 var s = this.selections;
14595 node = this.getNode(node);
14596 return s.indexOf(node) !== -1;
14601 * @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
14602 * @param {Boolean} keepExisting (optional) true to keep existing selections
14603 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14605 select : function(nodeInfo, keepExisting, suppressEvent){
14606 if(nodeInfo instanceof Array){
14608 this.clearSelections(true);
14610 for(var i = 0, len = nodeInfo.length; i < len; i++){
14611 this.select(nodeInfo[i], true, true);
14615 var node = this.getNode(nodeInfo);
14616 if(!node || this.isSelected(node)){
14617 return; // already selected.
14620 this.clearSelections(true);
14623 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14624 Roo.fly(node).addClass(this.selectedClass);
14625 this.selections.push(node);
14626 if(!suppressEvent){
14627 this.fireEvent("selectionchange", this, this.selections);
14635 * @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
14636 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14637 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14639 unselect : function(nodeInfo, keepExisting, suppressEvent)
14641 if(nodeInfo instanceof Array){
14642 Roo.each(this.selections, function(s) {
14643 this.unselect(s, nodeInfo);
14647 var node = this.getNode(nodeInfo);
14648 if(!node || !this.isSelected(node)){
14649 //Roo.log("not selected");
14650 return; // not selected.
14654 Roo.each(this.selections, function(s) {
14656 Roo.fly(node).removeClass(this.selectedClass);
14663 this.selections= ns;
14664 this.fireEvent("selectionchange", this, this.selections);
14668 * Gets a template node.
14669 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14670 * @return {HTMLElement} The node or null if it wasn't found
14672 getNode : function(nodeInfo){
14673 if(typeof nodeInfo == "string"){
14674 return document.getElementById(nodeInfo);
14675 }else if(typeof nodeInfo == "number"){
14676 return this.nodes[nodeInfo];
14682 * Gets a range template nodes.
14683 * @param {Number} startIndex
14684 * @param {Number} endIndex
14685 * @return {Array} An array of nodes
14687 getNodes : function(start, end){
14688 var ns = this.nodes;
14689 start = start || 0;
14690 end = typeof end == "undefined" ? ns.length - 1 : end;
14693 for(var i = start; i <= end; i++){
14697 for(var i = start; i >= end; i--){
14705 * Finds the index of the passed node
14706 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14707 * @return {Number} The index of the node or -1
14709 indexOf : function(node){
14710 node = this.getNode(node);
14711 if(typeof node.nodeIndex == "number"){
14712 return node.nodeIndex;
14714 var ns = this.nodes;
14715 for(var i = 0, len = ns.length; i < len; i++){
14726 * based on jquery fullcalendar
14730 Roo.bootstrap = Roo.bootstrap || {};
14732 * @class Roo.bootstrap.Calendar
14733 * @extends Roo.bootstrap.Component
14734 * Bootstrap Calendar class
14735 * @cfg {Boolean} loadMask (true|false) default false
14736 * @cfg {Object} header generate the user specific header of the calendar, default false
14739 * Create a new Container
14740 * @param {Object} config The config object
14745 Roo.bootstrap.Calendar = function(config){
14746 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14750 * Fires when a date is selected
14751 * @param {DatePicker} this
14752 * @param {Date} date The selected date
14756 * @event monthchange
14757 * Fires when the displayed month changes
14758 * @param {DatePicker} this
14759 * @param {Date} date The selected month
14761 'monthchange': true,
14763 * @event evententer
14764 * Fires when mouse over an event
14765 * @param {Calendar} this
14766 * @param {event} Event
14768 'evententer': true,
14770 * @event eventleave
14771 * Fires when the mouse leaves an
14772 * @param {Calendar} this
14775 'eventleave': true,
14777 * @event eventclick
14778 * Fires when the mouse click an
14779 * @param {Calendar} this
14788 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
14791 * @cfg {Number} startDay
14792 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14800 getAutoCreate : function(){
14803 var fc_button = function(name, corner, style, content ) {
14804 return Roo.apply({},{
14806 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
14808 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14811 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14822 style : 'width:100%',
14829 cls : 'fc-header-left',
14831 fc_button('prev', 'left', 'arrow', '‹' ),
14832 fc_button('next', 'right', 'arrow', '›' ),
14833 { tag: 'span', cls: 'fc-header-space' },
14834 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
14842 cls : 'fc-header-center',
14846 cls: 'fc-header-title',
14849 html : 'month / year'
14857 cls : 'fc-header-right',
14859 /* fc_button('month', 'left', '', 'month' ),
14860 fc_button('week', '', '', 'week' ),
14861 fc_button('day', 'right', '', 'day' )
14873 header = this.header;
14876 var cal_heads = function() {
14878 // fixme - handle this.
14880 for (var i =0; i < Date.dayNames.length; i++) {
14881 var d = Date.dayNames[i];
14884 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14885 html : d.substring(0,3)
14889 ret[0].cls += ' fc-first';
14890 ret[6].cls += ' fc-last';
14893 var cal_cell = function(n) {
14896 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14901 cls: 'fc-day-number',
14905 cls: 'fc-day-content',
14909 style: 'position: relative;' // height: 17px;
14921 var cal_rows = function() {
14924 for (var r = 0; r < 6; r++) {
14931 for (var i =0; i < Date.dayNames.length; i++) {
14932 var d = Date.dayNames[i];
14933 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14936 row.cn[0].cls+=' fc-first';
14937 row.cn[0].cn[0].style = 'min-height:90px';
14938 row.cn[6].cls+=' fc-last';
14942 ret[0].cls += ' fc-first';
14943 ret[4].cls += ' fc-prev-last';
14944 ret[5].cls += ' fc-last';
14951 cls: 'fc-border-separate',
14952 style : 'width:100%',
14960 cls : 'fc-first fc-last',
14978 cls : 'fc-content',
14979 style : "position: relative;",
14982 cls : 'fc-view fc-view-month fc-grid',
14983 style : 'position: relative',
14984 unselectable : 'on',
14987 cls : 'fc-event-container',
14988 style : 'position:absolute;z-index:8;top:0;left:0;'
15006 initEvents : function()
15009 throw "can not find store for calendar";
15015 style: "text-align:center",
15019 style: "background-color:white;width:50%;margin:250 auto",
15023 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15034 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15036 var size = this.el.select('.fc-content', true).first().getSize();
15037 this.maskEl.setSize(size.width, size.height);
15038 this.maskEl.enableDisplayMode("block");
15039 if(!this.loadMask){
15040 this.maskEl.hide();
15043 this.store = Roo.factory(this.store, Roo.data);
15044 this.store.on('load', this.onLoad, this);
15045 this.store.on('beforeload', this.onBeforeLoad, this);
15049 this.cells = this.el.select('.fc-day',true);
15050 //Roo.log(this.cells);
15051 this.textNodes = this.el.query('.fc-day-number');
15052 this.cells.addClassOnOver('fc-state-hover');
15054 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15055 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15056 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15057 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15059 this.on('monthchange', this.onMonthChange, this);
15061 this.update(new Date().clearTime());
15064 resize : function() {
15065 var sz = this.el.getSize();
15067 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15068 this.el.select('.fc-day-content div',true).setHeight(34);
15073 showPrevMonth : function(e){
15074 this.update(this.activeDate.add("mo", -1));
15076 showToday : function(e){
15077 this.update(new Date().clearTime());
15080 showNextMonth : function(e){
15081 this.update(this.activeDate.add("mo", 1));
15085 showPrevYear : function(){
15086 this.update(this.activeDate.add("y", -1));
15090 showNextYear : function(){
15091 this.update(this.activeDate.add("y", 1));
15096 update : function(date)
15098 var vd = this.activeDate;
15099 this.activeDate = date;
15100 // if(vd && this.el){
15101 // var t = date.getTime();
15102 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15103 // Roo.log('using add remove');
15105 // this.fireEvent('monthchange', this, date);
15107 // this.cells.removeClass("fc-state-highlight");
15108 // this.cells.each(function(c){
15109 // if(c.dateValue == t){
15110 // c.addClass("fc-state-highlight");
15111 // setTimeout(function(){
15112 // try{c.dom.firstChild.focus();}catch(e){}
15122 var days = date.getDaysInMonth();
15124 var firstOfMonth = date.getFirstDateOfMonth();
15125 var startingPos = firstOfMonth.getDay()-this.startDay;
15127 if(startingPos < this.startDay){
15131 var pm = date.add(Date.MONTH, -1);
15132 var prevStart = pm.getDaysInMonth()-startingPos;
15134 this.cells = this.el.select('.fc-day',true);
15135 this.textNodes = this.el.query('.fc-day-number');
15136 this.cells.addClassOnOver('fc-state-hover');
15138 var cells = this.cells.elements;
15139 var textEls = this.textNodes;
15141 Roo.each(cells, function(cell){
15142 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15145 days += startingPos;
15147 // convert everything to numbers so it's fast
15148 var day = 86400000;
15149 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15152 //Roo.log(prevStart);
15154 var today = new Date().clearTime().getTime();
15155 var sel = date.clearTime().getTime();
15156 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15157 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15158 var ddMatch = this.disabledDatesRE;
15159 var ddText = this.disabledDatesText;
15160 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15161 var ddaysText = this.disabledDaysText;
15162 var format = this.format;
15164 var setCellClass = function(cal, cell){
15168 //Roo.log('set Cell Class');
15170 var t = d.getTime();
15174 cell.dateValue = t;
15176 cell.className += " fc-today";
15177 cell.className += " fc-state-highlight";
15178 cell.title = cal.todayText;
15181 // disable highlight in other month..
15182 //cell.className += " fc-state-highlight";
15187 cell.className = " fc-state-disabled";
15188 cell.title = cal.minText;
15192 cell.className = " fc-state-disabled";
15193 cell.title = cal.maxText;
15197 if(ddays.indexOf(d.getDay()) != -1){
15198 cell.title = ddaysText;
15199 cell.className = " fc-state-disabled";
15202 if(ddMatch && format){
15203 var fvalue = d.dateFormat(format);
15204 if(ddMatch.test(fvalue)){
15205 cell.title = ddText.replace("%0", fvalue);
15206 cell.className = " fc-state-disabled";
15210 if (!cell.initialClassName) {
15211 cell.initialClassName = cell.dom.className;
15214 cell.dom.className = cell.initialClassName + ' ' + cell.className;
15219 for(; i < startingPos; i++) {
15220 textEls[i].innerHTML = (++prevStart);
15221 d.setDate(d.getDate()+1);
15223 cells[i].className = "fc-past fc-other-month";
15224 setCellClass(this, cells[i]);
15229 for(; i < days; i++){
15230 intDay = i - startingPos + 1;
15231 textEls[i].innerHTML = (intDay);
15232 d.setDate(d.getDate()+1);
15234 cells[i].className = ''; // "x-date-active";
15235 setCellClass(this, cells[i]);
15239 for(; i < 42; i++) {
15240 textEls[i].innerHTML = (++extraDays);
15241 d.setDate(d.getDate()+1);
15243 cells[i].className = "fc-future fc-other-month";
15244 setCellClass(this, cells[i]);
15247 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15249 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15251 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15252 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15254 if(totalRows != 6){
15255 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15256 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15259 this.fireEvent('monthchange', this, date);
15263 if(!this.internalRender){
15264 var main = this.el.dom.firstChild;
15265 var w = main.offsetWidth;
15266 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15267 Roo.fly(main).setWidth(w);
15268 this.internalRender = true;
15269 // opera does not respect the auto grow header center column
15270 // then, after it gets a width opera refuses to recalculate
15271 // without a second pass
15272 if(Roo.isOpera && !this.secondPass){
15273 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15274 this.secondPass = true;
15275 this.update.defer(10, this, [date]);
15282 findCell : function(dt) {
15283 dt = dt.clearTime().getTime();
15285 this.cells.each(function(c){
15286 //Roo.log("check " +c.dateValue + '?=' + dt);
15287 if(c.dateValue == dt){
15297 findCells : function(ev) {
15298 var s = ev.start.clone().clearTime().getTime();
15300 var e= ev.end.clone().clearTime().getTime();
15303 this.cells.each(function(c){
15304 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15306 if(c.dateValue > e){
15309 if(c.dateValue < s){
15318 // findBestRow: function(cells)
15322 // for (var i =0 ; i < cells.length;i++) {
15323 // ret = Math.max(cells[i].rows || 0,ret);
15330 addItem : function(ev)
15332 // look for vertical location slot in
15333 var cells = this.findCells(ev);
15335 // ev.row = this.findBestRow(cells);
15337 // work out the location.
15341 for(var i =0; i < cells.length; i++) {
15343 cells[i].row = cells[0].row;
15346 cells[i].row = cells[i].row + 1;
15356 if (crow.start.getY() == cells[i].getY()) {
15358 crow.end = cells[i];
15375 cells[0].events.push(ev);
15377 this.calevents.push(ev);
15380 clearEvents: function() {
15382 if(!this.calevents){
15386 Roo.each(this.cells.elements, function(c){
15392 Roo.each(this.calevents, function(e) {
15393 Roo.each(e.els, function(el) {
15394 el.un('mouseenter' ,this.onEventEnter, this);
15395 el.un('mouseleave' ,this.onEventLeave, this);
15400 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15406 renderEvents: function()
15410 this.cells.each(function(c) {
15419 if(c.row != c.events.length){
15420 r = 4 - (4 - (c.row - c.events.length));
15423 c.events = ev.slice(0, r);
15424 c.more = ev.slice(r);
15426 if(c.more.length && c.more.length == 1){
15427 c.events.push(c.more.pop());
15430 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15434 this.cells.each(function(c) {
15436 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15439 for (var e = 0; e < c.events.length; e++){
15440 var ev = c.events[e];
15441 var rows = ev.rows;
15443 for(var i = 0; i < rows.length; i++) {
15445 // how many rows should it span..
15448 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15449 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15451 unselectable : "on",
15454 cls: 'fc-event-inner',
15458 // cls: 'fc-event-time',
15459 // html : cells.length > 1 ? '' : ev.time
15463 cls: 'fc-event-title',
15464 html : String.format('{0}', ev.title)
15471 cls: 'ui-resizable-handle ui-resizable-e',
15472 html : '  '
15479 cfg.cls += ' fc-event-start';
15481 if ((i+1) == rows.length) {
15482 cfg.cls += ' fc-event-end';
15485 var ctr = _this.el.select('.fc-event-container',true).first();
15486 var cg = ctr.createChild(cfg);
15488 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15489 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15491 var r = (c.more.length) ? 1 : 0;
15492 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15493 cg.setWidth(ebox.right - sbox.x -2);
15495 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15496 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15497 cg.on('click', _this.onEventClick, _this, ev);
15508 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15509 style : 'position: absolute',
15510 unselectable : "on",
15513 cls: 'fc-event-inner',
15517 cls: 'fc-event-title',
15525 cls: 'ui-resizable-handle ui-resizable-e',
15526 html : '  '
15532 var ctr = _this.el.select('.fc-event-container',true).first();
15533 var cg = ctr.createChild(cfg);
15535 var sbox = c.select('.fc-day-content',true).first().getBox();
15536 var ebox = c.select('.fc-day-content',true).first().getBox();
15538 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15539 cg.setWidth(ebox.right - sbox.x -2);
15541 cg.on('click', _this.onMoreEventClick, _this, c.more);
15551 onEventEnter: function (e, el,event,d) {
15552 this.fireEvent('evententer', this, el, event);
15555 onEventLeave: function (e, el,event,d) {
15556 this.fireEvent('eventleave', this, el, event);
15559 onEventClick: function (e, el,event,d) {
15560 this.fireEvent('eventclick', this, el, event);
15563 onMonthChange: function () {
15567 onMoreEventClick: function(e, el, more)
15571 this.calpopover.placement = 'right';
15572 this.calpopover.setTitle('More');
15574 this.calpopover.setContent('');
15576 var ctr = this.calpopover.el.select('.popover-content', true).first();
15578 Roo.each(more, function(m){
15580 cls : 'fc-event-hori fc-event-draggable',
15583 var cg = ctr.createChild(cfg);
15585 cg.on('click', _this.onEventClick, _this, m);
15588 this.calpopover.show(el);
15593 onLoad: function ()
15595 this.calevents = [];
15598 if(this.store.getCount() > 0){
15599 this.store.data.each(function(d){
15602 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15603 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15604 time : d.data.start_time,
15605 title : d.data.title,
15606 description : d.data.description,
15607 venue : d.data.venue
15612 this.renderEvents();
15614 if(this.calevents.length && this.loadMask){
15615 this.maskEl.hide();
15619 onBeforeLoad: function()
15621 this.clearEvents();
15623 this.maskEl.show();
15637 * @class Roo.bootstrap.Popover
15638 * @extends Roo.bootstrap.Component
15639 * Bootstrap Popover class
15640 * @cfg {String} html contents of the popover (or false to use children..)
15641 * @cfg {String} title of popover (or false to hide)
15642 * @cfg {String} placement how it is placed
15643 * @cfg {String} trigger click || hover (or false to trigger manually)
15644 * @cfg {String} over what (parent or false to trigger manually.)
15645 * @cfg {Number} delay - delay before showing
15648 * Create a new Popover
15649 * @param {Object} config The config object
15652 Roo.bootstrap.Popover = function(config){
15653 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15659 * After the popover show
15661 * @param {Roo.bootstrap.Popover} this
15666 * After the popover hide
15668 * @param {Roo.bootstrap.Popover} this
15674 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
15676 title: 'Fill in a title',
15679 placement : 'right',
15680 trigger : 'hover', // hover
15686 can_build_overlaid : false,
15688 getChildContainer : function()
15690 return this.el.select('.popover-content',true).first();
15693 getAutoCreate : function(){
15696 cls : 'popover roo-dynamic',
15697 style: 'display:block',
15703 cls : 'popover-inner',
15707 cls: 'popover-title',
15711 cls : 'popover-content',
15722 setTitle: function(str)
15725 this.el.select('.popover-title',true).first().dom.innerHTML = str;
15727 setContent: function(str)
15730 this.el.select('.popover-content',true).first().dom.innerHTML = str;
15732 // as it get's added to the bottom of the page.
15733 onRender : function(ct, position)
15735 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15737 var cfg = Roo.apply({}, this.getAutoCreate());
15741 cfg.cls += ' ' + this.cls;
15744 cfg.style = this.style;
15746 //Roo.log("adding to ");
15747 this.el = Roo.get(document.body).createChild(cfg, position);
15748 // Roo.log(this.el);
15753 initEvents : function()
15755 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15756 this.el.enableDisplayMode('block');
15758 if (this.over === false) {
15761 if (this.triggers === false) {
15764 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15765 var triggers = this.trigger ? this.trigger.split(' ') : [];
15766 Roo.each(triggers, function(trigger) {
15768 if (trigger == 'click') {
15769 on_el.on('click', this.toggle, this);
15770 } else if (trigger != 'manual') {
15771 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
15772 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15774 on_el.on(eventIn ,this.enter, this);
15775 on_el.on(eventOut, this.leave, this);
15786 toggle : function () {
15787 this.hoverState == 'in' ? this.leave() : this.enter();
15790 enter : function () {
15793 clearTimeout(this.timeout);
15795 this.hoverState = 'in';
15797 if (!this.delay || !this.delay.show) {
15802 this.timeout = setTimeout(function () {
15803 if (_t.hoverState == 'in') {
15806 }, this.delay.show)
15808 leave : function() {
15809 clearTimeout(this.timeout);
15811 this.hoverState = 'out';
15813 if (!this.delay || !this.delay.hide) {
15818 this.timeout = setTimeout(function () {
15819 if (_t.hoverState == 'out') {
15822 }, this.delay.hide)
15825 show : function (on_el)
15828 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15831 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15832 if (this.html !== false) {
15833 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15835 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15836 if (!this.title.length) {
15837 this.el.select('.popover-title',true).hide();
15840 var placement = typeof this.placement == 'function' ?
15841 this.placement.call(this, this.el, on_el) :
15844 var autoToken = /\s?auto?\s?/i;
15845 var autoPlace = autoToken.test(placement);
15847 placement = placement.replace(autoToken, '') || 'top';
15851 //this.el.setXY([0,0]);
15853 this.el.dom.style.display='block';
15854 this.el.addClass(placement);
15856 //this.el.appendTo(on_el);
15858 var p = this.getPosition();
15859 var box = this.el.getBox();
15864 var align = Roo.bootstrap.Popover.alignment[placement];
15865 this.el.alignTo(on_el, align[0],align[1]);
15866 //var arrow = this.el.select('.arrow',true).first();
15867 //arrow.set(align[2],
15869 this.el.addClass('in');
15872 if (this.el.hasClass('fade')) {
15876 this.fireEvent('show', this);
15881 this.el.setXY([0,0]);
15882 this.el.removeClass('in');
15884 this.hoverState = null;
15886 this.fireEvent('hide', this);
15891 Roo.bootstrap.Popover.alignment = {
15892 'left' : ['r-l', [-10,0], 'right'],
15893 'right' : ['l-r', [10,0], 'left'],
15894 'bottom' : ['t-b', [0,10], 'top'],
15895 'top' : [ 'b-t', [0,-10], 'bottom']
15906 * @class Roo.bootstrap.Progress
15907 * @extends Roo.bootstrap.Component
15908 * Bootstrap Progress class
15909 * @cfg {Boolean} striped striped of the progress bar
15910 * @cfg {Boolean} active animated of the progress bar
15914 * Create a new Progress
15915 * @param {Object} config The config object
15918 Roo.bootstrap.Progress = function(config){
15919 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15922 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
15927 getAutoCreate : function(){
15935 cfg.cls += ' progress-striped';
15939 cfg.cls += ' active';
15958 * @class Roo.bootstrap.ProgressBar
15959 * @extends Roo.bootstrap.Component
15960 * Bootstrap ProgressBar class
15961 * @cfg {Number} aria_valuenow aria-value now
15962 * @cfg {Number} aria_valuemin aria-value min
15963 * @cfg {Number} aria_valuemax aria-value max
15964 * @cfg {String} label label for the progress bar
15965 * @cfg {String} panel (success | info | warning | danger )
15966 * @cfg {String} role role of the progress bar
15967 * @cfg {String} sr_only text
15971 * Create a new ProgressBar
15972 * @param {Object} config The config object
15975 Roo.bootstrap.ProgressBar = function(config){
15976 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15979 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
15983 aria_valuemax : 100,
15989 getAutoCreate : function()
15994 cls: 'progress-bar',
15995 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16007 cfg.role = this.role;
16010 if(this.aria_valuenow){
16011 cfg['aria-valuenow'] = this.aria_valuenow;
16014 if(this.aria_valuemin){
16015 cfg['aria-valuemin'] = this.aria_valuemin;
16018 if(this.aria_valuemax){
16019 cfg['aria-valuemax'] = this.aria_valuemax;
16022 if(this.label && !this.sr_only){
16023 cfg.html = this.label;
16027 cfg.cls += ' progress-bar-' + this.panel;
16033 update : function(aria_valuenow)
16035 this.aria_valuenow = aria_valuenow;
16037 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16052 * @class Roo.bootstrap.TabGroup
16053 * @extends Roo.bootstrap.Column
16054 * Bootstrap Column class
16055 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16056 * @cfg {Boolean} carousel true to make the group behave like a carousel
16057 * @cfg {Boolean} bullets show bullets for the panels
16058 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16059 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16060 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16063 * Create a new TabGroup
16064 * @param {Object} config The config object
16067 Roo.bootstrap.TabGroup = function(config){
16068 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16070 this.navId = Roo.id();
16073 Roo.bootstrap.TabGroup.register(this);
16077 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16080 transition : false,
16085 slideOnTouch : false,
16087 getAutoCreate : function()
16089 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16091 cfg.cls += ' tab-content';
16093 if (this.carousel) {
16094 cfg.cls += ' carousel slide';
16097 cls : 'carousel-inner'
16100 if(this.bullets && !Roo.isTouch){
16103 cls : 'carousel-bullets',
16107 if(this.bullets_cls){
16108 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16111 for (var i = 0; i < this.bullets; i++){
16113 cls : 'bullet bullet-' + i
16121 cfg.cn[0].cn = bullets;
16128 initEvents: function()
16130 if(Roo.isTouch && this.slideOnTouch){
16131 this.el.on("touchstart", this.onTouchStart, this);
16134 if(this.autoslide){
16137 this.slideFn = window.setInterval(function() {
16138 _this.showPanelNext();
16144 onTouchStart : function(e, el, o)
16146 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16150 this.showPanelNext();
16153 getChildContainer : function()
16155 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16159 * register a Navigation item
16160 * @param {Roo.bootstrap.NavItem} the navitem to add
16162 register : function(item)
16164 this.tabs.push( item);
16165 item.navId = this.navId; // not really needed..
16170 getActivePanel : function()
16173 Roo.each(this.tabs, function(t) {
16183 getPanelByName : function(n)
16186 Roo.each(this.tabs, function(t) {
16187 if (t.tabId == n) {
16195 indexOfPanel : function(p)
16198 Roo.each(this.tabs, function(t,i) {
16199 if (t.tabId == p.tabId) {
16208 * show a specific panel
16209 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16210 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16212 showPanel : function (pan)
16214 if(this.transition || typeof(pan) == 'undefined'){
16215 Roo.log("waiting for the transitionend");
16219 if (typeof(pan) == 'number') {
16220 pan = this.tabs[pan];
16223 if (typeof(pan) == 'string') {
16224 pan = this.getPanelByName(pan);
16227 var cur = this.getActivePanel();
16230 Roo.log('pan or acitve pan is undefined');
16234 if (pan.tabId == this.getActivePanel().tabId) {
16238 if (false === cur.fireEvent('beforedeactivate')) {
16242 if(this.bullets > 0 && !Roo.isTouch){
16243 this.setActiveBullet(this.indexOfPanel(pan));
16246 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16248 this.transition = true;
16249 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16250 var lr = dir == 'next' ? 'left' : 'right';
16251 pan.el.addClass(dir); // or prev
16252 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16253 cur.el.addClass(lr); // or right
16254 pan.el.addClass(lr);
16257 cur.el.on('transitionend', function() {
16258 Roo.log("trans end?");
16260 pan.el.removeClass([lr,dir]);
16261 pan.setActive(true);
16263 cur.el.removeClass([lr]);
16264 cur.setActive(false);
16266 _this.transition = false;
16268 }, this, { single: true } );
16273 cur.setActive(false);
16274 pan.setActive(true);
16279 showPanelNext : function()
16281 var i = this.indexOfPanel(this.getActivePanel());
16283 if (i >= this.tabs.length - 1 && !this.autoslide) {
16287 if (i >= this.tabs.length - 1 && this.autoslide) {
16291 this.showPanel(this.tabs[i+1]);
16294 showPanelPrev : function()
16296 var i = this.indexOfPanel(this.getActivePanel());
16298 if (i < 1 && !this.autoslide) {
16302 if (i < 1 && this.autoslide) {
16303 i = this.tabs.length;
16306 this.showPanel(this.tabs[i-1]);
16310 addBullet: function()
16312 if(!this.bullets || Roo.isTouch){
16315 var ctr = this.el.select('.carousel-bullets',true).first();
16316 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16317 var bullet = ctr.createChild({
16318 cls : 'bullet bullet-' + i
16319 },ctr.dom.lastChild);
16324 bullet.on('click', (function(e, el, o, ii, t){
16326 e.preventDefault();
16328 this.showPanel(ii);
16330 if(this.autoslide && this.slideFn){
16331 clearInterval(this.slideFn);
16332 this.slideFn = window.setInterval(function() {
16333 _this.showPanelNext();
16337 }).createDelegate(this, [i, bullet], true));
16342 setActiveBullet : function(i)
16348 Roo.each(this.el.select('.bullet', true).elements, function(el){
16349 el.removeClass('selected');
16352 var bullet = this.el.select('.bullet-' + i, true).first();
16358 bullet.addClass('selected');
16369 Roo.apply(Roo.bootstrap.TabGroup, {
16373 * register a Navigation Group
16374 * @param {Roo.bootstrap.NavGroup} the navgroup to add
16376 register : function(navgrp)
16378 this.groups[navgrp.navId] = navgrp;
16382 * fetch a Navigation Group based on the navigation ID
16383 * if one does not exist , it will get created.
16384 * @param {string} the navgroup to add
16385 * @returns {Roo.bootstrap.NavGroup} the navgroup
16387 get: function(navId) {
16388 if (typeof(this.groups[navId]) == 'undefined') {
16389 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16391 return this.groups[navId] ;
16406 * @class Roo.bootstrap.TabPanel
16407 * @extends Roo.bootstrap.Component
16408 * Bootstrap TabPanel class
16409 * @cfg {Boolean} active panel active
16410 * @cfg {String} html panel content
16411 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16412 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16416 * Create a new TabPanel
16417 * @param {Object} config The config object
16420 Roo.bootstrap.TabPanel = function(config){
16421 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16425 * Fires when the active status changes
16426 * @param {Roo.bootstrap.TabPanel} this
16427 * @param {Boolean} state the new state
16432 * @event beforedeactivate
16433 * Fires before a tab is de-activated - can be used to do validation on a form.
16434 * @param {Roo.bootstrap.TabPanel} this
16435 * @return {Boolean} false if there is an error
16438 'beforedeactivate': true
16441 this.tabId = this.tabId || Roo.id();
16445 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16452 getAutoCreate : function(){
16455 // item is needed for carousel - not sure if it has any effect otherwise
16456 cls: 'tab-pane item',
16457 html: this.html || ''
16461 cfg.cls += ' active';
16465 cfg.tabId = this.tabId;
16472 initEvents: function()
16474 var p = this.parent();
16475 this.navId = this.navId || p.navId;
16477 if (typeof(this.navId) != 'undefined') {
16478 // not really needed.. but just in case.. parent should be a NavGroup.
16479 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16483 var i = tg.tabs.length - 1;
16485 if(this.active && tg.bullets > 0 && i < tg.bullets){
16486 tg.setActiveBullet(i);
16493 onRender : function(ct, position)
16495 // Roo.log("Call onRender: " + this.xtype);
16497 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16505 setActive: function(state)
16507 Roo.log("panel - set active " + this.tabId + "=" + state);
16509 this.active = state;
16511 this.el.removeClass('active');
16513 } else if (!this.el.hasClass('active')) {
16514 this.el.addClass('active');
16517 this.fireEvent('changed', this, state);
16534 * @class Roo.bootstrap.DateField
16535 * @extends Roo.bootstrap.Input
16536 * Bootstrap DateField class
16537 * @cfg {Number} weekStart default 0
16538 * @cfg {String} viewMode default empty, (months|years)
16539 * @cfg {String} minViewMode default empty, (months|years)
16540 * @cfg {Number} startDate default -Infinity
16541 * @cfg {Number} endDate default Infinity
16542 * @cfg {Boolean} todayHighlight default false
16543 * @cfg {Boolean} todayBtn default false
16544 * @cfg {Boolean} calendarWeeks default false
16545 * @cfg {Object} daysOfWeekDisabled default empty
16546 * @cfg {Boolean} singleMode default false (true | false)
16548 * @cfg {Boolean} keyboardNavigation default true
16549 * @cfg {String} language default en
16552 * Create a new DateField
16553 * @param {Object} config The config object
16556 Roo.bootstrap.DateField = function(config){
16557 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16561 * Fires when this field show.
16562 * @param {Roo.bootstrap.DateField} this
16563 * @param {Mixed} date The date value
16568 * Fires when this field hide.
16569 * @param {Roo.bootstrap.DateField} this
16570 * @param {Mixed} date The date value
16575 * Fires when select a date.
16576 * @param {Roo.bootstrap.DateField} this
16577 * @param {Mixed} date The date value
16581 * @event beforeselect
16582 * Fires when before select a date.
16583 * @param {Roo.bootstrap.DateField} this
16584 * @param {Mixed} date The date value
16586 beforeselect : true
16590 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16593 * @cfg {String} format
16594 * The default date format string which can be overriden for localization support. The format must be
16595 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16599 * @cfg {String} altFormats
16600 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16601 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16603 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16611 todayHighlight : false,
16617 keyboardNavigation: true,
16619 calendarWeeks: false,
16621 startDate: -Infinity,
16625 daysOfWeekDisabled: [],
16629 singleMode : false,
16631 UTCDate: function()
16633 return new Date(Date.UTC.apply(Date, arguments));
16636 UTCToday: function()
16638 var today = new Date();
16639 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16642 getDate: function() {
16643 var d = this.getUTCDate();
16644 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16647 getUTCDate: function() {
16651 setDate: function(d) {
16652 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16655 setUTCDate: function(d) {
16657 this.setValue(this.formatDate(this.date));
16660 onRender: function(ct, position)
16663 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16665 this.language = this.language || 'en';
16666 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16667 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16669 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16670 this.format = this.format || 'm/d/y';
16671 this.isInline = false;
16672 this.isInput = true;
16673 this.component = this.el.select('.add-on', true).first() || false;
16674 this.component = (this.component && this.component.length === 0) ? false : this.component;
16675 this.hasInput = this.component && this.inputEL().length;
16677 if (typeof(this.minViewMode === 'string')) {
16678 switch (this.minViewMode) {
16680 this.minViewMode = 1;
16683 this.minViewMode = 2;
16686 this.minViewMode = 0;
16691 if (typeof(this.viewMode === 'string')) {
16692 switch (this.viewMode) {
16705 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16707 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16709 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16711 this.picker().on('mousedown', this.onMousedown, this);
16712 this.picker().on('click', this.onClick, this);
16714 this.picker().addClass('datepicker-dropdown');
16716 this.startViewMode = this.viewMode;
16718 if(this.singleMode){
16719 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16720 v.setVisibilityMode(Roo.Element.DISPLAY);
16724 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16725 v.setStyle('width', '189px');
16729 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16730 if(!this.calendarWeeks){
16735 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16736 v.attr('colspan', function(i, val){
16737 return parseInt(val) + 1;
16742 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16744 this.setStartDate(this.startDate);
16745 this.setEndDate(this.endDate);
16747 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16754 if(this.isInline) {
16759 picker : function()
16761 return this.pickerEl;
16762 // return this.el.select('.datepicker', true).first();
16765 fillDow: function()
16767 var dowCnt = this.weekStart;
16776 if(this.calendarWeeks){
16784 while (dowCnt < this.weekStart + 7) {
16788 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16792 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16795 fillMonths: function()
16798 var months = this.picker().select('>.datepicker-months td', true).first();
16800 months.dom.innerHTML = '';
16806 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16809 months.createChild(month);
16816 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;
16818 if (this.date < this.startDate) {
16819 this.viewDate = new Date(this.startDate);
16820 } else if (this.date > this.endDate) {
16821 this.viewDate = new Date(this.endDate);
16823 this.viewDate = new Date(this.date);
16831 var d = new Date(this.viewDate),
16832 year = d.getUTCFullYear(),
16833 month = d.getUTCMonth(),
16834 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16835 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16836 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16837 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16838 currentDate = this.date && this.date.valueOf(),
16839 today = this.UTCToday();
16841 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16843 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16845 // this.picker.select('>tfoot th.today').
16846 // .text(dates[this.language].today)
16847 // .toggle(this.todayBtn !== false);
16849 this.updateNavArrows();
16852 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16854 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16856 prevMonth.setUTCDate(day);
16858 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16860 var nextMonth = new Date(prevMonth);
16862 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16864 nextMonth = nextMonth.valueOf();
16866 var fillMonths = false;
16868 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16870 while(prevMonth.valueOf() < nextMonth) {
16873 if (prevMonth.getUTCDay() === this.weekStart) {
16875 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16883 if(this.calendarWeeks){
16884 // ISO 8601: First week contains first thursday.
16885 // ISO also states week starts on Monday, but we can be more abstract here.
16887 // Start of current week: based on weekstart/current date
16888 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16889 // Thursday of this week
16890 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16891 // First Thursday of year, year from thursday
16892 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16893 // Calendar week: ms between thursdays, div ms per day, div 7 days
16894 calWeek = (th - yth) / 864e5 / 7 + 1;
16896 fillMonths.cn.push({
16904 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16906 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16909 if (this.todayHighlight &&
16910 prevMonth.getUTCFullYear() == today.getFullYear() &&
16911 prevMonth.getUTCMonth() == today.getMonth() &&
16912 prevMonth.getUTCDate() == today.getDate()) {
16913 clsName += ' today';
16916 if (currentDate && prevMonth.valueOf() === currentDate) {
16917 clsName += ' active';
16920 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16921 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16922 clsName += ' disabled';
16925 fillMonths.cn.push({
16927 cls: 'day ' + clsName,
16928 html: prevMonth.getDate()
16931 prevMonth.setDate(prevMonth.getDate()+1);
16934 var currentYear = this.date && this.date.getUTCFullYear();
16935 var currentMonth = this.date && this.date.getUTCMonth();
16937 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16939 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16940 v.removeClass('active');
16942 if(currentYear === year && k === currentMonth){
16943 v.addClass('active');
16946 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16947 v.addClass('disabled');
16953 year = parseInt(year/10, 10) * 10;
16955 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16957 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16960 for (var i = -1; i < 11; i++) {
16961 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16963 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16971 showMode: function(dir)
16974 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16977 Roo.each(this.picker().select('>div',true).elements, function(v){
16978 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16981 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16986 if(this.isInline) {
16990 this.picker().removeClass(['bottom', 'top']);
16992 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16994 * place to the top of element!
16998 this.picker().addClass('top');
16999 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17004 this.picker().addClass('bottom');
17006 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17009 parseDate : function(value)
17011 if(!value || value instanceof Date){
17014 var v = Date.parseDate(value, this.format);
17015 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17016 v = Date.parseDate(value, 'Y-m-d');
17018 if(!v && this.altFormats){
17019 if(!this.altFormatsArray){
17020 this.altFormatsArray = this.altFormats.split("|");
17022 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17023 v = Date.parseDate(value, this.altFormatsArray[i]);
17029 formatDate : function(date, fmt)
17031 return (!date || !(date instanceof Date)) ?
17032 date : date.dateFormat(fmt || this.format);
17035 onFocus : function()
17037 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17041 onBlur : function()
17043 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17045 var d = this.inputEl().getValue();
17054 this.picker().show();
17058 this.fireEvent('show', this, this.date);
17063 if(this.isInline) {
17066 this.picker().hide();
17067 this.viewMode = this.startViewMode;
17070 this.fireEvent('hide', this, this.date);
17074 onMousedown: function(e)
17076 e.stopPropagation();
17077 e.preventDefault();
17082 Roo.bootstrap.DateField.superclass.keyup.call(this);
17086 setValue: function(v)
17088 if(this.fireEvent('beforeselect', this, v) !== false){
17089 var d = new Date(this.parseDate(v) ).clearTime();
17091 if(isNaN(d.getTime())){
17092 this.date = this.viewDate = '';
17093 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17097 v = this.formatDate(d);
17099 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17101 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17105 this.fireEvent('select', this, this.date);
17109 getValue: function()
17111 return this.formatDate(this.date);
17114 fireKey: function(e)
17116 if (!this.picker().isVisible()){
17117 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17123 var dateChanged = false,
17125 newDate, newViewDate;
17130 e.preventDefault();
17134 if (!this.keyboardNavigation) {
17137 dir = e.keyCode == 37 ? -1 : 1;
17140 newDate = this.moveYear(this.date, dir);
17141 newViewDate = this.moveYear(this.viewDate, dir);
17142 } else if (e.shiftKey){
17143 newDate = this.moveMonth(this.date, dir);
17144 newViewDate = this.moveMonth(this.viewDate, dir);
17146 newDate = new Date(this.date);
17147 newDate.setUTCDate(this.date.getUTCDate() + dir);
17148 newViewDate = new Date(this.viewDate);
17149 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17151 if (this.dateWithinRange(newDate)){
17152 this.date = newDate;
17153 this.viewDate = newViewDate;
17154 this.setValue(this.formatDate(this.date));
17156 e.preventDefault();
17157 dateChanged = true;
17162 if (!this.keyboardNavigation) {
17165 dir = e.keyCode == 38 ? -1 : 1;
17167 newDate = this.moveYear(this.date, dir);
17168 newViewDate = this.moveYear(this.viewDate, dir);
17169 } else if (e.shiftKey){
17170 newDate = this.moveMonth(this.date, dir);
17171 newViewDate = this.moveMonth(this.viewDate, dir);
17173 newDate = new Date(this.date);
17174 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17175 newViewDate = new Date(this.viewDate);
17176 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17178 if (this.dateWithinRange(newDate)){
17179 this.date = newDate;
17180 this.viewDate = newViewDate;
17181 this.setValue(this.formatDate(this.date));
17183 e.preventDefault();
17184 dateChanged = true;
17188 this.setValue(this.formatDate(this.date));
17190 e.preventDefault();
17193 this.setValue(this.formatDate(this.date));
17207 onClick: function(e)
17209 e.stopPropagation();
17210 e.preventDefault();
17212 var target = e.getTarget();
17214 if(target.nodeName.toLowerCase() === 'i'){
17215 target = Roo.get(target).dom.parentNode;
17218 var nodeName = target.nodeName;
17219 var className = target.className;
17220 var html = target.innerHTML;
17221 //Roo.log(nodeName);
17223 switch(nodeName.toLowerCase()) {
17225 switch(className) {
17231 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17232 switch(this.viewMode){
17234 this.viewDate = this.moveMonth(this.viewDate, dir);
17238 this.viewDate = this.moveYear(this.viewDate, dir);
17244 var date = new Date();
17245 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17247 this.setValue(this.formatDate(this.date));
17254 if (className.indexOf('disabled') < 0) {
17255 this.viewDate.setUTCDate(1);
17256 if (className.indexOf('month') > -1) {
17257 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17259 var year = parseInt(html, 10) || 0;
17260 this.viewDate.setUTCFullYear(year);
17264 if(this.singleMode){
17265 this.setValue(this.formatDate(this.viewDate));
17276 //Roo.log(className);
17277 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17278 var day = parseInt(html, 10) || 1;
17279 var year = this.viewDate.getUTCFullYear(),
17280 month = this.viewDate.getUTCMonth();
17282 if (className.indexOf('old') > -1) {
17289 } else if (className.indexOf('new') > -1) {
17297 //Roo.log([year,month,day]);
17298 this.date = this.UTCDate(year, month, day,0,0,0,0);
17299 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17301 //Roo.log(this.formatDate(this.date));
17302 this.setValue(this.formatDate(this.date));
17309 setStartDate: function(startDate)
17311 this.startDate = startDate || -Infinity;
17312 if (this.startDate !== -Infinity) {
17313 this.startDate = this.parseDate(this.startDate);
17316 this.updateNavArrows();
17319 setEndDate: function(endDate)
17321 this.endDate = endDate || Infinity;
17322 if (this.endDate !== Infinity) {
17323 this.endDate = this.parseDate(this.endDate);
17326 this.updateNavArrows();
17329 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17331 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17332 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17333 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17335 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17336 return parseInt(d, 10);
17339 this.updateNavArrows();
17342 updateNavArrows: function()
17344 if(this.singleMode){
17348 var d = new Date(this.viewDate),
17349 year = d.getUTCFullYear(),
17350 month = d.getUTCMonth();
17352 Roo.each(this.picker().select('.prev', true).elements, function(v){
17354 switch (this.viewMode) {
17357 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17363 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17370 Roo.each(this.picker().select('.next', true).elements, function(v){
17372 switch (this.viewMode) {
17375 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17381 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17389 moveMonth: function(date, dir)
17394 var new_date = new Date(date.valueOf()),
17395 day = new_date.getUTCDate(),
17396 month = new_date.getUTCMonth(),
17397 mag = Math.abs(dir),
17399 dir = dir > 0 ? 1 : -1;
17402 // If going back one month, make sure month is not current month
17403 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17405 return new_date.getUTCMonth() == month;
17407 // If going forward one month, make sure month is as expected
17408 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17410 return new_date.getUTCMonth() != new_month;
17412 new_month = month + dir;
17413 new_date.setUTCMonth(new_month);
17414 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17415 if (new_month < 0 || new_month > 11) {
17416 new_month = (new_month + 12) % 12;
17419 // For magnitudes >1, move one month at a time...
17420 for (var i=0; i<mag; i++) {
17421 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17422 new_date = this.moveMonth(new_date, dir);
17424 // ...then reset the day, keeping it in the new month
17425 new_month = new_date.getUTCMonth();
17426 new_date.setUTCDate(day);
17428 return new_month != new_date.getUTCMonth();
17431 // Common date-resetting loop -- if date is beyond end of month, make it
17434 new_date.setUTCDate(--day);
17435 new_date.setUTCMonth(new_month);
17440 moveYear: function(date, dir)
17442 return this.moveMonth(date, dir*12);
17445 dateWithinRange: function(date)
17447 return date >= this.startDate && date <= this.endDate;
17453 this.picker().remove();
17458 Roo.apply(Roo.bootstrap.DateField, {
17469 html: '<i class="fa fa-arrow-left"/>'
17479 html: '<i class="fa fa-arrow-right"/>'
17521 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17522 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17523 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17524 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17525 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17538 navFnc: 'FullYear',
17543 navFnc: 'FullYear',
17548 Roo.apply(Roo.bootstrap.DateField, {
17552 cls: 'datepicker dropdown-menu roo-dynamic',
17556 cls: 'datepicker-days',
17560 cls: 'table-condensed',
17562 Roo.bootstrap.DateField.head,
17566 Roo.bootstrap.DateField.footer
17573 cls: 'datepicker-months',
17577 cls: 'table-condensed',
17579 Roo.bootstrap.DateField.head,
17580 Roo.bootstrap.DateField.content,
17581 Roo.bootstrap.DateField.footer
17588 cls: 'datepicker-years',
17592 cls: 'table-condensed',
17594 Roo.bootstrap.DateField.head,
17595 Roo.bootstrap.DateField.content,
17596 Roo.bootstrap.DateField.footer
17615 * @class Roo.bootstrap.TimeField
17616 * @extends Roo.bootstrap.Input
17617 * Bootstrap DateField class
17621 * Create a new TimeField
17622 * @param {Object} config The config object
17625 Roo.bootstrap.TimeField = function(config){
17626 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17630 * Fires when this field show.
17631 * @param {Roo.bootstrap.DateField} thisthis
17632 * @param {Mixed} date The date value
17637 * Fires when this field hide.
17638 * @param {Roo.bootstrap.DateField} this
17639 * @param {Mixed} date The date value
17644 * Fires when select a date.
17645 * @param {Roo.bootstrap.DateField} this
17646 * @param {Mixed} date The date value
17652 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17655 * @cfg {String} format
17656 * The default time format string which can be overriden for localization support. The format must be
17657 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17661 onRender: function(ct, position)
17664 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17666 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17668 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17670 this.pop = this.picker().select('>.datepicker-time',true).first();
17671 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17673 this.picker().on('mousedown', this.onMousedown, this);
17674 this.picker().on('click', this.onClick, this);
17676 this.picker().addClass('datepicker-dropdown');
17681 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17682 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17683 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17684 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17685 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17686 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17690 fireKey: function(e){
17691 if (!this.picker().isVisible()){
17692 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17698 e.preventDefault();
17706 this.onTogglePeriod();
17709 this.onIncrementMinutes();
17712 this.onDecrementMinutes();
17721 onClick: function(e) {
17722 e.stopPropagation();
17723 e.preventDefault();
17726 picker : function()
17728 return this.el.select('.datepicker', true).first();
17731 fillTime: function()
17733 var time = this.pop.select('tbody', true).first();
17735 time.dom.innerHTML = '';
17750 cls: 'hours-up glyphicon glyphicon-chevron-up'
17770 cls: 'minutes-up glyphicon glyphicon-chevron-up'
17791 cls: 'timepicker-hour',
17806 cls: 'timepicker-minute',
17821 cls: 'btn btn-primary period',
17843 cls: 'hours-down glyphicon glyphicon-chevron-down'
17863 cls: 'minutes-down glyphicon glyphicon-chevron-down'
17881 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17888 var hours = this.time.getHours();
17889 var minutes = this.time.getMinutes();
17902 hours = hours - 12;
17906 hours = '0' + hours;
17910 minutes = '0' + minutes;
17913 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17914 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17915 this.pop.select('button', true).first().dom.innerHTML = period;
17921 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17923 var cls = ['bottom'];
17925 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17932 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17937 this.picker().addClass(cls.join('-'));
17941 Roo.each(cls, function(c){
17943 _this.picker().setTop(_this.inputEl().getHeight());
17947 _this.picker().setTop(0 - _this.picker().getHeight());
17952 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17956 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17963 onFocus : function()
17965 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17969 onBlur : function()
17971 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17977 this.picker().show();
17982 this.fireEvent('show', this, this.date);
17987 this.picker().hide();
17990 this.fireEvent('hide', this, this.date);
17993 setTime : function()
17996 this.setValue(this.time.format(this.format));
17998 this.fireEvent('select', this, this.date);
18003 onMousedown: function(e){
18004 e.stopPropagation();
18005 e.preventDefault();
18008 onIncrementHours: function()
18010 Roo.log('onIncrementHours');
18011 this.time = this.time.add(Date.HOUR, 1);
18016 onDecrementHours: function()
18018 Roo.log('onDecrementHours');
18019 this.time = this.time.add(Date.HOUR, -1);
18023 onIncrementMinutes: function()
18025 Roo.log('onIncrementMinutes');
18026 this.time = this.time.add(Date.MINUTE, 1);
18030 onDecrementMinutes: function()
18032 Roo.log('onDecrementMinutes');
18033 this.time = this.time.add(Date.MINUTE, -1);
18037 onTogglePeriod: function()
18039 Roo.log('onTogglePeriod');
18040 this.time = this.time.add(Date.HOUR, 12);
18047 Roo.apply(Roo.bootstrap.TimeField, {
18077 cls: 'btn btn-info ok',
18089 Roo.apply(Roo.bootstrap.TimeField, {
18093 cls: 'datepicker dropdown-menu',
18097 cls: 'datepicker-time',
18101 cls: 'table-condensed',
18103 Roo.bootstrap.TimeField.content,
18104 Roo.bootstrap.TimeField.footer
18123 * @class Roo.bootstrap.MonthField
18124 * @extends Roo.bootstrap.Input
18125 * Bootstrap MonthField class
18127 * @cfg {String} language default en
18130 * Create a new MonthField
18131 * @param {Object} config The config object
18134 Roo.bootstrap.MonthField = function(config){
18135 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18140 * Fires when this field show.
18141 * @param {Roo.bootstrap.MonthField} this
18142 * @param {Mixed} date The date value
18147 * Fires when this field hide.
18148 * @param {Roo.bootstrap.MonthField} this
18149 * @param {Mixed} date The date value
18154 * Fires when select a date.
18155 * @param {Roo.bootstrap.MonthField} this
18156 * @param {String} oldvalue The old value
18157 * @param {String} newvalue The new value
18163 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
18165 onRender: function(ct, position)
18168 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18170 this.language = this.language || 'en';
18171 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18172 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18174 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18175 this.isInline = false;
18176 this.isInput = true;
18177 this.component = this.el.select('.add-on', true).first() || false;
18178 this.component = (this.component && this.component.length === 0) ? false : this.component;
18179 this.hasInput = this.component && this.inputEL().length;
18181 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18183 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18185 this.picker().on('mousedown', this.onMousedown, this);
18186 this.picker().on('click', this.onClick, this);
18188 this.picker().addClass('datepicker-dropdown');
18190 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18191 v.setStyle('width', '189px');
18198 if(this.isInline) {
18204 setValue: function(v, suppressEvent)
18206 var o = this.getValue();
18208 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18212 if(suppressEvent !== true){
18213 this.fireEvent('select', this, o, v);
18218 getValue: function()
18223 onClick: function(e)
18225 e.stopPropagation();
18226 e.preventDefault();
18228 var target = e.getTarget();
18230 if(target.nodeName.toLowerCase() === 'i'){
18231 target = Roo.get(target).dom.parentNode;
18234 var nodeName = target.nodeName;
18235 var className = target.className;
18236 var html = target.innerHTML;
18238 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18242 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18244 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18250 picker : function()
18252 return this.pickerEl;
18255 fillMonths: function()
18258 var months = this.picker().select('>.datepicker-months td', true).first();
18260 months.dom.innerHTML = '';
18266 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18269 months.createChild(month);
18278 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18279 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18282 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18283 e.removeClass('active');
18285 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18286 e.addClass('active');
18293 if(this.isInline) {
18297 this.picker().removeClass(['bottom', 'top']);
18299 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18301 * place to the top of element!
18305 this.picker().addClass('top');
18306 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18311 this.picker().addClass('bottom');
18313 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18316 onFocus : function()
18318 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18322 onBlur : function()
18324 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18326 var d = this.inputEl().getValue();
18335 this.picker().show();
18336 this.picker().select('>.datepicker-months', true).first().show();
18340 this.fireEvent('show', this, this.date);
18345 if(this.isInline) {
18348 this.picker().hide();
18349 this.fireEvent('hide', this, this.date);
18353 onMousedown: function(e)
18355 e.stopPropagation();
18356 e.preventDefault();
18361 Roo.bootstrap.MonthField.superclass.keyup.call(this);
18365 fireKey: function(e)
18367 if (!this.picker().isVisible()){
18368 if (e.keyCode == 27) {// allow escape to hide and re-show picker
18379 e.preventDefault();
18383 dir = e.keyCode == 37 ? -1 : 1;
18385 this.vIndex = this.vIndex + dir;
18387 if(this.vIndex < 0){
18391 if(this.vIndex > 11){
18395 if(isNaN(this.vIndex)){
18399 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18405 dir = e.keyCode == 38 ? -1 : 1;
18407 this.vIndex = this.vIndex + dir * 4;
18409 if(this.vIndex < 0){
18413 if(this.vIndex > 11){
18417 if(isNaN(this.vIndex)){
18421 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18426 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18427 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18431 e.preventDefault();
18434 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18435 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18451 this.picker().remove();
18456 Roo.apply(Roo.bootstrap.MonthField, {
18475 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18476 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18481 Roo.apply(Roo.bootstrap.MonthField, {
18485 cls: 'datepicker dropdown-menu roo-dynamic',
18489 cls: 'datepicker-months',
18493 cls: 'table-condensed',
18495 Roo.bootstrap.DateField.content
18515 * @class Roo.bootstrap.CheckBox
18516 * @extends Roo.bootstrap.Input
18517 * Bootstrap CheckBox class
18519 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18520 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18521 * @cfg {String} boxLabel The text that appears beside the checkbox
18522 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18523 * @cfg {Boolean} checked initnal the element
18524 * @cfg {Boolean} inline inline the element (default false)
18525 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18528 * Create a new CheckBox
18529 * @param {Object} config The config object
18532 Roo.bootstrap.CheckBox = function(config){
18533 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18538 * Fires when the element is checked or unchecked.
18539 * @param {Roo.bootstrap.CheckBox} this This input
18540 * @param {Boolean} checked The new checked value
18547 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18549 inputType: 'checkbox',
18557 getAutoCreate : function()
18559 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18565 cfg.cls = 'form-group ' + this.inputType; //input-group
18568 cfg.cls += ' ' + this.inputType + '-inline';
18574 type : this.inputType,
18575 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18576 cls : 'roo-' + this.inputType, //'form-box',
18577 placeholder : this.placeholder || ''
18581 if (this.weight) { // Validity check?
18582 cfg.cls += " " + this.inputType + "-" + this.weight;
18585 if (this.disabled) {
18586 input.disabled=true;
18590 input.checked = this.checked;
18594 input.name = this.name;
18598 input.cls += ' input-' + this.size;
18603 ['xs','sm','md','lg'].map(function(size){
18604 if (settings[size]) {
18605 cfg.cls += ' col-' + size + '-' + settings[size];
18609 var inputblock = input;
18611 if (this.before || this.after) {
18614 cls : 'input-group',
18619 inputblock.cn.push({
18621 cls : 'input-group-addon',
18626 inputblock.cn.push(input);
18629 inputblock.cn.push({
18631 cls : 'input-group-addon',
18638 if (align ==='left' && this.fieldLabel.length) {
18639 // Roo.log("left and has label");
18645 cls : 'control-label col-md-' + this.labelWidth,
18646 html : this.fieldLabel
18650 cls : "col-md-" + (12 - this.labelWidth),
18657 } else if ( this.fieldLabel.length) {
18658 // Roo.log(" label");
18662 tag: this.boxLabel ? 'span' : 'label',
18664 cls: 'control-label box-input-label',
18665 //cls : 'input-group-addon',
18666 html : this.fieldLabel
18676 // Roo.log(" no label && no align");
18677 cfg.cn = [ inputblock ] ;
18682 var boxLabelCfg = {
18684 //'for': id, // box label is handled by onclick - so no for...
18686 html: this.boxLabel
18690 boxLabelCfg.tooltip = this.tooltip;
18693 cfg.cn.push(boxLabelCfg);
18703 * return the real input element.
18705 inputEl: function ()
18707 return this.el.select('input.roo-' + this.inputType,true).first();
18710 labelEl: function()
18712 return this.el.select('label.control-label',true).first();
18714 /* depricated... */
18718 return this.labelEl();
18721 initEvents : function()
18723 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18725 this.inputEl().on('click', this.onClick, this);
18727 if (this.boxLabel) {
18728 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18731 this.startValue = this.getValue();
18734 Roo.bootstrap.CheckBox.register(this);
18738 onClick : function()
18740 this.setChecked(!this.checked);
18743 setChecked : function(state,suppressEvent)
18745 this.startValue = this.getValue();
18747 if(this.inputType == 'radio'){
18749 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18750 e.dom.checked = false;
18753 this.inputEl().dom.checked = true;
18755 this.inputEl().dom.value = this.inputValue;
18757 if(suppressEvent !== true){
18758 this.fireEvent('check', this, true);
18766 this.checked = state;
18768 this.inputEl().dom.checked = state;
18770 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18772 if(suppressEvent !== true){
18773 this.fireEvent('check', this, state);
18779 getValue : function()
18781 if(this.inputType == 'radio'){
18782 return this.getGroupValue();
18785 return this.inputEl().getValue();
18789 getGroupValue : function()
18791 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18795 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18798 setValue : function(v,suppressEvent)
18800 if(this.inputType == 'radio'){
18801 this.setGroupValue(v, suppressEvent);
18805 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18810 setGroupValue : function(v, suppressEvent)
18812 this.startValue = this.getValue();
18814 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18815 e.dom.checked = false;
18817 if(e.dom.value == v){
18818 e.dom.checked = true;
18822 if(suppressEvent !== true){
18823 this.fireEvent('check', this, true);
18831 validate : function()
18835 (this.inputType == 'radio' && this.validateRadio()) ||
18836 (this.inputType == 'checkbox' && this.validateCheckbox())
18842 this.markInvalid();
18846 validateRadio : function()
18850 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18851 if(!e.dom.checked){
18863 validateCheckbox : function()
18866 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18869 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18877 for(var i in group){
18882 r = (group[i].getValue() == group[i].inputValue) ? true : false;
18889 * Mark this field as valid
18891 markValid : function()
18893 if(this.allowBlank){
18899 this.fireEvent('valid', this);
18901 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18904 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18911 if(this.inputType == 'radio'){
18912 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18913 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18914 e.findParent('.form-group', false, true).addClass(_this.validClass);
18921 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18922 this.el.findParent('.form-group', false, true).addClass(this.validClass);
18926 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18932 for(var i in group){
18933 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18934 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18939 * Mark this field as invalid
18940 * @param {String} msg The validation message
18942 markInvalid : function(msg)
18944 if(this.allowBlank){
18950 this.fireEvent('invalid', this, msg);
18952 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18955 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18959 label.markInvalid();
18962 if(this.inputType == 'radio'){
18963 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18964 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18965 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18972 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18973 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18977 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18983 for(var i in group){
18984 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18985 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18992 Roo.apply(Roo.bootstrap.CheckBox, {
18997 * register a CheckBox Group
18998 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19000 register : function(checkbox)
19002 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19003 this.groups[checkbox.groupId] = {};
19006 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19010 this.groups[checkbox.groupId][checkbox.name] = checkbox;
19014 * fetch a CheckBox Group based on the group ID
19015 * @param {string} the group ID
19016 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19018 get: function(groupId) {
19019 if (typeof(this.groups[groupId]) == 'undefined') {
19023 return this.groups[groupId] ;
19035 *<div class="radio">
19037 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19038 Option one is this and that—be sure to include why it's great
19045 *<label class="radio-inline">fieldLabel</label>
19046 *<label class="radio-inline">
19047 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19055 * @class Roo.bootstrap.Radio
19056 * @extends Roo.bootstrap.CheckBox
19057 * Bootstrap Radio class
19060 * Create a new Radio
19061 * @param {Object} config The config object
19064 Roo.bootstrap.Radio = function(config){
19065 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19069 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
19071 inputType: 'radio',
19075 getAutoCreate : function()
19077 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19078 align = align || 'left'; // default...
19085 tag : this.inline ? 'span' : 'div',
19090 var inline = this.inline ? ' radio-inline' : '';
19094 // does not need for, as we wrap the input with it..
19096 cls : 'control-label box-label' + inline,
19099 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19103 //cls : 'control-label' + inline,
19104 html : this.fieldLabel,
19105 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19114 type : this.inputType,
19115 //value : (!this.checked) ? this.valueOff : this.inputValue,
19116 value : this.inputValue,
19118 placeholder : this.placeholder || '' // ?? needed????
19121 if (this.weight) { // Validity check?
19122 input.cls += " radio-" + this.weight;
19124 if (this.disabled) {
19125 input.disabled=true;
19129 input.checked = this.checked;
19133 input.name = this.name;
19137 input.cls += ' input-' + this.size;
19140 //?? can span's inline have a width??
19143 ['xs','sm','md','lg'].map(function(size){
19144 if (settings[size]) {
19145 cfg.cls += ' col-' + size + '-' + settings[size];
19149 var inputblock = input;
19151 if (this.before || this.after) {
19154 cls : 'input-group',
19159 inputblock.cn.push({
19161 cls : 'input-group-addon',
19165 inputblock.cn.push(input);
19167 inputblock.cn.push({
19169 cls : 'input-group-addon',
19177 if (this.fieldLabel && this.fieldLabel.length) {
19178 cfg.cn.push(fieldLabel);
19181 // normal bootstrap puts the input inside the label.
19182 // however with our styled version - it has to go after the input.
19184 //lbl.cn.push(inputblock);
19188 cls: 'radio' + inline,
19195 cfg.cn.push( lblwrap);
19200 html: this.boxLabel
19209 initEvents : function()
19211 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19213 this.inputEl().on('click', this.onClick, this);
19214 if (this.boxLabel) {
19215 //Roo.log('find label');
19216 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
19221 inputEl: function ()
19223 return this.el.select('input.roo-radio',true).first();
19225 onClick : function()
19228 this.setChecked(true);
19231 setChecked : function(state,suppressEvent)
19234 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19235 v.dom.checked = false;
19238 Roo.log(this.inputEl().dom);
19239 this.checked = state;
19240 this.inputEl().dom.checked = state;
19242 if(suppressEvent !== true){
19243 this.fireEvent('check', this, state);
19246 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19250 getGroupValue : function()
19253 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19254 if(v.dom.checked == true){
19255 value = v.dom.value;
19263 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
19264 * @return {Mixed} value The field value
19266 getValue : function(){
19267 return this.getGroupValue();
19273 //<script type="text/javascript">
19276 * Based Ext JS Library 1.1.1
19277 * Copyright(c) 2006-2007, Ext JS, LLC.
19283 * @class Roo.HtmlEditorCore
19284 * @extends Roo.Component
19285 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19287 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19290 Roo.HtmlEditorCore = function(config){
19293 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19298 * @event initialize
19299 * Fires when the editor is fully initialized (including the iframe)
19300 * @param {Roo.HtmlEditorCore} this
19305 * Fires when the editor is first receives the focus. Any insertion must wait
19306 * until after this event.
19307 * @param {Roo.HtmlEditorCore} this
19311 * @event beforesync
19312 * Fires before the textarea is updated with content from the editor iframe. Return false
19313 * to cancel the sync.
19314 * @param {Roo.HtmlEditorCore} this
19315 * @param {String} html
19319 * @event beforepush
19320 * Fires before the iframe editor is updated with content from the textarea. Return false
19321 * to cancel the push.
19322 * @param {Roo.HtmlEditorCore} this
19323 * @param {String} html
19328 * Fires when the textarea is updated with content from the editor iframe.
19329 * @param {Roo.HtmlEditorCore} this
19330 * @param {String} html
19335 * Fires when the iframe editor is updated with content from the textarea.
19336 * @param {Roo.HtmlEditorCore} this
19337 * @param {String} html
19342 * @event editorevent
19343 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19344 * @param {Roo.HtmlEditorCore} this
19350 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19352 // defaults : white / black...
19353 this.applyBlacklists();
19360 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
19364 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
19370 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
19375 * @cfg {Number} height (in pixels)
19379 * @cfg {Number} width (in pixels)
19384 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19387 stylesheets: false,
19392 // private properties
19393 validationEvent : false,
19395 initialized : false,
19397 sourceEditMode : false,
19398 onFocus : Roo.emptyFn,
19400 hideMode:'offsets',
19404 // blacklist + whitelisted elements..
19411 * Protected method that will not generally be called directly. It
19412 * is called when the editor initializes the iframe with HTML contents. Override this method if you
19413 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19415 getDocMarkup : function(){
19419 // inherit styels from page...??
19420 if (this.stylesheets === false) {
19422 Roo.get(document.head).select('style').each(function(node) {
19423 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19426 Roo.get(document.head).select('link').each(function(node) {
19427 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19430 } else if (!this.stylesheets.length) {
19432 st = '<style type="text/css">' +
19433 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19439 st += '<style type="text/css">' +
19440 'IMG { cursor: pointer } ' +
19444 return '<html><head>' + st +
19445 //<style type="text/css">' +
19446 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19448 ' </head><body class="roo-htmleditor-body"></body></html>';
19452 onRender : function(ct, position)
19455 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19456 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19459 this.el.dom.style.border = '0 none';
19460 this.el.dom.setAttribute('tabIndex', -1);
19461 this.el.addClass('x-hidden hide');
19465 if(Roo.isIE){ // fix IE 1px bogus margin
19466 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19470 this.frameId = Roo.id();
19474 var iframe = this.owner.wrap.createChild({
19476 cls: 'form-control', // bootstrap..
19478 name: this.frameId,
19479 frameBorder : 'no',
19480 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19485 this.iframe = iframe.dom;
19487 this.assignDocWin();
19489 this.doc.designMode = 'on';
19492 this.doc.write(this.getDocMarkup());
19496 var task = { // must defer to wait for browser to be ready
19498 //console.log("run task?" + this.doc.readyState);
19499 this.assignDocWin();
19500 if(this.doc.body || this.doc.readyState == 'complete'){
19502 this.doc.designMode="on";
19506 Roo.TaskMgr.stop(task);
19507 this.initEditor.defer(10, this);
19514 Roo.TaskMgr.start(task);
19519 onResize : function(w, h)
19521 Roo.log('resize: ' +w + ',' + h );
19522 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19526 if(typeof w == 'number'){
19528 this.iframe.style.width = w + 'px';
19530 if(typeof h == 'number'){
19532 this.iframe.style.height = h + 'px';
19534 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19541 * Toggles the editor between standard and source edit mode.
19542 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19544 toggleSourceEdit : function(sourceEditMode){
19546 this.sourceEditMode = sourceEditMode === true;
19548 if(this.sourceEditMode){
19550 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19553 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19554 //this.iframe.className = '';
19557 //this.setSize(this.owner.wrap.getSize());
19558 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19565 * Protected method that will not generally be called directly. If you need/want
19566 * custom HTML cleanup, this is the method you should override.
19567 * @param {String} html The HTML to be cleaned
19568 * return {String} The cleaned HTML
19570 cleanHtml : function(html){
19571 html = String(html);
19572 if(html.length > 5){
19573 if(Roo.isSafari){ // strip safari nonsense
19574 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19577 if(html == ' '){
19584 * HTML Editor -> Textarea
19585 * Protected method that will not generally be called directly. Syncs the contents
19586 * of the editor iframe with the textarea.
19588 syncValue : function(){
19589 if(this.initialized){
19590 var bd = (this.doc.body || this.doc.documentElement);
19591 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19592 var html = bd.innerHTML;
19594 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19595 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19597 html = '<div style="'+m[0]+'">' + html + '</div>';
19600 html = this.cleanHtml(html);
19601 // fix up the special chars.. normaly like back quotes in word...
19602 // however we do not want to do this with chinese..
19603 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19604 var cc = b.charCodeAt();
19606 (cc >= 0x4E00 && cc < 0xA000 ) ||
19607 (cc >= 0x3400 && cc < 0x4E00 ) ||
19608 (cc >= 0xf900 && cc < 0xfb00 )
19614 if(this.owner.fireEvent('beforesync', this, html) !== false){
19615 this.el.dom.value = html;
19616 this.owner.fireEvent('sync', this, html);
19622 * Protected method that will not generally be called directly. Pushes the value of the textarea
19623 * into the iframe editor.
19625 pushValue : function(){
19626 if(this.initialized){
19627 var v = this.el.dom.value.trim();
19629 // if(v.length < 1){
19633 if(this.owner.fireEvent('beforepush', this, v) !== false){
19634 var d = (this.doc.body || this.doc.documentElement);
19636 this.cleanUpPaste();
19637 this.el.dom.value = d.innerHTML;
19638 this.owner.fireEvent('push', this, v);
19644 deferFocus : function(){
19645 this.focus.defer(10, this);
19649 focus : function(){
19650 if(this.win && !this.sourceEditMode){
19657 assignDocWin: function()
19659 var iframe = this.iframe;
19662 this.doc = iframe.contentWindow.document;
19663 this.win = iframe.contentWindow;
19665 // if (!Roo.get(this.frameId)) {
19668 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19669 // this.win = Roo.get(this.frameId).dom.contentWindow;
19671 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19675 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19676 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19681 initEditor : function(){
19682 //console.log("INIT EDITOR");
19683 this.assignDocWin();
19687 this.doc.designMode="on";
19689 this.doc.write(this.getDocMarkup());
19692 var dbody = (this.doc.body || this.doc.documentElement);
19693 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19694 // this copies styles from the containing element into thsi one..
19695 // not sure why we need all of this..
19696 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19698 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19699 //ss['background-attachment'] = 'fixed'; // w3c
19700 dbody.bgProperties = 'fixed'; // ie
19701 //Roo.DomHelper.applyStyles(dbody, ss);
19702 Roo.EventManager.on(this.doc, {
19703 //'mousedown': this.onEditorEvent,
19704 'mouseup': this.onEditorEvent,
19705 'dblclick': this.onEditorEvent,
19706 'click': this.onEditorEvent,
19707 'keyup': this.onEditorEvent,
19712 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19714 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19715 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19717 this.initialized = true;
19719 this.owner.fireEvent('initialize', this);
19724 onDestroy : function(){
19730 //for (var i =0; i < this.toolbars.length;i++) {
19731 // // fixme - ask toolbars for heights?
19732 // this.toolbars[i].onDestroy();
19735 //this.wrap.dom.innerHTML = '';
19736 //this.wrap.remove();
19741 onFirstFocus : function(){
19743 this.assignDocWin();
19746 this.activated = true;
19749 if(Roo.isGecko){ // prevent silly gecko errors
19751 var s = this.win.getSelection();
19752 if(!s.focusNode || s.focusNode.nodeType != 3){
19753 var r = s.getRangeAt(0);
19754 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19759 this.execCmd('useCSS', true);
19760 this.execCmd('styleWithCSS', false);
19763 this.owner.fireEvent('activate', this);
19767 adjustFont: function(btn){
19768 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19769 //if(Roo.isSafari){ // safari
19772 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19773 if(Roo.isSafari){ // safari
19774 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19775 v = (v < 10) ? 10 : v;
19776 v = (v > 48) ? 48 : v;
19777 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19782 v = Math.max(1, v+adjust);
19784 this.execCmd('FontSize', v );
19787 onEditorEvent : function(e)
19789 this.owner.fireEvent('editorevent', this, e);
19790 // this.updateToolbar();
19791 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19794 insertTag : function(tg)
19796 // could be a bit smarter... -> wrap the current selected tRoo..
19797 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19799 range = this.createRange(this.getSelection());
19800 var wrappingNode = this.doc.createElement(tg.toLowerCase());
19801 wrappingNode.appendChild(range.extractContents());
19802 range.insertNode(wrappingNode);
19809 this.execCmd("formatblock", tg);
19813 insertText : function(txt)
19817 var range = this.createRange();
19818 range.deleteContents();
19819 //alert(Sender.getAttribute('label'));
19821 range.insertNode(this.doc.createTextNode(txt));
19827 * Executes a Midas editor command on the editor document and performs necessary focus and
19828 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19829 * @param {String} cmd The Midas command
19830 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19832 relayCmd : function(cmd, value){
19834 this.execCmd(cmd, value);
19835 this.owner.fireEvent('editorevent', this);
19836 //this.updateToolbar();
19837 this.owner.deferFocus();
19841 * Executes a Midas editor command directly on the editor document.
19842 * For visual commands, you should use {@link #relayCmd} instead.
19843 * <b>This should only be called after the editor is initialized.</b>
19844 * @param {String} cmd The Midas command
19845 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19847 execCmd : function(cmd, value){
19848 this.doc.execCommand(cmd, false, value === undefined ? null : value);
19855 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19857 * @param {String} text | dom node..
19859 insertAtCursor : function(text)
19864 if(!this.activated){
19870 var r = this.doc.selection.createRange();
19881 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19885 // from jquery ui (MIT licenced)
19887 var win = this.win;
19889 if (win.getSelection && win.getSelection().getRangeAt) {
19890 range = win.getSelection().getRangeAt(0);
19891 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19892 range.insertNode(node);
19893 } else if (win.document.selection && win.document.selection.createRange) {
19894 // no firefox support
19895 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19896 win.document.selection.createRange().pasteHTML(txt);
19898 // no firefox support
19899 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19900 this.execCmd('InsertHTML', txt);
19909 mozKeyPress : function(e){
19911 var c = e.getCharCode(), cmd;
19914 c = String.fromCharCode(c).toLowerCase();
19928 this.cleanUpPaste.defer(100, this);
19936 e.preventDefault();
19944 fixKeys : function(){ // load time branching for fastest keydown performance
19946 return function(e){
19947 var k = e.getKey(), r;
19950 r = this.doc.selection.createRange();
19953 r.pasteHTML('    ');
19960 r = this.doc.selection.createRange();
19962 var target = r.parentElement();
19963 if(!target || target.tagName.toLowerCase() != 'li'){
19965 r.pasteHTML('<br />');
19971 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19972 this.cleanUpPaste.defer(100, this);
19978 }else if(Roo.isOpera){
19979 return function(e){
19980 var k = e.getKey();
19984 this.execCmd('InsertHTML','    ');
19987 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19988 this.cleanUpPaste.defer(100, this);
19993 }else if(Roo.isSafari){
19994 return function(e){
19995 var k = e.getKey();
19999 this.execCmd('InsertText','\t');
20003 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20004 this.cleanUpPaste.defer(100, this);
20012 getAllAncestors: function()
20014 var p = this.getSelectedNode();
20017 a.push(p); // push blank onto stack..
20018 p = this.getParentElement();
20022 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20026 a.push(this.doc.body);
20030 lastSelNode : false,
20033 getSelection : function()
20035 this.assignDocWin();
20036 return Roo.isIE ? this.doc.selection : this.win.getSelection();
20039 getSelectedNode: function()
20041 // this may only work on Gecko!!!
20043 // should we cache this!!!!
20048 var range = this.createRange(this.getSelection()).cloneRange();
20051 var parent = range.parentElement();
20053 var testRange = range.duplicate();
20054 testRange.moveToElementText(parent);
20055 if (testRange.inRange(range)) {
20058 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20061 parent = parent.parentElement;
20066 // is ancestor a text element.
20067 var ac = range.commonAncestorContainer;
20068 if (ac.nodeType == 3) {
20069 ac = ac.parentNode;
20072 var ar = ac.childNodes;
20075 var other_nodes = [];
20076 var has_other_nodes = false;
20077 for (var i=0;i<ar.length;i++) {
20078 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
20081 // fullly contained node.
20083 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20088 // probably selected..
20089 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20090 other_nodes.push(ar[i]);
20094 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
20099 has_other_nodes = true;
20101 if (!nodes.length && other_nodes.length) {
20102 nodes= other_nodes;
20104 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20110 createRange: function(sel)
20112 // this has strange effects when using with
20113 // top toolbar - not sure if it's a great idea.
20114 //this.editor.contentWindow.focus();
20115 if (typeof sel != "undefined") {
20117 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20119 return this.doc.createRange();
20122 return this.doc.createRange();
20125 getParentElement: function()
20128 this.assignDocWin();
20129 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20131 var range = this.createRange(sel);
20134 var p = range.commonAncestorContainer;
20135 while (p.nodeType == 3) { // text node
20146 * Range intersection.. the hard stuff...
20150 * [ -- selected range --- ]
20154 * if end is before start or hits it. fail.
20155 * if start is after end or hits it fail.
20157 * if either hits (but other is outside. - then it's not
20163 // @see http://www.thismuchiknow.co.uk/?p=64.
20164 rangeIntersectsNode : function(range, node)
20166 var nodeRange = node.ownerDocument.createRange();
20168 nodeRange.selectNode(node);
20170 nodeRange.selectNodeContents(node);
20173 var rangeStartRange = range.cloneRange();
20174 rangeStartRange.collapse(true);
20176 var rangeEndRange = range.cloneRange();
20177 rangeEndRange.collapse(false);
20179 var nodeStartRange = nodeRange.cloneRange();
20180 nodeStartRange.collapse(true);
20182 var nodeEndRange = nodeRange.cloneRange();
20183 nodeEndRange.collapse(false);
20185 return rangeStartRange.compareBoundaryPoints(
20186 Range.START_TO_START, nodeEndRange) == -1 &&
20187 rangeEndRange.compareBoundaryPoints(
20188 Range.START_TO_START, nodeStartRange) == 1;
20192 rangeCompareNode : function(range, node)
20194 var nodeRange = node.ownerDocument.createRange();
20196 nodeRange.selectNode(node);
20198 nodeRange.selectNodeContents(node);
20202 range.collapse(true);
20204 nodeRange.collapse(true);
20206 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20207 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
20209 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20211 var nodeIsBefore = ss == 1;
20212 var nodeIsAfter = ee == -1;
20214 if (nodeIsBefore && nodeIsAfter) {
20217 if (!nodeIsBefore && nodeIsAfter) {
20218 return 1; //right trailed.
20221 if (nodeIsBefore && !nodeIsAfter) {
20222 return 2; // left trailed.
20228 // private? - in a new class?
20229 cleanUpPaste : function()
20231 // cleans up the whole document..
20232 Roo.log('cleanuppaste');
20234 this.cleanUpChildren(this.doc.body);
20235 var clean = this.cleanWordChars(this.doc.body.innerHTML);
20236 if (clean != this.doc.body.innerHTML) {
20237 this.doc.body.innerHTML = clean;
20242 cleanWordChars : function(input) {// change the chars to hex code
20243 var he = Roo.HtmlEditorCore;
20245 var output = input;
20246 Roo.each(he.swapCodes, function(sw) {
20247 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20249 output = output.replace(swapper, sw[1]);
20256 cleanUpChildren : function (n)
20258 if (!n.childNodes.length) {
20261 for (var i = n.childNodes.length-1; i > -1 ; i--) {
20262 this.cleanUpChild(n.childNodes[i]);
20269 cleanUpChild : function (node)
20272 //console.log(node);
20273 if (node.nodeName == "#text") {
20274 // clean up silly Windows -- stuff?
20277 if (node.nodeName == "#comment") {
20278 node.parentNode.removeChild(node);
20279 // clean up silly Windows -- stuff?
20282 var lcname = node.tagName.toLowerCase();
20283 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20284 // whitelist of tags..
20286 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20288 node.parentNode.removeChild(node);
20293 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20295 // remove <a name=....> as rendering on yahoo mailer is borked with this.
20296 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20298 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20299 // remove_keep_children = true;
20302 if (remove_keep_children) {
20303 this.cleanUpChildren(node);
20304 // inserts everything just before this node...
20305 while (node.childNodes.length) {
20306 var cn = node.childNodes[0];
20307 node.removeChild(cn);
20308 node.parentNode.insertBefore(cn, node);
20310 node.parentNode.removeChild(node);
20314 if (!node.attributes || !node.attributes.length) {
20315 this.cleanUpChildren(node);
20319 function cleanAttr(n,v)
20322 if (v.match(/^\./) || v.match(/^\//)) {
20325 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20328 if (v.match(/^#/)) {
20331 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20332 node.removeAttribute(n);
20336 var cwhite = this.cwhite;
20337 var cblack = this.cblack;
20339 function cleanStyle(n,v)
20341 if (v.match(/expression/)) { //XSS?? should we even bother..
20342 node.removeAttribute(n);
20346 var parts = v.split(/;/);
20349 Roo.each(parts, function(p) {
20350 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20354 var l = p.split(':').shift().replace(/\s+/g,'');
20355 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20357 if ( cwhite.length && cblack.indexOf(l) > -1) {
20358 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20359 //node.removeAttribute(n);
20363 // only allow 'c whitelisted system attributes'
20364 if ( cwhite.length && cwhite.indexOf(l) < 0) {
20365 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20366 //node.removeAttribute(n);
20376 if (clean.length) {
20377 node.setAttribute(n, clean.join(';'));
20379 node.removeAttribute(n);
20385 for (var i = node.attributes.length-1; i > -1 ; i--) {
20386 var a = node.attributes[i];
20389 if (a.name.toLowerCase().substr(0,2)=='on') {
20390 node.removeAttribute(a.name);
20393 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20394 node.removeAttribute(a.name);
20397 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20398 cleanAttr(a.name,a.value); // fixme..
20401 if (a.name == 'style') {
20402 cleanStyle(a.name,a.value);
20405 /// clean up MS crap..
20406 // tecnically this should be a list of valid class'es..
20409 if (a.name == 'class') {
20410 if (a.value.match(/^Mso/)) {
20411 node.className = '';
20414 if (a.value.match(/body/)) {
20415 node.className = '';
20426 this.cleanUpChildren(node);
20432 * Clean up MS wordisms...
20434 cleanWord : function(node)
20439 this.cleanWord(this.doc.body);
20442 if (node.nodeName == "#text") {
20443 // clean up silly Windows -- stuff?
20446 if (node.nodeName == "#comment") {
20447 node.parentNode.removeChild(node);
20448 // clean up silly Windows -- stuff?
20452 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20453 node.parentNode.removeChild(node);
20457 // remove - but keep children..
20458 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20459 while (node.childNodes.length) {
20460 var cn = node.childNodes[0];
20461 node.removeChild(cn);
20462 node.parentNode.insertBefore(cn, node);
20464 node.parentNode.removeChild(node);
20465 this.iterateChildren(node, this.cleanWord);
20469 if (node.className.length) {
20471 var cn = node.className.split(/\W+/);
20473 Roo.each(cn, function(cls) {
20474 if (cls.match(/Mso[a-zA-Z]+/)) {
20479 node.className = cna.length ? cna.join(' ') : '';
20481 node.removeAttribute("class");
20485 if (node.hasAttribute("lang")) {
20486 node.removeAttribute("lang");
20489 if (node.hasAttribute("style")) {
20491 var styles = node.getAttribute("style").split(";");
20493 Roo.each(styles, function(s) {
20494 if (!s.match(/:/)) {
20497 var kv = s.split(":");
20498 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20501 // what ever is left... we allow.
20504 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20505 if (!nstyle.length) {
20506 node.removeAttribute('style');
20509 this.iterateChildren(node, this.cleanWord);
20515 * iterateChildren of a Node, calling fn each time, using this as the scole..
20516 * @param {DomNode} node node to iterate children of.
20517 * @param {Function} fn method of this class to call on each item.
20519 iterateChildren : function(node, fn)
20521 if (!node.childNodes.length) {
20524 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20525 fn.call(this, node.childNodes[i])
20531 * cleanTableWidths.
20533 * Quite often pasting from word etc.. results in tables with column and widths.
20534 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20537 cleanTableWidths : function(node)
20542 this.cleanTableWidths(this.doc.body);
20547 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20550 Roo.log(node.tagName);
20551 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20552 this.iterateChildren(node, this.cleanTableWidths);
20555 if (node.hasAttribute('width')) {
20556 node.removeAttribute('width');
20560 if (node.hasAttribute("style")) {
20563 var styles = node.getAttribute("style").split(";");
20565 Roo.each(styles, function(s) {
20566 if (!s.match(/:/)) {
20569 var kv = s.split(":");
20570 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20573 // what ever is left... we allow.
20576 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20577 if (!nstyle.length) {
20578 node.removeAttribute('style');
20582 this.iterateChildren(node, this.cleanTableWidths);
20590 domToHTML : function(currentElement, depth, nopadtext) {
20592 depth = depth || 0;
20593 nopadtext = nopadtext || false;
20595 if (!currentElement) {
20596 return this.domToHTML(this.doc.body);
20599 //Roo.log(currentElement);
20601 var allText = false;
20602 var nodeName = currentElement.nodeName;
20603 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20605 if (nodeName == '#text') {
20607 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20612 if (nodeName != 'BODY') {
20615 // Prints the node tagName, such as <A>, <IMG>, etc
20618 for(i = 0; i < currentElement.attributes.length;i++) {
20620 var aname = currentElement.attributes.item(i).name;
20621 if (!currentElement.attributes.item(i).value.length) {
20624 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20627 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20636 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20639 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20644 // Traverse the tree
20646 var currentElementChild = currentElement.childNodes.item(i);
20647 var allText = true;
20648 var innerHTML = '';
20650 while (currentElementChild) {
20651 // Formatting code (indent the tree so it looks nice on the screen)
20652 var nopad = nopadtext;
20653 if (lastnode == 'SPAN') {
20657 if (currentElementChild.nodeName == '#text') {
20658 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20659 toadd = nopadtext ? toadd : toadd.trim();
20660 if (!nopad && toadd.length > 80) {
20661 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20663 innerHTML += toadd;
20666 currentElementChild = currentElement.childNodes.item(i);
20672 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20674 // Recursively traverse the tree structure of the child node
20675 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20676 lastnode = currentElementChild.nodeName;
20678 currentElementChild=currentElement.childNodes.item(i);
20684 // The remaining code is mostly for formatting the tree
20685 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20690 ret+= "</"+tagName+">";
20696 applyBlacklists : function()
20698 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20699 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20703 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20704 if (b.indexOf(tag) > -1) {
20707 this.white.push(tag);
20711 Roo.each(w, function(tag) {
20712 if (b.indexOf(tag) > -1) {
20715 if (this.white.indexOf(tag) > -1) {
20718 this.white.push(tag);
20723 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20724 if (w.indexOf(tag) > -1) {
20727 this.black.push(tag);
20731 Roo.each(b, function(tag) {
20732 if (w.indexOf(tag) > -1) {
20735 if (this.black.indexOf(tag) > -1) {
20738 this.black.push(tag);
20743 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20744 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20748 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20749 if (b.indexOf(tag) > -1) {
20752 this.cwhite.push(tag);
20756 Roo.each(w, function(tag) {
20757 if (b.indexOf(tag) > -1) {
20760 if (this.cwhite.indexOf(tag) > -1) {
20763 this.cwhite.push(tag);
20768 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20769 if (w.indexOf(tag) > -1) {
20772 this.cblack.push(tag);
20776 Roo.each(b, function(tag) {
20777 if (w.indexOf(tag) > -1) {
20780 if (this.cblack.indexOf(tag) > -1) {
20783 this.cblack.push(tag);
20788 setStylesheets : function(stylesheets)
20790 if(typeof(stylesheets) == 'string'){
20791 Roo.get(this.iframe.contentDocument.head).createChild({
20793 rel : 'stylesheet',
20802 Roo.each(stylesheets, function(s) {
20807 Roo.get(_this.iframe.contentDocument.head).createChild({
20809 rel : 'stylesheet',
20818 removeStylesheets : function()
20822 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20827 // hide stuff that is not compatible
20841 * @event specialkey
20845 * @cfg {String} fieldClass @hide
20848 * @cfg {String} focusClass @hide
20851 * @cfg {String} autoCreate @hide
20854 * @cfg {String} inputType @hide
20857 * @cfg {String} invalidClass @hide
20860 * @cfg {String} invalidText @hide
20863 * @cfg {String} msgFx @hide
20866 * @cfg {String} validateOnBlur @hide
20870 Roo.HtmlEditorCore.white = [
20871 'area', 'br', 'img', 'input', 'hr', 'wbr',
20873 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
20874 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
20875 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
20876 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
20877 'table', 'ul', 'xmp',
20879 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
20882 'dir', 'menu', 'ol', 'ul', 'dl',
20888 Roo.HtmlEditorCore.black = [
20889 // 'embed', 'object', // enable - backend responsiblity to clean thiese
20891 'base', 'basefont', 'bgsound', 'blink', 'body',
20892 'frame', 'frameset', 'head', 'html', 'ilayer',
20893 'iframe', 'layer', 'link', 'meta', 'object',
20894 'script', 'style' ,'title', 'xml' // clean later..
20896 Roo.HtmlEditorCore.clean = [
20897 'script', 'style', 'title', 'xml'
20899 Roo.HtmlEditorCore.remove = [
20904 Roo.HtmlEditorCore.ablack = [
20908 Roo.HtmlEditorCore.aclean = [
20909 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
20913 Roo.HtmlEditorCore.pwhite= [
20914 'http', 'https', 'mailto'
20917 // white listed style attributes.
20918 Roo.HtmlEditorCore.cwhite= [
20919 // 'text-align', /// default is to allow most things..
20925 // black listed style attributes.
20926 Roo.HtmlEditorCore.cblack= [
20927 // 'font-size' -- this can be set by the project
20931 Roo.HtmlEditorCore.swapCodes =[
20950 * @class Roo.bootstrap.HtmlEditor
20951 * @extends Roo.bootstrap.TextArea
20952 * Bootstrap HtmlEditor class
20955 * Create a new HtmlEditor
20956 * @param {Object} config The config object
20959 Roo.bootstrap.HtmlEditor = function(config){
20960 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20961 if (!this.toolbars) {
20962 this.toolbars = [];
20964 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20967 * @event initialize
20968 * Fires when the editor is fully initialized (including the iframe)
20969 * @param {HtmlEditor} this
20974 * Fires when the editor is first receives the focus. Any insertion must wait
20975 * until after this event.
20976 * @param {HtmlEditor} this
20980 * @event beforesync
20981 * Fires before the textarea is updated with content from the editor iframe. Return false
20982 * to cancel the sync.
20983 * @param {HtmlEditor} this
20984 * @param {String} html
20988 * @event beforepush
20989 * Fires before the iframe editor is updated with content from the textarea. Return false
20990 * to cancel the push.
20991 * @param {HtmlEditor} this
20992 * @param {String} html
20997 * Fires when the textarea is updated with content from the editor iframe.
20998 * @param {HtmlEditor} this
20999 * @param {String} html
21004 * Fires when the iframe editor is updated with content from the textarea.
21005 * @param {HtmlEditor} this
21006 * @param {String} html
21010 * @event editmodechange
21011 * Fires when the editor switches edit modes
21012 * @param {HtmlEditor} this
21013 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21015 editmodechange: true,
21017 * @event editorevent
21018 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21019 * @param {HtmlEditor} this
21023 * @event firstfocus
21024 * Fires when on first focus - needed by toolbars..
21025 * @param {HtmlEditor} this
21030 * Auto save the htmlEditor value as a file into Events
21031 * @param {HtmlEditor} this
21035 * @event savedpreview
21036 * preview the saved version of htmlEditor
21037 * @param {HtmlEditor} this
21044 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
21048 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21053 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21058 * @cfg {Number} height (in pixels)
21062 * @cfg {Number} width (in pixels)
21067 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21070 stylesheets: false,
21075 // private properties
21076 validationEvent : false,
21078 initialized : false,
21081 onFocus : Roo.emptyFn,
21083 hideMode:'offsets',
21086 tbContainer : false,
21088 toolbarContainer :function() {
21089 return this.wrap.select('.x-html-editor-tb',true).first();
21093 * Protected method that will not generally be called directly. It
21094 * is called when the editor creates its toolbar. Override this method if you need to
21095 * add custom toolbar buttons.
21096 * @param {HtmlEditor} editor
21098 createToolbar : function(){
21100 Roo.log("create toolbars");
21102 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21103 this.toolbars[0].render(this.toolbarContainer());
21107 // if (!editor.toolbars || !editor.toolbars.length) {
21108 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21111 // for (var i =0 ; i < editor.toolbars.length;i++) {
21112 // editor.toolbars[i] = Roo.factory(
21113 // typeof(editor.toolbars[i]) == 'string' ?
21114 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
21115 // Roo.bootstrap.HtmlEditor);
21116 // editor.toolbars[i].init(editor);
21122 onRender : function(ct, position)
21124 // Roo.log("Call onRender: " + this.xtype);
21126 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21128 this.wrap = this.inputEl().wrap({
21129 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21132 this.editorcore.onRender(ct, position);
21134 if (this.resizable) {
21135 this.resizeEl = new Roo.Resizable(this.wrap, {
21139 minHeight : this.height,
21140 height: this.height,
21141 handles : this.resizable,
21144 resize : function(r, w, h) {
21145 _t.onResize(w,h); // -something
21151 this.createToolbar(this);
21154 if(!this.width && this.resizable){
21155 this.setSize(this.wrap.getSize());
21157 if (this.resizeEl) {
21158 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21159 // should trigger onReize..
21165 onResize : function(w, h)
21167 Roo.log('resize: ' +w + ',' + h );
21168 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21172 if(this.inputEl() ){
21173 if(typeof w == 'number'){
21174 var aw = w - this.wrap.getFrameWidth('lr');
21175 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21178 if(typeof h == 'number'){
21179 var tbh = -11; // fixme it needs to tool bar size!
21180 for (var i =0; i < this.toolbars.length;i++) {
21181 // fixme - ask toolbars for heights?
21182 tbh += this.toolbars[i].el.getHeight();
21183 //if (this.toolbars[i].footer) {
21184 // tbh += this.toolbars[i].footer.el.getHeight();
21192 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21193 ah -= 5; // knock a few pixes off for look..
21194 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21198 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21199 this.editorcore.onResize(ew,eh);
21204 * Toggles the editor between standard and source edit mode.
21205 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21207 toggleSourceEdit : function(sourceEditMode)
21209 this.editorcore.toggleSourceEdit(sourceEditMode);
21211 if(this.editorcore.sourceEditMode){
21212 Roo.log('editor - showing textarea');
21215 // Roo.log(this.syncValue());
21217 this.inputEl().removeClass(['hide', 'x-hidden']);
21218 this.inputEl().dom.removeAttribute('tabIndex');
21219 this.inputEl().focus();
21221 Roo.log('editor - hiding textarea');
21223 // Roo.log(this.pushValue());
21226 this.inputEl().addClass(['hide', 'x-hidden']);
21227 this.inputEl().dom.setAttribute('tabIndex', -1);
21228 //this.deferFocus();
21231 if(this.resizable){
21232 this.setSize(this.wrap.getSize());
21235 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21238 // private (for BoxComponent)
21239 adjustSize : Roo.BoxComponent.prototype.adjustSize,
21241 // private (for BoxComponent)
21242 getResizeEl : function(){
21246 // private (for BoxComponent)
21247 getPositionEl : function(){
21252 initEvents : function(){
21253 this.originalValue = this.getValue();
21257 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21260 // markInvalid : Roo.emptyFn,
21262 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21265 // clearInvalid : Roo.emptyFn,
21267 setValue : function(v){
21268 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21269 this.editorcore.pushValue();
21274 deferFocus : function(){
21275 this.focus.defer(10, this);
21279 focus : function(){
21280 this.editorcore.focus();
21286 onDestroy : function(){
21292 for (var i =0; i < this.toolbars.length;i++) {
21293 // fixme - ask toolbars for heights?
21294 this.toolbars[i].onDestroy();
21297 this.wrap.dom.innerHTML = '';
21298 this.wrap.remove();
21303 onFirstFocus : function(){
21304 //Roo.log("onFirstFocus");
21305 this.editorcore.onFirstFocus();
21306 for (var i =0; i < this.toolbars.length;i++) {
21307 this.toolbars[i].onFirstFocus();
21313 syncValue : function()
21315 this.editorcore.syncValue();
21318 pushValue : function()
21320 this.editorcore.pushValue();
21324 // hide stuff that is not compatible
21338 * @event specialkey
21342 * @cfg {String} fieldClass @hide
21345 * @cfg {String} focusClass @hide
21348 * @cfg {String} autoCreate @hide
21351 * @cfg {String} inputType @hide
21354 * @cfg {String} invalidClass @hide
21357 * @cfg {String} invalidText @hide
21360 * @cfg {String} msgFx @hide
21363 * @cfg {String} validateOnBlur @hide
21372 Roo.namespace('Roo.bootstrap.htmleditor');
21374 * @class Roo.bootstrap.HtmlEditorToolbar1
21379 new Roo.bootstrap.HtmlEditor({
21382 new Roo.bootstrap.HtmlEditorToolbar1({
21383 disable : { fonts: 1 , format: 1, ..., ... , ...],
21389 * @cfg {Object} disable List of elements to disable..
21390 * @cfg {Array} btns List of additional buttons.
21394 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21397 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21400 Roo.apply(this, config);
21402 // default disabled, based on 'good practice'..
21403 this.disable = this.disable || {};
21404 Roo.applyIf(this.disable, {
21407 specialElements : true
21409 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21411 this.editor = config.editor;
21412 this.editorcore = config.editor.editorcore;
21414 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21416 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21417 // dont call parent... till later.
21419 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
21424 editorcore : false,
21429 "h1","h2","h3","h4","h5","h6",
21431 "abbr", "acronym", "address", "cite", "samp", "var",
21435 onRender : function(ct, position)
21437 // Roo.log("Call onRender: " + this.xtype);
21439 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21441 this.el.dom.style.marginBottom = '0';
21443 var editorcore = this.editorcore;
21444 var editor= this.editor;
21447 var btn = function(id,cmd , toggle, handler){
21449 var event = toggle ? 'toggle' : 'click';
21454 xns: Roo.bootstrap,
21457 enableToggle:toggle !== false,
21459 pressed : toggle ? false : null,
21462 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21463 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21472 xns: Roo.bootstrap,
21473 glyphicon : 'font',
21477 xns: Roo.bootstrap,
21481 Roo.each(this.formats, function(f) {
21482 style.menu.items.push({
21484 xns: Roo.bootstrap,
21485 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21490 editorcore.insertTag(this.tagname);
21497 children.push(style);
21500 btn('bold',false,true);
21501 btn('italic',false,true);
21502 btn('align-left', 'justifyleft',true);
21503 btn('align-center', 'justifycenter',true);
21504 btn('align-right' , 'justifyright',true);
21505 btn('link', false, false, function(btn) {
21506 //Roo.log("create link?");
21507 var url = prompt(this.createLinkText, this.defaultLinkValue);
21508 if(url && url != 'http:/'+'/'){
21509 this.editorcore.relayCmd('createlink', url);
21512 btn('list','insertunorderedlist',true);
21513 btn('pencil', false,true, function(btn){
21516 this.toggleSourceEdit(btn.pressed);
21522 xns: Roo.bootstrap,
21527 xns: Roo.bootstrap,
21532 cog.menu.items.push({
21534 xns: Roo.bootstrap,
21535 html : Clean styles,
21540 editorcore.insertTag(this.tagname);
21549 this.xtype = 'NavSimplebar';
21551 for(var i=0;i< children.length;i++) {
21553 this.buttons.add(this.addxtypeChild(children[i]));
21557 editor.on('editorevent', this.updateToolbar, this);
21559 onBtnClick : function(id)
21561 this.editorcore.relayCmd(id);
21562 this.editorcore.focus();
21566 * Protected method that will not generally be called directly. It triggers
21567 * a toolbar update by reading the markup state of the current selection in the editor.
21569 updateToolbar: function(){
21571 if(!this.editorcore.activated){
21572 this.editor.onFirstFocus(); // is this neeed?
21576 var btns = this.buttons;
21577 var doc = this.editorcore.doc;
21578 btns.get('bold').setActive(doc.queryCommandState('bold'));
21579 btns.get('italic').setActive(doc.queryCommandState('italic'));
21580 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21582 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21583 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21584 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21586 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21587 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21590 var ans = this.editorcore.getAllAncestors();
21591 if (this.formatCombo) {
21594 var store = this.formatCombo.store;
21595 this.formatCombo.setValue("");
21596 for (var i =0; i < ans.length;i++) {
21597 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21599 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21607 // hides menus... - so this cant be on a menu...
21608 Roo.bootstrap.MenuMgr.hideAll();
21610 Roo.bootstrap.MenuMgr.hideAll();
21611 //this.editorsyncValue();
21613 onFirstFocus: function() {
21614 this.buttons.each(function(item){
21618 toggleSourceEdit : function(sourceEditMode){
21621 if(sourceEditMode){
21622 Roo.log("disabling buttons");
21623 this.buttons.each( function(item){
21624 if(item.cmd != 'pencil'){
21630 Roo.log("enabling buttons");
21631 if(this.editorcore.initialized){
21632 this.buttons.each( function(item){
21638 Roo.log("calling toggole on editor");
21639 // tell the editor that it's been pressed..
21640 this.editor.toggleSourceEdit(sourceEditMode);
21650 * @class Roo.bootstrap.Table.AbstractSelectionModel
21651 * @extends Roo.util.Observable
21652 * Abstract base class for grid SelectionModels. It provides the interface that should be
21653 * implemented by descendant classes. This class should not be directly instantiated.
21656 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21657 this.locked = false;
21658 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21662 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21663 /** @ignore Called by the grid automatically. Do not call directly. */
21664 init : function(grid){
21670 * Locks the selections.
21673 this.locked = true;
21677 * Unlocks the selections.
21679 unlock : function(){
21680 this.locked = false;
21684 * Returns true if the selections are locked.
21685 * @return {Boolean}
21687 isLocked : function(){
21688 return this.locked;
21692 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21693 * @class Roo.bootstrap.Table.RowSelectionModel
21694 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21695 * It supports multiple selections and keyboard selection/navigation.
21697 * @param {Object} config
21700 Roo.bootstrap.Table.RowSelectionModel = function(config){
21701 Roo.apply(this, config);
21702 this.selections = new Roo.util.MixedCollection(false, function(o){
21707 this.lastActive = false;
21711 * @event selectionchange
21712 * Fires when the selection changes
21713 * @param {SelectionModel} this
21715 "selectionchange" : true,
21717 * @event afterselectionchange
21718 * Fires after the selection changes (eg. by key press or clicking)
21719 * @param {SelectionModel} this
21721 "afterselectionchange" : true,
21723 * @event beforerowselect
21724 * Fires when a row is selected being selected, return false to cancel.
21725 * @param {SelectionModel} this
21726 * @param {Number} rowIndex The selected index
21727 * @param {Boolean} keepExisting False if other selections will be cleared
21729 "beforerowselect" : true,
21732 * Fires when a row is selected.
21733 * @param {SelectionModel} this
21734 * @param {Number} rowIndex The selected index
21735 * @param {Roo.data.Record} r The record
21737 "rowselect" : true,
21739 * @event rowdeselect
21740 * Fires when a row is deselected.
21741 * @param {SelectionModel} this
21742 * @param {Number} rowIndex The selected index
21744 "rowdeselect" : true
21746 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21747 this.locked = false;
21750 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21752 * @cfg {Boolean} singleSelect
21753 * True to allow selection of only one row at a time (defaults to false)
21755 singleSelect : false,
21758 initEvents : function(){
21760 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21761 this.grid.on("mousedown", this.handleMouseDown, this);
21762 }else{ // allow click to work like normal
21763 this.grid.on("rowclick", this.handleDragableRowClick, this);
21766 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21767 "up" : function(e){
21769 this.selectPrevious(e.shiftKey);
21770 }else if(this.last !== false && this.lastActive !== false){
21771 var last = this.last;
21772 this.selectRange(this.last, this.lastActive-1);
21773 this.grid.getView().focusRow(this.lastActive);
21774 if(last !== false){
21778 this.selectFirstRow();
21780 this.fireEvent("afterselectionchange", this);
21782 "down" : function(e){
21784 this.selectNext(e.shiftKey);
21785 }else if(this.last !== false && this.lastActive !== false){
21786 var last = this.last;
21787 this.selectRange(this.last, this.lastActive+1);
21788 this.grid.getView().focusRow(this.lastActive);
21789 if(last !== false){
21793 this.selectFirstRow();
21795 this.fireEvent("afterselectionchange", this);
21800 var view = this.grid.view;
21801 view.on("refresh", this.onRefresh, this);
21802 view.on("rowupdated", this.onRowUpdated, this);
21803 view.on("rowremoved", this.onRemove, this);
21807 onRefresh : function(){
21808 var ds = this.grid.dataSource, i, v = this.grid.view;
21809 var s = this.selections;
21810 s.each(function(r){
21811 if((i = ds.indexOfId(r.id)) != -1){
21820 onRemove : function(v, index, r){
21821 this.selections.remove(r);
21825 onRowUpdated : function(v, index, r){
21826 if(this.isSelected(r)){
21827 v.onRowSelect(index);
21833 * @param {Array} records The records to select
21834 * @param {Boolean} keepExisting (optional) True to keep existing selections
21836 selectRecords : function(records, keepExisting){
21838 this.clearSelections();
21840 var ds = this.grid.dataSource;
21841 for(var i = 0, len = records.length; i < len; i++){
21842 this.selectRow(ds.indexOf(records[i]), true);
21847 * Gets the number of selected rows.
21850 getCount : function(){
21851 return this.selections.length;
21855 * Selects the first row in the grid.
21857 selectFirstRow : function(){
21862 * Select the last row.
21863 * @param {Boolean} keepExisting (optional) True to keep existing selections
21865 selectLastRow : function(keepExisting){
21866 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21870 * Selects the row immediately following the last selected row.
21871 * @param {Boolean} keepExisting (optional) True to keep existing selections
21873 selectNext : function(keepExisting){
21874 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21875 this.selectRow(this.last+1, keepExisting);
21876 this.grid.getView().focusRow(this.last);
21881 * Selects the row that precedes the last selected row.
21882 * @param {Boolean} keepExisting (optional) True to keep existing selections
21884 selectPrevious : function(keepExisting){
21886 this.selectRow(this.last-1, keepExisting);
21887 this.grid.getView().focusRow(this.last);
21892 * Returns the selected records
21893 * @return {Array} Array of selected records
21895 getSelections : function(){
21896 return [].concat(this.selections.items);
21900 * Returns the first selected record.
21903 getSelected : function(){
21904 return this.selections.itemAt(0);
21909 * Clears all selections.
21911 clearSelections : function(fast){
21916 var ds = this.grid.dataSource;
21917 var s = this.selections;
21918 s.each(function(r){
21919 this.deselectRow(ds.indexOfId(r.id));
21923 this.selections.clear();
21930 * Selects all rows.
21932 selectAll : function(){
21936 this.selections.clear();
21937 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21938 this.selectRow(i, true);
21943 * Returns True if there is a selection.
21944 * @return {Boolean}
21946 hasSelection : function(){
21947 return this.selections.length > 0;
21951 * Returns True if the specified row is selected.
21952 * @param {Number/Record} record The record or index of the record to check
21953 * @return {Boolean}
21955 isSelected : function(index){
21956 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21957 return (r && this.selections.key(r.id) ? true : false);
21961 * Returns True if the specified record id is selected.
21962 * @param {String} id The id of record to check
21963 * @return {Boolean}
21965 isIdSelected : function(id){
21966 return (this.selections.key(id) ? true : false);
21970 handleMouseDown : function(e, t){
21971 var view = this.grid.getView(), rowIndex;
21972 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21975 if(e.shiftKey && this.last !== false){
21976 var last = this.last;
21977 this.selectRange(last, rowIndex, e.ctrlKey);
21978 this.last = last; // reset the last
21979 view.focusRow(rowIndex);
21981 var isSelected = this.isSelected(rowIndex);
21982 if(e.button !== 0 && isSelected){
21983 view.focusRow(rowIndex);
21984 }else if(e.ctrlKey && isSelected){
21985 this.deselectRow(rowIndex);
21986 }else if(!isSelected){
21987 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21988 view.focusRow(rowIndex);
21991 this.fireEvent("afterselectionchange", this);
21994 handleDragableRowClick : function(grid, rowIndex, e)
21996 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21997 this.selectRow(rowIndex, false);
21998 grid.view.focusRow(rowIndex);
21999 this.fireEvent("afterselectionchange", this);
22004 * Selects multiple rows.
22005 * @param {Array} rows Array of the indexes of the row to select
22006 * @param {Boolean} keepExisting (optional) True to keep existing selections
22008 selectRows : function(rows, keepExisting){
22010 this.clearSelections();
22012 for(var i = 0, len = rows.length; i < len; i++){
22013 this.selectRow(rows[i], true);
22018 * Selects a range of rows. All rows in between startRow and endRow are also selected.
22019 * @param {Number} startRow The index of the first row in the range
22020 * @param {Number} endRow The index of the last row in the range
22021 * @param {Boolean} keepExisting (optional) True to retain existing selections
22023 selectRange : function(startRow, endRow, keepExisting){
22028 this.clearSelections();
22030 if(startRow <= endRow){
22031 for(var i = startRow; i <= endRow; i++){
22032 this.selectRow(i, true);
22035 for(var i = startRow; i >= endRow; i--){
22036 this.selectRow(i, true);
22042 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22043 * @param {Number} startRow The index of the first row in the range
22044 * @param {Number} endRow The index of the last row in the range
22046 deselectRange : function(startRow, endRow, preventViewNotify){
22050 for(var i = startRow; i <= endRow; i++){
22051 this.deselectRow(i, preventViewNotify);
22057 * @param {Number} row The index of the row to select
22058 * @param {Boolean} keepExisting (optional) True to keep existing selections
22060 selectRow : function(index, keepExisting, preventViewNotify){
22061 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22064 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22065 if(!keepExisting || this.singleSelect){
22066 this.clearSelections();
22068 var r = this.grid.dataSource.getAt(index);
22069 this.selections.add(r);
22070 this.last = this.lastActive = index;
22071 if(!preventViewNotify){
22072 this.grid.getView().onRowSelect(index);
22074 this.fireEvent("rowselect", this, index, r);
22075 this.fireEvent("selectionchange", this);
22081 * @param {Number} row The index of the row to deselect
22083 deselectRow : function(index, preventViewNotify){
22087 if(this.last == index){
22090 if(this.lastActive == index){
22091 this.lastActive = false;
22093 var r = this.grid.dataSource.getAt(index);
22094 this.selections.remove(r);
22095 if(!preventViewNotify){
22096 this.grid.getView().onRowDeselect(index);
22098 this.fireEvent("rowdeselect", this, index);
22099 this.fireEvent("selectionchange", this);
22103 restoreLast : function(){
22105 this.last = this._last;
22110 acceptsNav : function(row, col, cm){
22111 return !cm.isHidden(col) && cm.isCellEditable(col, row);
22115 onEditorKey : function(field, e){
22116 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22121 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22123 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22125 }else if(k == e.ENTER && !e.ctrlKey){
22129 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22131 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22133 }else if(k == e.ESC){
22137 g.startEditing(newCell[0], newCell[1]);
22142 * Ext JS Library 1.1.1
22143 * Copyright(c) 2006-2007, Ext JS, LLC.
22145 * Originally Released Under LGPL - original licence link has changed is not relivant.
22148 * <script type="text/javascript">
22152 * @class Roo.bootstrap.PagingToolbar
22153 * @extends Roo.bootstrap.NavSimplebar
22154 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22156 * Create a new PagingToolbar
22157 * @param {Object} config The config object
22158 * @param {Roo.data.Store} store
22160 Roo.bootstrap.PagingToolbar = function(config)
22162 // old args format still supported... - xtype is prefered..
22163 // created from xtype...
22165 this.ds = config.dataSource;
22167 if (config.store && !this.ds) {
22168 this.store= Roo.factory(config.store, Roo.data);
22169 this.ds = this.store;
22170 this.ds.xmodule = this.xmodule || false;
22173 this.toolbarItems = [];
22174 if (config.items) {
22175 this.toolbarItems = config.items;
22178 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22183 this.bind(this.ds);
22186 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22190 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22192 * @cfg {Roo.data.Store} dataSource
22193 * The underlying data store providing the paged data
22196 * @cfg {String/HTMLElement/Element} container
22197 * container The id or element that will contain the toolbar
22200 * @cfg {Boolean} displayInfo
22201 * True to display the displayMsg (defaults to false)
22204 * @cfg {Number} pageSize
22205 * The number of records to display per page (defaults to 20)
22209 * @cfg {String} displayMsg
22210 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22212 displayMsg : 'Displaying {0} - {1} of {2}',
22214 * @cfg {String} emptyMsg
22215 * The message to display when no records are found (defaults to "No data to display")
22217 emptyMsg : 'No data to display',
22219 * Customizable piece of the default paging text (defaults to "Page")
22222 beforePageText : "Page",
22224 * Customizable piece of the default paging text (defaults to "of %0")
22227 afterPageText : "of {0}",
22229 * Customizable piece of the default paging text (defaults to "First Page")
22232 firstText : "First Page",
22234 * Customizable piece of the default paging text (defaults to "Previous Page")
22237 prevText : "Previous Page",
22239 * Customizable piece of the default paging text (defaults to "Next Page")
22242 nextText : "Next Page",
22244 * Customizable piece of the default paging text (defaults to "Last Page")
22247 lastText : "Last Page",
22249 * Customizable piece of the default paging text (defaults to "Refresh")
22252 refreshText : "Refresh",
22256 onRender : function(ct, position)
22258 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22259 this.navgroup.parentId = this.id;
22260 this.navgroup.onRender(this.el, null);
22261 // add the buttons to the navgroup
22263 if(this.displayInfo){
22264 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22265 this.displayEl = this.el.select('.x-paging-info', true).first();
22266 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22267 // this.displayEl = navel.el.select('span',true).first();
22273 Roo.each(_this.buttons, function(e){ // this might need to use render????
22274 Roo.factory(e).onRender(_this.el, null);
22278 Roo.each(_this.toolbarItems, function(e) {
22279 _this.navgroup.addItem(e);
22283 this.first = this.navgroup.addItem({
22284 tooltip: this.firstText,
22286 icon : 'fa fa-backward',
22288 preventDefault: true,
22289 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22292 this.prev = this.navgroup.addItem({
22293 tooltip: this.prevText,
22295 icon : 'fa fa-step-backward',
22297 preventDefault: true,
22298 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
22300 //this.addSeparator();
22303 var field = this.navgroup.addItem( {
22305 cls : 'x-paging-position',
22307 html : this.beforePageText +
22308 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22309 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
22312 this.field = field.el.select('input', true).first();
22313 this.field.on("keydown", this.onPagingKeydown, this);
22314 this.field.on("focus", function(){this.dom.select();});
22317 this.afterTextEl = field.el.select('.x-paging-after',true).first();
22318 //this.field.setHeight(18);
22319 //this.addSeparator();
22320 this.next = this.navgroup.addItem({
22321 tooltip: this.nextText,
22323 html : ' <i class="fa fa-step-forward">',
22325 preventDefault: true,
22326 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
22328 this.last = this.navgroup.addItem({
22329 tooltip: this.lastText,
22330 icon : 'fa fa-forward',
22333 preventDefault: true,
22334 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
22336 //this.addSeparator();
22337 this.loading = this.navgroup.addItem({
22338 tooltip: this.refreshText,
22339 icon: 'fa fa-refresh',
22340 preventDefault: true,
22341 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22347 updateInfo : function(){
22348 if(this.displayEl){
22349 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22350 var msg = count == 0 ?
22354 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
22356 this.displayEl.update(msg);
22361 onLoad : function(ds, r, o){
22362 this.cursor = o.params ? o.params.start : 0;
22363 var d = this.getPageData(),
22367 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22368 this.field.dom.value = ap;
22369 this.first.setDisabled(ap == 1);
22370 this.prev.setDisabled(ap == 1);
22371 this.next.setDisabled(ap == ps);
22372 this.last.setDisabled(ap == ps);
22373 this.loading.enable();
22378 getPageData : function(){
22379 var total = this.ds.getTotalCount();
22382 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22383 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22388 onLoadError : function(){
22389 this.loading.enable();
22393 onPagingKeydown : function(e){
22394 var k = e.getKey();
22395 var d = this.getPageData();
22397 var v = this.field.dom.value, pageNum;
22398 if(!v || isNaN(pageNum = parseInt(v, 10))){
22399 this.field.dom.value = d.activePage;
22402 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22403 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22406 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))
22408 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22409 this.field.dom.value = pageNum;
22410 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22413 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22415 var v = this.field.dom.value, pageNum;
22416 var increment = (e.shiftKey) ? 10 : 1;
22417 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22420 if(!v || isNaN(pageNum = parseInt(v, 10))) {
22421 this.field.dom.value = d.activePage;
22424 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22426 this.field.dom.value = parseInt(v, 10) + increment;
22427 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22428 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22435 beforeLoad : function(){
22437 this.loading.disable();
22442 onClick : function(which){
22451 ds.load({params:{start: 0, limit: this.pageSize}});
22454 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22457 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22460 var total = ds.getTotalCount();
22461 var extra = total % this.pageSize;
22462 var lastStart = extra ? (total - extra) : total-this.pageSize;
22463 ds.load({params:{start: lastStart, limit: this.pageSize}});
22466 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22472 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22473 * @param {Roo.data.Store} store The data store to unbind
22475 unbind : function(ds){
22476 ds.un("beforeload", this.beforeLoad, this);
22477 ds.un("load", this.onLoad, this);
22478 ds.un("loadexception", this.onLoadError, this);
22479 ds.un("remove", this.updateInfo, this);
22480 ds.un("add", this.updateInfo, this);
22481 this.ds = undefined;
22485 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22486 * @param {Roo.data.Store} store The data store to bind
22488 bind : function(ds){
22489 ds.on("beforeload", this.beforeLoad, this);
22490 ds.on("load", this.onLoad, this);
22491 ds.on("loadexception", this.onLoadError, this);
22492 ds.on("remove", this.updateInfo, this);
22493 ds.on("add", this.updateInfo, this);
22504 * @class Roo.bootstrap.MessageBar
22505 * @extends Roo.bootstrap.Component
22506 * Bootstrap MessageBar class
22507 * @cfg {String} html contents of the MessageBar
22508 * @cfg {String} weight (info | success | warning | danger) default info
22509 * @cfg {String} beforeClass insert the bar before the given class
22510 * @cfg {Boolean} closable (true | false) default false
22511 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22514 * Create a new Element
22515 * @param {Object} config The config object
22518 Roo.bootstrap.MessageBar = function(config){
22519 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22522 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22528 beforeClass: 'bootstrap-sticky-wrap',
22530 getAutoCreate : function(){
22534 cls: 'alert alert-dismissable alert-' + this.weight,
22539 html: this.html || ''
22545 cfg.cls += ' alert-messages-fixed';
22559 onRender : function(ct, position)
22561 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22564 var cfg = Roo.apply({}, this.getAutoCreate());
22568 cfg.cls += ' ' + this.cls;
22571 cfg.style = this.style;
22573 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22575 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22578 this.el.select('>button.close').on('click', this.hide, this);
22584 if (!this.rendered) {
22590 this.fireEvent('show', this);
22596 if (!this.rendered) {
22602 this.fireEvent('hide', this);
22605 update : function()
22607 // var e = this.el.dom.firstChild;
22609 // if(this.closable){
22610 // e = e.nextSibling;
22613 // e.data = this.html || '';
22615 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22631 * @class Roo.bootstrap.Graph
22632 * @extends Roo.bootstrap.Component
22633 * Bootstrap Graph class
22637 @cfg {String} graphtype bar | vbar | pie
22638 @cfg {number} g_x coodinator | centre x (pie)
22639 @cfg {number} g_y coodinator | centre y (pie)
22640 @cfg {number} g_r radius (pie)
22641 @cfg {number} g_height height of the chart (respected by all elements in the set)
22642 @cfg {number} g_width width of the chart (respected by all elements in the set)
22643 @cfg {Object} title The title of the chart
22646 -opts (object) options for the chart
22648 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22649 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22651 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.
22652 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22654 o stretch (boolean)
22656 -opts (object) options for the pie
22659 o startAngle (number)
22660 o endAngle (number)
22664 * Create a new Input
22665 * @param {Object} config The config object
22668 Roo.bootstrap.Graph = function(config){
22669 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22675 * The img click event for the img.
22676 * @param {Roo.EventObject} e
22682 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22693 //g_colors: this.colors,
22700 getAutoCreate : function(){
22711 onRender : function(ct,position){
22712 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22713 this.raphael = Raphael(this.el.dom);
22715 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22716 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22717 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22718 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22720 r.text(160, 10, "Single Series Chart").attr(txtattr);
22721 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22722 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22723 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22725 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22726 r.barchart(330, 10, 300, 220, data1);
22727 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22728 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22731 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22732 // r.barchart(30, 30, 560, 250, xdata, {
22733 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22734 // axis : "0 0 1 1",
22735 // axisxlabels : xdata
22736 // //yvalues : cols,
22739 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22741 // this.load(null,xdata,{
22742 // axis : "0 0 1 1",
22743 // axisxlabels : xdata
22748 load : function(graphtype,xdata,opts){
22749 this.raphael.clear();
22751 graphtype = this.graphtype;
22756 var r = this.raphael,
22757 fin = function () {
22758 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22760 fout = function () {
22761 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22763 pfin = function() {
22764 this.sector.stop();
22765 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22768 this.label[0].stop();
22769 this.label[0].attr({ r: 7.5 });
22770 this.label[1].attr({ "font-weight": 800 });
22773 pfout = function() {
22774 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22777 this.label[0].animate({ r: 5 }, 500, "bounce");
22778 this.label[1].attr({ "font-weight": 400 });
22784 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22787 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22790 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
22791 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22793 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22800 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22805 setTitle: function(o)
22810 initEvents: function() {
22813 this.el.on('click', this.onClick, this);
22817 onClick : function(e)
22819 Roo.log('img onclick');
22820 this.fireEvent('click', this, e);
22832 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22835 * @class Roo.bootstrap.dash.NumberBox
22836 * @extends Roo.bootstrap.Component
22837 * Bootstrap NumberBox class
22838 * @cfg {String} headline Box headline
22839 * @cfg {String} content Box content
22840 * @cfg {String} icon Box icon
22841 * @cfg {String} footer Footer text
22842 * @cfg {String} fhref Footer href
22845 * Create a new NumberBox
22846 * @param {Object} config The config object
22850 Roo.bootstrap.dash.NumberBox = function(config){
22851 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22855 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
22864 getAutoCreate : function(){
22868 cls : 'small-box ',
22876 cls : 'roo-headline',
22877 html : this.headline
22881 cls : 'roo-content',
22882 html : this.content
22896 cls : 'ion ' + this.icon
22905 cls : 'small-box-footer',
22906 href : this.fhref || '#',
22910 cfg.cn.push(footer);
22917 onRender : function(ct,position){
22918 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22925 setHeadline: function (value)
22927 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22930 setFooter: function (value, href)
22932 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22935 this.el.select('a.small-box-footer',true).first().attr('href', href);
22940 setContent: function (value)
22942 this.el.select('.roo-content',true).first().dom.innerHTML = value;
22945 initEvents: function()
22959 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22962 * @class Roo.bootstrap.dash.TabBox
22963 * @extends Roo.bootstrap.Component
22964 * Bootstrap TabBox class
22965 * @cfg {String} title Title of the TabBox
22966 * @cfg {String} icon Icon of the TabBox
22967 * @cfg {Boolean} showtabs (true|false) show the tabs default true
22968 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22971 * Create a new TabBox
22972 * @param {Object} config The config object
22976 Roo.bootstrap.dash.TabBox = function(config){
22977 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22982 * When a pane is added
22983 * @param {Roo.bootstrap.dash.TabPane} pane
22987 * @event activatepane
22988 * When a pane is activated
22989 * @param {Roo.bootstrap.dash.TabPane} pane
22991 "activatepane" : true
22999 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
23004 tabScrollable : false,
23006 getChildContainer : function()
23008 return this.el.select('.tab-content', true).first();
23011 getAutoCreate : function(){
23015 cls: 'pull-left header',
23023 cls: 'fa ' + this.icon
23029 cls: 'nav nav-tabs pull-right',
23035 if(this.tabScrollable){
23042 cls: 'nav nav-tabs pull-right',
23053 cls: 'nav-tabs-custom',
23058 cls: 'tab-content no-padding',
23066 initEvents : function()
23068 //Roo.log('add add pane handler');
23069 this.on('addpane', this.onAddPane, this);
23072 * Updates the box title
23073 * @param {String} html to set the title to.
23075 setTitle : function(value)
23077 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23079 onAddPane : function(pane)
23081 this.panes.push(pane);
23082 //Roo.log('addpane');
23084 // tabs are rendere left to right..
23085 if(!this.showtabs){
23089 var ctr = this.el.select('.nav-tabs', true).first();
23092 var existing = ctr.select('.nav-tab',true);
23093 var qty = existing.getCount();;
23096 var tab = ctr.createChild({
23098 cls : 'nav-tab' + (qty ? '' : ' active'),
23106 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23109 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23111 pane.el.addClass('active');
23116 onTabClick : function(ev,un,ob,pane)
23118 //Roo.log('tab - prev default');
23119 ev.preventDefault();
23122 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23123 pane.tab.addClass('active');
23124 //Roo.log(pane.title);
23125 this.getChildContainer().select('.tab-pane',true).removeClass('active');
23126 // technically we should have a deactivate event.. but maybe add later.
23127 // and it should not de-activate the selected tab...
23128 this.fireEvent('activatepane', pane);
23129 pane.el.addClass('active');
23130 pane.fireEvent('activate');
23135 getActivePane : function()
23138 Roo.each(this.panes, function(p) {
23139 if(p.el.hasClass('active')){
23160 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23162 * @class Roo.bootstrap.TabPane
23163 * @extends Roo.bootstrap.Component
23164 * Bootstrap TabPane class
23165 * @cfg {Boolean} active (false | true) Default false
23166 * @cfg {String} title title of panel
23170 * Create a new TabPane
23171 * @param {Object} config The config object
23174 Roo.bootstrap.dash.TabPane = function(config){
23175 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23181 * When a pane is activated
23182 * @param {Roo.bootstrap.dash.TabPane} pane
23189 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
23194 // the tabBox that this is attached to.
23197 getAutoCreate : function()
23205 cfg.cls += ' active';
23210 initEvents : function()
23212 //Roo.log('trigger add pane handler');
23213 this.parent().fireEvent('addpane', this)
23217 * Updates the tab title
23218 * @param {String} html to set the title to.
23220 setTitle: function(str)
23226 this.tab.select('a', true).first().dom.innerHTML = str;
23243 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23246 * @class Roo.bootstrap.menu.Menu
23247 * @extends Roo.bootstrap.Component
23248 * Bootstrap Menu class - container for Menu
23249 * @cfg {String} html Text of the menu
23250 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23251 * @cfg {String} icon Font awesome icon
23252 * @cfg {String} pos Menu align to (top | bottom) default bottom
23256 * Create a new Menu
23257 * @param {Object} config The config object
23261 Roo.bootstrap.menu.Menu = function(config){
23262 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23266 * @event beforeshow
23267 * Fires before this menu is displayed
23268 * @param {Roo.bootstrap.menu.Menu} this
23272 * @event beforehide
23273 * Fires before this menu is hidden
23274 * @param {Roo.bootstrap.menu.Menu} this
23279 * Fires after this menu is displayed
23280 * @param {Roo.bootstrap.menu.Menu} this
23285 * Fires after this menu is hidden
23286 * @param {Roo.bootstrap.menu.Menu} this
23291 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23292 * @param {Roo.bootstrap.menu.Menu} this
23293 * @param {Roo.EventObject} e
23300 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
23304 weight : 'default',
23309 getChildContainer : function() {
23310 if(this.isSubMenu){
23314 return this.el.select('ul.dropdown-menu', true).first();
23317 getAutoCreate : function()
23322 cls : 'roo-menu-text',
23330 cls : 'fa ' + this.icon
23341 cls : 'dropdown-button btn btn-' + this.weight,
23346 cls : 'dropdown-toggle btn btn-' + this.weight,
23356 cls : 'dropdown-menu'
23362 if(this.pos == 'top'){
23363 cfg.cls += ' dropup';
23366 if(this.isSubMenu){
23369 cls : 'dropdown-menu'
23376 onRender : function(ct, position)
23378 this.isSubMenu = ct.hasClass('dropdown-submenu');
23380 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23383 initEvents : function()
23385 if(this.isSubMenu){
23389 this.hidden = true;
23391 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23392 this.triggerEl.on('click', this.onTriggerPress, this);
23394 this.buttonEl = this.el.select('button.dropdown-button', true).first();
23395 this.buttonEl.on('click', this.onClick, this);
23401 if(this.isSubMenu){
23405 return this.el.select('ul.dropdown-menu', true).first();
23408 onClick : function(e)
23410 this.fireEvent("click", this, e);
23413 onTriggerPress : function(e)
23415 if (this.isVisible()) {
23422 isVisible : function(){
23423 return !this.hidden;
23428 this.fireEvent("beforeshow", this);
23430 this.hidden = false;
23431 this.el.addClass('open');
23433 Roo.get(document).on("mouseup", this.onMouseUp, this);
23435 this.fireEvent("show", this);
23442 this.fireEvent("beforehide", this);
23444 this.hidden = true;
23445 this.el.removeClass('open');
23447 Roo.get(document).un("mouseup", this.onMouseUp);
23449 this.fireEvent("hide", this);
23452 onMouseUp : function()
23466 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23469 * @class Roo.bootstrap.menu.Item
23470 * @extends Roo.bootstrap.Component
23471 * Bootstrap MenuItem class
23472 * @cfg {Boolean} submenu (true | false) default false
23473 * @cfg {String} html text of the item
23474 * @cfg {String} href the link
23475 * @cfg {Boolean} disable (true | false) default false
23476 * @cfg {Boolean} preventDefault (true | false) default true
23477 * @cfg {String} icon Font awesome icon
23478 * @cfg {String} pos Submenu align to (left | right) default right
23482 * Create a new Item
23483 * @param {Object} config The config object
23487 Roo.bootstrap.menu.Item = function(config){
23488 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23492 * Fires when the mouse is hovering over this menu
23493 * @param {Roo.bootstrap.menu.Item} this
23494 * @param {Roo.EventObject} e
23499 * Fires when the mouse exits this menu
23500 * @param {Roo.bootstrap.menu.Item} this
23501 * @param {Roo.EventObject} e
23507 * The raw click event for the entire grid.
23508 * @param {Roo.EventObject} e
23514 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23519 preventDefault: true,
23524 getAutoCreate : function()
23529 cls : 'roo-menu-item-text',
23537 cls : 'fa ' + this.icon
23546 href : this.href || '#',
23553 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23557 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23559 if(this.pos == 'left'){
23560 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23567 initEvents : function()
23569 this.el.on('mouseover', this.onMouseOver, this);
23570 this.el.on('mouseout', this.onMouseOut, this);
23572 this.el.select('a', true).first().on('click', this.onClick, this);
23576 onClick : function(e)
23578 if(this.preventDefault){
23579 e.preventDefault();
23582 this.fireEvent("click", this, e);
23585 onMouseOver : function(e)
23587 if(this.submenu && this.pos == 'left'){
23588 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23591 this.fireEvent("mouseover", this, e);
23594 onMouseOut : function(e)
23596 this.fireEvent("mouseout", this, e);
23608 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23611 * @class Roo.bootstrap.menu.Separator
23612 * @extends Roo.bootstrap.Component
23613 * Bootstrap Separator class
23616 * Create a new Separator
23617 * @param {Object} config The config object
23621 Roo.bootstrap.menu.Separator = function(config){
23622 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23625 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23627 getAutoCreate : function(){
23648 * @class Roo.bootstrap.Tooltip
23649 * Bootstrap Tooltip class
23650 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23651 * to determine which dom element triggers the tooltip.
23653 * It needs to add support for additional attributes like tooltip-position
23656 * Create a new Toolti
23657 * @param {Object} config The config object
23660 Roo.bootstrap.Tooltip = function(config){
23661 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23664 Roo.apply(Roo.bootstrap.Tooltip, {
23666 * @function init initialize tooltip monitoring.
23670 currentTip : false,
23671 currentRegion : false,
23677 Roo.get(document).on('mouseover', this.enter ,this);
23678 Roo.get(document).on('mouseout', this.leave, this);
23681 this.currentTip = new Roo.bootstrap.Tooltip();
23684 enter : function(ev)
23686 var dom = ev.getTarget();
23688 //Roo.log(['enter',dom]);
23689 var el = Roo.fly(dom);
23690 if (this.currentEl) {
23692 //Roo.log(this.currentEl);
23693 //Roo.log(this.currentEl.contains(dom));
23694 if (this.currentEl == el) {
23697 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23703 if (this.currentTip.el) {
23704 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23709 // you can not look for children, as if el is the body.. then everythign is the child..
23710 if (!el.attr('tooltip')) { //
23711 if (!el.select("[tooltip]").elements.length) {
23714 // is the mouse over this child...?
23715 bindEl = el.select("[tooltip]").first();
23716 var xy = ev.getXY();
23717 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23718 //Roo.log("not in region.");
23721 //Roo.log("child element over..");
23724 this.currentEl = bindEl;
23725 this.currentTip.bind(bindEl);
23726 this.currentRegion = Roo.lib.Region.getRegion(dom);
23727 this.currentTip.enter();
23730 leave : function(ev)
23732 var dom = ev.getTarget();
23733 //Roo.log(['leave',dom]);
23734 if (!this.currentEl) {
23739 if (dom != this.currentEl.dom) {
23742 var xy = ev.getXY();
23743 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23746 // only activate leave if mouse cursor is outside... bounding box..
23751 if (this.currentTip) {
23752 this.currentTip.leave();
23754 //Roo.log('clear currentEl');
23755 this.currentEl = false;
23760 'left' : ['r-l', [-2,0], 'right'],
23761 'right' : ['l-r', [2,0], 'left'],
23762 'bottom' : ['t-b', [0,2], 'top'],
23763 'top' : [ 'b-t', [0,-2], 'bottom']
23769 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
23774 delay : null, // can be { show : 300 , hide: 500}
23778 hoverState : null, //???
23780 placement : 'bottom',
23782 getAutoCreate : function(){
23789 cls : 'tooltip-arrow'
23792 cls : 'tooltip-inner'
23799 bind : function(el)
23805 enter : function () {
23807 if (this.timeout != null) {
23808 clearTimeout(this.timeout);
23811 this.hoverState = 'in';
23812 //Roo.log("enter - show");
23813 if (!this.delay || !this.delay.show) {
23818 this.timeout = setTimeout(function () {
23819 if (_t.hoverState == 'in') {
23822 }, this.delay.show);
23826 clearTimeout(this.timeout);
23828 this.hoverState = 'out';
23829 if (!this.delay || !this.delay.hide) {
23835 this.timeout = setTimeout(function () {
23836 //Roo.log("leave - timeout");
23838 if (_t.hoverState == 'out') {
23840 Roo.bootstrap.Tooltip.currentEl = false;
23848 this.render(document.body);
23851 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23853 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23855 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23857 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23859 var placement = typeof this.placement == 'function' ?
23860 this.placement.call(this, this.el, on_el) :
23863 var autoToken = /\s?auto?\s?/i;
23864 var autoPlace = autoToken.test(placement);
23866 placement = placement.replace(autoToken, '') || 'top';
23870 //this.el.setXY([0,0]);
23872 //this.el.dom.style.display='block';
23874 //this.el.appendTo(on_el);
23876 var p = this.getPosition();
23877 var box = this.el.getBox();
23883 var align = Roo.bootstrap.Tooltip.alignment[placement];
23885 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23887 if(placement == 'top' || placement == 'bottom'){
23889 placement = 'right';
23892 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23893 placement = 'left';
23897 align = Roo.bootstrap.Tooltip.alignment[placement];
23899 this.el.alignTo(this.bindEl, align[0],align[1]);
23900 //var arrow = this.el.select('.arrow',true).first();
23901 //arrow.set(align[2],
23903 this.el.addClass(placement);
23905 this.el.addClass('in fade');
23907 this.hoverState = null;
23909 if (this.el.hasClass('fade')) {
23920 //this.el.setXY([0,0]);
23921 this.el.removeClass('in');
23937 * @class Roo.bootstrap.LocationPicker
23938 * @extends Roo.bootstrap.Component
23939 * Bootstrap LocationPicker class
23940 * @cfg {Number} latitude Position when init default 0
23941 * @cfg {Number} longitude Position when init default 0
23942 * @cfg {Number} zoom default 15
23943 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23944 * @cfg {Boolean} mapTypeControl default false
23945 * @cfg {Boolean} disableDoubleClickZoom default false
23946 * @cfg {Boolean} scrollwheel default true
23947 * @cfg {Boolean} streetViewControl default false
23948 * @cfg {Number} radius default 0
23949 * @cfg {String} locationName
23950 * @cfg {Boolean} draggable default true
23951 * @cfg {Boolean} enableAutocomplete default false
23952 * @cfg {Boolean} enableReverseGeocode default true
23953 * @cfg {String} markerTitle
23956 * Create a new LocationPicker
23957 * @param {Object} config The config object
23961 Roo.bootstrap.LocationPicker = function(config){
23963 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23968 * Fires when the picker initialized.
23969 * @param {Roo.bootstrap.LocationPicker} this
23970 * @param {Google Location} location
23974 * @event positionchanged
23975 * Fires when the picker position changed.
23976 * @param {Roo.bootstrap.LocationPicker} this
23977 * @param {Google Location} location
23979 positionchanged : true,
23982 * Fires when the map resize.
23983 * @param {Roo.bootstrap.LocationPicker} this
23988 * Fires when the map show.
23989 * @param {Roo.bootstrap.LocationPicker} this
23994 * Fires when the map hide.
23995 * @param {Roo.bootstrap.LocationPicker} this
24000 * Fires when click the map.
24001 * @param {Roo.bootstrap.LocationPicker} this
24002 * @param {Map event} e
24006 * @event mapRightClick
24007 * Fires when right click the map.
24008 * @param {Roo.bootstrap.LocationPicker} this
24009 * @param {Map event} e
24011 mapRightClick : true,
24013 * @event markerClick
24014 * Fires when click the marker.
24015 * @param {Roo.bootstrap.LocationPicker} this
24016 * @param {Map event} e
24018 markerClick : true,
24020 * @event markerRightClick
24021 * Fires when right click the marker.
24022 * @param {Roo.bootstrap.LocationPicker} this
24023 * @param {Map event} e
24025 markerRightClick : true,
24027 * @event OverlayViewDraw
24028 * Fires when OverlayView Draw
24029 * @param {Roo.bootstrap.LocationPicker} this
24031 OverlayViewDraw : true,
24033 * @event OverlayViewOnAdd
24034 * Fires when OverlayView Draw
24035 * @param {Roo.bootstrap.LocationPicker} this
24037 OverlayViewOnAdd : true,
24039 * @event OverlayViewOnRemove
24040 * Fires when OverlayView Draw
24041 * @param {Roo.bootstrap.LocationPicker} this
24043 OverlayViewOnRemove : true,
24045 * @event OverlayViewShow
24046 * Fires when OverlayView Draw
24047 * @param {Roo.bootstrap.LocationPicker} this
24048 * @param {Pixel} cpx
24050 OverlayViewShow : true,
24052 * @event OverlayViewHide
24053 * Fires when OverlayView Draw
24054 * @param {Roo.bootstrap.LocationPicker} this
24056 OverlayViewHide : true,
24058 * @event loadexception
24059 * Fires when load google lib failed.
24060 * @param {Roo.bootstrap.LocationPicker} this
24062 loadexception : true
24067 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
24069 gMapContext: false,
24075 mapTypeControl: false,
24076 disableDoubleClickZoom: false,
24078 streetViewControl: false,
24082 enableAutocomplete: false,
24083 enableReverseGeocode: true,
24086 getAutoCreate: function()
24091 cls: 'roo-location-picker'
24097 initEvents: function(ct, position)
24099 if(!this.el.getWidth() || this.isApplied()){
24103 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24108 initial: function()
24110 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24111 this.fireEvent('loadexception', this);
24115 if(!this.mapTypeId){
24116 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24119 this.gMapContext = this.GMapContext();
24121 this.initOverlayView();
24123 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24127 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24128 _this.setPosition(_this.gMapContext.marker.position);
24131 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24132 _this.fireEvent('mapClick', this, event);
24136 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24137 _this.fireEvent('mapRightClick', this, event);
24141 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24142 _this.fireEvent('markerClick', this, event);
24146 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24147 _this.fireEvent('markerRightClick', this, event);
24151 this.setPosition(this.gMapContext.location);
24153 this.fireEvent('initial', this, this.gMapContext.location);
24156 initOverlayView: function()
24160 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24164 _this.fireEvent('OverlayViewDraw', _this);
24169 _this.fireEvent('OverlayViewOnAdd', _this);
24172 onRemove: function()
24174 _this.fireEvent('OverlayViewOnRemove', _this);
24177 show: function(cpx)
24179 _this.fireEvent('OverlayViewShow', _this, cpx);
24184 _this.fireEvent('OverlayViewHide', _this);
24190 fromLatLngToContainerPixel: function(event)
24192 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24195 isApplied: function()
24197 return this.getGmapContext() == false ? false : true;
24200 getGmapContext: function()
24202 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24205 GMapContext: function()
24207 var position = new google.maps.LatLng(this.latitude, this.longitude);
24209 var _map = new google.maps.Map(this.el.dom, {
24212 mapTypeId: this.mapTypeId,
24213 mapTypeControl: this.mapTypeControl,
24214 disableDoubleClickZoom: this.disableDoubleClickZoom,
24215 scrollwheel: this.scrollwheel,
24216 streetViewControl: this.streetViewControl,
24217 locationName: this.locationName,
24218 draggable: this.draggable,
24219 enableAutocomplete: this.enableAutocomplete,
24220 enableReverseGeocode: this.enableReverseGeocode
24223 var _marker = new google.maps.Marker({
24224 position: position,
24226 title: this.markerTitle,
24227 draggable: this.draggable
24234 location: position,
24235 radius: this.radius,
24236 locationName: this.locationName,
24237 addressComponents: {
24238 formatted_address: null,
24239 addressLine1: null,
24240 addressLine2: null,
24242 streetNumber: null,
24246 stateOrProvince: null
24249 domContainer: this.el.dom,
24250 geodecoder: new google.maps.Geocoder()
24254 drawCircle: function(center, radius, options)
24256 if (this.gMapContext.circle != null) {
24257 this.gMapContext.circle.setMap(null);
24261 options = Roo.apply({}, options, {
24262 strokeColor: "#0000FF",
24263 strokeOpacity: .35,
24265 fillColor: "#0000FF",
24269 options.map = this.gMapContext.map;
24270 options.radius = radius;
24271 options.center = center;
24272 this.gMapContext.circle = new google.maps.Circle(options);
24273 return this.gMapContext.circle;
24279 setPosition: function(location)
24281 this.gMapContext.location = location;
24282 this.gMapContext.marker.setPosition(location);
24283 this.gMapContext.map.panTo(location);
24284 this.drawCircle(location, this.gMapContext.radius, {});
24288 if (this.gMapContext.settings.enableReverseGeocode) {
24289 this.gMapContext.geodecoder.geocode({
24290 latLng: this.gMapContext.location
24291 }, function(results, status) {
24293 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24294 _this.gMapContext.locationName = results[0].formatted_address;
24295 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24297 _this.fireEvent('positionchanged', this, location);
24304 this.fireEvent('positionchanged', this, location);
24309 google.maps.event.trigger(this.gMapContext.map, "resize");
24311 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24313 this.fireEvent('resize', this);
24316 setPositionByLatLng: function(latitude, longitude)
24318 this.setPosition(new google.maps.LatLng(latitude, longitude));
24321 getCurrentPosition: function()
24324 latitude: this.gMapContext.location.lat(),
24325 longitude: this.gMapContext.location.lng()
24329 getAddressName: function()
24331 return this.gMapContext.locationName;
24334 getAddressComponents: function()
24336 return this.gMapContext.addressComponents;
24339 address_component_from_google_geocode: function(address_components)
24343 for (var i = 0; i < address_components.length; i++) {
24344 var component = address_components[i];
24345 if (component.types.indexOf("postal_code") >= 0) {
24346 result.postalCode = component.short_name;
24347 } else if (component.types.indexOf("street_number") >= 0) {
24348 result.streetNumber = component.short_name;
24349 } else if (component.types.indexOf("route") >= 0) {
24350 result.streetName = component.short_name;
24351 } else if (component.types.indexOf("neighborhood") >= 0) {
24352 result.city = component.short_name;
24353 } else if (component.types.indexOf("locality") >= 0) {
24354 result.city = component.short_name;
24355 } else if (component.types.indexOf("sublocality") >= 0) {
24356 result.district = component.short_name;
24357 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24358 result.stateOrProvince = component.short_name;
24359 } else if (component.types.indexOf("country") >= 0) {
24360 result.country = component.short_name;
24364 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24365 result.addressLine2 = "";
24369 setZoomLevel: function(zoom)
24371 this.gMapContext.map.setZoom(zoom);
24384 this.fireEvent('show', this);
24395 this.fireEvent('hide', this);
24400 Roo.apply(Roo.bootstrap.LocationPicker, {
24402 OverlayView : function(map, options)
24404 options = options || {};
24418 * @class Roo.bootstrap.Alert
24419 * @extends Roo.bootstrap.Component
24420 * Bootstrap Alert class
24421 * @cfg {String} title The title of alert
24422 * @cfg {String} html The content of alert
24423 * @cfg {String} weight ( success | info | warning | danger )
24424 * @cfg {String} faicon font-awesomeicon
24427 * Create a new alert
24428 * @param {Object} config The config object
24432 Roo.bootstrap.Alert = function(config){
24433 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24437 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
24444 getAutoCreate : function()
24453 cls : 'roo-alert-icon'
24458 cls : 'roo-alert-title',
24463 cls : 'roo-alert-text',
24470 cfg.cn[0].cls += ' fa ' + this.faicon;
24474 cfg.cls += ' alert-' + this.weight;
24480 initEvents: function()
24482 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24485 setTitle : function(str)
24487 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24490 setText : function(str)
24492 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24495 setWeight : function(weight)
24498 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24501 this.weight = weight;
24503 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24506 setIcon : function(icon)
24509 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24512 this.faicon = icon;
24514 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24535 * @class Roo.bootstrap.UploadCropbox
24536 * @extends Roo.bootstrap.Component
24537 * Bootstrap UploadCropbox class
24538 * @cfg {String} emptyText show when image has been loaded
24539 * @cfg {String} rotateNotify show when image too small to rotate
24540 * @cfg {Number} errorTimeout default 3000
24541 * @cfg {Number} minWidth default 300
24542 * @cfg {Number} minHeight default 300
24543 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24544 * @cfg {Boolean} isDocument (true|false) default false
24545 * @cfg {String} url action url
24546 * @cfg {String} paramName default 'imageUpload'
24547 * @cfg {String} method default POST
24548 * @cfg {Boolean} loadMask (true|false) default true
24549 * @cfg {Boolean} loadingText default 'Loading...'
24552 * Create a new UploadCropbox
24553 * @param {Object} config The config object
24556 Roo.bootstrap.UploadCropbox = function(config){
24557 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24561 * @event beforeselectfile
24562 * Fire before select file
24563 * @param {Roo.bootstrap.UploadCropbox} this
24565 "beforeselectfile" : true,
24568 * Fire after initEvent
24569 * @param {Roo.bootstrap.UploadCropbox} this
24574 * Fire after initEvent
24575 * @param {Roo.bootstrap.UploadCropbox} this
24576 * @param {String} data
24581 * Fire when preparing the file data
24582 * @param {Roo.bootstrap.UploadCropbox} this
24583 * @param {Object} file
24588 * Fire when get exception
24589 * @param {Roo.bootstrap.UploadCropbox} this
24590 * @param {XMLHttpRequest} xhr
24592 "exception" : true,
24594 * @event beforeloadcanvas
24595 * Fire before load the canvas
24596 * @param {Roo.bootstrap.UploadCropbox} this
24597 * @param {String} src
24599 "beforeloadcanvas" : true,
24602 * Fire when trash image
24603 * @param {Roo.bootstrap.UploadCropbox} this
24608 * Fire when download the image
24609 * @param {Roo.bootstrap.UploadCropbox} this
24613 * @event footerbuttonclick
24614 * Fire when footerbuttonclick
24615 * @param {Roo.bootstrap.UploadCropbox} this
24616 * @param {String} type
24618 "footerbuttonclick" : true,
24622 * @param {Roo.bootstrap.UploadCropbox} this
24627 * Fire when rotate the image
24628 * @param {Roo.bootstrap.UploadCropbox} this
24629 * @param {String} pos
24634 * Fire when inspect the file
24635 * @param {Roo.bootstrap.UploadCropbox} this
24636 * @param {Object} file
24641 * Fire when xhr upload the file
24642 * @param {Roo.bootstrap.UploadCropbox} this
24643 * @param {Object} data
24648 * Fire when arrange the file data
24649 * @param {Roo.bootstrap.UploadCropbox} this
24650 * @param {Object} formData
24655 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24658 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24660 emptyText : 'Click to upload image',
24661 rotateNotify : 'Image is too small to rotate',
24662 errorTimeout : 3000,
24676 cropType : 'image/jpeg',
24678 canvasLoaded : false,
24679 isDocument : false,
24681 paramName : 'imageUpload',
24683 loadingText : 'Loading...',
24686 getAutoCreate : function()
24690 cls : 'roo-upload-cropbox',
24694 cls : 'roo-upload-cropbox-selector',
24699 cls : 'roo-upload-cropbox-body',
24700 style : 'cursor:pointer',
24704 cls : 'roo-upload-cropbox-preview'
24708 cls : 'roo-upload-cropbox-thumb'
24712 cls : 'roo-upload-cropbox-empty-notify',
24713 html : this.emptyText
24717 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24718 html : this.rotateNotify
24724 cls : 'roo-upload-cropbox-footer',
24727 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24737 onRender : function(ct, position)
24739 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24741 if (this.buttons.length) {
24743 Roo.each(this.buttons, function(bb) {
24745 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24747 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24753 this.maskEl = this.el;
24757 initEvents : function()
24759 this.urlAPI = (window.createObjectURL && window) ||
24760 (window.URL && URL.revokeObjectURL && URL) ||
24761 (window.webkitURL && webkitURL);
24763 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24764 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24766 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24767 this.selectorEl.hide();
24769 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24770 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24772 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24773 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24774 this.thumbEl.hide();
24776 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24777 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24779 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24780 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24781 this.errorEl.hide();
24783 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24784 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24785 this.footerEl.hide();
24787 this.setThumbBoxSize();
24793 this.fireEvent('initial', this);
24800 window.addEventListener("resize", function() { _this.resize(); } );
24802 this.bodyEl.on('click', this.beforeSelectFile, this);
24805 this.bodyEl.on('touchstart', this.onTouchStart, this);
24806 this.bodyEl.on('touchmove', this.onTouchMove, this);
24807 this.bodyEl.on('touchend', this.onTouchEnd, this);
24811 this.bodyEl.on('mousedown', this.onMouseDown, this);
24812 this.bodyEl.on('mousemove', this.onMouseMove, this);
24813 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24814 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24815 Roo.get(document).on('mouseup', this.onMouseUp, this);
24818 this.selectorEl.on('change', this.onFileSelected, this);
24824 this.baseScale = 1;
24826 this.baseRotate = 1;
24827 this.dragable = false;
24828 this.pinching = false;
24831 this.cropData = false;
24832 this.notifyEl.dom.innerHTML = this.emptyText;
24834 this.selectorEl.dom.value = '';
24838 resize : function()
24840 if(this.fireEvent('resize', this) != false){
24841 this.setThumbBoxPosition();
24842 this.setCanvasPosition();
24846 onFooterButtonClick : function(e, el, o, type)
24849 case 'rotate-left' :
24850 this.onRotateLeft(e);
24852 case 'rotate-right' :
24853 this.onRotateRight(e);
24856 this.beforeSelectFile(e);
24871 this.fireEvent('footerbuttonclick', this, type);
24874 beforeSelectFile : function(e)
24876 e.preventDefault();
24878 if(this.fireEvent('beforeselectfile', this) != false){
24879 this.selectorEl.dom.click();
24883 onFileSelected : function(e)
24885 e.preventDefault();
24887 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24891 var file = this.selectorEl.dom.files[0];
24893 if(this.fireEvent('inspect', this, file) != false){
24894 this.prepare(file);
24899 trash : function(e)
24901 this.fireEvent('trash', this);
24904 download : function(e)
24906 this.fireEvent('download', this);
24909 loadCanvas : function(src)
24911 if(this.fireEvent('beforeloadcanvas', this, src) != false){
24915 this.imageEl = document.createElement('img');
24919 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24921 this.imageEl.src = src;
24925 onLoadCanvas : function()
24927 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24928 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24930 this.bodyEl.un('click', this.beforeSelectFile, this);
24932 this.notifyEl.hide();
24933 this.thumbEl.show();
24934 this.footerEl.show();
24936 this.baseRotateLevel();
24938 if(this.isDocument){
24939 this.setThumbBoxSize();
24942 this.setThumbBoxPosition();
24944 this.baseScaleLevel();
24950 this.canvasLoaded = true;
24953 this.maskEl.unmask();
24958 setCanvasPosition : function()
24960 if(!this.canvasEl){
24964 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24965 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24967 this.previewEl.setLeft(pw);
24968 this.previewEl.setTop(ph);
24972 onMouseDown : function(e)
24976 this.dragable = true;
24977 this.pinching = false;
24979 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24980 this.dragable = false;
24984 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24985 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24989 onMouseMove : function(e)
24993 if(!this.canvasLoaded){
24997 if (!this.dragable){
25001 var minX = Math.ceil(this.thumbEl.getLeft(true));
25002 var minY = Math.ceil(this.thumbEl.getTop(true));
25004 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25005 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25007 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25008 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25010 x = x - this.mouseX;
25011 y = y - this.mouseY;
25013 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25014 var bgY = Math.ceil(y + this.previewEl.getTop(true));
25016 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25017 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25019 this.previewEl.setLeft(bgX);
25020 this.previewEl.setTop(bgY);
25022 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25023 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25026 onMouseUp : function(e)
25030 this.dragable = false;
25033 onMouseWheel : function(e)
25037 this.startScale = this.scale;
25039 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25041 if(!this.zoomable()){
25042 this.scale = this.startScale;
25051 zoomable : function()
25053 var minScale = this.thumbEl.getWidth() / this.minWidth;
25055 if(this.minWidth < this.minHeight){
25056 minScale = this.thumbEl.getHeight() / this.minHeight;
25059 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25060 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25064 (this.rotate == 0 || this.rotate == 180) &&
25066 width > this.imageEl.OriginWidth ||
25067 height > this.imageEl.OriginHeight ||
25068 (width < this.minWidth && height < this.minHeight)
25076 (this.rotate == 90 || this.rotate == 270) &&
25078 width > this.imageEl.OriginWidth ||
25079 height > this.imageEl.OriginHeight ||
25080 (width < this.minHeight && height < this.minWidth)
25087 !this.isDocument &&
25088 (this.rotate == 0 || this.rotate == 180) &&
25090 width < this.minWidth ||
25091 width > this.imageEl.OriginWidth ||
25092 height < this.minHeight ||
25093 height > this.imageEl.OriginHeight
25100 !this.isDocument &&
25101 (this.rotate == 90 || this.rotate == 270) &&
25103 width < this.minHeight ||
25104 width > this.imageEl.OriginWidth ||
25105 height < this.minWidth ||
25106 height > this.imageEl.OriginHeight
25116 onRotateLeft : function(e)
25118 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25120 var minScale = this.thumbEl.getWidth() / this.minWidth;
25122 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25123 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25125 this.startScale = this.scale;
25127 while (this.getScaleLevel() < minScale){
25129 this.scale = this.scale + 1;
25131 if(!this.zoomable()){
25136 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25137 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25142 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25149 this.scale = this.startScale;
25151 this.onRotateFail();
25156 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25158 if(this.isDocument){
25159 this.setThumbBoxSize();
25160 this.setThumbBoxPosition();
25161 this.setCanvasPosition();
25166 this.fireEvent('rotate', this, 'left');
25170 onRotateRight : function(e)
25172 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25174 var minScale = this.thumbEl.getWidth() / this.minWidth;
25176 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25177 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25179 this.startScale = this.scale;
25181 while (this.getScaleLevel() < minScale){
25183 this.scale = this.scale + 1;
25185 if(!this.zoomable()){
25190 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25191 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25196 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25203 this.scale = this.startScale;
25205 this.onRotateFail();
25210 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25212 if(this.isDocument){
25213 this.setThumbBoxSize();
25214 this.setThumbBoxPosition();
25215 this.setCanvasPosition();
25220 this.fireEvent('rotate', this, 'right');
25223 onRotateFail : function()
25225 this.errorEl.show(true);
25229 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25234 this.previewEl.dom.innerHTML = '';
25236 var canvasEl = document.createElement("canvas");
25238 var contextEl = canvasEl.getContext("2d");
25240 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25241 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25242 var center = this.imageEl.OriginWidth / 2;
25244 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25245 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25246 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25247 center = this.imageEl.OriginHeight / 2;
25250 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25252 contextEl.translate(center, center);
25253 contextEl.rotate(this.rotate * Math.PI / 180);
25255 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25257 this.canvasEl = document.createElement("canvas");
25259 this.contextEl = this.canvasEl.getContext("2d");
25261 switch (this.rotate) {
25264 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25265 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25267 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25272 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25273 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25275 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25276 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);
25280 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25285 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25286 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25288 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25289 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);
25293 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);
25298 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25299 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25301 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25302 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25306 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);
25313 this.previewEl.appendChild(this.canvasEl);
25315 this.setCanvasPosition();
25320 if(!this.canvasLoaded){
25324 var imageCanvas = document.createElement("canvas");
25326 var imageContext = imageCanvas.getContext("2d");
25328 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25329 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25331 var center = imageCanvas.width / 2;
25333 imageContext.translate(center, center);
25335 imageContext.rotate(this.rotate * Math.PI / 180);
25337 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25339 var canvas = document.createElement("canvas");
25341 var context = canvas.getContext("2d");
25343 canvas.width = this.minWidth;
25344 canvas.height = this.minHeight;
25346 switch (this.rotate) {
25349 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25350 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25352 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25353 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25355 var targetWidth = this.minWidth - 2 * x;
25356 var targetHeight = this.minHeight - 2 * y;
25360 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25361 scale = targetWidth / width;
25364 if(x > 0 && y == 0){
25365 scale = targetHeight / height;
25368 if(x > 0 && y > 0){
25369 scale = targetWidth / width;
25371 if(width < height){
25372 scale = targetHeight / height;
25376 context.scale(scale, scale);
25378 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25379 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25381 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25382 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25384 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25389 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25390 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25392 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25393 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25395 var targetWidth = this.minWidth - 2 * x;
25396 var targetHeight = this.minHeight - 2 * y;
25400 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25401 scale = targetWidth / width;
25404 if(x > 0 && y == 0){
25405 scale = targetHeight / height;
25408 if(x > 0 && y > 0){
25409 scale = targetWidth / width;
25411 if(width < height){
25412 scale = targetHeight / height;
25416 context.scale(scale, scale);
25418 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25419 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25421 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25422 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25424 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25426 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25431 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25432 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25434 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25435 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25437 var targetWidth = this.minWidth - 2 * x;
25438 var targetHeight = this.minHeight - 2 * y;
25442 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25443 scale = targetWidth / width;
25446 if(x > 0 && y == 0){
25447 scale = targetHeight / height;
25450 if(x > 0 && y > 0){
25451 scale = targetWidth / width;
25453 if(width < height){
25454 scale = targetHeight / height;
25458 context.scale(scale, scale);
25460 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25461 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25463 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25464 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25466 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25467 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25469 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25474 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25475 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25477 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25478 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25480 var targetWidth = this.minWidth - 2 * x;
25481 var targetHeight = this.minHeight - 2 * y;
25485 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25486 scale = targetWidth / width;
25489 if(x > 0 && y == 0){
25490 scale = targetHeight / height;
25493 if(x > 0 && y > 0){
25494 scale = targetWidth / width;
25496 if(width < height){
25497 scale = targetHeight / height;
25501 context.scale(scale, scale);
25503 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25504 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25506 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25507 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25509 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25511 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25518 this.cropData = canvas.toDataURL(this.cropType);
25520 if(this.fireEvent('crop', this, this.cropData) !== false){
25521 this.process(this.file, this.cropData);
25528 setThumbBoxSize : function()
25532 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25533 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25534 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25536 this.minWidth = width;
25537 this.minHeight = height;
25539 if(this.rotate == 90 || this.rotate == 270){
25540 this.minWidth = height;
25541 this.minHeight = width;
25546 width = Math.ceil(this.minWidth * height / this.minHeight);
25548 if(this.minWidth > this.minHeight){
25550 height = Math.ceil(this.minHeight * width / this.minWidth);
25553 this.thumbEl.setStyle({
25554 width : width + 'px',
25555 height : height + 'px'
25562 setThumbBoxPosition : function()
25564 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25565 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25567 this.thumbEl.setLeft(x);
25568 this.thumbEl.setTop(y);
25572 baseRotateLevel : function()
25574 this.baseRotate = 1;
25577 typeof(this.exif) != 'undefined' &&
25578 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25579 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25581 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25584 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25588 baseScaleLevel : function()
25592 if(this.isDocument){
25594 if(this.baseRotate == 6 || this.baseRotate == 8){
25596 height = this.thumbEl.getHeight();
25597 this.baseScale = height / this.imageEl.OriginWidth;
25599 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25600 width = this.thumbEl.getWidth();
25601 this.baseScale = width / this.imageEl.OriginHeight;
25607 height = this.thumbEl.getHeight();
25608 this.baseScale = height / this.imageEl.OriginHeight;
25610 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25611 width = this.thumbEl.getWidth();
25612 this.baseScale = width / this.imageEl.OriginWidth;
25618 if(this.baseRotate == 6 || this.baseRotate == 8){
25620 width = this.thumbEl.getHeight();
25621 this.baseScale = width / this.imageEl.OriginHeight;
25623 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25624 height = this.thumbEl.getWidth();
25625 this.baseScale = height / this.imageEl.OriginHeight;
25628 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25629 height = this.thumbEl.getWidth();
25630 this.baseScale = height / this.imageEl.OriginHeight;
25632 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25633 width = this.thumbEl.getHeight();
25634 this.baseScale = width / this.imageEl.OriginWidth;
25641 width = this.thumbEl.getWidth();
25642 this.baseScale = width / this.imageEl.OriginWidth;
25644 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25645 height = this.thumbEl.getHeight();
25646 this.baseScale = height / this.imageEl.OriginHeight;
25649 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25651 height = this.thumbEl.getHeight();
25652 this.baseScale = height / this.imageEl.OriginHeight;
25654 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25655 width = this.thumbEl.getWidth();
25656 this.baseScale = width / this.imageEl.OriginWidth;
25664 getScaleLevel : function()
25666 return this.baseScale * Math.pow(1.1, this.scale);
25669 onTouchStart : function(e)
25671 if(!this.canvasLoaded){
25672 this.beforeSelectFile(e);
25676 var touches = e.browserEvent.touches;
25682 if(touches.length == 1){
25683 this.onMouseDown(e);
25687 if(touches.length != 2){
25693 for(var i = 0, finger; finger = touches[i]; i++){
25694 coords.push(finger.pageX, finger.pageY);
25697 var x = Math.pow(coords[0] - coords[2], 2);
25698 var y = Math.pow(coords[1] - coords[3], 2);
25700 this.startDistance = Math.sqrt(x + y);
25702 this.startScale = this.scale;
25704 this.pinching = true;
25705 this.dragable = false;
25709 onTouchMove : function(e)
25711 if(!this.pinching && !this.dragable){
25715 var touches = e.browserEvent.touches;
25722 this.onMouseMove(e);
25728 for(var i = 0, finger; finger = touches[i]; i++){
25729 coords.push(finger.pageX, finger.pageY);
25732 var x = Math.pow(coords[0] - coords[2], 2);
25733 var y = Math.pow(coords[1] - coords[3], 2);
25735 this.endDistance = Math.sqrt(x + y);
25737 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25739 if(!this.zoomable()){
25740 this.scale = this.startScale;
25748 onTouchEnd : function(e)
25750 this.pinching = false;
25751 this.dragable = false;
25755 process : function(file, crop)
25758 this.maskEl.mask(this.loadingText);
25761 this.xhr = new XMLHttpRequest();
25763 file.xhr = this.xhr;
25765 this.xhr.open(this.method, this.url, true);
25768 "Accept": "application/json",
25769 "Cache-Control": "no-cache",
25770 "X-Requested-With": "XMLHttpRequest"
25773 for (var headerName in headers) {
25774 var headerValue = headers[headerName];
25776 this.xhr.setRequestHeader(headerName, headerValue);
25782 this.xhr.onload = function()
25784 _this.xhrOnLoad(_this.xhr);
25787 this.xhr.onerror = function()
25789 _this.xhrOnError(_this.xhr);
25792 var formData = new FormData();
25794 formData.append('returnHTML', 'NO');
25797 formData.append('crop', crop);
25800 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25801 formData.append(this.paramName, file, file.name);
25804 if(typeof(file.filename) != 'undefined'){
25805 formData.append('filename', file.filename);
25808 if(typeof(file.mimetype) != 'undefined'){
25809 formData.append('mimetype', file.mimetype);
25812 if(this.fireEvent('arrange', this, formData) != false){
25813 this.xhr.send(formData);
25817 xhrOnLoad : function(xhr)
25820 this.maskEl.unmask();
25823 if (xhr.readyState !== 4) {
25824 this.fireEvent('exception', this, xhr);
25828 var response = Roo.decode(xhr.responseText);
25830 if(!response.success){
25831 this.fireEvent('exception', this, xhr);
25835 var response = Roo.decode(xhr.responseText);
25837 this.fireEvent('upload', this, response);
25841 xhrOnError : function()
25844 this.maskEl.unmask();
25847 Roo.log('xhr on error');
25849 var response = Roo.decode(xhr.responseText);
25855 prepare : function(file)
25858 this.maskEl.mask(this.loadingText);
25864 if(typeof(file) === 'string'){
25865 this.loadCanvas(file);
25869 if(!file || !this.urlAPI){
25874 this.cropType = file.type;
25878 if(this.fireEvent('prepare', this, this.file) != false){
25880 var reader = new FileReader();
25882 reader.onload = function (e) {
25883 if (e.target.error) {
25884 Roo.log(e.target.error);
25888 var buffer = e.target.result,
25889 dataView = new DataView(buffer),
25891 maxOffset = dataView.byteLength - 4,
25895 if (dataView.getUint16(0) === 0xffd8) {
25896 while (offset < maxOffset) {
25897 markerBytes = dataView.getUint16(offset);
25899 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25900 markerLength = dataView.getUint16(offset + 2) + 2;
25901 if (offset + markerLength > dataView.byteLength) {
25902 Roo.log('Invalid meta data: Invalid segment size.');
25906 if(markerBytes == 0xffe1){
25907 _this.parseExifData(
25914 offset += markerLength;
25924 var url = _this.urlAPI.createObjectURL(_this.file);
25926 _this.loadCanvas(url);
25931 reader.readAsArrayBuffer(this.file);
25937 parseExifData : function(dataView, offset, length)
25939 var tiffOffset = offset + 10,
25943 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25944 // No Exif data, might be XMP data instead
25948 // Check for the ASCII code for "Exif" (0x45786966):
25949 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25950 // No Exif data, might be XMP data instead
25953 if (tiffOffset + 8 > dataView.byteLength) {
25954 Roo.log('Invalid Exif data: Invalid segment size.');
25957 // Check for the two null bytes:
25958 if (dataView.getUint16(offset + 8) !== 0x0000) {
25959 Roo.log('Invalid Exif data: Missing byte alignment offset.');
25962 // Check the byte alignment:
25963 switch (dataView.getUint16(tiffOffset)) {
25965 littleEndian = true;
25968 littleEndian = false;
25971 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25974 // Check for the TIFF tag marker (0x002A):
25975 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25976 Roo.log('Invalid Exif data: Missing TIFF marker.');
25979 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25980 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25982 this.parseExifTags(
25985 tiffOffset + dirOffset,
25990 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25995 if (dirOffset + 6 > dataView.byteLength) {
25996 Roo.log('Invalid Exif data: Invalid directory offset.');
25999 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26000 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26001 if (dirEndOffset + 4 > dataView.byteLength) {
26002 Roo.log('Invalid Exif data: Invalid directory size.');
26005 for (i = 0; i < tagsNumber; i += 1) {
26009 dirOffset + 2 + 12 * i, // tag offset
26013 // Return the offset to the next directory:
26014 return dataView.getUint32(dirEndOffset, littleEndian);
26017 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
26019 var tag = dataView.getUint16(offset, littleEndian);
26021 this.exif[tag] = this.getExifValue(
26025 dataView.getUint16(offset + 2, littleEndian), // tag type
26026 dataView.getUint32(offset + 4, littleEndian), // tag length
26031 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26033 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26042 Roo.log('Invalid Exif data: Invalid tag type.');
26046 tagSize = tagType.size * length;
26047 // Determine if the value is contained in the dataOffset bytes,
26048 // or if the value at the dataOffset is a pointer to the actual data:
26049 dataOffset = tagSize > 4 ?
26050 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26051 if (dataOffset + tagSize > dataView.byteLength) {
26052 Roo.log('Invalid Exif data: Invalid data offset.');
26055 if (length === 1) {
26056 return tagType.getValue(dataView, dataOffset, littleEndian);
26059 for (i = 0; i < length; i += 1) {
26060 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26063 if (tagType.ascii) {
26065 // Concatenate the chars:
26066 for (i = 0; i < values.length; i += 1) {
26068 // Ignore the terminating NULL byte(s):
26069 if (c === '\u0000') {
26081 Roo.apply(Roo.bootstrap.UploadCropbox, {
26083 'Orientation': 0x0112
26087 1: 0, //'top-left',
26089 3: 180, //'bottom-right',
26090 // 4: 'bottom-left',
26092 6: 90, //'right-top',
26093 // 7: 'right-bottom',
26094 8: 270 //'left-bottom'
26098 // byte, 8-bit unsigned int:
26100 getValue: function (dataView, dataOffset) {
26101 return dataView.getUint8(dataOffset);
26105 // ascii, 8-bit byte:
26107 getValue: function (dataView, dataOffset) {
26108 return String.fromCharCode(dataView.getUint8(dataOffset));
26113 // short, 16 bit int:
26115 getValue: function (dataView, dataOffset, littleEndian) {
26116 return dataView.getUint16(dataOffset, littleEndian);
26120 // long, 32 bit int:
26122 getValue: function (dataView, dataOffset, littleEndian) {
26123 return dataView.getUint32(dataOffset, littleEndian);
26127 // rational = two long values, first is numerator, second is denominator:
26129 getValue: function (dataView, dataOffset, littleEndian) {
26130 return dataView.getUint32(dataOffset, littleEndian) /
26131 dataView.getUint32(dataOffset + 4, littleEndian);
26135 // slong, 32 bit signed int:
26137 getValue: function (dataView, dataOffset, littleEndian) {
26138 return dataView.getInt32(dataOffset, littleEndian);
26142 // srational, two slongs, first is numerator, second is denominator:
26144 getValue: function (dataView, dataOffset, littleEndian) {
26145 return dataView.getInt32(dataOffset, littleEndian) /
26146 dataView.getInt32(dataOffset + 4, littleEndian);
26156 cls : 'btn-group roo-upload-cropbox-rotate-left',
26157 action : 'rotate-left',
26161 cls : 'btn btn-default',
26162 html : '<i class="fa fa-undo"></i>'
26168 cls : 'btn-group roo-upload-cropbox-picture',
26169 action : 'picture',
26173 cls : 'btn btn-default',
26174 html : '<i class="fa fa-picture-o"></i>'
26180 cls : 'btn-group roo-upload-cropbox-rotate-right',
26181 action : 'rotate-right',
26185 cls : 'btn btn-default',
26186 html : '<i class="fa fa-repeat"></i>'
26194 cls : 'btn-group roo-upload-cropbox-rotate-left',
26195 action : 'rotate-left',
26199 cls : 'btn btn-default',
26200 html : '<i class="fa fa-undo"></i>'
26206 cls : 'btn-group roo-upload-cropbox-download',
26207 action : 'download',
26211 cls : 'btn btn-default',
26212 html : '<i class="fa fa-download"></i>'
26218 cls : 'btn-group roo-upload-cropbox-crop',
26223 cls : 'btn btn-default',
26224 html : '<i class="fa fa-crop"></i>'
26230 cls : 'btn-group roo-upload-cropbox-trash',
26235 cls : 'btn btn-default',
26236 html : '<i class="fa fa-trash"></i>'
26242 cls : 'btn-group roo-upload-cropbox-rotate-right',
26243 action : 'rotate-right',
26247 cls : 'btn btn-default',
26248 html : '<i class="fa fa-repeat"></i>'
26256 cls : 'btn-group roo-upload-cropbox-rotate-left',
26257 action : 'rotate-left',
26261 cls : 'btn btn-default',
26262 html : '<i class="fa fa-undo"></i>'
26268 cls : 'btn-group roo-upload-cropbox-rotate-right',
26269 action : 'rotate-right',
26273 cls : 'btn btn-default',
26274 html : '<i class="fa fa-repeat"></i>'
26287 * @class Roo.bootstrap.DocumentManager
26288 * @extends Roo.bootstrap.Component
26289 * Bootstrap DocumentManager class
26290 * @cfg {String} paramName default 'imageUpload'
26291 * @cfg {String} method default POST
26292 * @cfg {String} url action url
26293 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26294 * @cfg {Boolean} multiple multiple upload default true
26295 * @cfg {Number} thumbSize default 300
26296 * @cfg {String} fieldLabel
26297 * @cfg {Number} labelWidth default 4
26298 * @cfg {String} labelAlign (left|top) default left
26299 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26302 * Create a new DocumentManager
26303 * @param {Object} config The config object
26306 Roo.bootstrap.DocumentManager = function(config){
26307 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26312 * Fire when initial the DocumentManager
26313 * @param {Roo.bootstrap.DocumentManager} this
26318 * inspect selected file
26319 * @param {Roo.bootstrap.DocumentManager} this
26320 * @param {File} file
26325 * Fire when xhr load exception
26326 * @param {Roo.bootstrap.DocumentManager} this
26327 * @param {XMLHttpRequest} xhr
26329 "exception" : true,
26332 * prepare the form data
26333 * @param {Roo.bootstrap.DocumentManager} this
26334 * @param {Object} formData
26339 * Fire when remove the file
26340 * @param {Roo.bootstrap.DocumentManager} this
26341 * @param {Object} file
26346 * Fire after refresh the file
26347 * @param {Roo.bootstrap.DocumentManager} this
26352 * Fire after click the image
26353 * @param {Roo.bootstrap.DocumentManager} this
26354 * @param {Object} file
26359 * Fire when upload a image and editable set to true
26360 * @param {Roo.bootstrap.DocumentManager} this
26361 * @param {Object} file
26365 * @event beforeselectfile
26366 * Fire before select file
26367 * @param {Roo.bootstrap.DocumentManager} this
26369 "beforeselectfile" : true,
26372 * Fire before process file
26373 * @param {Roo.bootstrap.DocumentManager} this
26374 * @param {Object} file
26381 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
26390 paramName : 'imageUpload',
26393 labelAlign : 'left',
26400 getAutoCreate : function()
26402 var managerWidget = {
26404 cls : 'roo-document-manager',
26408 cls : 'roo-document-manager-selector',
26413 cls : 'roo-document-manager-uploader',
26417 cls : 'roo-document-manager-upload-btn',
26418 html : '<i class="fa fa-plus"></i>'
26429 cls : 'column col-md-12',
26434 if(this.fieldLabel.length){
26439 cls : 'column col-md-12',
26440 html : this.fieldLabel
26444 cls : 'column col-md-12',
26449 if(this.labelAlign == 'left'){
26453 cls : 'column col-md-' + this.labelWidth,
26454 html : this.fieldLabel
26458 cls : 'column col-md-' + (12 - this.labelWidth),
26468 cls : 'row clearfix',
26476 initEvents : function()
26478 this.managerEl = this.el.select('.roo-document-manager', true).first();
26479 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26481 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26482 this.selectorEl.hide();
26485 this.selectorEl.attr('multiple', 'multiple');
26488 this.selectorEl.on('change', this.onFileSelected, this);
26490 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26491 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26493 this.uploader.on('click', this.onUploaderClick, this);
26495 this.renderProgressDialog();
26499 window.addEventListener("resize", function() { _this.refresh(); } );
26501 this.fireEvent('initial', this);
26504 renderProgressDialog : function()
26508 this.progressDialog = new Roo.bootstrap.Modal({
26509 cls : 'roo-document-manager-progress-dialog',
26510 allow_close : false,
26520 btnclick : function() {
26521 _this.uploadCancel();
26527 this.progressDialog.render(Roo.get(document.body));
26529 this.progress = new Roo.bootstrap.Progress({
26530 cls : 'roo-document-manager-progress',
26535 this.progress.render(this.progressDialog.getChildContainer());
26537 this.progressBar = new Roo.bootstrap.ProgressBar({
26538 cls : 'roo-document-manager-progress-bar',
26541 aria_valuemax : 12,
26545 this.progressBar.render(this.progress.getChildContainer());
26548 onUploaderClick : function(e)
26550 e.preventDefault();
26552 if(this.fireEvent('beforeselectfile', this) != false){
26553 this.selectorEl.dom.click();
26558 onFileSelected : function(e)
26560 e.preventDefault();
26562 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26566 Roo.each(this.selectorEl.dom.files, function(file){
26567 if(this.fireEvent('inspect', this, file) != false){
26568 this.files.push(file);
26578 this.selectorEl.dom.value = '';
26580 if(!this.files.length){
26584 if(this.boxes > 0 && this.files.length > this.boxes){
26585 this.files = this.files.slice(0, this.boxes);
26588 this.uploader.show();
26590 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26591 this.uploader.hide();
26600 Roo.each(this.files, function(file){
26602 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26603 var f = this.renderPreview(file);
26608 if(file.type.indexOf('image') != -1){
26609 this.delegates.push(
26611 _this.process(file);
26612 }).createDelegate(this)
26620 _this.process(file);
26621 }).createDelegate(this)
26626 this.files = files;
26628 this.delegates = this.delegates.concat(docs);
26630 if(!this.delegates.length){
26635 this.progressBar.aria_valuemax = this.delegates.length;
26642 arrange : function()
26644 if(!this.delegates.length){
26645 this.progressDialog.hide();
26650 var delegate = this.delegates.shift();
26652 this.progressDialog.show();
26654 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26656 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26661 refresh : function()
26663 this.uploader.show();
26665 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26666 this.uploader.hide();
26669 Roo.isTouch ? this.closable(false) : this.closable(true);
26671 this.fireEvent('refresh', this);
26674 onRemove : function(e, el, o)
26676 e.preventDefault();
26678 this.fireEvent('remove', this, o);
26682 remove : function(o)
26686 Roo.each(this.files, function(file){
26687 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26696 this.files = files;
26703 Roo.each(this.files, function(file){
26708 file.target.remove();
26717 onClick : function(e, el, o)
26719 e.preventDefault();
26721 this.fireEvent('click', this, o);
26725 closable : function(closable)
26727 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26729 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26741 xhrOnLoad : function(xhr)
26743 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26747 if (xhr.readyState !== 4) {
26749 this.fireEvent('exception', this, xhr);
26753 var response = Roo.decode(xhr.responseText);
26755 if(!response.success){
26757 this.fireEvent('exception', this, xhr);
26761 var file = this.renderPreview(response.data);
26763 this.files.push(file);
26769 xhrOnError : function(xhr)
26771 Roo.log('xhr on error');
26773 var response = Roo.decode(xhr.responseText);
26780 process : function(file)
26782 if(this.fireEvent('process', this, file) !== false){
26783 if(this.editable && file.type.indexOf('image') != -1){
26784 this.fireEvent('edit', this, file);
26788 this.uploadStart(file, false);
26795 uploadStart : function(file, crop)
26797 this.xhr = new XMLHttpRequest();
26799 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26804 file.xhr = this.xhr;
26806 this.managerEl.createChild({
26808 cls : 'roo-document-manager-loading',
26812 tooltip : file.name,
26813 cls : 'roo-document-manager-thumb',
26814 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26820 this.xhr.open(this.method, this.url, true);
26823 "Accept": "application/json",
26824 "Cache-Control": "no-cache",
26825 "X-Requested-With": "XMLHttpRequest"
26828 for (var headerName in headers) {
26829 var headerValue = headers[headerName];
26831 this.xhr.setRequestHeader(headerName, headerValue);
26837 this.xhr.onload = function()
26839 _this.xhrOnLoad(_this.xhr);
26842 this.xhr.onerror = function()
26844 _this.xhrOnError(_this.xhr);
26847 var formData = new FormData();
26849 formData.append('returnHTML', 'NO');
26852 formData.append('crop', crop);
26855 formData.append(this.paramName, file, file.name);
26857 if(this.fireEvent('prepare', this, formData) != false){
26858 this.xhr.send(formData);
26862 uploadCancel : function()
26869 this.delegates = [];
26871 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26878 renderPreview : function(file)
26880 if(typeof(file.target) != 'undefined' && file.target){
26884 var previewEl = this.managerEl.createChild({
26886 cls : 'roo-document-manager-preview',
26890 tooltip : file.filename,
26891 cls : 'roo-document-manager-thumb',
26892 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26897 html : '<i class="fa fa-times-circle"></i>'
26902 var close = previewEl.select('button.close', true).first();
26904 close.on('click', this.onRemove, this, file);
26906 file.target = previewEl;
26908 var image = previewEl.select('img', true).first();
26912 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26914 image.on('click', this.onClick, this, file);
26920 onPreviewLoad : function(file, image)
26922 if(typeof(file.target) == 'undefined' || !file.target){
26926 var width = image.dom.naturalWidth || image.dom.width;
26927 var height = image.dom.naturalHeight || image.dom.height;
26929 if(width > height){
26930 file.target.addClass('wide');
26934 file.target.addClass('tall');
26939 uploadFromSource : function(file, crop)
26941 this.xhr = new XMLHttpRequest();
26943 this.managerEl.createChild({
26945 cls : 'roo-document-manager-loading',
26949 tooltip : file.name,
26950 cls : 'roo-document-manager-thumb',
26951 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26957 this.xhr.open(this.method, this.url, true);
26960 "Accept": "application/json",
26961 "Cache-Control": "no-cache",
26962 "X-Requested-With": "XMLHttpRequest"
26965 for (var headerName in headers) {
26966 var headerValue = headers[headerName];
26968 this.xhr.setRequestHeader(headerName, headerValue);
26974 this.xhr.onload = function()
26976 _this.xhrOnLoad(_this.xhr);
26979 this.xhr.onerror = function()
26981 _this.xhrOnError(_this.xhr);
26984 var formData = new FormData();
26986 formData.append('returnHTML', 'NO');
26988 formData.append('crop', crop);
26990 if(typeof(file.filename) != 'undefined'){
26991 formData.append('filename', file.filename);
26994 if(typeof(file.mimetype) != 'undefined'){
26995 formData.append('mimetype', file.mimetype);
26998 if(this.fireEvent('prepare', this, formData) != false){
26999 this.xhr.send(formData);
27009 * @class Roo.bootstrap.DocumentViewer
27010 * @extends Roo.bootstrap.Component
27011 * Bootstrap DocumentViewer class
27014 * Create a new DocumentViewer
27015 * @param {Object} config The config object
27018 Roo.bootstrap.DocumentViewer = function(config){
27019 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27024 * Fire after initEvent
27025 * @param {Roo.bootstrap.DocumentViewer} this
27031 * @param {Roo.bootstrap.DocumentViewer} this
27036 * Fire after trash button
27037 * @param {Roo.bootstrap.DocumentViewer} this
27044 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
27046 getAutoCreate : function()
27050 cls : 'roo-document-viewer',
27054 cls : 'roo-document-viewer-body',
27058 cls : 'roo-document-viewer-thumb',
27062 cls : 'roo-document-viewer-image'
27070 cls : 'roo-document-viewer-footer',
27073 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27081 cls : 'btn btn-default roo-document-viewer-trash',
27082 html : '<i class="fa fa-trash"></i>'
27095 initEvents : function()
27098 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27099 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27101 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27102 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27104 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27105 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27107 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27108 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27110 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27111 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27113 this.bodyEl.on('click', this.onClick, this);
27115 this.trashBtn.on('click', this.onTrash, this);
27119 initial : function()
27121 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27124 this.fireEvent('initial', this);
27128 onClick : function(e)
27130 e.preventDefault();
27132 this.fireEvent('click', this);
27135 onTrash : function(e)
27137 e.preventDefault();
27139 this.fireEvent('trash', this);
27151 * @class Roo.bootstrap.NavProgressBar
27152 * @extends Roo.bootstrap.Component
27153 * Bootstrap NavProgressBar class
27156 * Create a new nav progress bar
27157 * @param {Object} config The config object
27160 Roo.bootstrap.NavProgressBar = function(config){
27161 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27163 this.bullets = this.bullets || [];
27165 // Roo.bootstrap.NavProgressBar.register(this);
27169 * Fires when the active item changes
27170 * @param {Roo.bootstrap.NavProgressBar} this
27171 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27172 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
27179 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
27184 getAutoCreate : function()
27186 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27190 cls : 'roo-navigation-bar-group',
27194 cls : 'roo-navigation-top-bar'
27198 cls : 'roo-navigation-bullets-bar',
27202 cls : 'roo-navigation-bar'
27209 cls : 'roo-navigation-bottom-bar'
27219 initEvents: function()
27224 onRender : function(ct, position)
27226 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27228 if(this.bullets.length){
27229 Roo.each(this.bullets, function(b){
27238 addItem : function(cfg)
27240 var item = new Roo.bootstrap.NavProgressItem(cfg);
27242 item.parentId = this.id;
27243 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27246 var top = new Roo.bootstrap.Element({
27248 cls : 'roo-navigation-bar-text'
27251 var bottom = new Roo.bootstrap.Element({
27253 cls : 'roo-navigation-bar-text'
27256 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27257 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27259 var topText = new Roo.bootstrap.Element({
27261 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27264 var bottomText = new Roo.bootstrap.Element({
27266 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27269 topText.onRender(top.el, null);
27270 bottomText.onRender(bottom.el, null);
27273 item.bottomEl = bottom;
27276 this.barItems.push(item);
27281 getActive : function()
27283 var active = false;
27285 Roo.each(this.barItems, function(v){
27287 if (!v.isActive()) {
27299 setActiveItem : function(item)
27303 Roo.each(this.barItems, function(v){
27304 if (v.rid == item.rid) {
27308 if (v.isActive()) {
27309 v.setActive(false);
27314 item.setActive(true);
27316 this.fireEvent('changed', this, item, prev);
27319 getBarItem: function(rid)
27323 Roo.each(this.barItems, function(e) {
27324 if (e.rid != rid) {
27335 indexOfItem : function(item)
27339 Roo.each(this.barItems, function(v, i){
27341 if (v.rid != item.rid) {
27352 setActiveNext : function()
27354 var i = this.indexOfItem(this.getActive());
27356 if (i > this.barItems.length) {
27360 this.setActiveItem(this.barItems[i+1]);
27363 setActivePrev : function()
27365 var i = this.indexOfItem(this.getActive());
27371 this.setActiveItem(this.barItems[i-1]);
27374 format : function()
27376 if(!this.barItems.length){
27380 var width = 100 / this.barItems.length;
27382 Roo.each(this.barItems, function(i){
27383 i.el.setStyle('width', width + '%');
27384 i.topEl.el.setStyle('width', width + '%');
27385 i.bottomEl.el.setStyle('width', width + '%');
27394 * Nav Progress Item
27399 * @class Roo.bootstrap.NavProgressItem
27400 * @extends Roo.bootstrap.Component
27401 * Bootstrap NavProgressItem class
27402 * @cfg {String} rid the reference id
27403 * @cfg {Boolean} active (true|false) Is item active default false
27404 * @cfg {Boolean} disabled (true|false) Is item active default false
27405 * @cfg {String} html
27406 * @cfg {String} position (top|bottom) text position default bottom
27407 * @cfg {String} icon show icon instead of number
27410 * Create a new NavProgressItem
27411 * @param {Object} config The config object
27413 Roo.bootstrap.NavProgressItem = function(config){
27414 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27419 * The raw click event for the entire grid.
27420 * @param {Roo.bootstrap.NavProgressItem} this
27421 * @param {Roo.EventObject} e
27428 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
27434 position : 'bottom',
27437 getAutoCreate : function()
27439 var iconCls = 'roo-navigation-bar-item-icon';
27441 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27445 cls: 'roo-navigation-bar-item',
27455 cfg.cls += ' active';
27458 cfg.cls += ' disabled';
27464 disable : function()
27466 this.setDisabled(true);
27469 enable : function()
27471 this.setDisabled(false);
27474 initEvents: function()
27476 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27478 this.iconEl.on('click', this.onClick, this);
27481 onClick : function(e)
27483 e.preventDefault();
27489 if(this.fireEvent('click', this, e) === false){
27493 this.parent().setActiveItem(this);
27496 isActive: function ()
27498 return this.active;
27501 setActive : function(state)
27503 if(this.active == state){
27507 this.active = state;
27510 this.el.addClass('active');
27514 this.el.removeClass('active');
27519 setDisabled : function(state)
27521 if(this.disabled == state){
27525 this.disabled = state;
27528 this.el.addClass('disabled');
27532 this.el.removeClass('disabled');
27535 tooltipEl : function()
27537 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27550 * @class Roo.bootstrap.FieldLabel
27551 * @extends Roo.bootstrap.Component
27552 * Bootstrap FieldLabel class
27553 * @cfg {String} html contents of the element
27554 * @cfg {String} tag tag of the element default label
27555 * @cfg {String} cls class of the element
27556 * @cfg {String} target label target
27557 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27558 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27559 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27560 * @cfg {String} iconTooltip default "This field is required"
27563 * Create a new FieldLabel
27564 * @param {Object} config The config object
27567 Roo.bootstrap.FieldLabel = function(config){
27568 Roo.bootstrap.Element.superclass.constructor.call(this, config);
27573 * Fires after the field has been marked as invalid.
27574 * @param {Roo.form.FieldLabel} this
27575 * @param {String} msg The validation message
27580 * Fires after the field has been validated with no errors.
27581 * @param {Roo.form.FieldLabel} this
27587 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
27594 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27595 validClass : 'text-success fa fa-lg fa-check',
27596 iconTooltip : 'This field is required',
27598 getAutoCreate : function(){
27602 cls : 'roo-bootstrap-field-label ' + this.cls,
27608 tooltip : this.iconTooltip
27620 initEvents: function()
27622 Roo.bootstrap.Element.superclass.initEvents.call(this);
27624 this.iconEl = this.el.select('i', true).first();
27626 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27628 Roo.bootstrap.FieldLabel.register(this);
27632 * Mark this field as valid
27634 markValid : function()
27636 this.iconEl.show();
27638 this.iconEl.removeClass(this.invalidClass);
27640 this.iconEl.addClass(this.validClass);
27642 this.fireEvent('valid', this);
27646 * Mark this field as invalid
27647 * @param {String} msg The validation message
27649 markInvalid : function(msg)
27651 this.iconEl.show();
27653 this.iconEl.removeClass(this.validClass);
27655 this.iconEl.addClass(this.invalidClass);
27657 this.fireEvent('invalid', this, msg);
27663 Roo.apply(Roo.bootstrap.FieldLabel, {
27668 * register a FieldLabel Group
27669 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27671 register : function(label)
27673 if(this.groups.hasOwnProperty(label.target)){
27677 this.groups[label.target] = label;
27681 * fetch a FieldLabel Group based on the target
27682 * @param {string} target
27683 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27685 get: function(target) {
27686 if (typeof(this.groups[target]) == 'undefined') {
27690 return this.groups[target] ;
27699 * page DateSplitField.
27705 * @class Roo.bootstrap.DateSplitField
27706 * @extends Roo.bootstrap.Component
27707 * Bootstrap DateSplitField class
27708 * @cfg {string} fieldLabel - the label associated
27709 * @cfg {Number} labelWidth set the width of label (0-12)
27710 * @cfg {String} labelAlign (top|left)
27711 * @cfg {Boolean} dayAllowBlank (true|false) default false
27712 * @cfg {Boolean} monthAllowBlank (true|false) default false
27713 * @cfg {Boolean} yearAllowBlank (true|false) default false
27714 * @cfg {string} dayPlaceholder
27715 * @cfg {string} monthPlaceholder
27716 * @cfg {string} yearPlaceholder
27717 * @cfg {string} dayFormat default 'd'
27718 * @cfg {string} monthFormat default 'm'
27719 * @cfg {string} yearFormat default 'Y'
27723 * Create a new DateSplitField
27724 * @param {Object} config The config object
27727 Roo.bootstrap.DateSplitField = function(config){
27728 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27734 * getting the data of years
27735 * @param {Roo.bootstrap.DateSplitField} this
27736 * @param {Object} years
27741 * getting the data of days
27742 * @param {Roo.bootstrap.DateSplitField} this
27743 * @param {Object} days
27748 * Fires after the field has been marked as invalid.
27749 * @param {Roo.form.Field} this
27750 * @param {String} msg The validation message
27755 * Fires after the field has been validated with no errors.
27756 * @param {Roo.form.Field} this
27762 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
27765 labelAlign : 'top',
27767 dayAllowBlank : false,
27768 monthAllowBlank : false,
27769 yearAllowBlank : false,
27770 dayPlaceholder : '',
27771 monthPlaceholder : '',
27772 yearPlaceholder : '',
27776 isFormField : true,
27778 getAutoCreate : function()
27782 cls : 'row roo-date-split-field-group',
27787 cls : 'form-hidden-field roo-date-split-field-group-value',
27793 if(this.fieldLabel){
27796 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27800 html : this.fieldLabel
27806 Roo.each(['day', 'month', 'year'], function(t){
27809 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27816 inputEl: function ()
27818 return this.el.select('.roo-date-split-field-group-value', true).first();
27821 onRender : function(ct, position)
27825 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27827 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27829 this.dayField = new Roo.bootstrap.ComboBox({
27830 allowBlank : this.dayAllowBlank,
27831 alwaysQuery : true,
27832 displayField : 'value',
27835 forceSelection : true,
27837 placeholder : this.dayPlaceholder,
27838 selectOnFocus : true,
27839 tpl : '<div class="select2-result"><b>{value}</b></div>',
27840 triggerAction : 'all',
27842 valueField : 'value',
27843 store : new Roo.data.SimpleStore({
27844 data : (function() {
27846 _this.fireEvent('days', _this, days);
27849 fields : [ 'value' ]
27852 select : function (_self, record, index)
27854 _this.setValue(_this.getValue());
27859 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27861 this.monthField = new Roo.bootstrap.MonthField({
27862 after : '<i class=\"fa fa-calendar\"></i>',
27863 allowBlank : this.monthAllowBlank,
27864 placeholder : this.monthPlaceholder,
27867 render : function (_self)
27869 this.el.select('span.input-group-addon', true).first().on('click', function(e){
27870 e.preventDefault();
27874 select : function (_self, oldvalue, newvalue)
27876 _this.setValue(_this.getValue());
27881 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27883 this.yearField = new Roo.bootstrap.ComboBox({
27884 allowBlank : this.yearAllowBlank,
27885 alwaysQuery : true,
27886 displayField : 'value',
27889 forceSelection : true,
27891 placeholder : this.yearPlaceholder,
27892 selectOnFocus : true,
27893 tpl : '<div class="select2-result"><b>{value}</b></div>',
27894 triggerAction : 'all',
27896 valueField : 'value',
27897 store : new Roo.data.SimpleStore({
27898 data : (function() {
27900 _this.fireEvent('years', _this, years);
27903 fields : [ 'value' ]
27906 select : function (_self, record, index)
27908 _this.setValue(_this.getValue());
27913 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
27916 setValue : function(v, format)
27918 this.inputEl.dom.value = v;
27920 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
27922 var d = Date.parseDate(v, f);
27929 this.setDay(d.format(this.dayFormat));
27930 this.setMonth(d.format(this.monthFormat));
27931 this.setYear(d.format(this.yearFormat));
27938 setDay : function(v)
27940 this.dayField.setValue(v);
27941 this.inputEl.dom.value = this.getValue();
27946 setMonth : function(v)
27948 this.monthField.setValue(v, true);
27949 this.inputEl.dom.value = this.getValue();
27954 setYear : function(v)
27956 this.yearField.setValue(v);
27957 this.inputEl.dom.value = this.getValue();
27962 getDay : function()
27964 return this.dayField.getValue();
27967 getMonth : function()
27969 return this.monthField.getValue();
27972 getYear : function()
27974 return this.yearField.getValue();
27977 getValue : function()
27979 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
27981 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
27991 this.inputEl.dom.value = '';
27996 validate : function()
27998 var d = this.dayField.validate();
27999 var m = this.monthField.validate();
28000 var y = this.yearField.validate();
28005 (!this.dayAllowBlank && !d) ||
28006 (!this.monthAllowBlank && !m) ||
28007 (!this.yearAllowBlank && !y)
28012 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28021 this.markInvalid();
28026 markValid : function()
28029 var label = this.el.select('label', true).first();
28030 var icon = this.el.select('i.fa-star', true).first();
28036 this.fireEvent('valid', this);
28040 * Mark this field as invalid
28041 * @param {String} msg The validation message
28043 markInvalid : function(msg)
28046 var label = this.el.select('label', true).first();
28047 var icon = this.el.select('i.fa-star', true).first();
28049 if(label && !icon){
28050 this.el.select('.roo-date-split-field-label', true).createChild({
28052 cls : 'text-danger fa fa-lg fa-star',
28053 tooltip : 'This field is required',
28054 style : 'margin-right:5px;'
28058 this.fireEvent('invalid', this, msg);
28061 clearInvalid : function()
28063 var label = this.el.select('label', true).first();
28064 var icon = this.el.select('i.fa-star', true).first();
28070 this.fireEvent('valid', this);
28073 getName: function()