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
8358 this.el.removeClass(this.invalidClass);
8360 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8362 var feedback = this.el.select('.form-control-feedback', true).first();
8365 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8370 this.fireEvent('valid', this);
8374 * Mark this field as valid
8376 markValid : function()
8378 if(!this.el || this.preventMark){ // not rendered
8382 this.el.removeClass([this.invalidClass, this.validClass]);
8384 var feedback = this.el.select('.form-control-feedback', true).first();
8387 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8390 if(this.disabled || this.allowBlank){
8394 var formGroup = this.el.findParent('.form-group', false, true);
8398 var label = formGroup.select('label', true).first();
8399 var icon = formGroup.select('i.fa-star', true).first();
8406 this.el.addClass(this.validClass);
8408 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8410 var feedback = this.el.select('.form-control-feedback', true).first();
8413 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8414 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8419 this.fireEvent('valid', this);
8423 * Mark this field as invalid
8424 * @param {String} msg The validation message
8426 markInvalid : function(msg)
8428 if(!this.el || this.preventMark){ // not rendered
8432 this.el.removeClass([this.invalidClass, this.validClass]);
8434 var feedback = this.el.select('.form-control-feedback', true).first();
8437 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8440 if(this.disabled || this.allowBlank){
8444 var formGroup = this.el.findParent('.form-group', false, true);
8447 var label = formGroup.select('label', true).first();
8448 var icon = formGroup.select('i.fa-star', true).first();
8450 if(!this.getValue().length && label && !icon){
8451 this.el.findParent('.form-group', false, true).createChild({
8453 cls : 'text-danger fa fa-lg fa-star',
8454 tooltip : 'This field is required',
8455 style : 'margin-right:5px;'
8461 this.el.addClass(this.invalidClass);
8463 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8465 var feedback = this.el.select('.form-control-feedback', true).first();
8468 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8470 if(this.getValue().length || this.forceFeedback){
8471 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8478 this.fireEvent('invalid', this, msg);
8481 SafariOnKeyDown : function(event)
8483 // this is a workaround for a password hang bug on chrome/ webkit.
8485 var isSelectAll = false;
8487 if(this.inputEl().dom.selectionEnd > 0){
8488 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8490 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8491 event.preventDefault();
8496 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8498 event.preventDefault();
8499 // this is very hacky as keydown always get's upper case.
8501 var cc = String.fromCharCode(event.getCharCode());
8502 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8506 adjustWidth : function(tag, w){
8507 tag = tag.toLowerCase();
8508 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8509 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8513 if(tag == 'textarea'){
8516 }else if(Roo.isOpera){
8520 if(tag == 'textarea'){
8539 * @class Roo.bootstrap.TextArea
8540 * @extends Roo.bootstrap.Input
8541 * Bootstrap TextArea class
8542 * @cfg {Number} cols Specifies the visible width of a text area
8543 * @cfg {Number} rows Specifies the visible number of lines in a text area
8544 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8545 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8546 * @cfg {string} html text
8549 * Create a new TextArea
8550 * @param {Object} config The config object
8553 Roo.bootstrap.TextArea = function(config){
8554 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8558 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8568 getAutoCreate : function(){
8570 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8581 value : this.value || '',
8582 html: this.html || '',
8583 cls : 'form-control',
8584 placeholder : this.placeholder || ''
8588 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8589 input.maxLength = this.maxLength;
8593 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8597 input.cols = this.cols;
8600 if (this.readOnly) {
8601 input.readonly = true;
8605 input.name = this.name;
8609 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8613 ['xs','sm','md','lg'].map(function(size){
8614 if (settings[size]) {
8615 cfg.cls += ' col-' + size + '-' + settings[size];
8619 var inputblock = input;
8621 if(this.hasFeedback && !this.allowBlank){
8625 cls: 'glyphicon form-control-feedback'
8629 cls : 'has-feedback',
8638 if (this.before || this.after) {
8641 cls : 'input-group',
8645 inputblock.cn.push({
8647 cls : 'input-group-addon',
8652 inputblock.cn.push(input);
8654 if(this.hasFeedback && !this.allowBlank){
8655 inputblock.cls += ' has-feedback';
8656 inputblock.cn.push(feedback);
8660 inputblock.cn.push({
8662 cls : 'input-group-addon',
8669 if (align ==='left' && this.fieldLabel.length) {
8670 // Roo.log("left and has label");
8676 cls : 'control-label col-sm-' + this.labelWidth,
8677 html : this.fieldLabel
8681 cls : "col-sm-" + (12 - this.labelWidth),
8688 } else if ( this.fieldLabel.length) {
8689 // Roo.log(" label");
8694 //cls : 'input-group-addon',
8695 html : this.fieldLabel
8705 // Roo.log(" no label && no align");
8715 if (this.disabled) {
8716 input.disabled=true;
8723 * return the real textarea element.
8725 inputEl: function ()
8727 return this.el.select('textarea.form-control',true).first();
8731 * Clear any invalid styles/messages for this field
8733 clearInvalid : function()
8736 if(!this.el || this.preventMark){ // not rendered
8740 var label = this.el.select('label', true).first();
8741 var icon = this.el.select('i.fa-star', true).first();
8747 this.el.removeClass(this.invalidClass);
8749 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8751 var feedback = this.el.select('.form-control-feedback', true).first();
8754 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8759 this.fireEvent('valid', this);
8763 * Mark this field as valid
8765 markValid : function()
8767 if(!this.el || this.preventMark){ // not rendered
8771 this.el.removeClass([this.invalidClass, this.validClass]);
8773 var feedback = this.el.select('.form-control-feedback', true).first();
8776 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8779 if(this.disabled || this.allowBlank){
8783 var label = this.el.select('label', true).first();
8784 var icon = this.el.select('i.fa-star', true).first();
8790 this.el.addClass(this.validClass);
8792 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8794 var feedback = this.el.select('.form-control-feedback', true).first();
8797 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8798 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8803 this.fireEvent('valid', this);
8807 * Mark this field as invalid
8808 * @param {String} msg The validation message
8810 markInvalid : function(msg)
8812 if(!this.el || this.preventMark){ // not rendered
8816 this.el.removeClass([this.invalidClass, this.validClass]);
8818 var feedback = this.el.select('.form-control-feedback', true).first();
8821 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8824 if(this.disabled || this.allowBlank){
8828 var label = this.el.select('label', true).first();
8829 var icon = this.el.select('i.fa-star', true).first();
8831 if(!this.getValue().length && label && !icon){
8832 this.el.createChild({
8834 cls : 'text-danger fa fa-lg fa-star',
8835 tooltip : 'This field is required',
8836 style : 'margin-right:5px;'
8840 this.el.addClass(this.invalidClass);
8842 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8844 var feedback = this.el.select('.form-control-feedback', true).first();
8847 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8849 if(this.getValue().length || this.forceFeedback){
8850 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8857 this.fireEvent('invalid', this, msg);
8865 * trigger field - base class for combo..
8870 * @class Roo.bootstrap.TriggerField
8871 * @extends Roo.bootstrap.Input
8872 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8873 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8874 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8875 * for which you can provide a custom implementation. For example:
8877 var trigger = new Roo.bootstrap.TriggerField();
8878 trigger.onTriggerClick = myTriggerFn;
8879 trigger.applyTo('my-field');
8882 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8883 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8884 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
8885 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8886 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8889 * Create a new TriggerField.
8890 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8891 * to the base TextField)
8893 Roo.bootstrap.TriggerField = function(config){
8894 this.mimicing = false;
8895 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8898 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
8900 * @cfg {String} triggerClass A CSS class to apply to the trigger
8903 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8908 * @cfg {Boolean} removable (true|false) special filter default false
8912 /** @cfg {Boolean} grow @hide */
8913 /** @cfg {Number} growMin @hide */
8914 /** @cfg {Number} growMax @hide */
8920 autoSize: Roo.emptyFn,
8927 actionMode : 'wrap',
8932 getAutoCreate : function(){
8934 var align = this.labelAlign || this.parentLabelAlign();
8939 cls: 'form-group' //input-group
8946 type : this.inputType,
8947 cls : 'form-control',
8948 autocomplete: 'new-password',
8949 placeholder : this.placeholder || ''
8953 input.name = this.name;
8956 input.cls += ' input-' + this.size;
8959 if (this.disabled) {
8960 input.disabled=true;
8963 var inputblock = input;
8965 if(this.hasFeedback && !this.allowBlank){
8969 cls: 'glyphicon form-control-feedback'
8972 if(this.removable && !this.editable && !this.tickable){
8974 cls : 'has-feedback',
8980 cls : 'roo-combo-removable-btn close'
8987 cls : 'has-feedback',
8996 if(this.removable && !this.editable && !this.tickable){
8998 cls : 'roo-removable',
9004 cls : 'roo-combo-removable-btn close'
9011 if (this.before || this.after) {
9014 cls : 'input-group',
9018 inputblock.cn.push({
9020 cls : 'input-group-addon',
9025 inputblock.cn.push(input);
9027 if(this.hasFeedback && !this.allowBlank){
9028 inputblock.cls += ' has-feedback';
9029 inputblock.cn.push(feedback);
9033 inputblock.cn.push({
9035 cls : 'input-group-addon',
9048 cls: 'form-hidden-field'
9062 cls: 'form-hidden-field'
9066 cls: 'select2-choices',
9070 cls: 'select2-search-field',
9083 cls: 'select2-container input-group',
9088 // cls: 'typeahead typeahead-long dropdown-menu',
9089 // style: 'display:none'
9094 if(!this.multiple && this.showToggleBtn){
9100 if (this.caret != false) {
9103 cls: 'fa fa-' + this.caret
9110 cls : 'input-group-addon btn dropdown-toggle',
9115 cls: 'combobox-clear',
9129 combobox.cls += ' select2-container-multi';
9132 if (align ==='left' && this.fieldLabel.length) {
9134 // Roo.log("left and has label");
9140 cls : 'control-label col-sm-' + this.labelWidth,
9141 html : this.fieldLabel
9145 cls : "col-sm-" + (12 - this.labelWidth),
9152 } else if ( this.fieldLabel.length) {
9153 // Roo.log(" label");
9158 //cls : 'input-group-addon',
9159 html : this.fieldLabel
9169 // Roo.log(" no label && no align");
9176 ['xs','sm','md','lg'].map(function(size){
9177 if (settings[size]) {
9178 cfg.cls += ' col-' + size + '-' + settings[size];
9189 onResize : function(w, h){
9190 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9191 // if(typeof w == 'number'){
9192 // var x = w - this.trigger.getWidth();
9193 // this.inputEl().setWidth(this.adjustWidth('input', x));
9194 // this.trigger.setStyle('left', x+'px');
9199 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9202 getResizeEl : function(){
9203 return this.inputEl();
9207 getPositionEl : function(){
9208 return this.inputEl();
9212 alignErrorIcon : function(){
9213 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9217 initEvents : function(){
9221 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9222 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9223 if(!this.multiple && this.showToggleBtn){
9224 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9225 if(this.hideTrigger){
9226 this.trigger.setDisplayed(false);
9228 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9232 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9235 if(this.removable && !this.editable && !this.tickable){
9236 var close = this.closeTriggerEl();
9239 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9240 close.on('click', this.removeBtnClick, this, close);
9244 //this.trigger.addClassOnOver('x-form-trigger-over');
9245 //this.trigger.addClassOnClick('x-form-trigger-click');
9248 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9252 closeTriggerEl : function()
9254 var close = this.el.select('.roo-combo-removable-btn', true).first();
9255 return close ? close : false;
9258 removeBtnClick : function(e, h, el)
9262 if(this.fireEvent("remove", this) !== false){
9267 createList : function()
9269 this.list = Roo.get(document.body).createChild({
9271 cls: 'typeahead typeahead-long dropdown-menu',
9272 style: 'display:none'
9275 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9280 initTrigger : function(){
9285 onDestroy : function(){
9287 this.trigger.removeAllListeners();
9288 // this.trigger.remove();
9291 // this.wrap.remove();
9293 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9297 onFocus : function(){
9298 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9301 this.wrap.addClass('x-trigger-wrap-focus');
9302 this.mimicing = true;
9303 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9304 if(this.monitorTab){
9305 this.el.on("keydown", this.checkTab, this);
9312 checkTab : function(e){
9313 if(e.getKey() == e.TAB){
9319 onBlur : function(){
9324 mimicBlur : function(e, t){
9326 if(!this.wrap.contains(t) && this.validateBlur()){
9333 triggerBlur : function(){
9334 this.mimicing = false;
9335 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9336 if(this.monitorTab){
9337 this.el.un("keydown", this.checkTab, this);
9339 //this.wrap.removeClass('x-trigger-wrap-focus');
9340 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9344 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9345 validateBlur : function(e, t){
9350 onDisable : function(){
9351 this.inputEl().dom.disabled = true;
9352 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9354 // this.wrap.addClass('x-item-disabled');
9359 onEnable : function(){
9360 this.inputEl().dom.disabled = false;
9361 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9363 // this.el.removeClass('x-item-disabled');
9368 onShow : function(){
9369 var ae = this.getActionEl();
9372 ae.dom.style.display = '';
9373 ae.dom.style.visibility = 'visible';
9379 onHide : function(){
9380 var ae = this.getActionEl();
9381 ae.dom.style.display = 'none';
9385 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9386 * by an implementing function.
9388 * @param {EventObject} e
9390 onTriggerClick : Roo.emptyFn
9394 * Ext JS Library 1.1.1
9395 * Copyright(c) 2006-2007, Ext JS, LLC.
9397 * Originally Released Under LGPL - original licence link has changed is not relivant.
9400 * <script type="text/javascript">
9405 * @class Roo.data.SortTypes
9407 * Defines the default sorting (casting?) comparison functions used when sorting data.
9409 Roo.data.SortTypes = {
9411 * Default sort that does nothing
9412 * @param {Mixed} s The value being converted
9413 * @return {Mixed} The comparison value
9420 * The regular expression used to strip tags
9424 stripTagsRE : /<\/?[^>]+>/gi,
9427 * Strips all HTML tags to sort on text only
9428 * @param {Mixed} s The value being converted
9429 * @return {String} The comparison value
9431 asText : function(s){
9432 return String(s).replace(this.stripTagsRE, "");
9436 * Strips all HTML tags to sort on text only - Case insensitive
9437 * @param {Mixed} s The value being converted
9438 * @return {String} The comparison value
9440 asUCText : function(s){
9441 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9445 * Case insensitive string
9446 * @param {Mixed} s The value being converted
9447 * @return {String} The comparison value
9449 asUCString : function(s) {
9450 return String(s).toUpperCase();
9455 * @param {Mixed} s The value being converted
9456 * @return {Number} The comparison value
9458 asDate : function(s) {
9462 if(s instanceof Date){
9465 return Date.parse(String(s));
9470 * @param {Mixed} s The value being converted
9471 * @return {Float} The comparison value
9473 asFloat : function(s) {
9474 var val = parseFloat(String(s).replace(/,/g, ""));
9483 * @param {Mixed} s The value being converted
9484 * @return {Number} The comparison value
9486 asInt : function(s) {
9487 var val = parseInt(String(s).replace(/,/g, ""));
9495 * Ext JS Library 1.1.1
9496 * Copyright(c) 2006-2007, Ext JS, LLC.
9498 * Originally Released Under LGPL - original licence link has changed is not relivant.
9501 * <script type="text/javascript">
9505 * @class Roo.data.Record
9506 * Instances of this class encapsulate both record <em>definition</em> information, and record
9507 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9508 * to access Records cached in an {@link Roo.data.Store} object.<br>
9510 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9511 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9514 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9516 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9517 * {@link #create}. The parameters are the same.
9518 * @param {Array} data An associative Array of data values keyed by the field name.
9519 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9520 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9521 * not specified an integer id is generated.
9523 Roo.data.Record = function(data, id){
9524 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9529 * Generate a constructor for a specific record layout.
9530 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9531 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9532 * Each field definition object may contain the following properties: <ul>
9533 * <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,
9534 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9535 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9536 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9537 * is being used, then this is a string containing the javascript expression to reference the data relative to
9538 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9539 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9540 * this may be omitted.</p></li>
9541 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9542 * <ul><li>auto (Default, implies no conversion)</li>
9547 * <li>date</li></ul></p></li>
9548 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9549 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9550 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9551 * by the Reader into an object that will be stored in the Record. It is passed the
9552 * following parameters:<ul>
9553 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9555 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9557 * <br>usage:<br><pre><code>
9558 var TopicRecord = Roo.data.Record.create(
9559 {name: 'title', mapping: 'topic_title'},
9560 {name: 'author', mapping: 'username'},
9561 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9562 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9563 {name: 'lastPoster', mapping: 'user2'},
9564 {name: 'excerpt', mapping: 'post_text'}
9567 var myNewRecord = new TopicRecord({
9568 title: 'Do my job please',
9571 lastPost: new Date(),
9572 lastPoster: 'Animal',
9573 excerpt: 'No way dude!'
9575 myStore.add(myNewRecord);
9580 Roo.data.Record.create = function(o){
9582 f.superclass.constructor.apply(this, arguments);
9584 Roo.extend(f, Roo.data.Record);
9585 var p = f.prototype;
9586 p.fields = new Roo.util.MixedCollection(false, function(field){
9589 for(var i = 0, len = o.length; i < len; i++){
9590 p.fields.add(new Roo.data.Field(o[i]));
9592 f.getField = function(name){
9593 return p.fields.get(name);
9598 Roo.data.Record.AUTO_ID = 1000;
9599 Roo.data.Record.EDIT = 'edit';
9600 Roo.data.Record.REJECT = 'reject';
9601 Roo.data.Record.COMMIT = 'commit';
9603 Roo.data.Record.prototype = {
9605 * Readonly flag - true if this record has been modified.
9614 join : function(store){
9619 * Set the named field to the specified value.
9620 * @param {String} name The name of the field to set.
9621 * @param {Object} value The value to set the field to.
9623 set : function(name, value){
9624 if(this.data[name] == value){
9631 if(typeof this.modified[name] == 'undefined'){
9632 this.modified[name] = this.data[name];
9634 this.data[name] = value;
9635 if(!this.editing && this.store){
9636 this.store.afterEdit(this);
9641 * Get the value of the named field.
9642 * @param {String} name The name of the field to get the value of.
9643 * @return {Object} The value of the field.
9645 get : function(name){
9646 return this.data[name];
9650 beginEdit : function(){
9651 this.editing = true;
9656 cancelEdit : function(){
9657 this.editing = false;
9658 delete this.modified;
9662 endEdit : function(){
9663 this.editing = false;
9664 if(this.dirty && this.store){
9665 this.store.afterEdit(this);
9670 * Usually called by the {@link Roo.data.Store} which owns the Record.
9671 * Rejects all changes made to the Record since either creation, or the last commit operation.
9672 * Modified fields are reverted to their original values.
9674 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9675 * of reject operations.
9677 reject : function(){
9678 var m = this.modified;
9680 if(typeof m[n] != "function"){
9681 this.data[n] = m[n];
9685 delete this.modified;
9686 this.editing = false;
9688 this.store.afterReject(this);
9693 * Usually called by the {@link Roo.data.Store} which owns the Record.
9694 * Commits all changes made to the Record since either creation, or the last commit operation.
9696 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9697 * of commit operations.
9699 commit : function(){
9701 delete this.modified;
9702 this.editing = false;
9704 this.store.afterCommit(this);
9709 hasError : function(){
9710 return this.error != null;
9714 clearError : function(){
9719 * Creates a copy of this record.
9720 * @param {String} id (optional) A new record id if you don't want to use this record's id
9723 copy : function(newId) {
9724 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9728 * Ext JS Library 1.1.1
9729 * Copyright(c) 2006-2007, Ext JS, LLC.
9731 * Originally Released Under LGPL - original licence link has changed is not relivant.
9734 * <script type="text/javascript">
9740 * @class Roo.data.Store
9741 * @extends Roo.util.Observable
9742 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9743 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9745 * 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
9746 * has no knowledge of the format of the data returned by the Proxy.<br>
9748 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9749 * instances from the data object. These records are cached and made available through accessor functions.
9751 * Creates a new Store.
9752 * @param {Object} config A config object containing the objects needed for the Store to access data,
9753 * and read the data into Records.
9755 Roo.data.Store = function(config){
9756 this.data = new Roo.util.MixedCollection(false);
9757 this.data.getKey = function(o){
9760 this.baseParams = {};
9767 "multisort" : "_multisort"
9770 if(config && config.data){
9771 this.inlineData = config.data;
9775 Roo.apply(this, config);
9777 if(this.reader){ // reader passed
9778 this.reader = Roo.factory(this.reader, Roo.data);
9779 this.reader.xmodule = this.xmodule || false;
9780 if(!this.recordType){
9781 this.recordType = this.reader.recordType;
9783 if(this.reader.onMetaChange){
9784 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9788 if(this.recordType){
9789 this.fields = this.recordType.prototype.fields;
9795 * @event datachanged
9796 * Fires when the data cache has changed, and a widget which is using this Store
9797 * as a Record cache should refresh its view.
9798 * @param {Store} this
9803 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9804 * @param {Store} this
9805 * @param {Object} meta The JSON metadata
9810 * Fires when Records have been added to the Store
9811 * @param {Store} this
9812 * @param {Roo.data.Record[]} records The array of Records added
9813 * @param {Number} index The index at which the record(s) were added
9818 * Fires when a Record has been removed from the Store
9819 * @param {Store} this
9820 * @param {Roo.data.Record} record The Record that was removed
9821 * @param {Number} index The index at which the record was removed
9826 * Fires when a Record has been updated
9827 * @param {Store} this
9828 * @param {Roo.data.Record} record The Record that was updated
9829 * @param {String} operation The update operation being performed. Value may be one of:
9831 Roo.data.Record.EDIT
9832 Roo.data.Record.REJECT
9833 Roo.data.Record.COMMIT
9839 * Fires when the data cache has been cleared.
9840 * @param {Store} this
9845 * Fires before a request is made for a new data object. If the beforeload handler returns false
9846 * the load action will be canceled.
9847 * @param {Store} this
9848 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9852 * @event beforeloadadd
9853 * Fires after a new set of Records has been loaded.
9854 * @param {Store} this
9855 * @param {Roo.data.Record[]} records The Records that were loaded
9856 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9858 beforeloadadd : true,
9861 * Fires after a new set of Records has been loaded, before they are added to the store.
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)
9865 * @params {Object} return from reader
9869 * @event loadexception
9870 * Fires if an exception occurs in the Proxy during loading.
9871 * Called with the signature of the Proxy's "loadexception" event.
9872 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9875 * @param {Object} return from JsonData.reader() - success, totalRecords, records
9876 * @param {Object} load options
9877 * @param {Object} jsonData from your request (normally this contains the Exception)
9879 loadexception : true
9883 this.proxy = Roo.factory(this.proxy, Roo.data);
9884 this.proxy.xmodule = this.xmodule || false;
9885 this.relayEvents(this.proxy, ["loadexception"]);
9887 this.sortToggle = {};
9888 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9890 Roo.data.Store.superclass.constructor.call(this);
9892 if(this.inlineData){
9893 this.loadData(this.inlineData);
9894 delete this.inlineData;
9898 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9900 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
9901 * without a remote query - used by combo/forms at present.
9905 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9908 * @cfg {Array} data Inline data to be loaded when the store is initialized.
9911 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9912 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9915 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9916 * on any HTTP request
9919 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9922 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9926 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9927 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9932 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9933 * loaded or when a record is removed. (defaults to false).
9935 pruneModifiedRecords : false,
9941 * Add Records to the Store and fires the add event.
9942 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9944 add : function(records){
9945 records = [].concat(records);
9946 for(var i = 0, len = records.length; i < len; i++){
9947 records[i].join(this);
9949 var index = this.data.length;
9950 this.data.addAll(records);
9951 this.fireEvent("add", this, records, index);
9955 * Remove a Record from the Store and fires the remove event.
9956 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9958 remove : function(record){
9959 var index = this.data.indexOf(record);
9960 this.data.removeAt(index);
9961 if(this.pruneModifiedRecords){
9962 this.modified.remove(record);
9964 this.fireEvent("remove", this, record, index);
9968 * Remove all Records from the Store and fires the clear event.
9970 removeAll : function(){
9972 if(this.pruneModifiedRecords){
9975 this.fireEvent("clear", this);
9979 * Inserts Records to the Store at the given index and fires the add event.
9980 * @param {Number} index The start index at which to insert the passed Records.
9981 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9983 insert : function(index, records){
9984 records = [].concat(records);
9985 for(var i = 0, len = records.length; i < len; i++){
9986 this.data.insert(index, records[i]);
9987 records[i].join(this);
9989 this.fireEvent("add", this, records, index);
9993 * Get the index within the cache of the passed Record.
9994 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9995 * @return {Number} The index of the passed Record. Returns -1 if not found.
9997 indexOf : function(record){
9998 return this.data.indexOf(record);
10002 * Get the index within the cache of the Record with the passed id.
10003 * @param {String} id The id of the Record to find.
10004 * @return {Number} The index of the Record. Returns -1 if not found.
10006 indexOfId : function(id){
10007 return this.data.indexOfKey(id);
10011 * Get the Record with the specified id.
10012 * @param {String} id The id of the Record to find.
10013 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10015 getById : function(id){
10016 return this.data.key(id);
10020 * Get the Record at the specified index.
10021 * @param {Number} index The index of the Record to find.
10022 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10024 getAt : function(index){
10025 return this.data.itemAt(index);
10029 * Returns a range of Records between specified indices.
10030 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10031 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10032 * @return {Roo.data.Record[]} An array of Records
10034 getRange : function(start, end){
10035 return this.data.getRange(start, end);
10039 storeOptions : function(o){
10040 o = Roo.apply({}, o);
10043 this.lastOptions = o;
10047 * Loads the Record cache from the configured Proxy using the configured Reader.
10049 * If using remote paging, then the first load call must specify the <em>start</em>
10050 * and <em>limit</em> properties in the options.params property to establish the initial
10051 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10053 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10054 * and this call will return before the new data has been loaded. Perform any post-processing
10055 * in a callback function, or in a "load" event handler.</strong>
10057 * @param {Object} options An object containing properties which control loading options:<ul>
10058 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10059 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10060 * passed the following arguments:<ul>
10061 * <li>r : Roo.data.Record[]</li>
10062 * <li>options: Options object from the load call</li>
10063 * <li>success: Boolean success indicator</li></ul></li>
10064 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10065 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10068 load : function(options){
10069 options = options || {};
10070 if(this.fireEvent("beforeload", this, options) !== false){
10071 this.storeOptions(options);
10072 var p = Roo.apply(options.params || {}, this.baseParams);
10073 // if meta was not loaded from remote source.. try requesting it.
10074 if (!this.reader.metaFromRemote) {
10075 p._requestMeta = 1;
10077 if(this.sortInfo && this.remoteSort){
10078 var pn = this.paramNames;
10079 p[pn["sort"]] = this.sortInfo.field;
10080 p[pn["dir"]] = this.sortInfo.direction;
10082 if (this.multiSort) {
10083 var pn = this.paramNames;
10084 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10087 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10092 * Reloads the Record cache from the configured Proxy using the configured Reader and
10093 * the options from the last load operation performed.
10094 * @param {Object} options (optional) An object containing properties which may override the options
10095 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10096 * the most recently used options are reused).
10098 reload : function(options){
10099 this.load(Roo.applyIf(options||{}, this.lastOptions));
10103 // Called as a callback by the Reader during a load operation.
10104 loadRecords : function(o, options, success){
10105 if(!o || success === false){
10106 if(success !== false){
10107 this.fireEvent("load", this, [], options, o);
10109 if(options.callback){
10110 options.callback.call(options.scope || this, [], options, false);
10114 // if data returned failure - throw an exception.
10115 if (o.success === false) {
10116 // show a message if no listener is registered.
10117 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10118 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10120 // loadmask wil be hooked into this..
10121 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10124 var r = o.records, t = o.totalRecords || r.length;
10126 this.fireEvent("beforeloadadd", this, r, options, o);
10128 if(!options || options.add !== true){
10129 if(this.pruneModifiedRecords){
10130 this.modified = [];
10132 for(var i = 0, len = r.length; i < len; i++){
10136 this.data = this.snapshot;
10137 delete this.snapshot;
10140 this.data.addAll(r);
10141 this.totalLength = t;
10143 this.fireEvent("datachanged", this);
10145 this.totalLength = Math.max(t, this.data.length+r.length);
10148 this.fireEvent("load", this, r, options, o);
10149 if(options.callback){
10150 options.callback.call(options.scope || this, r, options, true);
10156 * Loads data from a passed data block. A Reader which understands the format of the data
10157 * must have been configured in the constructor.
10158 * @param {Object} data The data block from which to read the Records. The format of the data expected
10159 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10160 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10162 loadData : function(o, append){
10163 var r = this.reader.readRecords(o);
10164 this.loadRecords(r, {add: append}, true);
10168 * Gets the number of cached records.
10170 * <em>If using paging, this may not be the total size of the dataset. If the data object
10171 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10172 * the data set size</em>
10174 getCount : function(){
10175 return this.data.length || 0;
10179 * Gets the total number of records in the dataset as returned by the server.
10181 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10182 * the dataset size</em>
10184 getTotalCount : function(){
10185 return this.totalLength || 0;
10189 * Returns the sort state of the Store as an object with two properties:
10191 field {String} The name of the field by which the Records are sorted
10192 direction {String} The sort order, "ASC" or "DESC"
10195 getSortState : function(){
10196 return this.sortInfo;
10200 applySort : function(){
10201 if(this.sortInfo && !this.remoteSort){
10202 var s = this.sortInfo, f = s.field;
10203 var st = this.fields.get(f).sortType;
10204 var fn = function(r1, r2){
10205 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10206 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10208 this.data.sort(s.direction, fn);
10209 if(this.snapshot && this.snapshot != this.data){
10210 this.snapshot.sort(s.direction, fn);
10216 * Sets the default sort column and order to be used by the next load operation.
10217 * @param {String} fieldName The name of the field to sort by.
10218 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10220 setDefaultSort : function(field, dir){
10221 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10225 * Sort the Records.
10226 * If remote sorting is used, the sort is performed on the server, and the cache is
10227 * reloaded. If local sorting is used, the cache is sorted internally.
10228 * @param {String} fieldName The name of the field to sort by.
10229 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10231 sort : function(fieldName, dir){
10232 var f = this.fields.get(fieldName);
10234 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10236 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10237 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10242 this.sortToggle[f.name] = dir;
10243 this.sortInfo = {field: f.name, direction: dir};
10244 if(!this.remoteSort){
10246 this.fireEvent("datachanged", this);
10248 this.load(this.lastOptions);
10253 * Calls the specified function for each of the Records in the cache.
10254 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10255 * Returning <em>false</em> aborts and exits the iteration.
10256 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10258 each : function(fn, scope){
10259 this.data.each(fn, scope);
10263 * Gets all records modified since the last commit. Modified records are persisted across load operations
10264 * (e.g., during paging).
10265 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10267 getModifiedRecords : function(){
10268 return this.modified;
10272 createFilterFn : function(property, value, anyMatch){
10273 if(!value.exec){ // not a regex
10274 value = String(value);
10275 if(value.length == 0){
10278 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10280 return function(r){
10281 return value.test(r.data[property]);
10286 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10287 * @param {String} property A field on your records
10288 * @param {Number} start The record index to start at (defaults to 0)
10289 * @param {Number} end The last record index to include (defaults to length - 1)
10290 * @return {Number} The sum
10292 sum : function(property, start, end){
10293 var rs = this.data.items, v = 0;
10294 start = start || 0;
10295 end = (end || end === 0) ? end : rs.length-1;
10297 for(var i = start; i <= end; i++){
10298 v += (rs[i].data[property] || 0);
10304 * Filter the records by a specified property.
10305 * @param {String} field A field on your records
10306 * @param {String/RegExp} value Either a string that the field
10307 * should start with or a RegExp to test against the field
10308 * @param {Boolean} anyMatch True to match any part not just the beginning
10310 filter : function(property, value, anyMatch){
10311 var fn = this.createFilterFn(property, value, anyMatch);
10312 return fn ? this.filterBy(fn) : this.clearFilter();
10316 * Filter by a function. The specified function will be called with each
10317 * record in this data source. If the function returns true the record is included,
10318 * otherwise it is filtered.
10319 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10320 * @param {Object} scope (optional) The scope of the function (defaults to this)
10322 filterBy : function(fn, scope){
10323 this.snapshot = this.snapshot || this.data;
10324 this.data = this.queryBy(fn, scope||this);
10325 this.fireEvent("datachanged", this);
10329 * Query the records by a specified property.
10330 * @param {String} field A field on your records
10331 * @param {String/RegExp} value Either a string that the field
10332 * should start with or a RegExp to test against the field
10333 * @param {Boolean} anyMatch True to match any part not just the beginning
10334 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10336 query : function(property, value, anyMatch){
10337 var fn = this.createFilterFn(property, value, anyMatch);
10338 return fn ? this.queryBy(fn) : this.data.clone();
10342 * Query by a function. The specified function will be called with each
10343 * record in this data source. If the function returns true the record is included
10345 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10346 * @param {Object} scope (optional) The scope of the function (defaults to this)
10347 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10349 queryBy : function(fn, scope){
10350 var data = this.snapshot || this.data;
10351 return data.filterBy(fn, scope||this);
10355 * Collects unique values for a particular dataIndex from this store.
10356 * @param {String} dataIndex The property to collect
10357 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10358 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10359 * @return {Array} An array of the unique values
10361 collect : function(dataIndex, allowNull, bypassFilter){
10362 var d = (bypassFilter === true && this.snapshot) ?
10363 this.snapshot.items : this.data.items;
10364 var v, sv, r = [], l = {};
10365 for(var i = 0, len = d.length; i < len; i++){
10366 v = d[i].data[dataIndex];
10368 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10377 * Revert to a view of the Record cache with no filtering applied.
10378 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10380 clearFilter : function(suppressEvent){
10381 if(this.snapshot && this.snapshot != this.data){
10382 this.data = this.snapshot;
10383 delete this.snapshot;
10384 if(suppressEvent !== true){
10385 this.fireEvent("datachanged", this);
10391 afterEdit : function(record){
10392 if(this.modified.indexOf(record) == -1){
10393 this.modified.push(record);
10395 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10399 afterReject : function(record){
10400 this.modified.remove(record);
10401 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10405 afterCommit : function(record){
10406 this.modified.remove(record);
10407 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10411 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10412 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10414 commitChanges : function(){
10415 var m = this.modified.slice(0);
10416 this.modified = [];
10417 for(var i = 0, len = m.length; i < len; i++){
10423 * Cancel outstanding changes on all changed records.
10425 rejectChanges : function(){
10426 var m = this.modified.slice(0);
10427 this.modified = [];
10428 for(var i = 0, len = m.length; i < len; i++){
10433 onMetaChange : function(meta, rtype, o){
10434 this.recordType = rtype;
10435 this.fields = rtype.prototype.fields;
10436 delete this.snapshot;
10437 this.sortInfo = meta.sortInfo || this.sortInfo;
10438 this.modified = [];
10439 this.fireEvent('metachange', this, this.reader.meta);
10442 moveIndex : function(data, type)
10444 var index = this.indexOf(data);
10446 var newIndex = index + type;
10450 this.insert(newIndex, data);
10455 * Ext JS Library 1.1.1
10456 * Copyright(c) 2006-2007, Ext JS, LLC.
10458 * Originally Released Under LGPL - original licence link has changed is not relivant.
10461 * <script type="text/javascript">
10465 * @class Roo.data.SimpleStore
10466 * @extends Roo.data.Store
10467 * Small helper class to make creating Stores from Array data easier.
10468 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10469 * @cfg {Array} fields An array of field definition objects, or field name strings.
10470 * @cfg {Array} data The multi-dimensional array of data
10472 * @param {Object} config
10474 Roo.data.SimpleStore = function(config){
10475 Roo.data.SimpleStore.superclass.constructor.call(this, {
10477 reader: new Roo.data.ArrayReader({
10480 Roo.data.Record.create(config.fields)
10482 proxy : new Roo.data.MemoryProxy(config.data)
10486 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10488 * Ext JS Library 1.1.1
10489 * Copyright(c) 2006-2007, Ext JS, LLC.
10491 * Originally Released Under LGPL - original licence link has changed is not relivant.
10494 * <script type="text/javascript">
10499 * @extends Roo.data.Store
10500 * @class Roo.data.JsonStore
10501 * Small helper class to make creating Stores for JSON data easier. <br/>
10503 var store = new Roo.data.JsonStore({
10504 url: 'get-images.php',
10506 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10509 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10510 * JsonReader and HttpProxy (unless inline data is provided).</b>
10511 * @cfg {Array} fields An array of field definition objects, or field name strings.
10513 * @param {Object} config
10515 Roo.data.JsonStore = function(c){
10516 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10517 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10518 reader: new Roo.data.JsonReader(c, c.fields)
10521 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10523 * Ext JS Library 1.1.1
10524 * Copyright(c) 2006-2007, Ext JS, LLC.
10526 * Originally Released Under LGPL - original licence link has changed is not relivant.
10529 * <script type="text/javascript">
10533 Roo.data.Field = function(config){
10534 if(typeof config == "string"){
10535 config = {name: config};
10537 Roo.apply(this, config);
10540 this.type = "auto";
10543 var st = Roo.data.SortTypes;
10544 // named sortTypes are supported, here we look them up
10545 if(typeof this.sortType == "string"){
10546 this.sortType = st[this.sortType];
10549 // set default sortType for strings and dates
10550 if(!this.sortType){
10553 this.sortType = st.asUCString;
10556 this.sortType = st.asDate;
10559 this.sortType = st.none;
10564 var stripRe = /[\$,%]/g;
10566 // prebuilt conversion function for this field, instead of
10567 // switching every time we're reading a value
10569 var cv, dateFormat = this.dateFormat;
10574 cv = function(v){ return v; };
10577 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10581 return v !== undefined && v !== null && v !== '' ?
10582 parseInt(String(v).replace(stripRe, ""), 10) : '';
10587 return v !== undefined && v !== null && v !== '' ?
10588 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10593 cv = function(v){ return v === true || v === "true" || v == 1; };
10600 if(v instanceof Date){
10604 if(dateFormat == "timestamp"){
10605 return new Date(v*1000);
10607 return Date.parseDate(v, dateFormat);
10609 var parsed = Date.parse(v);
10610 return parsed ? new Date(parsed) : null;
10619 Roo.data.Field.prototype = {
10627 * Ext JS Library 1.1.1
10628 * Copyright(c) 2006-2007, Ext JS, LLC.
10630 * Originally Released Under LGPL - original licence link has changed is not relivant.
10633 * <script type="text/javascript">
10636 // Base class for reading structured data from a data source. This class is intended to be
10637 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10640 * @class Roo.data.DataReader
10641 * Base class for reading structured data from a data source. This class is intended to be
10642 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10645 Roo.data.DataReader = function(meta, recordType){
10649 this.recordType = recordType instanceof Array ?
10650 Roo.data.Record.create(recordType) : recordType;
10653 Roo.data.DataReader.prototype = {
10655 * Create an empty record
10656 * @param {Object} data (optional) - overlay some values
10657 * @return {Roo.data.Record} record created.
10659 newRow : function(d) {
10661 this.recordType.prototype.fields.each(function(c) {
10663 case 'int' : da[c.name] = 0; break;
10664 case 'date' : da[c.name] = new Date(); break;
10665 case 'float' : da[c.name] = 0.0; break;
10666 case 'boolean' : da[c.name] = false; break;
10667 default : da[c.name] = ""; break;
10671 return new this.recordType(Roo.apply(da, d));
10676 * Ext JS Library 1.1.1
10677 * Copyright(c) 2006-2007, Ext JS, LLC.
10679 * Originally Released Under LGPL - original licence link has changed is not relivant.
10682 * <script type="text/javascript">
10686 * @class Roo.data.DataProxy
10687 * @extends Roo.data.Observable
10688 * This class is an abstract base class for implementations which provide retrieval of
10689 * unformatted data objects.<br>
10691 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10692 * (of the appropriate type which knows how to parse the data object) to provide a block of
10693 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10695 * Custom implementations must implement the load method as described in
10696 * {@link Roo.data.HttpProxy#load}.
10698 Roo.data.DataProxy = function(){
10701 * @event beforeload
10702 * Fires before a network request is made to retrieve a data object.
10703 * @param {Object} This DataProxy object.
10704 * @param {Object} params The params parameter to the load function.
10709 * Fires before the load method's callback is called.
10710 * @param {Object} This DataProxy object.
10711 * @param {Object} o The data object.
10712 * @param {Object} arg The callback argument object passed to the load function.
10716 * @event loadexception
10717 * Fires if an Exception occurs during data retrieval.
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.
10721 * @param {Object} e The Exception.
10723 loadexception : true
10725 Roo.data.DataProxy.superclass.constructor.call(this);
10728 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10731 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10735 * Ext JS Library 1.1.1
10736 * Copyright(c) 2006-2007, Ext JS, LLC.
10738 * Originally Released Under LGPL - original licence link has changed is not relivant.
10741 * <script type="text/javascript">
10744 * @class Roo.data.MemoryProxy
10745 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10746 * to the Reader when its load method is called.
10748 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10750 Roo.data.MemoryProxy = function(data){
10754 Roo.data.MemoryProxy.superclass.constructor.call(this);
10758 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10760 * Load data from the requested source (in this case an in-memory
10761 * data object passed to the constructor), read the data object into
10762 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10763 * process that block using the passed callback.
10764 * @param {Object} params This parameter is not used by the MemoryProxy class.
10765 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10766 * object into a block of Roo.data.Records.
10767 * @param {Function} callback The function into which to pass the block of Roo.data.records.
10768 * The function must be passed <ul>
10769 * <li>The Record block object</li>
10770 * <li>The "arg" argument from the load function</li>
10771 * <li>A boolean success indicator</li>
10773 * @param {Object} scope The scope in which to call the callback
10774 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10776 load : function(params, reader, callback, scope, arg){
10777 params = params || {};
10780 result = reader.readRecords(this.data);
10782 this.fireEvent("loadexception", this, arg, null, e);
10783 callback.call(scope, null, arg, false);
10786 callback.call(scope, result, arg, true);
10790 update : function(params, records){
10795 * Ext JS Library 1.1.1
10796 * Copyright(c) 2006-2007, Ext JS, LLC.
10798 * Originally Released Under LGPL - original licence link has changed is not relivant.
10801 * <script type="text/javascript">
10804 * @class Roo.data.HttpProxy
10805 * @extends Roo.data.DataProxy
10806 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10807 * configured to reference a certain URL.<br><br>
10809 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10810 * from which the running page was served.<br><br>
10812 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10814 * Be aware that to enable the browser to parse an XML document, the server must set
10815 * the Content-Type header in the HTTP response to "text/xml".
10817 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10818 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
10819 * will be used to make the request.
10821 Roo.data.HttpProxy = function(conn){
10822 Roo.data.HttpProxy.superclass.constructor.call(this);
10823 // is conn a conn config or a real conn?
10825 this.useAjax = !conn || !conn.events;
10829 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10830 // thse are take from connection...
10833 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10836 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10837 * extra parameters to each request made by this object. (defaults to undefined)
10840 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10841 * to each request made by this object. (defaults to undefined)
10844 * @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)
10847 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10850 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10856 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10860 * Return the {@link Roo.data.Connection} object being used by this Proxy.
10861 * @return {Connection} The Connection object. This object may be used to subscribe to events on
10862 * a finer-grained basis than the DataProxy events.
10864 getConnection : function(){
10865 return this.useAjax ? Roo.Ajax : this.conn;
10869 * Load data from the configured {@link Roo.data.Connection}, read the data object into
10870 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10871 * process that block using the passed callback.
10872 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10873 * for the request to the remote server.
10874 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10875 * object into a block of Roo.data.Records.
10876 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10877 * The function must be passed <ul>
10878 * <li>The Record block object</li>
10879 * <li>The "arg" argument from the load function</li>
10880 * <li>A boolean success indicator</li>
10882 * @param {Object} scope The scope in which to call the callback
10883 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10885 load : function(params, reader, callback, scope, arg){
10886 if(this.fireEvent("beforeload", this, params) !== false){
10888 params : params || {},
10890 callback : callback,
10895 callback : this.loadResponse,
10899 Roo.applyIf(o, this.conn);
10900 if(this.activeRequest){
10901 Roo.Ajax.abort(this.activeRequest);
10903 this.activeRequest = Roo.Ajax.request(o);
10905 this.conn.request(o);
10908 callback.call(scope||this, null, arg, false);
10913 loadResponse : function(o, success, response){
10914 delete this.activeRequest;
10916 this.fireEvent("loadexception", this, o, response);
10917 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10922 result = o.reader.read(response);
10924 this.fireEvent("loadexception", this, o, response, e);
10925 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10929 this.fireEvent("load", this, o, o.request.arg);
10930 o.request.callback.call(o.request.scope, result, o.request.arg, true);
10934 update : function(dataSet){
10939 updateResponse : function(dataSet){
10944 * Ext JS Library 1.1.1
10945 * Copyright(c) 2006-2007, Ext JS, LLC.
10947 * Originally Released Under LGPL - original licence link has changed is not relivant.
10950 * <script type="text/javascript">
10954 * @class Roo.data.ScriptTagProxy
10955 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10956 * other than the originating domain of the running page.<br><br>
10958 * <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
10959 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10961 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10962 * source code that is used as the source inside a <script> tag.<br><br>
10964 * In order for the browser to process the returned data, the server must wrap the data object
10965 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10966 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10967 * depending on whether the callback name was passed:
10970 boolean scriptTag = false;
10971 String cb = request.getParameter("callback");
10974 response.setContentType("text/javascript");
10976 response.setContentType("application/x-json");
10978 Writer out = response.getWriter();
10980 out.write(cb + "(");
10982 out.print(dataBlock.toJsonString());
10989 * @param {Object} config A configuration object.
10991 Roo.data.ScriptTagProxy = function(config){
10992 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10993 Roo.apply(this, config);
10994 this.head = document.getElementsByTagName("head")[0];
10997 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10999 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11001 * @cfg {String} url The URL from which to request the data object.
11004 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11008 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11009 * the server the name of the callback function set up by the load call to process the returned data object.
11010 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11011 * javascript output which calls this named function passing the data object as its only parameter.
11013 callbackParam : "callback",
11015 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11016 * name to the request.
11021 * Load data from the configured URL, read the data object into
11022 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11023 * process that block using the passed callback.
11024 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11025 * for the request to the remote server.
11026 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11027 * object into a block of Roo.data.Records.
11028 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11029 * The function must be passed <ul>
11030 * <li>The Record block object</li>
11031 * <li>The "arg" argument from the load function</li>
11032 * <li>A boolean success indicator</li>
11034 * @param {Object} scope The scope in which to call the callback
11035 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11037 load : function(params, reader, callback, scope, arg){
11038 if(this.fireEvent("beforeload", this, params) !== false){
11040 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11042 var url = this.url;
11043 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11045 url += "&_dc=" + (new Date().getTime());
11047 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11050 cb : "stcCallback"+transId,
11051 scriptId : "stcScript"+transId,
11055 callback : callback,
11061 window[trans.cb] = function(o){
11062 conn.handleResponse(o, trans);
11065 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11067 if(this.autoAbort !== false){
11071 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11073 var script = document.createElement("script");
11074 script.setAttribute("src", url);
11075 script.setAttribute("type", "text/javascript");
11076 script.setAttribute("id", trans.scriptId);
11077 this.head.appendChild(script);
11079 this.trans = trans;
11081 callback.call(scope||this, null, arg, false);
11086 isLoading : function(){
11087 return this.trans ? true : false;
11091 * Abort the current server request.
11093 abort : function(){
11094 if(this.isLoading()){
11095 this.destroyTrans(this.trans);
11100 destroyTrans : function(trans, isLoaded){
11101 this.head.removeChild(document.getElementById(trans.scriptId));
11102 clearTimeout(trans.timeoutId);
11104 window[trans.cb] = undefined;
11106 delete window[trans.cb];
11109 // if hasn't been loaded, wait for load to remove it to prevent script error
11110 window[trans.cb] = function(){
11111 window[trans.cb] = undefined;
11113 delete window[trans.cb];
11120 handleResponse : function(o, trans){
11121 this.trans = false;
11122 this.destroyTrans(trans, true);
11125 result = trans.reader.readRecords(o);
11127 this.fireEvent("loadexception", this, o, trans.arg, e);
11128 trans.callback.call(trans.scope||window, null, trans.arg, false);
11131 this.fireEvent("load", this, o, trans.arg);
11132 trans.callback.call(trans.scope||window, result, trans.arg, true);
11136 handleFailure : function(trans){
11137 this.trans = false;
11138 this.destroyTrans(trans, false);
11139 this.fireEvent("loadexception", this, null, trans.arg);
11140 trans.callback.call(trans.scope||window, null, trans.arg, false);
11144 * Ext JS Library 1.1.1
11145 * Copyright(c) 2006-2007, Ext JS, LLC.
11147 * Originally Released Under LGPL - original licence link has changed is not relivant.
11150 * <script type="text/javascript">
11154 * @class Roo.data.JsonReader
11155 * @extends Roo.data.DataReader
11156 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11157 * based on mappings in a provided Roo.data.Record constructor.
11159 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11160 * in the reply previously.
11165 var RecordDef = Roo.data.Record.create([
11166 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11167 {name: 'occupation'} // This field will use "occupation" as the mapping.
11169 var myReader = new Roo.data.JsonReader({
11170 totalProperty: "results", // The property which contains the total dataset size (optional)
11171 root: "rows", // The property which contains an Array of row objects
11172 id: "id" // The property within each row object that provides an ID for the record (optional)
11176 * This would consume a JSON file like this:
11178 { 'results': 2, 'rows': [
11179 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11180 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11183 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11184 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11185 * paged from the remote server.
11186 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11187 * @cfg {String} root name of the property which contains the Array of row objects.
11188 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11189 * @cfg {Array} fields Array of field definition objects
11191 * Create a new JsonReader
11192 * @param {Object} meta Metadata configuration options
11193 * @param {Object} recordType Either an Array of field definition objects,
11194 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11196 Roo.data.JsonReader = function(meta, recordType){
11199 // set some defaults:
11200 Roo.applyIf(meta, {
11201 totalProperty: 'total',
11202 successProperty : 'success',
11207 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11209 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11212 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11213 * Used by Store query builder to append _requestMeta to params.
11216 metaFromRemote : false,
11218 * This method is only used by a DataProxy which has retrieved data from a remote server.
11219 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11220 * @return {Object} data A data block which is used by an Roo.data.Store object as
11221 * a cache of Roo.data.Records.
11223 read : function(response){
11224 var json = response.responseText;
11226 var o = /* eval:var:o */ eval("("+json+")");
11228 throw {message: "JsonReader.read: Json object not found"};
11234 this.metaFromRemote = true;
11235 this.meta = o.metaData;
11236 this.recordType = Roo.data.Record.create(o.metaData.fields);
11237 this.onMetaChange(this.meta, this.recordType, o);
11239 return this.readRecords(o);
11242 // private function a store will implement
11243 onMetaChange : function(meta, recordType, o){
11250 simpleAccess: function(obj, subsc) {
11257 getJsonAccessor: function(){
11259 return function(expr) {
11261 return(re.test(expr))
11262 ? new Function("obj", "return obj." + expr)
11267 return Roo.emptyFn;
11272 * Create a data block containing Roo.data.Records from an XML document.
11273 * @param {Object} o An object which contains an Array of row objects in the property specified
11274 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11275 * which contains the total size of the dataset.
11276 * @return {Object} data A data block which is used by an Roo.data.Store object as
11277 * a cache of Roo.data.Records.
11279 readRecords : function(o){
11281 * After any data loads, the raw JSON data is available for further custom processing.
11285 var s = this.meta, Record = this.recordType,
11286 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11288 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11290 if(s.totalProperty) {
11291 this.getTotal = this.getJsonAccessor(s.totalProperty);
11293 if(s.successProperty) {
11294 this.getSuccess = this.getJsonAccessor(s.successProperty);
11296 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11298 var g = this.getJsonAccessor(s.id);
11299 this.getId = function(rec) {
11301 return (r === undefined || r === "") ? null : r;
11304 this.getId = function(){return null;};
11307 for(var jj = 0; jj < fl; jj++){
11309 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11310 this.ef[jj] = this.getJsonAccessor(map);
11314 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11315 if(s.totalProperty){
11316 var vt = parseInt(this.getTotal(o), 10);
11321 if(s.successProperty){
11322 var vs = this.getSuccess(o);
11323 if(vs === false || vs === 'false'){
11328 for(var i = 0; i < c; i++){
11331 var id = this.getId(n);
11332 for(var j = 0; j < fl; j++){
11334 var v = this.ef[j](n);
11336 Roo.log('missing convert for ' + f.name);
11340 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11342 var record = new Record(values, id);
11344 records[i] = record;
11350 totalRecords : totalRecords
11355 * Ext JS Library 1.1.1
11356 * Copyright(c) 2006-2007, Ext JS, LLC.
11358 * Originally Released Under LGPL - original licence link has changed is not relivant.
11361 * <script type="text/javascript">
11365 * @class Roo.data.ArrayReader
11366 * @extends Roo.data.DataReader
11367 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11368 * Each element of that Array represents a row of data fields. The
11369 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11370 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11374 var RecordDef = Roo.data.Record.create([
11375 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11376 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11378 var myReader = new Roo.data.ArrayReader({
11379 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11383 * This would consume an Array like this:
11385 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11387 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11389 * Create a new JsonReader
11390 * @param {Object} meta Metadata configuration options.
11391 * @param {Object} recordType Either an Array of field definition objects
11392 * as specified to {@link Roo.data.Record#create},
11393 * or an {@link Roo.data.Record} object
11394 * created using {@link Roo.data.Record#create}.
11396 Roo.data.ArrayReader = function(meta, recordType){
11397 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11400 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11402 * Create a data block containing Roo.data.Records from an XML document.
11403 * @param {Object} o An Array of row objects which represents the dataset.
11404 * @return {Object} data A data block which is used by an Roo.data.Store object as
11405 * a cache of Roo.data.Records.
11407 readRecords : function(o){
11408 var sid = this.meta ? this.meta.id : null;
11409 var recordType = this.recordType, fields = recordType.prototype.fields;
11412 for(var i = 0; i < root.length; i++){
11415 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11416 for(var j = 0, jlen = fields.length; j < jlen; j++){
11417 var f = fields.items[j];
11418 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11419 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11421 values[f.name] = v;
11423 var record = new recordType(values, id);
11425 records[records.length] = record;
11429 totalRecords : records.length
11438 * @class Roo.bootstrap.ComboBox
11439 * @extends Roo.bootstrap.TriggerField
11440 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11441 * @cfg {Boolean} append (true|false) default false
11442 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11443 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11444 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11445 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11446 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11447 * @cfg {Boolean} animate default true
11448 * @cfg {Boolean} emptyResultText only for touch device
11449 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11451 * Create a new ComboBox.
11452 * @param {Object} config Configuration options
11454 Roo.bootstrap.ComboBox = function(config){
11455 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11459 * Fires when the dropdown list is expanded
11460 * @param {Roo.bootstrap.ComboBox} combo This combo box
11465 * Fires when the dropdown list is collapsed
11466 * @param {Roo.bootstrap.ComboBox} combo This combo box
11470 * @event beforeselect
11471 * Fires before a list item is selected. Return false to cancel the selection.
11472 * @param {Roo.bootstrap.ComboBox} combo This combo box
11473 * @param {Roo.data.Record} record The data record returned from the underlying store
11474 * @param {Number} index The index of the selected item in the dropdown list
11476 'beforeselect' : true,
11479 * Fires when a list item is selected
11480 * @param {Roo.bootstrap.ComboBox} combo This combo box
11481 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11482 * @param {Number} index The index of the selected item in the dropdown list
11486 * @event beforequery
11487 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11488 * The event object passed has these properties:
11489 * @param {Roo.bootstrap.ComboBox} combo This combo box
11490 * @param {String} query The query
11491 * @param {Boolean} forceAll true to force "all" query
11492 * @param {Boolean} cancel true to cancel the query
11493 * @param {Object} e The query event object
11495 'beforequery': true,
11498 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11499 * @param {Roo.bootstrap.ComboBox} combo This combo box
11504 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11505 * @param {Roo.bootstrap.ComboBox} combo This combo box
11506 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11511 * Fires when the remove value from the combobox array
11512 * @param {Roo.bootstrap.ComboBox} combo This combo box
11516 * @event specialfilter
11517 * Fires when specialfilter
11518 * @param {Roo.bootstrap.ComboBox} combo This combo box
11520 'specialfilter' : true,
11523 * Fires when tick the element
11524 * @param {Roo.bootstrap.ComboBox} combo This combo box
11528 * @event touchviewdisplay
11529 * Fires when touch view require special display (default is using displayField)
11530 * @param {Roo.bootstrap.ComboBox} combo This combo box
11531 * @param {Object} cfg set html .
11533 'touchviewdisplay' : true
11538 this.tickItems = [];
11540 this.selectedIndex = -1;
11541 if(this.mode == 'local'){
11542 if(config.queryDelay === undefined){
11543 this.queryDelay = 10;
11545 if(config.minChars === undefined){
11551 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11554 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11555 * rendering into an Roo.Editor, defaults to false)
11558 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11559 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11562 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11565 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11566 * the dropdown list (defaults to undefined, with no header element)
11570 * @cfg {String/Roo.Template} tpl The template to use to render the output
11574 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11576 listWidth: undefined,
11578 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11579 * mode = 'remote' or 'text' if mode = 'local')
11581 displayField: undefined,
11584 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11585 * mode = 'remote' or 'value' if mode = 'local').
11586 * Note: use of a valueField requires the user make a selection
11587 * in order for a value to be mapped.
11589 valueField: undefined,
11593 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11594 * field's data value (defaults to the underlying DOM element's name)
11596 hiddenName: undefined,
11598 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11602 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11604 selectedClass: 'active',
11607 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11611 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11612 * anchor positions (defaults to 'tl-bl')
11614 listAlign: 'tl-bl?',
11616 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11620 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11621 * query specified by the allQuery config option (defaults to 'query')
11623 triggerAction: 'query',
11625 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11626 * (defaults to 4, does not apply if editable = false)
11630 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11631 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11635 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11636 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11640 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11641 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11645 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11646 * when editable = true (defaults to false)
11648 selectOnFocus:false,
11650 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11652 queryParam: 'query',
11654 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
11655 * when mode = 'remote' (defaults to 'Loading...')
11657 loadingText: 'Loading...',
11659 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11663 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11667 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11668 * traditional select (defaults to true)
11672 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11676 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11680 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11681 * listWidth has a higher value)
11685 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11686 * allow the user to set arbitrary text into the field (defaults to false)
11688 forceSelection:false,
11690 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11691 * if typeAhead = true (defaults to 250)
11693 typeAheadDelay : 250,
11695 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11696 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11698 valueNotFoundText : undefined,
11700 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11702 blockFocus : false,
11705 * @cfg {Boolean} disableClear Disable showing of clear button.
11707 disableClear : false,
11709 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
11711 alwaysQuery : false,
11714 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
11719 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11721 invalidClass : "has-warning",
11724 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11726 validClass : "has-success",
11729 * @cfg {Boolean} specialFilter (true|false) special filter default false
11731 specialFilter : false,
11734 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11736 mobileTouchView : true,
11748 btnPosition : 'right',
11749 triggerList : true,
11750 showToggleBtn : true,
11752 emptyResultText: 'Empty',
11753 triggerText : 'Select',
11755 // element that contains real text value.. (when hidden is used..)
11757 getAutoCreate : function()
11765 if(Roo.isTouch && this.mobileTouchView){
11766 cfg = this.getAutoCreateTouchView();
11773 if(!this.tickable){
11774 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11779 * ComboBox with tickable selections
11782 var align = this.labelAlign || this.parentLabelAlign();
11785 cls : 'form-group roo-combobox-tickable' //input-group
11790 cls : 'tickable-buttons',
11795 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11796 html : this.triggerText
11802 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11809 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11816 buttons.cn.unshift({
11818 cls: 'select2-search-field-input'
11824 Roo.each(buttons.cn, function(c){
11826 c.cls += ' btn-' + _this.size;
11829 if (_this.disabled) {
11840 cls: 'form-hidden-field'
11844 cls: 'select2-choices',
11848 cls: 'select2-search-field',
11860 cls: 'select2-container input-group select2-container-multi',
11865 // cls: 'typeahead typeahead-long dropdown-menu',
11866 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
11871 if(this.hasFeedback && !this.allowBlank){
11875 cls: 'glyphicon form-control-feedback'
11878 combobox.cn.push(feedback);
11881 if (align ==='left' && this.fieldLabel.length) {
11883 // Roo.log("left and has label");
11889 cls : 'control-label col-sm-' + this.labelWidth,
11890 html : this.fieldLabel
11894 cls : "col-sm-" + (12 - this.labelWidth),
11901 } else if ( this.fieldLabel.length) {
11902 // Roo.log(" label");
11907 //cls : 'input-group-addon',
11908 html : this.fieldLabel
11918 // Roo.log(" no label && no align");
11925 ['xs','sm','md','lg'].map(function(size){
11926 if (settings[size]) {
11927 cfg.cls += ' col-' + size + '-' + settings[size];
11935 _initEventsCalled : false,
11938 initEvents: function()
11941 if (this._initEventsCalled) { // as we call render... prevent looping...
11944 this._initEventsCalled = true;
11947 throw "can not find store for combo";
11950 this.store = Roo.factory(this.store, Roo.data);
11952 // if we are building from html. then this element is so complex, that we can not really
11953 // use the rendered HTML.
11954 // so we have to trash and replace the previous code.
11955 if (Roo.XComponent.build_from_html) {
11957 // remove this element....
11958 var e = this.el.dom, k=0;
11959 while (e ) { e = e.previousSibling; ++k;}
11964 this.rendered = false;
11966 this.render(this.parent().getChildContainer(true), k);
11977 if(Roo.isTouch && this.mobileTouchView){
11978 this.initTouchView();
11983 this.initTickableEvents();
11987 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11989 if(this.hiddenName){
11991 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11993 this.hiddenField.dom.value =
11994 this.hiddenValue !== undefined ? this.hiddenValue :
11995 this.value !== undefined ? this.value : '';
11997 // prevent input submission
11998 this.el.dom.removeAttribute('name');
11999 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12004 // this.el.dom.setAttribute('autocomplete', 'off');
12007 var cls = 'x-combo-list';
12009 //this.list = new Roo.Layer({
12010 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12016 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12017 _this.list.setWidth(lw);
12020 this.list.on('mouseover', this.onViewOver, this);
12021 this.list.on('mousemove', this.onViewMove, this);
12023 this.list.on('scroll', this.onViewScroll, this);
12026 this.list.swallowEvent('mousewheel');
12027 this.assetHeight = 0;
12030 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12031 this.assetHeight += this.header.getHeight();
12034 this.innerList = this.list.createChild({cls:cls+'-inner'});
12035 this.innerList.on('mouseover', this.onViewOver, this);
12036 this.innerList.on('mousemove', this.onViewMove, this);
12037 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12039 if(this.allowBlank && !this.pageSize && !this.disableClear){
12040 this.footer = this.list.createChild({cls:cls+'-ft'});
12041 this.pageTb = new Roo.Toolbar(this.footer);
12045 this.footer = this.list.createChild({cls:cls+'-ft'});
12046 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12047 {pageSize: this.pageSize});
12051 if (this.pageTb && this.allowBlank && !this.disableClear) {
12053 this.pageTb.add(new Roo.Toolbar.Fill(), {
12054 cls: 'x-btn-icon x-btn-clear',
12056 handler: function()
12059 _this.clearValue();
12060 _this.onSelect(false, -1);
12065 this.assetHeight += this.footer.getHeight();
12070 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12073 this.view = new Roo.View(this.list, this.tpl, {
12074 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12076 //this.view.wrapEl.setDisplayed(false);
12077 this.view.on('click', this.onViewClick, this);
12081 this.store.on('beforeload', this.onBeforeLoad, this);
12082 this.store.on('load', this.onLoad, this);
12083 this.store.on('loadexception', this.onLoadException, this);
12085 if(this.resizable){
12086 this.resizer = new Roo.Resizable(this.list, {
12087 pinned:true, handles:'se'
12089 this.resizer.on('resize', function(r, w, h){
12090 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12091 this.listWidth = w;
12092 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12093 this.restrictHeight();
12095 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12098 if(!this.editable){
12099 this.editable = true;
12100 this.setEditable(false);
12105 if (typeof(this.events.add.listeners) != 'undefined') {
12107 this.addicon = this.wrap.createChild(
12108 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12110 this.addicon.on('click', function(e) {
12111 this.fireEvent('add', this);
12114 if (typeof(this.events.edit.listeners) != 'undefined') {
12116 this.editicon = this.wrap.createChild(
12117 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12118 if (this.addicon) {
12119 this.editicon.setStyle('margin-left', '40px');
12121 this.editicon.on('click', function(e) {
12123 // we fire even if inothing is selected..
12124 this.fireEvent('edit', this, this.lastData );
12130 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12131 "up" : function(e){
12132 this.inKeyMode = true;
12136 "down" : function(e){
12137 if(!this.isExpanded()){
12138 this.onTriggerClick();
12140 this.inKeyMode = true;
12145 "enter" : function(e){
12146 // this.onViewClick();
12150 if(this.fireEvent("specialkey", this, e)){
12151 this.onViewClick(false);
12157 "esc" : function(e){
12161 "tab" : function(e){
12164 if(this.fireEvent("specialkey", this, e)){
12165 this.onViewClick(false);
12173 doRelay : function(foo, bar, hname){
12174 if(hname == 'down' || this.scope.isExpanded()){
12175 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12184 this.queryDelay = Math.max(this.queryDelay || 10,
12185 this.mode == 'local' ? 10 : 250);
12188 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12190 if(this.typeAhead){
12191 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12193 if(this.editable !== false){
12194 this.inputEl().on("keyup", this.onKeyUp, this);
12196 if(this.forceSelection){
12197 this.inputEl().on('blur', this.doForce, this);
12201 this.choices = this.el.select('ul.select2-choices', true).first();
12202 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12206 initTickableEvents: function()
12210 if(this.hiddenName){
12212 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12214 this.hiddenField.dom.value =
12215 this.hiddenValue !== undefined ? this.hiddenValue :
12216 this.value !== undefined ? this.value : '';
12218 // prevent input submission
12219 this.el.dom.removeAttribute('name');
12220 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12225 // this.list = this.el.select('ul.dropdown-menu',true).first();
12227 this.choices = this.el.select('ul.select2-choices', true).first();
12228 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12229 if(this.triggerList){
12230 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12233 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12234 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12236 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12237 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12239 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12240 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12242 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12243 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12244 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12247 this.cancelBtn.hide();
12252 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12253 _this.list.setWidth(lw);
12256 this.list.on('mouseover', this.onViewOver, this);
12257 this.list.on('mousemove', this.onViewMove, this);
12259 this.list.on('scroll', this.onViewScroll, this);
12262 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>';
12265 this.view = new Roo.View(this.list, this.tpl, {
12266 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12269 //this.view.wrapEl.setDisplayed(false);
12270 this.view.on('click', this.onViewClick, this);
12274 this.store.on('beforeload', this.onBeforeLoad, this);
12275 this.store.on('load', this.onLoad, this);
12276 this.store.on('loadexception', this.onLoadException, this);
12279 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12280 "up" : function(e){
12281 this.inKeyMode = true;
12285 "down" : function(e){
12286 this.inKeyMode = true;
12290 "enter" : function(e){
12291 if(this.fireEvent("specialkey", this, e)){
12292 this.onViewClick(false);
12298 "esc" : function(e){
12299 this.onTickableFooterButtonClick(e, false, false);
12302 "tab" : function(e){
12303 this.fireEvent("specialkey", this, e);
12305 this.onTickableFooterButtonClick(e, false, false);
12312 doRelay : function(e, fn, key){
12313 if(this.scope.isExpanded()){
12314 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12323 this.queryDelay = Math.max(this.queryDelay || 10,
12324 this.mode == 'local' ? 10 : 250);
12327 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12329 if(this.typeAhead){
12330 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12333 if(this.editable !== false){
12334 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12339 onDestroy : function(){
12341 this.view.setStore(null);
12342 this.view.el.removeAllListeners();
12343 this.view.el.remove();
12344 this.view.purgeListeners();
12347 this.list.dom.innerHTML = '';
12351 this.store.un('beforeload', this.onBeforeLoad, this);
12352 this.store.un('load', this.onLoad, this);
12353 this.store.un('loadexception', this.onLoadException, this);
12355 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12359 fireKey : function(e){
12360 if(e.isNavKeyPress() && !this.list.isVisible()){
12361 this.fireEvent("specialkey", this, e);
12366 onResize: function(w, h){
12367 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12369 // if(typeof w != 'number'){
12370 // // we do not handle it!?!?
12373 // var tw = this.trigger.getWidth();
12374 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12375 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12377 // this.inputEl().setWidth( this.adjustWidth('input', x));
12379 // //this.trigger.setStyle('left', x+'px');
12381 // if(this.list && this.listWidth === undefined){
12382 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12383 // this.list.setWidth(lw);
12384 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12392 * Allow or prevent the user from directly editing the field text. If false is passed,
12393 * the user will only be able to select from the items defined in the dropdown list. This method
12394 * is the runtime equivalent of setting the 'editable' config option at config time.
12395 * @param {Boolean} value True to allow the user to directly edit the field text
12397 setEditable : function(value){
12398 if(value == this.editable){
12401 this.editable = value;
12403 this.inputEl().dom.setAttribute('readOnly', true);
12404 this.inputEl().on('mousedown', this.onTriggerClick, this);
12405 this.inputEl().addClass('x-combo-noedit');
12407 this.inputEl().dom.setAttribute('readOnly', false);
12408 this.inputEl().un('mousedown', this.onTriggerClick, this);
12409 this.inputEl().removeClass('x-combo-noedit');
12415 onBeforeLoad : function(combo,opts){
12416 if(!this.hasFocus){
12420 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12422 this.restrictHeight();
12423 this.selectedIndex = -1;
12427 onLoad : function(){
12429 this.hasQuery = false;
12431 if(!this.hasFocus){
12435 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12436 this.loading.hide();
12439 if(this.store.getCount() > 0){
12441 this.restrictHeight();
12442 if(this.lastQuery == this.allQuery){
12443 if(this.editable && !this.tickable){
12444 this.inputEl().dom.select();
12448 !this.selectByValue(this.value, true) &&
12451 !this.store.lastOptions ||
12452 typeof(this.store.lastOptions.add) == 'undefined' ||
12453 this.store.lastOptions.add != true
12456 this.select(0, true);
12459 if(this.autoFocus){
12462 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12463 this.taTask.delay(this.typeAheadDelay);
12467 this.onEmptyResults();
12473 onLoadException : function()
12475 this.hasQuery = false;
12477 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12478 this.loading.hide();
12481 if(this.tickable && this.editable){
12486 // only causes errors at present
12487 //Roo.log(this.store.reader.jsonData);
12488 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12490 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12496 onTypeAhead : function(){
12497 if(this.store.getCount() > 0){
12498 var r = this.store.getAt(0);
12499 var newValue = r.data[this.displayField];
12500 var len = newValue.length;
12501 var selStart = this.getRawValue().length;
12503 if(selStart != len){
12504 this.setRawValue(newValue);
12505 this.selectText(selStart, newValue.length);
12511 onSelect : function(record, index){
12513 if(this.fireEvent('beforeselect', this, record, index) !== false){
12515 this.setFromData(index > -1 ? record.data : false);
12518 this.fireEvent('select', this, record, index);
12523 * Returns the currently selected field value or empty string if no value is set.
12524 * @return {String} value The selected value
12526 getValue : function(){
12529 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12532 if(this.valueField){
12533 return typeof this.value != 'undefined' ? this.value : '';
12535 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12540 * Clears any text/value currently set in the field
12542 clearValue : function(){
12543 if(this.hiddenField){
12544 this.hiddenField.dom.value = '';
12547 this.setRawValue('');
12548 this.lastSelectionText = '';
12549 this.lastData = false;
12551 var close = this.closeTriggerEl();
12560 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12561 * will be displayed in the field. If the value does not match the data value of an existing item,
12562 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12563 * Otherwise the field will be blank (although the value will still be set).
12564 * @param {String} value The value to match
12566 setValue : function(v){
12573 if(this.valueField){
12574 var r = this.findRecord(this.valueField, v);
12576 text = r.data[this.displayField];
12577 }else if(this.valueNotFoundText !== undefined){
12578 text = this.valueNotFoundText;
12581 this.lastSelectionText = text;
12582 if(this.hiddenField){
12583 this.hiddenField.dom.value = v;
12585 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12588 var close = this.closeTriggerEl();
12591 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12595 * @property {Object} the last set data for the element
12600 * Sets the value of the field based on a object which is related to the record format for the store.
12601 * @param {Object} value the value to set as. or false on reset?
12603 setFromData : function(o){
12610 var dv = ''; // display value
12611 var vv = ''; // value value..
12613 if (this.displayField) {
12614 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12616 // this is an error condition!!!
12617 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12620 if(this.valueField){
12621 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12624 var close = this.closeTriggerEl();
12627 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12630 if(this.hiddenField){
12631 this.hiddenField.dom.value = vv;
12633 this.lastSelectionText = dv;
12634 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12638 // no hidden field.. - we store the value in 'value', but still display
12639 // display field!!!!
12640 this.lastSelectionText = dv;
12641 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12648 reset : function(){
12649 // overridden so that last data is reset..
12656 this.setValue(this.originalValue);
12657 this.clearInvalid();
12658 this.lastData = false;
12660 this.view.clearSelections();
12664 findRecord : function(prop, value){
12666 if(this.store.getCount() > 0){
12667 this.store.each(function(r){
12668 if(r.data[prop] == value){
12678 getName: function()
12680 // returns hidden if it's set..
12681 if (!this.rendered) {return ''};
12682 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
12686 onViewMove : function(e, t){
12687 this.inKeyMode = false;
12691 onViewOver : function(e, t){
12692 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12695 var item = this.view.findItemFromChild(t);
12698 var index = this.view.indexOf(item);
12699 this.select(index, false);
12704 onViewClick : function(view, doFocus, el, e)
12706 var index = this.view.getSelectedIndexes()[0];
12708 var r = this.store.getAt(index);
12712 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12719 Roo.each(this.tickItems, function(v,k){
12721 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12723 _this.tickItems.splice(k, 1);
12725 if(typeof(e) == 'undefined' && view == false){
12726 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12738 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
12739 this.tickItems.push(r.data);
12742 if(typeof(e) == 'undefined' && view == false){
12743 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12750 this.onSelect(r, index);
12752 if(doFocus !== false && !this.blockFocus){
12753 this.inputEl().focus();
12758 restrictHeight : function(){
12759 //this.innerList.dom.style.height = '';
12760 //var inner = this.innerList.dom;
12761 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12762 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12763 //this.list.beginUpdate();
12764 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12765 this.list.alignTo(this.inputEl(), this.listAlign);
12766 this.list.alignTo(this.inputEl(), this.listAlign);
12767 //this.list.endUpdate();
12771 onEmptyResults : function(){
12773 if(this.tickable && this.editable){
12774 this.restrictHeight();
12782 * Returns true if the dropdown list is expanded, else false.
12784 isExpanded : function(){
12785 return this.list.isVisible();
12789 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12790 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12791 * @param {String} value The data value of the item to select
12792 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12793 * selected item if it is not currently in view (defaults to true)
12794 * @return {Boolean} True if the value matched an item in the list, else false
12796 selectByValue : function(v, scrollIntoView){
12797 if(v !== undefined && v !== null){
12798 var r = this.findRecord(this.valueField || this.displayField, v);
12800 this.select(this.store.indexOf(r), scrollIntoView);
12808 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12809 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12810 * @param {Number} index The zero-based index of the list item to select
12811 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12812 * selected item if it is not currently in view (defaults to true)
12814 select : function(index, scrollIntoView){
12815 this.selectedIndex = index;
12816 this.view.select(index);
12817 if(scrollIntoView !== false){
12818 var el = this.view.getNode(index);
12820 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12823 this.list.scrollChildIntoView(el, false);
12829 selectNext : function(){
12830 var ct = this.store.getCount();
12832 if(this.selectedIndex == -1){
12834 }else if(this.selectedIndex < ct-1){
12835 this.select(this.selectedIndex+1);
12841 selectPrev : function(){
12842 var ct = this.store.getCount();
12844 if(this.selectedIndex == -1){
12846 }else if(this.selectedIndex != 0){
12847 this.select(this.selectedIndex-1);
12853 onKeyUp : function(e){
12854 if(this.editable !== false && !e.isSpecialKey()){
12855 this.lastKey = e.getKey();
12856 this.dqTask.delay(this.queryDelay);
12861 validateBlur : function(){
12862 return !this.list || !this.list.isVisible();
12866 initQuery : function(){
12868 var v = this.getRawValue();
12870 if(this.tickable && this.editable){
12871 v = this.tickableInputEl().getValue();
12878 doForce : function(){
12879 if(this.inputEl().dom.value.length > 0){
12880 this.inputEl().dom.value =
12881 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12887 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
12888 * query allowing the query action to be canceled if needed.
12889 * @param {String} query The SQL query to execute
12890 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12891 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
12892 * saved in the current store (defaults to false)
12894 doQuery : function(q, forceAll){
12896 if(q === undefined || q === null){
12901 forceAll: forceAll,
12905 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12910 forceAll = qe.forceAll;
12911 if(forceAll === true || (q.length >= this.minChars)){
12913 this.hasQuery = true;
12915 if(this.lastQuery != q || this.alwaysQuery){
12916 this.lastQuery = q;
12917 if(this.mode == 'local'){
12918 this.selectedIndex = -1;
12920 this.store.clearFilter();
12923 if(this.specialFilter){
12924 this.fireEvent('specialfilter', this);
12929 this.store.filter(this.displayField, q);
12932 this.store.fireEvent("datachanged", this.store);
12939 this.store.baseParams[this.queryParam] = q;
12941 var options = {params : this.getParams(q)};
12944 options.add = true;
12945 options.params.start = this.page * this.pageSize;
12948 this.store.load(options);
12951 * this code will make the page width larger, at the beginning, the list not align correctly,
12952 * we should expand the list on onLoad
12953 * so command out it
12958 this.selectedIndex = -1;
12963 this.loadNext = false;
12967 getParams : function(q){
12969 //p[this.queryParam] = q;
12973 p.limit = this.pageSize;
12979 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12981 collapse : function(){
12982 if(!this.isExpanded()){
12989 this.hasFocus = false;
12991 this.cancelBtn.hide();
12992 this.trigger.show();
12995 this.tickableInputEl().dom.value = '';
12996 this.tickableInputEl().blur();
13001 Roo.get(document).un('mousedown', this.collapseIf, this);
13002 Roo.get(document).un('mousewheel', this.collapseIf, this);
13003 if (!this.editable) {
13004 Roo.get(document).un('keydown', this.listKeyPress, this);
13006 this.fireEvent('collapse', this);
13010 collapseIf : function(e){
13011 var in_combo = e.within(this.el);
13012 var in_list = e.within(this.list);
13013 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13015 if (in_combo || in_list || is_list) {
13016 //e.stopPropagation();
13021 this.onTickableFooterButtonClick(e, false, false);
13029 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13031 expand : function(){
13033 if(this.isExpanded() || !this.hasFocus){
13037 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13038 this.list.setWidth(lw);
13045 this.restrictHeight();
13049 this.tickItems = Roo.apply([], this.item);
13052 this.cancelBtn.show();
13053 this.trigger.hide();
13056 this.tickableInputEl().focus();
13061 Roo.get(document).on('mousedown', this.collapseIf, this);
13062 Roo.get(document).on('mousewheel', this.collapseIf, this);
13063 if (!this.editable) {
13064 Roo.get(document).on('keydown', this.listKeyPress, this);
13067 this.fireEvent('expand', this);
13071 // Implements the default empty TriggerField.onTriggerClick function
13072 onTriggerClick : function(e)
13074 Roo.log('trigger click');
13076 if(this.disabled || !this.triggerList){
13081 this.loadNext = false;
13083 if(this.isExpanded()){
13085 if (!this.blockFocus) {
13086 this.inputEl().focus();
13090 this.hasFocus = true;
13091 if(this.triggerAction == 'all') {
13092 this.doQuery(this.allQuery, true);
13094 this.doQuery(this.getRawValue());
13096 if (!this.blockFocus) {
13097 this.inputEl().focus();
13102 onTickableTriggerClick : function(e)
13109 this.loadNext = false;
13110 this.hasFocus = true;
13112 if(this.triggerAction == 'all') {
13113 this.doQuery(this.allQuery, true);
13115 this.doQuery(this.getRawValue());
13119 onSearchFieldClick : function(e)
13121 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13122 this.onTickableFooterButtonClick(e, false, false);
13126 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13131 this.loadNext = false;
13132 this.hasFocus = true;
13134 if(this.triggerAction == 'all') {
13135 this.doQuery(this.allQuery, true);
13137 this.doQuery(this.getRawValue());
13141 listKeyPress : function(e)
13143 //Roo.log('listkeypress');
13144 // scroll to first matching element based on key pres..
13145 if (e.isSpecialKey()) {
13148 var k = String.fromCharCode(e.getKey()).toUpperCase();
13151 var csel = this.view.getSelectedNodes();
13152 var cselitem = false;
13154 var ix = this.view.indexOf(csel[0]);
13155 cselitem = this.store.getAt(ix);
13156 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13162 this.store.each(function(v) {
13164 // start at existing selection.
13165 if (cselitem.id == v.id) {
13171 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13172 match = this.store.indexOf(v);
13178 if (match === false) {
13179 return true; // no more action?
13182 this.view.select(match);
13183 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13184 sn.scrollIntoView(sn.dom.parentNode, false);
13187 onViewScroll : function(e, t){
13189 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){
13193 this.hasQuery = true;
13195 this.loading = this.list.select('.loading', true).first();
13197 if(this.loading === null){
13198 this.list.createChild({
13200 cls: 'loading select2-more-results select2-active',
13201 html: 'Loading more results...'
13204 this.loading = this.list.select('.loading', true).first();
13206 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13208 this.loading.hide();
13211 this.loading.show();
13216 this.loadNext = true;
13218 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13223 addItem : function(o)
13225 var dv = ''; // display value
13227 if (this.displayField) {
13228 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13230 // this is an error condition!!!
13231 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13238 var choice = this.choices.createChild({
13240 cls: 'select2-search-choice',
13249 cls: 'select2-search-choice-close',
13254 }, this.searchField);
13256 var close = choice.select('a.select2-search-choice-close', true).first();
13258 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13266 this.inputEl().dom.value = '';
13271 onRemoveItem : function(e, _self, o)
13273 e.preventDefault();
13275 this.lastItem = Roo.apply([], this.item);
13277 var index = this.item.indexOf(o.data) * 1;
13280 Roo.log('not this item?!');
13284 this.item.splice(index, 1);
13289 this.fireEvent('remove', this, e);
13295 syncValue : function()
13297 if(!this.item.length){
13304 Roo.each(this.item, function(i){
13305 if(_this.valueField){
13306 value.push(i[_this.valueField]);
13313 this.value = value.join(',');
13315 if(this.hiddenField){
13316 this.hiddenField.dom.value = this.value;
13319 this.store.fireEvent("datachanged", this.store);
13322 clearItem : function()
13324 if(!this.multiple){
13330 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13338 if(this.tickable && !Roo.isTouch){
13339 this.view.refresh();
13343 inputEl: function ()
13345 if(Roo.isTouch && this.mobileTouchView){
13346 return this.el.select('input.form-control',true).first();
13350 return this.searchField;
13353 return this.el.select('input.form-control',true).first();
13357 onTickableFooterButtonClick : function(e, btn, el)
13359 e.preventDefault();
13361 this.lastItem = Roo.apply([], this.item);
13363 if(btn && btn.name == 'cancel'){
13364 this.tickItems = Roo.apply([], this.item);
13373 Roo.each(this.tickItems, function(o){
13381 validate : function()
13383 var v = this.getRawValue();
13386 v = this.getValue();
13389 if(this.disabled || this.allowBlank || v.length){
13394 this.markInvalid();
13398 tickableInputEl : function()
13400 if(!this.tickable || !this.editable){
13401 return this.inputEl();
13404 return this.inputEl().select('.select2-search-field-input', true).first();
13408 getAutoCreateTouchView : function()
13413 cls: 'form-group' //input-group
13419 type : this.inputType,
13420 cls : 'form-control x-combo-noedit',
13421 autocomplete: 'new-password',
13422 placeholder : this.placeholder || '',
13427 input.name = this.name;
13431 input.cls += ' input-' + this.size;
13434 if (this.disabled) {
13435 input.disabled = true;
13446 inputblock.cls += ' input-group';
13448 inputblock.cn.unshift({
13450 cls : 'input-group-addon',
13455 if(this.removable && !this.multiple){
13456 inputblock.cls += ' roo-removable';
13458 inputblock.cn.push({
13461 cls : 'roo-combo-removable-btn close'
13465 if(this.hasFeedback && !this.allowBlank){
13467 inputblock.cls += ' has-feedback';
13469 inputblock.cn.push({
13471 cls: 'glyphicon form-control-feedback'
13478 inputblock.cls += (this.before) ? '' : ' input-group';
13480 inputblock.cn.push({
13482 cls : 'input-group-addon',
13493 cls: 'form-hidden-field'
13507 cls: 'form-hidden-field'
13511 cls: 'select2-choices',
13515 cls: 'select2-search-field',
13528 cls: 'select2-container input-group',
13535 combobox.cls += ' select2-container-multi';
13538 var align = this.labelAlign || this.parentLabelAlign();
13542 if(this.fieldLabel.length){
13544 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13545 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13550 cls : 'control-label ' + lw,
13551 html : this.fieldLabel
13563 var settings = this;
13565 ['xs','sm','md','lg'].map(function(size){
13566 if (settings[size]) {
13567 cfg.cls += ' col-' + size + '-' + settings[size];
13574 initTouchView : function()
13576 this.renderTouchView();
13578 this.touchViewEl.on('scroll', function(){
13579 this.el.dom.scrollTop = 0;
13582 this.originalValue = this.getValue();
13584 this.inputEl().on("click", this.showTouchView, this);
13586 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13587 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13589 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13591 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13592 this.store.on('load', this.onTouchViewLoad, this);
13593 this.store.on('loadexception', this.onTouchViewLoadException, this);
13595 if(this.hiddenName){
13597 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13599 this.hiddenField.dom.value =
13600 this.hiddenValue !== undefined ? this.hiddenValue :
13601 this.value !== undefined ? this.value : '';
13603 this.el.dom.removeAttribute('name');
13604 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13608 this.choices = this.el.select('ul.select2-choices', true).first();
13609 this.searchField = this.el.select('ul li.select2-search-field', true).first();
13612 if(this.removable && !this.multiple){
13613 var close = this.closeTriggerEl();
13615 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13616 close.on('click', this.removeBtnClick, this, close);
13620 * fix the bug in Safari iOS8
13622 this.inputEl().on("focus", function(e){
13623 document.activeElement.blur();
13631 renderTouchView : function()
13633 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13634 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13636 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13637 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13639 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13640 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13641 this.touchViewBodyEl.setStyle('overflow', 'auto');
13643 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13644 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13646 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13647 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13651 showTouchView : function()
13657 this.touchViewHeaderEl.hide();
13659 if(this.fieldLabel.length){
13660 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13661 this.touchViewHeaderEl.show();
13664 this.touchViewEl.show();
13666 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13667 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13669 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13671 if(this.fieldLabel.length){
13672 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13675 this.touchViewBodyEl.setHeight(bodyHeight);
13679 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13681 this.touchViewEl.addClass('in');
13684 this.doTouchViewQuery();
13688 hideTouchView : function()
13690 this.touchViewEl.removeClass('in');
13694 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13696 this.touchViewEl.setStyle('display', 'none');
13701 setTouchViewValue : function()
13708 Roo.each(this.tickItems, function(o){
13713 this.hideTouchView();
13716 doTouchViewQuery : function()
13725 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13729 if(!this.alwaysQuery || this.mode == 'local'){
13730 this.onTouchViewLoad();
13737 onTouchViewBeforeLoad : function(combo,opts)
13743 onTouchViewLoad : function()
13745 if(this.store.getCount() < 1){
13746 this.onTouchViewEmptyResults();
13750 this.clearTouchView();
13752 var rawValue = this.getRawValue();
13754 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13756 this.tickItems = [];
13758 this.store.data.each(function(d, rowIndex){
13759 var row = this.touchViewListGroup.createChild(template);
13761 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13764 html : d.data[this.displayField]
13767 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
13768 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
13772 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13773 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13776 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13777 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13778 this.tickItems.push(d.data);
13781 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13785 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13787 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13789 if(this.fieldLabel.length){
13790 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13793 var listHeight = this.touchViewListGroup.getHeight();
13797 if(firstChecked && listHeight > bodyHeight){
13798 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13803 onTouchViewLoadException : function()
13805 this.hideTouchView();
13808 onTouchViewEmptyResults : function()
13810 this.clearTouchView();
13812 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13814 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13818 clearTouchView : function()
13820 this.touchViewListGroup.dom.innerHTML = '';
13823 onTouchViewClick : function(e, el, o)
13825 e.preventDefault();
13828 var rowIndex = o.rowIndex;
13830 var r = this.store.getAt(rowIndex);
13832 if(!this.multiple){
13833 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13834 c.dom.removeAttribute('checked');
13837 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13839 this.setFromData(r.data);
13841 var close = this.closeTriggerEl();
13847 this.hideTouchView();
13849 this.fireEvent('select', this, r, rowIndex);
13854 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13855 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13856 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13860 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13861 this.addItem(r.data);
13862 this.tickItems.push(r.data);
13868 * @cfg {Boolean} grow
13872 * @cfg {Number} growMin
13876 * @cfg {Number} growMax
13885 Roo.apply(Roo.bootstrap.ComboBox, {
13889 cls: 'modal-header',
13911 cls: 'list-group-item',
13915 cls: 'roo-combobox-list-group-item-value'
13919 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13933 listItemCheckbox : {
13935 cls: 'list-group-item',
13939 cls: 'roo-combobox-list-group-item-value'
13943 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13959 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13964 cls: 'modal-footer',
13972 cls: 'col-xs-6 text-left',
13975 cls: 'btn btn-danger roo-touch-view-cancel',
13981 cls: 'col-xs-6 text-right',
13984 cls: 'btn btn-success roo-touch-view-ok',
13995 Roo.apply(Roo.bootstrap.ComboBox, {
13997 touchViewTemplate : {
13999 cls: 'modal fade roo-combobox-touch-view',
14003 cls: 'modal-dialog',
14004 style : 'position:fixed', // we have to fix position....
14008 cls: 'modal-content',
14010 Roo.bootstrap.ComboBox.header,
14011 Roo.bootstrap.ComboBox.body,
14012 Roo.bootstrap.ComboBox.footer
14021 * Ext JS Library 1.1.1
14022 * Copyright(c) 2006-2007, Ext JS, LLC.
14024 * Originally Released Under LGPL - original licence link has changed is not relivant.
14027 * <script type="text/javascript">
14032 * @extends Roo.util.Observable
14033 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14034 * This class also supports single and multi selection modes. <br>
14035 * Create a data model bound view:
14037 var store = new Roo.data.Store(...);
14039 var view = new Roo.View({
14041 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14043 singleSelect: true,
14044 selectedClass: "ydataview-selected",
14048 // listen for node click?
14049 view.on("click", function(vw, index, node, e){
14050 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14054 dataModel.load("foobar.xml");
14056 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14058 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14059 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14061 * Note: old style constructor is still suported (container, template, config)
14064 * Create a new View
14065 * @param {Object} config The config object
14068 Roo.View = function(config, depreciated_tpl, depreciated_config){
14070 this.parent = false;
14072 if (typeof(depreciated_tpl) == 'undefined') {
14073 // new way.. - universal constructor.
14074 Roo.apply(this, config);
14075 this.el = Roo.get(this.el);
14078 this.el = Roo.get(config);
14079 this.tpl = depreciated_tpl;
14080 Roo.apply(this, depreciated_config);
14082 this.wrapEl = this.el.wrap().wrap();
14083 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14086 if(typeof(this.tpl) == "string"){
14087 this.tpl = new Roo.Template(this.tpl);
14089 // support xtype ctors..
14090 this.tpl = new Roo.factory(this.tpl, Roo);
14094 this.tpl.compile();
14099 * @event beforeclick
14100 * Fires before a click is processed. Returns false to cancel the default action.
14101 * @param {Roo.View} this
14102 * @param {Number} index The index of the target node
14103 * @param {HTMLElement} node The target node
14104 * @param {Roo.EventObject} e The raw event object
14106 "beforeclick" : true,
14109 * Fires when a template node is clicked.
14110 * @param {Roo.View} this
14111 * @param {Number} index The index of the target node
14112 * @param {HTMLElement} node The target node
14113 * @param {Roo.EventObject} e The raw event object
14118 * Fires when a template node is double clicked.
14119 * @param {Roo.View} this
14120 * @param {Number} index The index of the target node
14121 * @param {HTMLElement} node The target node
14122 * @param {Roo.EventObject} e The raw event object
14126 * @event contextmenu
14127 * Fires when a template node is right clicked.
14128 * @param {Roo.View} this
14129 * @param {Number} index The index of the target node
14130 * @param {HTMLElement} node The target node
14131 * @param {Roo.EventObject} e The raw event object
14133 "contextmenu" : true,
14135 * @event selectionchange
14136 * Fires when the selected nodes change.
14137 * @param {Roo.View} this
14138 * @param {Array} selections Array of the selected nodes
14140 "selectionchange" : true,
14143 * @event beforeselect
14144 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14145 * @param {Roo.View} this
14146 * @param {HTMLElement} node The node to be selected
14147 * @param {Array} selections Array of currently selected nodes
14149 "beforeselect" : true,
14151 * @event preparedata
14152 * Fires on every row to render, to allow you to change the data.
14153 * @param {Roo.View} this
14154 * @param {Object} data to be rendered (change this)
14156 "preparedata" : true
14164 "click": this.onClick,
14165 "dblclick": this.onDblClick,
14166 "contextmenu": this.onContextMenu,
14170 this.selections = [];
14172 this.cmp = new Roo.CompositeElementLite([]);
14174 this.store = Roo.factory(this.store, Roo.data);
14175 this.setStore(this.store, true);
14178 if ( this.footer && this.footer.xtype) {
14180 var fctr = this.wrapEl.appendChild(document.createElement("div"));
14182 this.footer.dataSource = this.store;
14183 this.footer.container = fctr;
14184 this.footer = Roo.factory(this.footer, Roo);
14185 fctr.insertFirst(this.el);
14187 // this is a bit insane - as the paging toolbar seems to detach the el..
14188 // dom.parentNode.parentNode.parentNode
14189 // they get detached?
14193 Roo.View.superclass.constructor.call(this);
14198 Roo.extend(Roo.View, Roo.util.Observable, {
14201 * @cfg {Roo.data.Store} store Data store to load data from.
14206 * @cfg {String|Roo.Element} el The container element.
14211 * @cfg {String|Roo.Template} tpl The template used by this View
14215 * @cfg {String} dataName the named area of the template to use as the data area
14216 * Works with domtemplates roo-name="name"
14220 * @cfg {String} selectedClass The css class to add to selected nodes
14222 selectedClass : "x-view-selected",
14224 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14229 * @cfg {String} text to display on mask (default Loading)
14233 * @cfg {Boolean} multiSelect Allow multiple selection
14235 multiSelect : false,
14237 * @cfg {Boolean} singleSelect Allow single selection
14239 singleSelect: false,
14242 * @cfg {Boolean} toggleSelect - selecting
14244 toggleSelect : false,
14247 * @cfg {Boolean} tickable - selecting
14252 * Returns the element this view is bound to.
14253 * @return {Roo.Element}
14255 getEl : function(){
14256 return this.wrapEl;
14262 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14264 refresh : function(){
14265 //Roo.log('refresh');
14268 // if we are using something like 'domtemplate', then
14269 // the what gets used is:
14270 // t.applySubtemplate(NAME, data, wrapping data..)
14271 // the outer template then get' applied with
14272 // the store 'extra data'
14273 // and the body get's added to the
14274 // roo-name="data" node?
14275 // <span class='roo-tpl-{name}'></span> ?????
14279 this.clearSelections();
14280 this.el.update("");
14282 var records = this.store.getRange();
14283 if(records.length < 1) {
14285 // is this valid?? = should it render a template??
14287 this.el.update(this.emptyText);
14291 if (this.dataName) {
14292 this.el.update(t.apply(this.store.meta)); //????
14293 el = this.el.child('.roo-tpl-' + this.dataName);
14296 for(var i = 0, len = records.length; i < len; i++){
14297 var data = this.prepareData(records[i].data, i, records[i]);
14298 this.fireEvent("preparedata", this, data, i, records[i]);
14300 var d = Roo.apply({}, data);
14303 Roo.apply(d, {'roo-id' : Roo.id()});
14307 Roo.each(this.parent.item, function(item){
14308 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14311 Roo.apply(d, {'roo-data-checked' : 'checked'});
14315 html[html.length] = Roo.util.Format.trim(
14317 t.applySubtemplate(this.dataName, d, this.store.meta) :
14324 el.update(html.join(""));
14325 this.nodes = el.dom.childNodes;
14326 this.updateIndexes(0);
14331 * Function to override to reformat the data that is sent to
14332 * the template for each node.
14333 * DEPRICATED - use the preparedata event handler.
14334 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14335 * a JSON object for an UpdateManager bound view).
14337 prepareData : function(data, index, record)
14339 this.fireEvent("preparedata", this, data, index, record);
14343 onUpdate : function(ds, record){
14344 // Roo.log('on update');
14345 this.clearSelections();
14346 var index = this.store.indexOf(record);
14347 var n = this.nodes[index];
14348 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14349 n.parentNode.removeChild(n);
14350 this.updateIndexes(index, index);
14356 onAdd : function(ds, records, index)
14358 //Roo.log(['on Add', ds, records, index] );
14359 this.clearSelections();
14360 if(this.nodes.length == 0){
14364 var n = this.nodes[index];
14365 for(var i = 0, len = records.length; i < len; i++){
14366 var d = this.prepareData(records[i].data, i, records[i]);
14368 this.tpl.insertBefore(n, d);
14371 this.tpl.append(this.el, d);
14374 this.updateIndexes(index);
14377 onRemove : function(ds, record, index){
14378 // Roo.log('onRemove');
14379 this.clearSelections();
14380 var el = this.dataName ?
14381 this.el.child('.roo-tpl-' + this.dataName) :
14384 el.dom.removeChild(this.nodes[index]);
14385 this.updateIndexes(index);
14389 * Refresh an individual node.
14390 * @param {Number} index
14392 refreshNode : function(index){
14393 this.onUpdate(this.store, this.store.getAt(index));
14396 updateIndexes : function(startIndex, endIndex){
14397 var ns = this.nodes;
14398 startIndex = startIndex || 0;
14399 endIndex = endIndex || ns.length - 1;
14400 for(var i = startIndex; i <= endIndex; i++){
14401 ns[i].nodeIndex = i;
14406 * Changes the data store this view uses and refresh the view.
14407 * @param {Store} store
14409 setStore : function(store, initial){
14410 if(!initial && this.store){
14411 this.store.un("datachanged", this.refresh);
14412 this.store.un("add", this.onAdd);
14413 this.store.un("remove", this.onRemove);
14414 this.store.un("update", this.onUpdate);
14415 this.store.un("clear", this.refresh);
14416 this.store.un("beforeload", this.onBeforeLoad);
14417 this.store.un("load", this.onLoad);
14418 this.store.un("loadexception", this.onLoad);
14422 store.on("datachanged", this.refresh, this);
14423 store.on("add", this.onAdd, this);
14424 store.on("remove", this.onRemove, this);
14425 store.on("update", this.onUpdate, this);
14426 store.on("clear", this.refresh, this);
14427 store.on("beforeload", this.onBeforeLoad, this);
14428 store.on("load", this.onLoad, this);
14429 store.on("loadexception", this.onLoad, this);
14437 * onbeforeLoad - masks the loading area.
14440 onBeforeLoad : function(store,opts)
14442 //Roo.log('onBeforeLoad');
14444 this.el.update("");
14446 this.el.mask(this.mask ? this.mask : "Loading" );
14448 onLoad : function ()
14455 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14456 * @param {HTMLElement} node
14457 * @return {HTMLElement} The template node
14459 findItemFromChild : function(node){
14460 var el = this.dataName ?
14461 this.el.child('.roo-tpl-' + this.dataName,true) :
14464 if(!node || node.parentNode == el){
14467 var p = node.parentNode;
14468 while(p && p != el){
14469 if(p.parentNode == el){
14478 onClick : function(e){
14479 var item = this.findItemFromChild(e.getTarget());
14481 var index = this.indexOf(item);
14482 if(this.onItemClick(item, index, e) !== false){
14483 this.fireEvent("click", this, index, item, e);
14486 this.clearSelections();
14491 onContextMenu : function(e){
14492 var item = this.findItemFromChild(e.getTarget());
14494 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14499 onDblClick : function(e){
14500 var item = this.findItemFromChild(e.getTarget());
14502 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14506 onItemClick : function(item, index, e)
14508 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14511 if (this.toggleSelect) {
14512 var m = this.isSelected(item) ? 'unselect' : 'select';
14515 _t[m](item, true, false);
14518 if(this.multiSelect || this.singleSelect){
14519 if(this.multiSelect && e.shiftKey && this.lastSelection){
14520 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14522 this.select(item, this.multiSelect && e.ctrlKey);
14523 this.lastSelection = item;
14526 if(!this.tickable){
14527 e.preventDefault();
14535 * Get the number of selected nodes.
14538 getSelectionCount : function(){
14539 return this.selections.length;
14543 * Get the currently selected nodes.
14544 * @return {Array} An array of HTMLElements
14546 getSelectedNodes : function(){
14547 return this.selections;
14551 * Get the indexes of the selected nodes.
14554 getSelectedIndexes : function(){
14555 var indexes = [], s = this.selections;
14556 for(var i = 0, len = s.length; i < len; i++){
14557 indexes.push(s[i].nodeIndex);
14563 * Clear all selections
14564 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14566 clearSelections : function(suppressEvent){
14567 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14568 this.cmp.elements = this.selections;
14569 this.cmp.removeClass(this.selectedClass);
14570 this.selections = [];
14571 if(!suppressEvent){
14572 this.fireEvent("selectionchange", this, this.selections);
14578 * Returns true if the passed node is selected
14579 * @param {HTMLElement/Number} node The node or node index
14580 * @return {Boolean}
14582 isSelected : function(node){
14583 var s = this.selections;
14587 node = this.getNode(node);
14588 return s.indexOf(node) !== -1;
14593 * @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
14594 * @param {Boolean} keepExisting (optional) true to keep existing selections
14595 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14597 select : function(nodeInfo, keepExisting, suppressEvent){
14598 if(nodeInfo instanceof Array){
14600 this.clearSelections(true);
14602 for(var i = 0, len = nodeInfo.length; i < len; i++){
14603 this.select(nodeInfo[i], true, true);
14607 var node = this.getNode(nodeInfo);
14608 if(!node || this.isSelected(node)){
14609 return; // already selected.
14612 this.clearSelections(true);
14615 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14616 Roo.fly(node).addClass(this.selectedClass);
14617 this.selections.push(node);
14618 if(!suppressEvent){
14619 this.fireEvent("selectionchange", this, this.selections);
14627 * @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
14628 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14629 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14631 unselect : function(nodeInfo, keepExisting, suppressEvent)
14633 if(nodeInfo instanceof Array){
14634 Roo.each(this.selections, function(s) {
14635 this.unselect(s, nodeInfo);
14639 var node = this.getNode(nodeInfo);
14640 if(!node || !this.isSelected(node)){
14641 //Roo.log("not selected");
14642 return; // not selected.
14646 Roo.each(this.selections, function(s) {
14648 Roo.fly(node).removeClass(this.selectedClass);
14655 this.selections= ns;
14656 this.fireEvent("selectionchange", this, this.selections);
14660 * Gets a template node.
14661 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14662 * @return {HTMLElement} The node or null if it wasn't found
14664 getNode : function(nodeInfo){
14665 if(typeof nodeInfo == "string"){
14666 return document.getElementById(nodeInfo);
14667 }else if(typeof nodeInfo == "number"){
14668 return this.nodes[nodeInfo];
14674 * Gets a range template nodes.
14675 * @param {Number} startIndex
14676 * @param {Number} endIndex
14677 * @return {Array} An array of nodes
14679 getNodes : function(start, end){
14680 var ns = this.nodes;
14681 start = start || 0;
14682 end = typeof end == "undefined" ? ns.length - 1 : end;
14685 for(var i = start; i <= end; i++){
14689 for(var i = start; i >= end; i--){
14697 * Finds the index of the passed node
14698 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14699 * @return {Number} The index of the node or -1
14701 indexOf : function(node){
14702 node = this.getNode(node);
14703 if(typeof node.nodeIndex == "number"){
14704 return node.nodeIndex;
14706 var ns = this.nodes;
14707 for(var i = 0, len = ns.length; i < len; i++){
14718 * based on jquery fullcalendar
14722 Roo.bootstrap = Roo.bootstrap || {};
14724 * @class Roo.bootstrap.Calendar
14725 * @extends Roo.bootstrap.Component
14726 * Bootstrap Calendar class
14727 * @cfg {Boolean} loadMask (true|false) default false
14728 * @cfg {Object} header generate the user specific header of the calendar, default false
14731 * Create a new Container
14732 * @param {Object} config The config object
14737 Roo.bootstrap.Calendar = function(config){
14738 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14742 * Fires when a date is selected
14743 * @param {DatePicker} this
14744 * @param {Date} date The selected date
14748 * @event monthchange
14749 * Fires when the displayed month changes
14750 * @param {DatePicker} this
14751 * @param {Date} date The selected month
14753 'monthchange': true,
14755 * @event evententer
14756 * Fires when mouse over an event
14757 * @param {Calendar} this
14758 * @param {event} Event
14760 'evententer': true,
14762 * @event eventleave
14763 * Fires when the mouse leaves an
14764 * @param {Calendar} this
14767 'eventleave': true,
14769 * @event eventclick
14770 * Fires when the mouse click an
14771 * @param {Calendar} this
14780 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
14783 * @cfg {Number} startDay
14784 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14792 getAutoCreate : function(){
14795 var fc_button = function(name, corner, style, content ) {
14796 return Roo.apply({},{
14798 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
14800 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14803 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14814 style : 'width:100%',
14821 cls : 'fc-header-left',
14823 fc_button('prev', 'left', 'arrow', '‹' ),
14824 fc_button('next', 'right', 'arrow', '›' ),
14825 { tag: 'span', cls: 'fc-header-space' },
14826 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
14834 cls : 'fc-header-center',
14838 cls: 'fc-header-title',
14841 html : 'month / year'
14849 cls : 'fc-header-right',
14851 /* fc_button('month', 'left', '', 'month' ),
14852 fc_button('week', '', '', 'week' ),
14853 fc_button('day', 'right', '', 'day' )
14865 header = this.header;
14868 var cal_heads = function() {
14870 // fixme - handle this.
14872 for (var i =0; i < Date.dayNames.length; i++) {
14873 var d = Date.dayNames[i];
14876 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14877 html : d.substring(0,3)
14881 ret[0].cls += ' fc-first';
14882 ret[6].cls += ' fc-last';
14885 var cal_cell = function(n) {
14888 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14893 cls: 'fc-day-number',
14897 cls: 'fc-day-content',
14901 style: 'position: relative;' // height: 17px;
14913 var cal_rows = function() {
14916 for (var r = 0; r < 6; r++) {
14923 for (var i =0; i < Date.dayNames.length; i++) {
14924 var d = Date.dayNames[i];
14925 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14928 row.cn[0].cls+=' fc-first';
14929 row.cn[0].cn[0].style = 'min-height:90px';
14930 row.cn[6].cls+=' fc-last';
14934 ret[0].cls += ' fc-first';
14935 ret[4].cls += ' fc-prev-last';
14936 ret[5].cls += ' fc-last';
14943 cls: 'fc-border-separate',
14944 style : 'width:100%',
14952 cls : 'fc-first fc-last',
14970 cls : 'fc-content',
14971 style : "position: relative;",
14974 cls : 'fc-view fc-view-month fc-grid',
14975 style : 'position: relative',
14976 unselectable : 'on',
14979 cls : 'fc-event-container',
14980 style : 'position:absolute;z-index:8;top:0;left:0;'
14998 initEvents : function()
15001 throw "can not find store for calendar";
15007 style: "text-align:center",
15011 style: "background-color:white;width:50%;margin:250 auto",
15015 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15026 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15028 var size = this.el.select('.fc-content', true).first().getSize();
15029 this.maskEl.setSize(size.width, size.height);
15030 this.maskEl.enableDisplayMode("block");
15031 if(!this.loadMask){
15032 this.maskEl.hide();
15035 this.store = Roo.factory(this.store, Roo.data);
15036 this.store.on('load', this.onLoad, this);
15037 this.store.on('beforeload', this.onBeforeLoad, this);
15041 this.cells = this.el.select('.fc-day',true);
15042 //Roo.log(this.cells);
15043 this.textNodes = this.el.query('.fc-day-number');
15044 this.cells.addClassOnOver('fc-state-hover');
15046 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15047 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15048 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15049 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15051 this.on('monthchange', this.onMonthChange, this);
15053 this.update(new Date().clearTime());
15056 resize : function() {
15057 var sz = this.el.getSize();
15059 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15060 this.el.select('.fc-day-content div',true).setHeight(34);
15065 showPrevMonth : function(e){
15066 this.update(this.activeDate.add("mo", -1));
15068 showToday : function(e){
15069 this.update(new Date().clearTime());
15072 showNextMonth : function(e){
15073 this.update(this.activeDate.add("mo", 1));
15077 showPrevYear : function(){
15078 this.update(this.activeDate.add("y", -1));
15082 showNextYear : function(){
15083 this.update(this.activeDate.add("y", 1));
15088 update : function(date)
15090 var vd = this.activeDate;
15091 this.activeDate = date;
15092 // if(vd && this.el){
15093 // var t = date.getTime();
15094 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15095 // Roo.log('using add remove');
15097 // this.fireEvent('monthchange', this, date);
15099 // this.cells.removeClass("fc-state-highlight");
15100 // this.cells.each(function(c){
15101 // if(c.dateValue == t){
15102 // c.addClass("fc-state-highlight");
15103 // setTimeout(function(){
15104 // try{c.dom.firstChild.focus();}catch(e){}
15114 var days = date.getDaysInMonth();
15116 var firstOfMonth = date.getFirstDateOfMonth();
15117 var startingPos = firstOfMonth.getDay()-this.startDay;
15119 if(startingPos < this.startDay){
15123 var pm = date.add(Date.MONTH, -1);
15124 var prevStart = pm.getDaysInMonth()-startingPos;
15126 this.cells = this.el.select('.fc-day',true);
15127 this.textNodes = this.el.query('.fc-day-number');
15128 this.cells.addClassOnOver('fc-state-hover');
15130 var cells = this.cells.elements;
15131 var textEls = this.textNodes;
15133 Roo.each(cells, function(cell){
15134 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15137 days += startingPos;
15139 // convert everything to numbers so it's fast
15140 var day = 86400000;
15141 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15144 //Roo.log(prevStart);
15146 var today = new Date().clearTime().getTime();
15147 var sel = date.clearTime().getTime();
15148 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15149 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15150 var ddMatch = this.disabledDatesRE;
15151 var ddText = this.disabledDatesText;
15152 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15153 var ddaysText = this.disabledDaysText;
15154 var format = this.format;
15156 var setCellClass = function(cal, cell){
15160 //Roo.log('set Cell Class');
15162 var t = d.getTime();
15166 cell.dateValue = t;
15168 cell.className += " fc-today";
15169 cell.className += " fc-state-highlight";
15170 cell.title = cal.todayText;
15173 // disable highlight in other month..
15174 //cell.className += " fc-state-highlight";
15179 cell.className = " fc-state-disabled";
15180 cell.title = cal.minText;
15184 cell.className = " fc-state-disabled";
15185 cell.title = cal.maxText;
15189 if(ddays.indexOf(d.getDay()) != -1){
15190 cell.title = ddaysText;
15191 cell.className = " fc-state-disabled";
15194 if(ddMatch && format){
15195 var fvalue = d.dateFormat(format);
15196 if(ddMatch.test(fvalue)){
15197 cell.title = ddText.replace("%0", fvalue);
15198 cell.className = " fc-state-disabled";
15202 if (!cell.initialClassName) {
15203 cell.initialClassName = cell.dom.className;
15206 cell.dom.className = cell.initialClassName + ' ' + cell.className;
15211 for(; i < startingPos; i++) {
15212 textEls[i].innerHTML = (++prevStart);
15213 d.setDate(d.getDate()+1);
15215 cells[i].className = "fc-past fc-other-month";
15216 setCellClass(this, cells[i]);
15221 for(; i < days; i++){
15222 intDay = i - startingPos + 1;
15223 textEls[i].innerHTML = (intDay);
15224 d.setDate(d.getDate()+1);
15226 cells[i].className = ''; // "x-date-active";
15227 setCellClass(this, cells[i]);
15231 for(; i < 42; i++) {
15232 textEls[i].innerHTML = (++extraDays);
15233 d.setDate(d.getDate()+1);
15235 cells[i].className = "fc-future fc-other-month";
15236 setCellClass(this, cells[i]);
15239 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15241 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15243 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15244 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15246 if(totalRows != 6){
15247 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15248 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15251 this.fireEvent('monthchange', this, date);
15255 if(!this.internalRender){
15256 var main = this.el.dom.firstChild;
15257 var w = main.offsetWidth;
15258 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15259 Roo.fly(main).setWidth(w);
15260 this.internalRender = true;
15261 // opera does not respect the auto grow header center column
15262 // then, after it gets a width opera refuses to recalculate
15263 // without a second pass
15264 if(Roo.isOpera && !this.secondPass){
15265 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15266 this.secondPass = true;
15267 this.update.defer(10, this, [date]);
15274 findCell : function(dt) {
15275 dt = dt.clearTime().getTime();
15277 this.cells.each(function(c){
15278 //Roo.log("check " +c.dateValue + '?=' + dt);
15279 if(c.dateValue == dt){
15289 findCells : function(ev) {
15290 var s = ev.start.clone().clearTime().getTime();
15292 var e= ev.end.clone().clearTime().getTime();
15295 this.cells.each(function(c){
15296 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15298 if(c.dateValue > e){
15301 if(c.dateValue < s){
15310 // findBestRow: function(cells)
15314 // for (var i =0 ; i < cells.length;i++) {
15315 // ret = Math.max(cells[i].rows || 0,ret);
15322 addItem : function(ev)
15324 // look for vertical location slot in
15325 var cells = this.findCells(ev);
15327 // ev.row = this.findBestRow(cells);
15329 // work out the location.
15333 for(var i =0; i < cells.length; i++) {
15335 cells[i].row = cells[0].row;
15338 cells[i].row = cells[i].row + 1;
15348 if (crow.start.getY() == cells[i].getY()) {
15350 crow.end = cells[i];
15367 cells[0].events.push(ev);
15369 this.calevents.push(ev);
15372 clearEvents: function() {
15374 if(!this.calevents){
15378 Roo.each(this.cells.elements, function(c){
15384 Roo.each(this.calevents, function(e) {
15385 Roo.each(e.els, function(el) {
15386 el.un('mouseenter' ,this.onEventEnter, this);
15387 el.un('mouseleave' ,this.onEventLeave, this);
15392 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15398 renderEvents: function()
15402 this.cells.each(function(c) {
15411 if(c.row != c.events.length){
15412 r = 4 - (4 - (c.row - c.events.length));
15415 c.events = ev.slice(0, r);
15416 c.more = ev.slice(r);
15418 if(c.more.length && c.more.length == 1){
15419 c.events.push(c.more.pop());
15422 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15426 this.cells.each(function(c) {
15428 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15431 for (var e = 0; e < c.events.length; e++){
15432 var ev = c.events[e];
15433 var rows = ev.rows;
15435 for(var i = 0; i < rows.length; i++) {
15437 // how many rows should it span..
15440 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15441 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15443 unselectable : "on",
15446 cls: 'fc-event-inner',
15450 // cls: 'fc-event-time',
15451 // html : cells.length > 1 ? '' : ev.time
15455 cls: 'fc-event-title',
15456 html : String.format('{0}', ev.title)
15463 cls: 'ui-resizable-handle ui-resizable-e',
15464 html : '  '
15471 cfg.cls += ' fc-event-start';
15473 if ((i+1) == rows.length) {
15474 cfg.cls += ' fc-event-end';
15477 var ctr = _this.el.select('.fc-event-container',true).first();
15478 var cg = ctr.createChild(cfg);
15480 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15481 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15483 var r = (c.more.length) ? 1 : 0;
15484 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15485 cg.setWidth(ebox.right - sbox.x -2);
15487 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15488 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15489 cg.on('click', _this.onEventClick, _this, ev);
15500 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15501 style : 'position: absolute',
15502 unselectable : "on",
15505 cls: 'fc-event-inner',
15509 cls: 'fc-event-title',
15517 cls: 'ui-resizable-handle ui-resizable-e',
15518 html : '  '
15524 var ctr = _this.el.select('.fc-event-container',true).first();
15525 var cg = ctr.createChild(cfg);
15527 var sbox = c.select('.fc-day-content',true).first().getBox();
15528 var ebox = c.select('.fc-day-content',true).first().getBox();
15530 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15531 cg.setWidth(ebox.right - sbox.x -2);
15533 cg.on('click', _this.onMoreEventClick, _this, c.more);
15543 onEventEnter: function (e, el,event,d) {
15544 this.fireEvent('evententer', this, el, event);
15547 onEventLeave: function (e, el,event,d) {
15548 this.fireEvent('eventleave', this, el, event);
15551 onEventClick: function (e, el,event,d) {
15552 this.fireEvent('eventclick', this, el, event);
15555 onMonthChange: function () {
15559 onMoreEventClick: function(e, el, more)
15563 this.calpopover.placement = 'right';
15564 this.calpopover.setTitle('More');
15566 this.calpopover.setContent('');
15568 var ctr = this.calpopover.el.select('.popover-content', true).first();
15570 Roo.each(more, function(m){
15572 cls : 'fc-event-hori fc-event-draggable',
15575 var cg = ctr.createChild(cfg);
15577 cg.on('click', _this.onEventClick, _this, m);
15580 this.calpopover.show(el);
15585 onLoad: function ()
15587 this.calevents = [];
15590 if(this.store.getCount() > 0){
15591 this.store.data.each(function(d){
15594 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15595 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15596 time : d.data.start_time,
15597 title : d.data.title,
15598 description : d.data.description,
15599 venue : d.data.venue
15604 this.renderEvents();
15606 if(this.calevents.length && this.loadMask){
15607 this.maskEl.hide();
15611 onBeforeLoad: function()
15613 this.clearEvents();
15615 this.maskEl.show();
15629 * @class Roo.bootstrap.Popover
15630 * @extends Roo.bootstrap.Component
15631 * Bootstrap Popover class
15632 * @cfg {String} html contents of the popover (or false to use children..)
15633 * @cfg {String} title of popover (or false to hide)
15634 * @cfg {String} placement how it is placed
15635 * @cfg {String} trigger click || hover (or false to trigger manually)
15636 * @cfg {String} over what (parent or false to trigger manually.)
15637 * @cfg {Number} delay - delay before showing
15640 * Create a new Popover
15641 * @param {Object} config The config object
15644 Roo.bootstrap.Popover = function(config){
15645 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15651 * After the popover show
15653 * @param {Roo.bootstrap.Popover} this
15658 * After the popover hide
15660 * @param {Roo.bootstrap.Popover} this
15666 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
15668 title: 'Fill in a title',
15671 placement : 'right',
15672 trigger : 'hover', // hover
15678 can_build_overlaid : false,
15680 getChildContainer : function()
15682 return this.el.select('.popover-content',true).first();
15685 getAutoCreate : function(){
15688 cls : 'popover roo-dynamic',
15689 style: 'display:block',
15695 cls : 'popover-inner',
15699 cls: 'popover-title',
15703 cls : 'popover-content',
15714 setTitle: function(str)
15717 this.el.select('.popover-title',true).first().dom.innerHTML = str;
15719 setContent: function(str)
15722 this.el.select('.popover-content',true).first().dom.innerHTML = str;
15724 // as it get's added to the bottom of the page.
15725 onRender : function(ct, position)
15727 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15729 var cfg = Roo.apply({}, this.getAutoCreate());
15733 cfg.cls += ' ' + this.cls;
15736 cfg.style = this.style;
15738 //Roo.log("adding to ");
15739 this.el = Roo.get(document.body).createChild(cfg, position);
15740 // Roo.log(this.el);
15745 initEvents : function()
15747 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15748 this.el.enableDisplayMode('block');
15750 if (this.over === false) {
15753 if (this.triggers === false) {
15756 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15757 var triggers = this.trigger ? this.trigger.split(' ') : [];
15758 Roo.each(triggers, function(trigger) {
15760 if (trigger == 'click') {
15761 on_el.on('click', this.toggle, this);
15762 } else if (trigger != 'manual') {
15763 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
15764 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15766 on_el.on(eventIn ,this.enter, this);
15767 on_el.on(eventOut, this.leave, this);
15778 toggle : function () {
15779 this.hoverState == 'in' ? this.leave() : this.enter();
15782 enter : function () {
15785 clearTimeout(this.timeout);
15787 this.hoverState = 'in';
15789 if (!this.delay || !this.delay.show) {
15794 this.timeout = setTimeout(function () {
15795 if (_t.hoverState == 'in') {
15798 }, this.delay.show)
15800 leave : function() {
15801 clearTimeout(this.timeout);
15803 this.hoverState = 'out';
15805 if (!this.delay || !this.delay.hide) {
15810 this.timeout = setTimeout(function () {
15811 if (_t.hoverState == 'out') {
15814 }, this.delay.hide)
15817 show : function (on_el)
15820 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15823 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15824 if (this.html !== false) {
15825 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15827 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15828 if (!this.title.length) {
15829 this.el.select('.popover-title',true).hide();
15832 var placement = typeof this.placement == 'function' ?
15833 this.placement.call(this, this.el, on_el) :
15836 var autoToken = /\s?auto?\s?/i;
15837 var autoPlace = autoToken.test(placement);
15839 placement = placement.replace(autoToken, '') || 'top';
15843 //this.el.setXY([0,0]);
15845 this.el.dom.style.display='block';
15846 this.el.addClass(placement);
15848 //this.el.appendTo(on_el);
15850 var p = this.getPosition();
15851 var box = this.el.getBox();
15856 var align = Roo.bootstrap.Popover.alignment[placement];
15857 this.el.alignTo(on_el, align[0],align[1]);
15858 //var arrow = this.el.select('.arrow',true).first();
15859 //arrow.set(align[2],
15861 this.el.addClass('in');
15864 if (this.el.hasClass('fade')) {
15868 this.fireEvent('show', this);
15873 this.el.setXY([0,0]);
15874 this.el.removeClass('in');
15876 this.hoverState = null;
15878 this.fireEvent('hide', this);
15883 Roo.bootstrap.Popover.alignment = {
15884 'left' : ['r-l', [-10,0], 'right'],
15885 'right' : ['l-r', [10,0], 'left'],
15886 'bottom' : ['t-b', [0,10], 'top'],
15887 'top' : [ 'b-t', [0,-10], 'bottom']
15898 * @class Roo.bootstrap.Progress
15899 * @extends Roo.bootstrap.Component
15900 * Bootstrap Progress class
15901 * @cfg {Boolean} striped striped of the progress bar
15902 * @cfg {Boolean} active animated of the progress bar
15906 * Create a new Progress
15907 * @param {Object} config The config object
15910 Roo.bootstrap.Progress = function(config){
15911 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15914 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
15919 getAutoCreate : function(){
15927 cfg.cls += ' progress-striped';
15931 cfg.cls += ' active';
15950 * @class Roo.bootstrap.ProgressBar
15951 * @extends Roo.bootstrap.Component
15952 * Bootstrap ProgressBar class
15953 * @cfg {Number} aria_valuenow aria-value now
15954 * @cfg {Number} aria_valuemin aria-value min
15955 * @cfg {Number} aria_valuemax aria-value max
15956 * @cfg {String} label label for the progress bar
15957 * @cfg {String} panel (success | info | warning | danger )
15958 * @cfg {String} role role of the progress bar
15959 * @cfg {String} sr_only text
15963 * Create a new ProgressBar
15964 * @param {Object} config The config object
15967 Roo.bootstrap.ProgressBar = function(config){
15968 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15971 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
15975 aria_valuemax : 100,
15981 getAutoCreate : function()
15986 cls: 'progress-bar',
15987 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15999 cfg.role = this.role;
16002 if(this.aria_valuenow){
16003 cfg['aria-valuenow'] = this.aria_valuenow;
16006 if(this.aria_valuemin){
16007 cfg['aria-valuemin'] = this.aria_valuemin;
16010 if(this.aria_valuemax){
16011 cfg['aria-valuemax'] = this.aria_valuemax;
16014 if(this.label && !this.sr_only){
16015 cfg.html = this.label;
16019 cfg.cls += ' progress-bar-' + this.panel;
16025 update : function(aria_valuenow)
16027 this.aria_valuenow = aria_valuenow;
16029 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16044 * @class Roo.bootstrap.TabGroup
16045 * @extends Roo.bootstrap.Column
16046 * Bootstrap Column class
16047 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16048 * @cfg {Boolean} carousel true to make the group behave like a carousel
16049 * @cfg {Boolean} bullets show bullets for the panels
16050 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16051 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16052 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16055 * Create a new TabGroup
16056 * @param {Object} config The config object
16059 Roo.bootstrap.TabGroup = function(config){
16060 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16062 this.navId = Roo.id();
16065 Roo.bootstrap.TabGroup.register(this);
16069 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16072 transition : false,
16077 slideOnTouch : false,
16079 getAutoCreate : function()
16081 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16083 cfg.cls += ' tab-content';
16085 if (this.carousel) {
16086 cfg.cls += ' carousel slide';
16089 cls : 'carousel-inner'
16092 if(this.bullets && !Roo.isTouch){
16095 cls : 'carousel-bullets',
16099 if(this.bullets_cls){
16100 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16103 for (var i = 0; i < this.bullets; i++){
16105 cls : 'bullet bullet-' + i
16113 cfg.cn[0].cn = bullets;
16120 initEvents: function()
16122 if(Roo.isTouch && this.slideOnTouch){
16123 this.el.on("touchstart", this.onTouchStart, this);
16126 if(this.autoslide){
16129 this.slideFn = window.setInterval(function() {
16130 _this.showPanelNext();
16136 onTouchStart : function(e, el, o)
16138 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16142 this.showPanelNext();
16145 getChildContainer : function()
16147 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16151 * register a Navigation item
16152 * @param {Roo.bootstrap.NavItem} the navitem to add
16154 register : function(item)
16156 this.tabs.push( item);
16157 item.navId = this.navId; // not really needed..
16162 getActivePanel : function()
16165 Roo.each(this.tabs, function(t) {
16175 getPanelByName : function(n)
16178 Roo.each(this.tabs, function(t) {
16179 if (t.tabId == n) {
16187 indexOfPanel : function(p)
16190 Roo.each(this.tabs, function(t,i) {
16191 if (t.tabId == p.tabId) {
16200 * show a specific panel
16201 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16202 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16204 showPanel : function (pan)
16206 if(this.transition || typeof(pan) == 'undefined'){
16207 Roo.log("waiting for the transitionend");
16211 if (typeof(pan) == 'number') {
16212 pan = this.tabs[pan];
16215 if (typeof(pan) == 'string') {
16216 pan = this.getPanelByName(pan);
16219 var cur = this.getActivePanel();
16222 Roo.log('pan or acitve pan is undefined');
16226 if (pan.tabId == this.getActivePanel().tabId) {
16230 if (false === cur.fireEvent('beforedeactivate')) {
16234 if(this.bullets > 0 && !Roo.isTouch){
16235 this.setActiveBullet(this.indexOfPanel(pan));
16238 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16240 this.transition = true;
16241 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16242 var lr = dir == 'next' ? 'left' : 'right';
16243 pan.el.addClass(dir); // or prev
16244 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16245 cur.el.addClass(lr); // or right
16246 pan.el.addClass(lr);
16249 cur.el.on('transitionend', function() {
16250 Roo.log("trans end?");
16252 pan.el.removeClass([lr,dir]);
16253 pan.setActive(true);
16255 cur.el.removeClass([lr]);
16256 cur.setActive(false);
16258 _this.transition = false;
16260 }, this, { single: true } );
16265 cur.setActive(false);
16266 pan.setActive(true);
16271 showPanelNext : function()
16273 var i = this.indexOfPanel(this.getActivePanel());
16275 if (i >= this.tabs.length - 1 && !this.autoslide) {
16279 if (i >= this.tabs.length - 1 && this.autoslide) {
16283 this.showPanel(this.tabs[i+1]);
16286 showPanelPrev : function()
16288 var i = this.indexOfPanel(this.getActivePanel());
16290 if (i < 1 && !this.autoslide) {
16294 if (i < 1 && this.autoslide) {
16295 i = this.tabs.length;
16298 this.showPanel(this.tabs[i-1]);
16302 addBullet: function()
16304 if(!this.bullets || Roo.isTouch){
16307 var ctr = this.el.select('.carousel-bullets',true).first();
16308 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16309 var bullet = ctr.createChild({
16310 cls : 'bullet bullet-' + i
16311 },ctr.dom.lastChild);
16316 bullet.on('click', (function(e, el, o, ii, t){
16318 e.preventDefault();
16320 this.showPanel(ii);
16322 if(this.autoslide && this.slideFn){
16323 clearInterval(this.slideFn);
16324 this.slideFn = window.setInterval(function() {
16325 _this.showPanelNext();
16329 }).createDelegate(this, [i, bullet], true));
16334 setActiveBullet : function(i)
16340 Roo.each(this.el.select('.bullet', true).elements, function(el){
16341 el.removeClass('selected');
16344 var bullet = this.el.select('.bullet-' + i, true).first();
16350 bullet.addClass('selected');
16361 Roo.apply(Roo.bootstrap.TabGroup, {
16365 * register a Navigation Group
16366 * @param {Roo.bootstrap.NavGroup} the navgroup to add
16368 register : function(navgrp)
16370 this.groups[navgrp.navId] = navgrp;
16374 * fetch a Navigation Group based on the navigation ID
16375 * if one does not exist , it will get created.
16376 * @param {string} the navgroup to add
16377 * @returns {Roo.bootstrap.NavGroup} the navgroup
16379 get: function(navId) {
16380 if (typeof(this.groups[navId]) == 'undefined') {
16381 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16383 return this.groups[navId] ;
16398 * @class Roo.bootstrap.TabPanel
16399 * @extends Roo.bootstrap.Component
16400 * Bootstrap TabPanel class
16401 * @cfg {Boolean} active panel active
16402 * @cfg {String} html panel content
16403 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16404 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16408 * Create a new TabPanel
16409 * @param {Object} config The config object
16412 Roo.bootstrap.TabPanel = function(config){
16413 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16417 * Fires when the active status changes
16418 * @param {Roo.bootstrap.TabPanel} this
16419 * @param {Boolean} state the new state
16424 * @event beforedeactivate
16425 * Fires before a tab is de-activated - can be used to do validation on a form.
16426 * @param {Roo.bootstrap.TabPanel} this
16427 * @return {Boolean} false if there is an error
16430 'beforedeactivate': true
16433 this.tabId = this.tabId || Roo.id();
16437 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16444 getAutoCreate : function(){
16447 // item is needed for carousel - not sure if it has any effect otherwise
16448 cls: 'tab-pane item',
16449 html: this.html || ''
16453 cfg.cls += ' active';
16457 cfg.tabId = this.tabId;
16464 initEvents: function()
16466 var p = this.parent();
16467 this.navId = this.navId || p.navId;
16469 if (typeof(this.navId) != 'undefined') {
16470 // not really needed.. but just in case.. parent should be a NavGroup.
16471 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16475 var i = tg.tabs.length - 1;
16477 if(this.active && tg.bullets > 0 && i < tg.bullets){
16478 tg.setActiveBullet(i);
16485 onRender : function(ct, position)
16487 // Roo.log("Call onRender: " + this.xtype);
16489 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16497 setActive: function(state)
16499 Roo.log("panel - set active " + this.tabId + "=" + state);
16501 this.active = state;
16503 this.el.removeClass('active');
16505 } else if (!this.el.hasClass('active')) {
16506 this.el.addClass('active');
16509 this.fireEvent('changed', this, state);
16526 * @class Roo.bootstrap.DateField
16527 * @extends Roo.bootstrap.Input
16528 * Bootstrap DateField class
16529 * @cfg {Number} weekStart default 0
16530 * @cfg {String} viewMode default empty, (months|years)
16531 * @cfg {String} minViewMode default empty, (months|years)
16532 * @cfg {Number} startDate default -Infinity
16533 * @cfg {Number} endDate default Infinity
16534 * @cfg {Boolean} todayHighlight default false
16535 * @cfg {Boolean} todayBtn default false
16536 * @cfg {Boolean} calendarWeeks default false
16537 * @cfg {Object} daysOfWeekDisabled default empty
16538 * @cfg {Boolean} singleMode default false (true | false)
16540 * @cfg {Boolean} keyboardNavigation default true
16541 * @cfg {String} language default en
16544 * Create a new DateField
16545 * @param {Object} config The config object
16548 Roo.bootstrap.DateField = function(config){
16549 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16553 * Fires when this field show.
16554 * @param {Roo.bootstrap.DateField} this
16555 * @param {Mixed} date The date value
16560 * Fires when this field hide.
16561 * @param {Roo.bootstrap.DateField} this
16562 * @param {Mixed} date The date value
16567 * Fires when select a date.
16568 * @param {Roo.bootstrap.DateField} this
16569 * @param {Mixed} date The date value
16573 * @event beforeselect
16574 * Fires when before select a date.
16575 * @param {Roo.bootstrap.DateField} this
16576 * @param {Mixed} date The date value
16578 beforeselect : true
16582 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16585 * @cfg {String} format
16586 * The default date format string which can be overriden for localization support. The format must be
16587 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16591 * @cfg {String} altFormats
16592 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16593 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16595 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16603 todayHighlight : false,
16609 keyboardNavigation: true,
16611 calendarWeeks: false,
16613 startDate: -Infinity,
16617 daysOfWeekDisabled: [],
16621 singleMode : false,
16623 UTCDate: function()
16625 return new Date(Date.UTC.apply(Date, arguments));
16628 UTCToday: function()
16630 var today = new Date();
16631 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16634 getDate: function() {
16635 var d = this.getUTCDate();
16636 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16639 getUTCDate: function() {
16643 setDate: function(d) {
16644 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16647 setUTCDate: function(d) {
16649 this.setValue(this.formatDate(this.date));
16652 onRender: function(ct, position)
16655 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16657 this.language = this.language || 'en';
16658 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16659 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16661 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16662 this.format = this.format || 'm/d/y';
16663 this.isInline = false;
16664 this.isInput = true;
16665 this.component = this.el.select('.add-on', true).first() || false;
16666 this.component = (this.component && this.component.length === 0) ? false : this.component;
16667 this.hasInput = this.component && this.inputEL().length;
16669 if (typeof(this.minViewMode === 'string')) {
16670 switch (this.minViewMode) {
16672 this.minViewMode = 1;
16675 this.minViewMode = 2;
16678 this.minViewMode = 0;
16683 if (typeof(this.viewMode === 'string')) {
16684 switch (this.viewMode) {
16697 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16699 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16701 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16703 this.picker().on('mousedown', this.onMousedown, this);
16704 this.picker().on('click', this.onClick, this);
16706 this.picker().addClass('datepicker-dropdown');
16708 this.startViewMode = this.viewMode;
16710 if(this.singleMode){
16711 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16712 v.setVisibilityMode(Roo.Element.DISPLAY);
16716 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16717 v.setStyle('width', '189px');
16721 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16722 if(!this.calendarWeeks){
16727 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16728 v.attr('colspan', function(i, val){
16729 return parseInt(val) + 1;
16734 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16736 this.setStartDate(this.startDate);
16737 this.setEndDate(this.endDate);
16739 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16746 if(this.isInline) {
16751 picker : function()
16753 return this.pickerEl;
16754 // return this.el.select('.datepicker', true).first();
16757 fillDow: function()
16759 var dowCnt = this.weekStart;
16768 if(this.calendarWeeks){
16776 while (dowCnt < this.weekStart + 7) {
16780 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16784 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16787 fillMonths: function()
16790 var months = this.picker().select('>.datepicker-months td', true).first();
16792 months.dom.innerHTML = '';
16798 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16801 months.createChild(month);
16808 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;
16810 if (this.date < this.startDate) {
16811 this.viewDate = new Date(this.startDate);
16812 } else if (this.date > this.endDate) {
16813 this.viewDate = new Date(this.endDate);
16815 this.viewDate = new Date(this.date);
16823 var d = new Date(this.viewDate),
16824 year = d.getUTCFullYear(),
16825 month = d.getUTCMonth(),
16826 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16827 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16828 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16829 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16830 currentDate = this.date && this.date.valueOf(),
16831 today = this.UTCToday();
16833 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16835 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16837 // this.picker.select('>tfoot th.today').
16838 // .text(dates[this.language].today)
16839 // .toggle(this.todayBtn !== false);
16841 this.updateNavArrows();
16844 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16846 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16848 prevMonth.setUTCDate(day);
16850 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16852 var nextMonth = new Date(prevMonth);
16854 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16856 nextMonth = nextMonth.valueOf();
16858 var fillMonths = false;
16860 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16862 while(prevMonth.valueOf() < nextMonth) {
16865 if (prevMonth.getUTCDay() === this.weekStart) {
16867 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16875 if(this.calendarWeeks){
16876 // ISO 8601: First week contains first thursday.
16877 // ISO also states week starts on Monday, but we can be more abstract here.
16879 // Start of current week: based on weekstart/current date
16880 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16881 // Thursday of this week
16882 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16883 // First Thursday of year, year from thursday
16884 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16885 // Calendar week: ms between thursdays, div ms per day, div 7 days
16886 calWeek = (th - yth) / 864e5 / 7 + 1;
16888 fillMonths.cn.push({
16896 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16898 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16901 if (this.todayHighlight &&
16902 prevMonth.getUTCFullYear() == today.getFullYear() &&
16903 prevMonth.getUTCMonth() == today.getMonth() &&
16904 prevMonth.getUTCDate() == today.getDate()) {
16905 clsName += ' today';
16908 if (currentDate && prevMonth.valueOf() === currentDate) {
16909 clsName += ' active';
16912 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16913 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16914 clsName += ' disabled';
16917 fillMonths.cn.push({
16919 cls: 'day ' + clsName,
16920 html: prevMonth.getDate()
16923 prevMonth.setDate(prevMonth.getDate()+1);
16926 var currentYear = this.date && this.date.getUTCFullYear();
16927 var currentMonth = this.date && this.date.getUTCMonth();
16929 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16931 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16932 v.removeClass('active');
16934 if(currentYear === year && k === currentMonth){
16935 v.addClass('active');
16938 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16939 v.addClass('disabled');
16945 year = parseInt(year/10, 10) * 10;
16947 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16949 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16952 for (var i = -1; i < 11; i++) {
16953 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16955 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16963 showMode: function(dir)
16966 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16969 Roo.each(this.picker().select('>div',true).elements, function(v){
16970 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16973 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16978 if(this.isInline) {
16982 this.picker().removeClass(['bottom', 'top']);
16984 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16986 * place to the top of element!
16990 this.picker().addClass('top');
16991 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16996 this.picker().addClass('bottom');
16998 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17001 parseDate : function(value)
17003 if(!value || value instanceof Date){
17006 var v = Date.parseDate(value, this.format);
17007 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17008 v = Date.parseDate(value, 'Y-m-d');
17010 if(!v && this.altFormats){
17011 if(!this.altFormatsArray){
17012 this.altFormatsArray = this.altFormats.split("|");
17014 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17015 v = Date.parseDate(value, this.altFormatsArray[i]);
17021 formatDate : function(date, fmt)
17023 return (!date || !(date instanceof Date)) ?
17024 date : date.dateFormat(fmt || this.format);
17027 onFocus : function()
17029 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17033 onBlur : function()
17035 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17037 var d = this.inputEl().getValue();
17046 this.picker().show();
17050 this.fireEvent('show', this, this.date);
17055 if(this.isInline) {
17058 this.picker().hide();
17059 this.viewMode = this.startViewMode;
17062 this.fireEvent('hide', this, this.date);
17066 onMousedown: function(e)
17068 e.stopPropagation();
17069 e.preventDefault();
17074 Roo.bootstrap.DateField.superclass.keyup.call(this);
17078 setValue: function(v)
17080 if(this.fireEvent('beforeselect', this, v) !== false){
17081 var d = new Date(this.parseDate(v) ).clearTime();
17083 if(isNaN(d.getTime())){
17084 this.date = this.viewDate = '';
17085 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17089 v = this.formatDate(d);
17091 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17093 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17097 this.fireEvent('select', this, this.date);
17101 getValue: function()
17103 return this.formatDate(this.date);
17106 fireKey: function(e)
17108 if (!this.picker().isVisible()){
17109 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17115 var dateChanged = false,
17117 newDate, newViewDate;
17122 e.preventDefault();
17126 if (!this.keyboardNavigation) {
17129 dir = e.keyCode == 37 ? -1 : 1;
17132 newDate = this.moveYear(this.date, dir);
17133 newViewDate = this.moveYear(this.viewDate, dir);
17134 } else if (e.shiftKey){
17135 newDate = this.moveMonth(this.date, dir);
17136 newViewDate = this.moveMonth(this.viewDate, dir);
17138 newDate = new Date(this.date);
17139 newDate.setUTCDate(this.date.getUTCDate() + dir);
17140 newViewDate = new Date(this.viewDate);
17141 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17143 if (this.dateWithinRange(newDate)){
17144 this.date = newDate;
17145 this.viewDate = newViewDate;
17146 this.setValue(this.formatDate(this.date));
17148 e.preventDefault();
17149 dateChanged = true;
17154 if (!this.keyboardNavigation) {
17157 dir = e.keyCode == 38 ? -1 : 1;
17159 newDate = this.moveYear(this.date, dir);
17160 newViewDate = this.moveYear(this.viewDate, dir);
17161 } else if (e.shiftKey){
17162 newDate = this.moveMonth(this.date, dir);
17163 newViewDate = this.moveMonth(this.viewDate, dir);
17165 newDate = new Date(this.date);
17166 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17167 newViewDate = new Date(this.viewDate);
17168 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17170 if (this.dateWithinRange(newDate)){
17171 this.date = newDate;
17172 this.viewDate = newViewDate;
17173 this.setValue(this.formatDate(this.date));
17175 e.preventDefault();
17176 dateChanged = true;
17180 this.setValue(this.formatDate(this.date));
17182 e.preventDefault();
17185 this.setValue(this.formatDate(this.date));
17199 onClick: function(e)
17201 e.stopPropagation();
17202 e.preventDefault();
17204 var target = e.getTarget();
17206 if(target.nodeName.toLowerCase() === 'i'){
17207 target = Roo.get(target).dom.parentNode;
17210 var nodeName = target.nodeName;
17211 var className = target.className;
17212 var html = target.innerHTML;
17213 //Roo.log(nodeName);
17215 switch(nodeName.toLowerCase()) {
17217 switch(className) {
17223 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17224 switch(this.viewMode){
17226 this.viewDate = this.moveMonth(this.viewDate, dir);
17230 this.viewDate = this.moveYear(this.viewDate, dir);
17236 var date = new Date();
17237 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17239 this.setValue(this.formatDate(this.date));
17246 if (className.indexOf('disabled') < 0) {
17247 this.viewDate.setUTCDate(1);
17248 if (className.indexOf('month') > -1) {
17249 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17251 var year = parseInt(html, 10) || 0;
17252 this.viewDate.setUTCFullYear(year);
17256 if(this.singleMode){
17257 this.setValue(this.formatDate(this.viewDate));
17268 //Roo.log(className);
17269 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17270 var day = parseInt(html, 10) || 1;
17271 var year = this.viewDate.getUTCFullYear(),
17272 month = this.viewDate.getUTCMonth();
17274 if (className.indexOf('old') > -1) {
17281 } else if (className.indexOf('new') > -1) {
17289 //Roo.log([year,month,day]);
17290 this.date = this.UTCDate(year, month, day,0,0,0,0);
17291 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17293 //Roo.log(this.formatDate(this.date));
17294 this.setValue(this.formatDate(this.date));
17301 setStartDate: function(startDate)
17303 this.startDate = startDate || -Infinity;
17304 if (this.startDate !== -Infinity) {
17305 this.startDate = this.parseDate(this.startDate);
17308 this.updateNavArrows();
17311 setEndDate: function(endDate)
17313 this.endDate = endDate || Infinity;
17314 if (this.endDate !== Infinity) {
17315 this.endDate = this.parseDate(this.endDate);
17318 this.updateNavArrows();
17321 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17323 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17324 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17325 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17327 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17328 return parseInt(d, 10);
17331 this.updateNavArrows();
17334 updateNavArrows: function()
17336 if(this.singleMode){
17340 var d = new Date(this.viewDate),
17341 year = d.getUTCFullYear(),
17342 month = d.getUTCMonth();
17344 Roo.each(this.picker().select('.prev', true).elements, function(v){
17346 switch (this.viewMode) {
17349 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17355 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17362 Roo.each(this.picker().select('.next', true).elements, function(v){
17364 switch (this.viewMode) {
17367 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17373 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17381 moveMonth: function(date, dir)
17386 var new_date = new Date(date.valueOf()),
17387 day = new_date.getUTCDate(),
17388 month = new_date.getUTCMonth(),
17389 mag = Math.abs(dir),
17391 dir = dir > 0 ? 1 : -1;
17394 // If going back one month, make sure month is not current month
17395 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17397 return new_date.getUTCMonth() == month;
17399 // If going forward one month, make sure month is as expected
17400 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17402 return new_date.getUTCMonth() != new_month;
17404 new_month = month + dir;
17405 new_date.setUTCMonth(new_month);
17406 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17407 if (new_month < 0 || new_month > 11) {
17408 new_month = (new_month + 12) % 12;
17411 // For magnitudes >1, move one month at a time...
17412 for (var i=0; i<mag; i++) {
17413 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17414 new_date = this.moveMonth(new_date, dir);
17416 // ...then reset the day, keeping it in the new month
17417 new_month = new_date.getUTCMonth();
17418 new_date.setUTCDate(day);
17420 return new_month != new_date.getUTCMonth();
17423 // Common date-resetting loop -- if date is beyond end of month, make it
17426 new_date.setUTCDate(--day);
17427 new_date.setUTCMonth(new_month);
17432 moveYear: function(date, dir)
17434 return this.moveMonth(date, dir*12);
17437 dateWithinRange: function(date)
17439 return date >= this.startDate && date <= this.endDate;
17445 this.picker().remove();
17450 Roo.apply(Roo.bootstrap.DateField, {
17461 html: '<i class="fa fa-arrow-left"/>'
17471 html: '<i class="fa fa-arrow-right"/>'
17513 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17514 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17515 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17516 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17517 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17530 navFnc: 'FullYear',
17535 navFnc: 'FullYear',
17540 Roo.apply(Roo.bootstrap.DateField, {
17544 cls: 'datepicker dropdown-menu roo-dynamic',
17548 cls: 'datepicker-days',
17552 cls: 'table-condensed',
17554 Roo.bootstrap.DateField.head,
17558 Roo.bootstrap.DateField.footer
17565 cls: 'datepicker-months',
17569 cls: 'table-condensed',
17571 Roo.bootstrap.DateField.head,
17572 Roo.bootstrap.DateField.content,
17573 Roo.bootstrap.DateField.footer
17580 cls: 'datepicker-years',
17584 cls: 'table-condensed',
17586 Roo.bootstrap.DateField.head,
17587 Roo.bootstrap.DateField.content,
17588 Roo.bootstrap.DateField.footer
17607 * @class Roo.bootstrap.TimeField
17608 * @extends Roo.bootstrap.Input
17609 * Bootstrap DateField class
17613 * Create a new TimeField
17614 * @param {Object} config The config object
17617 Roo.bootstrap.TimeField = function(config){
17618 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17622 * Fires when this field show.
17623 * @param {Roo.bootstrap.DateField} thisthis
17624 * @param {Mixed} date The date value
17629 * Fires when this field hide.
17630 * @param {Roo.bootstrap.DateField} this
17631 * @param {Mixed} date The date value
17636 * Fires when select a date.
17637 * @param {Roo.bootstrap.DateField} this
17638 * @param {Mixed} date The date value
17644 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17647 * @cfg {String} format
17648 * The default time format string which can be overriden for localization support. The format must be
17649 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17653 onRender: function(ct, position)
17656 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17658 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17660 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17662 this.pop = this.picker().select('>.datepicker-time',true).first();
17663 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17665 this.picker().on('mousedown', this.onMousedown, this);
17666 this.picker().on('click', this.onClick, this);
17668 this.picker().addClass('datepicker-dropdown');
17673 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17674 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17675 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17676 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17677 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17678 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17682 fireKey: function(e){
17683 if (!this.picker().isVisible()){
17684 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17690 e.preventDefault();
17698 this.onTogglePeriod();
17701 this.onIncrementMinutes();
17704 this.onDecrementMinutes();
17713 onClick: function(e) {
17714 e.stopPropagation();
17715 e.preventDefault();
17718 picker : function()
17720 return this.el.select('.datepicker', true).first();
17723 fillTime: function()
17725 var time = this.pop.select('tbody', true).first();
17727 time.dom.innerHTML = '';
17742 cls: 'hours-up glyphicon glyphicon-chevron-up'
17762 cls: 'minutes-up glyphicon glyphicon-chevron-up'
17783 cls: 'timepicker-hour',
17798 cls: 'timepicker-minute',
17813 cls: 'btn btn-primary period',
17835 cls: 'hours-down glyphicon glyphicon-chevron-down'
17855 cls: 'minutes-down glyphicon glyphicon-chevron-down'
17873 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17880 var hours = this.time.getHours();
17881 var minutes = this.time.getMinutes();
17894 hours = hours - 12;
17898 hours = '0' + hours;
17902 minutes = '0' + minutes;
17905 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17906 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17907 this.pop.select('button', true).first().dom.innerHTML = period;
17913 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17915 var cls = ['bottom'];
17917 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17924 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17929 this.picker().addClass(cls.join('-'));
17933 Roo.each(cls, function(c){
17935 _this.picker().setTop(_this.inputEl().getHeight());
17939 _this.picker().setTop(0 - _this.picker().getHeight());
17944 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17948 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17955 onFocus : function()
17957 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17961 onBlur : function()
17963 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17969 this.picker().show();
17974 this.fireEvent('show', this, this.date);
17979 this.picker().hide();
17982 this.fireEvent('hide', this, this.date);
17985 setTime : function()
17988 this.setValue(this.time.format(this.format));
17990 this.fireEvent('select', this, this.date);
17995 onMousedown: function(e){
17996 e.stopPropagation();
17997 e.preventDefault();
18000 onIncrementHours: function()
18002 Roo.log('onIncrementHours');
18003 this.time = this.time.add(Date.HOUR, 1);
18008 onDecrementHours: function()
18010 Roo.log('onDecrementHours');
18011 this.time = this.time.add(Date.HOUR, -1);
18015 onIncrementMinutes: function()
18017 Roo.log('onIncrementMinutes');
18018 this.time = this.time.add(Date.MINUTE, 1);
18022 onDecrementMinutes: function()
18024 Roo.log('onDecrementMinutes');
18025 this.time = this.time.add(Date.MINUTE, -1);
18029 onTogglePeriod: function()
18031 Roo.log('onTogglePeriod');
18032 this.time = this.time.add(Date.HOUR, 12);
18039 Roo.apply(Roo.bootstrap.TimeField, {
18069 cls: 'btn btn-info ok',
18081 Roo.apply(Roo.bootstrap.TimeField, {
18085 cls: 'datepicker dropdown-menu',
18089 cls: 'datepicker-time',
18093 cls: 'table-condensed',
18095 Roo.bootstrap.TimeField.content,
18096 Roo.bootstrap.TimeField.footer
18115 * @class Roo.bootstrap.MonthField
18116 * @extends Roo.bootstrap.Input
18117 * Bootstrap MonthField class
18119 * @cfg {String} language default en
18122 * Create a new MonthField
18123 * @param {Object} config The config object
18126 Roo.bootstrap.MonthField = function(config){
18127 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18132 * Fires when this field show.
18133 * @param {Roo.bootstrap.MonthField} this
18134 * @param {Mixed} date The date value
18139 * Fires when this field hide.
18140 * @param {Roo.bootstrap.MonthField} this
18141 * @param {Mixed} date The date value
18146 * Fires when select a date.
18147 * @param {Roo.bootstrap.MonthField} this
18148 * @param {String} oldvalue The old value
18149 * @param {String} newvalue The new value
18155 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
18157 onRender: function(ct, position)
18160 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18162 this.language = this.language || 'en';
18163 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18164 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18166 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18167 this.isInline = false;
18168 this.isInput = true;
18169 this.component = this.el.select('.add-on', true).first() || false;
18170 this.component = (this.component && this.component.length === 0) ? false : this.component;
18171 this.hasInput = this.component && this.inputEL().length;
18173 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18175 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18177 this.picker().on('mousedown', this.onMousedown, this);
18178 this.picker().on('click', this.onClick, this);
18180 this.picker().addClass('datepicker-dropdown');
18182 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18183 v.setStyle('width', '189px');
18190 if(this.isInline) {
18196 setValue: function(v, suppressEvent)
18198 var o = this.getValue();
18200 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18204 if(suppressEvent !== true){
18205 this.fireEvent('select', this, o, v);
18210 getValue: function()
18215 onClick: function(e)
18217 e.stopPropagation();
18218 e.preventDefault();
18220 var target = e.getTarget();
18222 if(target.nodeName.toLowerCase() === 'i'){
18223 target = Roo.get(target).dom.parentNode;
18226 var nodeName = target.nodeName;
18227 var className = target.className;
18228 var html = target.innerHTML;
18230 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18234 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18236 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18242 picker : function()
18244 return this.pickerEl;
18247 fillMonths: function()
18250 var months = this.picker().select('>.datepicker-months td', true).first();
18252 months.dom.innerHTML = '';
18258 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18261 months.createChild(month);
18270 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18271 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18274 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18275 e.removeClass('active');
18277 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18278 e.addClass('active');
18285 if(this.isInline) {
18289 this.picker().removeClass(['bottom', 'top']);
18291 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18293 * place to the top of element!
18297 this.picker().addClass('top');
18298 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18303 this.picker().addClass('bottom');
18305 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18308 onFocus : function()
18310 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18314 onBlur : function()
18316 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18318 var d = this.inputEl().getValue();
18327 this.picker().show();
18328 this.picker().select('>.datepicker-months', true).first().show();
18332 this.fireEvent('show', this, this.date);
18337 if(this.isInline) {
18340 this.picker().hide();
18341 this.fireEvent('hide', this, this.date);
18345 onMousedown: function(e)
18347 e.stopPropagation();
18348 e.preventDefault();
18353 Roo.bootstrap.MonthField.superclass.keyup.call(this);
18357 fireKey: function(e)
18359 if (!this.picker().isVisible()){
18360 if (e.keyCode == 27) {// allow escape to hide and re-show picker
18371 e.preventDefault();
18375 dir = e.keyCode == 37 ? -1 : 1;
18377 this.vIndex = this.vIndex + dir;
18379 if(this.vIndex < 0){
18383 if(this.vIndex > 11){
18387 if(isNaN(this.vIndex)){
18391 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18397 dir = e.keyCode == 38 ? -1 : 1;
18399 this.vIndex = this.vIndex + dir * 4;
18401 if(this.vIndex < 0){
18405 if(this.vIndex > 11){
18409 if(isNaN(this.vIndex)){
18413 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18418 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18419 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18423 e.preventDefault();
18426 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18427 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18443 this.picker().remove();
18448 Roo.apply(Roo.bootstrap.MonthField, {
18467 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18468 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18473 Roo.apply(Roo.bootstrap.MonthField, {
18477 cls: 'datepicker dropdown-menu roo-dynamic',
18481 cls: 'datepicker-months',
18485 cls: 'table-condensed',
18487 Roo.bootstrap.DateField.content
18507 * @class Roo.bootstrap.CheckBox
18508 * @extends Roo.bootstrap.Input
18509 * Bootstrap CheckBox class
18511 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18512 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18513 * @cfg {String} boxLabel The text that appears beside the checkbox
18514 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18515 * @cfg {Boolean} checked initnal the element
18516 * @cfg {Boolean} inline inline the element (default false)
18517 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18520 * Create a new CheckBox
18521 * @param {Object} config The config object
18524 Roo.bootstrap.CheckBox = function(config){
18525 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18530 * Fires when the element is checked or unchecked.
18531 * @param {Roo.bootstrap.CheckBox} this This input
18532 * @param {Boolean} checked The new checked value
18539 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18541 inputType: 'checkbox',
18549 getAutoCreate : function()
18551 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18557 cfg.cls = 'form-group ' + this.inputType; //input-group
18560 cfg.cls += ' ' + this.inputType + '-inline';
18566 type : this.inputType,
18567 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18568 cls : 'roo-' + this.inputType, //'form-box',
18569 placeholder : this.placeholder || ''
18573 if (this.weight) { // Validity check?
18574 cfg.cls += " " + this.inputType + "-" + this.weight;
18577 if (this.disabled) {
18578 input.disabled=true;
18582 input.checked = this.checked;
18586 input.name = this.name;
18590 input.cls += ' input-' + this.size;
18595 ['xs','sm','md','lg'].map(function(size){
18596 if (settings[size]) {
18597 cfg.cls += ' col-' + size + '-' + settings[size];
18601 var inputblock = input;
18603 if (this.before || this.after) {
18606 cls : 'input-group',
18611 inputblock.cn.push({
18613 cls : 'input-group-addon',
18618 inputblock.cn.push(input);
18621 inputblock.cn.push({
18623 cls : 'input-group-addon',
18630 if (align ==='left' && this.fieldLabel.length) {
18631 // Roo.log("left and has label");
18637 cls : 'control-label col-md-' + this.labelWidth,
18638 html : this.fieldLabel
18642 cls : "col-md-" + (12 - this.labelWidth),
18649 } else if ( this.fieldLabel.length) {
18650 // Roo.log(" label");
18654 tag: this.boxLabel ? 'span' : 'label',
18656 cls: 'control-label box-input-label',
18657 //cls : 'input-group-addon',
18658 html : this.fieldLabel
18668 // Roo.log(" no label && no align");
18669 cfg.cn = [ inputblock ] ;
18674 var boxLabelCfg = {
18676 //'for': id, // box label is handled by onclick - so no for...
18678 html: this.boxLabel
18682 boxLabelCfg.tooltip = this.tooltip;
18685 cfg.cn.push(boxLabelCfg);
18695 * return the real input element.
18697 inputEl: function ()
18699 return this.el.select('input.roo-' + this.inputType,true).first();
18702 labelEl: function()
18704 return this.el.select('label.control-label',true).first();
18706 /* depricated... */
18710 return this.labelEl();
18713 initEvents : function()
18715 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18717 this.inputEl().on('click', this.onClick, this);
18719 if (this.boxLabel) {
18720 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18723 this.startValue = this.getValue();
18726 Roo.bootstrap.CheckBox.register(this);
18730 onClick : function()
18732 this.setChecked(!this.checked);
18735 setChecked : function(state,suppressEvent)
18737 this.startValue = this.getValue();
18739 if(this.inputType == 'radio'){
18741 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18742 e.dom.checked = false;
18745 this.inputEl().dom.checked = true;
18747 this.inputEl().dom.value = this.inputValue;
18749 if(suppressEvent !== true){
18750 this.fireEvent('check', this, true);
18758 this.checked = state;
18760 this.inputEl().dom.checked = state;
18762 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18764 if(suppressEvent !== true){
18765 this.fireEvent('check', this, state);
18771 getValue : function()
18773 if(this.inputType == 'radio'){
18774 return this.getGroupValue();
18777 return this.inputEl().getValue();
18781 getGroupValue : function()
18783 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18787 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18790 setValue : function(v,suppressEvent)
18792 if(this.inputType == 'radio'){
18793 this.setGroupValue(v, suppressEvent);
18797 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18802 setGroupValue : function(v, suppressEvent)
18804 this.startValue = this.getValue();
18806 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18807 e.dom.checked = false;
18809 if(e.dom.value == v){
18810 e.dom.checked = true;
18814 if(suppressEvent !== true){
18815 this.fireEvent('check', this, true);
18823 validate : function()
18827 (this.inputType == 'radio' && this.validateRadio()) ||
18828 (this.inputType == 'checkbox' && this.validateCheckbox())
18834 this.markInvalid();
18838 validateRadio : function()
18842 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18843 if(!e.dom.checked){
18855 validateCheckbox : function()
18858 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18861 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18869 for(var i in group){
18874 r = (group[i].getValue() == group[i].inputValue) ? true : false;
18881 * Mark this field as valid
18883 markValid : function()
18885 if(this.allowBlank){
18891 this.fireEvent('valid', this);
18893 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18896 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18903 if(this.inputType == 'radio'){
18904 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18905 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18906 e.findParent('.form-group', false, true).addClass(_this.validClass);
18913 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18914 this.el.findParent('.form-group', false, true).addClass(this.validClass);
18918 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18924 for(var i in group){
18925 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18926 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18931 * Mark this field as invalid
18932 * @param {String} msg The validation message
18934 markInvalid : function(msg)
18936 if(this.allowBlank){
18942 this.fireEvent('invalid', this, msg);
18944 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18947 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18951 label.markInvalid();
18954 if(this.inputType == 'radio'){
18955 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18956 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18957 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18964 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18965 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18969 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18975 for(var i in group){
18976 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18977 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18984 Roo.apply(Roo.bootstrap.CheckBox, {
18989 * register a CheckBox Group
18990 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18992 register : function(checkbox)
18994 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18995 this.groups[checkbox.groupId] = {};
18998 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19002 this.groups[checkbox.groupId][checkbox.name] = checkbox;
19006 * fetch a CheckBox Group based on the group ID
19007 * @param {string} the group ID
19008 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19010 get: function(groupId) {
19011 if (typeof(this.groups[groupId]) == 'undefined') {
19015 return this.groups[groupId] ;
19027 *<div class="radio">
19029 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19030 Option one is this and that—be sure to include why it's great
19037 *<label class="radio-inline">fieldLabel</label>
19038 *<label class="radio-inline">
19039 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19047 * @class Roo.bootstrap.Radio
19048 * @extends Roo.bootstrap.CheckBox
19049 * Bootstrap Radio class
19052 * Create a new Radio
19053 * @param {Object} config The config object
19056 Roo.bootstrap.Radio = function(config){
19057 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19061 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
19063 inputType: 'radio',
19067 getAutoCreate : function()
19069 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19070 align = align || 'left'; // default...
19077 tag : this.inline ? 'span' : 'div',
19082 var inline = this.inline ? ' radio-inline' : '';
19086 // does not need for, as we wrap the input with it..
19088 cls : 'control-label box-label' + inline,
19091 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19095 //cls : 'control-label' + inline,
19096 html : this.fieldLabel,
19097 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19106 type : this.inputType,
19107 //value : (!this.checked) ? this.valueOff : this.inputValue,
19108 value : this.inputValue,
19110 placeholder : this.placeholder || '' // ?? needed????
19113 if (this.weight) { // Validity check?
19114 input.cls += " radio-" + this.weight;
19116 if (this.disabled) {
19117 input.disabled=true;
19121 input.checked = this.checked;
19125 input.name = this.name;
19129 input.cls += ' input-' + this.size;
19132 //?? can span's inline have a width??
19135 ['xs','sm','md','lg'].map(function(size){
19136 if (settings[size]) {
19137 cfg.cls += ' col-' + size + '-' + settings[size];
19141 var inputblock = input;
19143 if (this.before || this.after) {
19146 cls : 'input-group',
19151 inputblock.cn.push({
19153 cls : 'input-group-addon',
19157 inputblock.cn.push(input);
19159 inputblock.cn.push({
19161 cls : 'input-group-addon',
19169 if (this.fieldLabel && this.fieldLabel.length) {
19170 cfg.cn.push(fieldLabel);
19173 // normal bootstrap puts the input inside the label.
19174 // however with our styled version - it has to go after the input.
19176 //lbl.cn.push(inputblock);
19180 cls: 'radio' + inline,
19187 cfg.cn.push( lblwrap);
19192 html: this.boxLabel
19201 initEvents : function()
19203 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19205 this.inputEl().on('click', this.onClick, this);
19206 if (this.boxLabel) {
19207 //Roo.log('find label');
19208 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
19213 inputEl: function ()
19215 return this.el.select('input.roo-radio',true).first();
19217 onClick : function()
19220 this.setChecked(true);
19223 setChecked : function(state,suppressEvent)
19226 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19227 v.dom.checked = false;
19230 Roo.log(this.inputEl().dom);
19231 this.checked = state;
19232 this.inputEl().dom.checked = state;
19234 if(suppressEvent !== true){
19235 this.fireEvent('check', this, state);
19238 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19242 getGroupValue : function()
19245 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19246 if(v.dom.checked == true){
19247 value = v.dom.value;
19255 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
19256 * @return {Mixed} value The field value
19258 getValue : function(){
19259 return this.getGroupValue();
19265 //<script type="text/javascript">
19268 * Based Ext JS Library 1.1.1
19269 * Copyright(c) 2006-2007, Ext JS, LLC.
19275 * @class Roo.HtmlEditorCore
19276 * @extends Roo.Component
19277 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19279 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19282 Roo.HtmlEditorCore = function(config){
19285 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19290 * @event initialize
19291 * Fires when the editor is fully initialized (including the iframe)
19292 * @param {Roo.HtmlEditorCore} this
19297 * Fires when the editor is first receives the focus. Any insertion must wait
19298 * until after this event.
19299 * @param {Roo.HtmlEditorCore} this
19303 * @event beforesync
19304 * Fires before the textarea is updated with content from the editor iframe. Return false
19305 * to cancel the sync.
19306 * @param {Roo.HtmlEditorCore} this
19307 * @param {String} html
19311 * @event beforepush
19312 * Fires before the iframe editor is updated with content from the textarea. Return false
19313 * to cancel the push.
19314 * @param {Roo.HtmlEditorCore} this
19315 * @param {String} html
19320 * Fires when the textarea is updated with content from the editor iframe.
19321 * @param {Roo.HtmlEditorCore} this
19322 * @param {String} html
19327 * Fires when the iframe editor is updated with content from the textarea.
19328 * @param {Roo.HtmlEditorCore} this
19329 * @param {String} html
19334 * @event editorevent
19335 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19336 * @param {Roo.HtmlEditorCore} this
19342 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19344 // defaults : white / black...
19345 this.applyBlacklists();
19352 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
19356 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
19362 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
19367 * @cfg {Number} height (in pixels)
19371 * @cfg {Number} width (in pixels)
19376 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19379 stylesheets: false,
19384 // private properties
19385 validationEvent : false,
19387 initialized : false,
19389 sourceEditMode : false,
19390 onFocus : Roo.emptyFn,
19392 hideMode:'offsets',
19396 // blacklist + whitelisted elements..
19403 * Protected method that will not generally be called directly. It
19404 * is called when the editor initializes the iframe with HTML contents. Override this method if you
19405 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19407 getDocMarkup : function(){
19411 // inherit styels from page...??
19412 if (this.stylesheets === false) {
19414 Roo.get(document.head).select('style').each(function(node) {
19415 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19418 Roo.get(document.head).select('link').each(function(node) {
19419 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19422 } else if (!this.stylesheets.length) {
19424 st = '<style type="text/css">' +
19425 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19431 st += '<style type="text/css">' +
19432 'IMG { cursor: pointer } ' +
19436 return '<html><head>' + st +
19437 //<style type="text/css">' +
19438 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19440 ' </head><body class="roo-htmleditor-body"></body></html>';
19444 onRender : function(ct, position)
19447 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19448 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19451 this.el.dom.style.border = '0 none';
19452 this.el.dom.setAttribute('tabIndex', -1);
19453 this.el.addClass('x-hidden hide');
19457 if(Roo.isIE){ // fix IE 1px bogus margin
19458 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19462 this.frameId = Roo.id();
19466 var iframe = this.owner.wrap.createChild({
19468 cls: 'form-control', // bootstrap..
19470 name: this.frameId,
19471 frameBorder : 'no',
19472 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19477 this.iframe = iframe.dom;
19479 this.assignDocWin();
19481 this.doc.designMode = 'on';
19484 this.doc.write(this.getDocMarkup());
19488 var task = { // must defer to wait for browser to be ready
19490 //console.log("run task?" + this.doc.readyState);
19491 this.assignDocWin();
19492 if(this.doc.body || this.doc.readyState == 'complete'){
19494 this.doc.designMode="on";
19498 Roo.TaskMgr.stop(task);
19499 this.initEditor.defer(10, this);
19506 Roo.TaskMgr.start(task);
19511 onResize : function(w, h)
19513 Roo.log('resize: ' +w + ',' + h );
19514 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19518 if(typeof w == 'number'){
19520 this.iframe.style.width = w + 'px';
19522 if(typeof h == 'number'){
19524 this.iframe.style.height = h + 'px';
19526 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19533 * Toggles the editor between standard and source edit mode.
19534 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19536 toggleSourceEdit : function(sourceEditMode){
19538 this.sourceEditMode = sourceEditMode === true;
19540 if(this.sourceEditMode){
19542 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19545 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19546 //this.iframe.className = '';
19549 //this.setSize(this.owner.wrap.getSize());
19550 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19557 * Protected method that will not generally be called directly. If you need/want
19558 * custom HTML cleanup, this is the method you should override.
19559 * @param {String} html The HTML to be cleaned
19560 * return {String} The cleaned HTML
19562 cleanHtml : function(html){
19563 html = String(html);
19564 if(html.length > 5){
19565 if(Roo.isSafari){ // strip safari nonsense
19566 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19569 if(html == ' '){
19576 * HTML Editor -> Textarea
19577 * Protected method that will not generally be called directly. Syncs the contents
19578 * of the editor iframe with the textarea.
19580 syncValue : function(){
19581 if(this.initialized){
19582 var bd = (this.doc.body || this.doc.documentElement);
19583 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19584 var html = bd.innerHTML;
19586 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19587 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19589 html = '<div style="'+m[0]+'">' + html + '</div>';
19592 html = this.cleanHtml(html);
19593 // fix up the special chars.. normaly like back quotes in word...
19594 // however we do not want to do this with chinese..
19595 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19596 var cc = b.charCodeAt();
19598 (cc >= 0x4E00 && cc < 0xA000 ) ||
19599 (cc >= 0x3400 && cc < 0x4E00 ) ||
19600 (cc >= 0xf900 && cc < 0xfb00 )
19606 if(this.owner.fireEvent('beforesync', this, html) !== false){
19607 this.el.dom.value = html;
19608 this.owner.fireEvent('sync', this, html);
19614 * Protected method that will not generally be called directly. Pushes the value of the textarea
19615 * into the iframe editor.
19617 pushValue : function(){
19618 if(this.initialized){
19619 var v = this.el.dom.value.trim();
19621 // if(v.length < 1){
19625 if(this.owner.fireEvent('beforepush', this, v) !== false){
19626 var d = (this.doc.body || this.doc.documentElement);
19628 this.cleanUpPaste();
19629 this.el.dom.value = d.innerHTML;
19630 this.owner.fireEvent('push', this, v);
19636 deferFocus : function(){
19637 this.focus.defer(10, this);
19641 focus : function(){
19642 if(this.win && !this.sourceEditMode){
19649 assignDocWin: function()
19651 var iframe = this.iframe;
19654 this.doc = iframe.contentWindow.document;
19655 this.win = iframe.contentWindow;
19657 // if (!Roo.get(this.frameId)) {
19660 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19661 // this.win = Roo.get(this.frameId).dom.contentWindow;
19663 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19667 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19668 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19673 initEditor : function(){
19674 //console.log("INIT EDITOR");
19675 this.assignDocWin();
19679 this.doc.designMode="on";
19681 this.doc.write(this.getDocMarkup());
19684 var dbody = (this.doc.body || this.doc.documentElement);
19685 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19686 // this copies styles from the containing element into thsi one..
19687 // not sure why we need all of this..
19688 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19690 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19691 //ss['background-attachment'] = 'fixed'; // w3c
19692 dbody.bgProperties = 'fixed'; // ie
19693 //Roo.DomHelper.applyStyles(dbody, ss);
19694 Roo.EventManager.on(this.doc, {
19695 //'mousedown': this.onEditorEvent,
19696 'mouseup': this.onEditorEvent,
19697 'dblclick': this.onEditorEvent,
19698 'click': this.onEditorEvent,
19699 'keyup': this.onEditorEvent,
19704 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19706 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19707 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19709 this.initialized = true;
19711 this.owner.fireEvent('initialize', this);
19716 onDestroy : function(){
19722 //for (var i =0; i < this.toolbars.length;i++) {
19723 // // fixme - ask toolbars for heights?
19724 // this.toolbars[i].onDestroy();
19727 //this.wrap.dom.innerHTML = '';
19728 //this.wrap.remove();
19733 onFirstFocus : function(){
19735 this.assignDocWin();
19738 this.activated = true;
19741 if(Roo.isGecko){ // prevent silly gecko errors
19743 var s = this.win.getSelection();
19744 if(!s.focusNode || s.focusNode.nodeType != 3){
19745 var r = s.getRangeAt(0);
19746 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19751 this.execCmd('useCSS', true);
19752 this.execCmd('styleWithCSS', false);
19755 this.owner.fireEvent('activate', this);
19759 adjustFont: function(btn){
19760 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19761 //if(Roo.isSafari){ // safari
19764 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19765 if(Roo.isSafari){ // safari
19766 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19767 v = (v < 10) ? 10 : v;
19768 v = (v > 48) ? 48 : v;
19769 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19774 v = Math.max(1, v+adjust);
19776 this.execCmd('FontSize', v );
19779 onEditorEvent : function(e)
19781 this.owner.fireEvent('editorevent', this, e);
19782 // this.updateToolbar();
19783 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19786 insertTag : function(tg)
19788 // could be a bit smarter... -> wrap the current selected tRoo..
19789 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19791 range = this.createRange(this.getSelection());
19792 var wrappingNode = this.doc.createElement(tg.toLowerCase());
19793 wrappingNode.appendChild(range.extractContents());
19794 range.insertNode(wrappingNode);
19801 this.execCmd("formatblock", tg);
19805 insertText : function(txt)
19809 var range = this.createRange();
19810 range.deleteContents();
19811 //alert(Sender.getAttribute('label'));
19813 range.insertNode(this.doc.createTextNode(txt));
19819 * Executes a Midas editor command on the editor document and performs necessary focus and
19820 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19821 * @param {String} cmd The Midas command
19822 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19824 relayCmd : function(cmd, value){
19826 this.execCmd(cmd, value);
19827 this.owner.fireEvent('editorevent', this);
19828 //this.updateToolbar();
19829 this.owner.deferFocus();
19833 * Executes a Midas editor command directly on the editor document.
19834 * For visual commands, you should use {@link #relayCmd} instead.
19835 * <b>This should only be called after the editor is initialized.</b>
19836 * @param {String} cmd The Midas command
19837 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19839 execCmd : function(cmd, value){
19840 this.doc.execCommand(cmd, false, value === undefined ? null : value);
19847 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19849 * @param {String} text | dom node..
19851 insertAtCursor : function(text)
19856 if(!this.activated){
19862 var r = this.doc.selection.createRange();
19873 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19877 // from jquery ui (MIT licenced)
19879 var win = this.win;
19881 if (win.getSelection && win.getSelection().getRangeAt) {
19882 range = win.getSelection().getRangeAt(0);
19883 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19884 range.insertNode(node);
19885 } else if (win.document.selection && win.document.selection.createRange) {
19886 // no firefox support
19887 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19888 win.document.selection.createRange().pasteHTML(txt);
19890 // no firefox support
19891 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19892 this.execCmd('InsertHTML', txt);
19901 mozKeyPress : function(e){
19903 var c = e.getCharCode(), cmd;
19906 c = String.fromCharCode(c).toLowerCase();
19920 this.cleanUpPaste.defer(100, this);
19928 e.preventDefault();
19936 fixKeys : function(){ // load time branching for fastest keydown performance
19938 return function(e){
19939 var k = e.getKey(), r;
19942 r = this.doc.selection.createRange();
19945 r.pasteHTML('    ');
19952 r = this.doc.selection.createRange();
19954 var target = r.parentElement();
19955 if(!target || target.tagName.toLowerCase() != 'li'){
19957 r.pasteHTML('<br />');
19963 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19964 this.cleanUpPaste.defer(100, this);
19970 }else if(Roo.isOpera){
19971 return function(e){
19972 var k = e.getKey();
19976 this.execCmd('InsertHTML','    ');
19979 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19980 this.cleanUpPaste.defer(100, this);
19985 }else if(Roo.isSafari){
19986 return function(e){
19987 var k = e.getKey();
19991 this.execCmd('InsertText','\t');
19995 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19996 this.cleanUpPaste.defer(100, this);
20004 getAllAncestors: function()
20006 var p = this.getSelectedNode();
20009 a.push(p); // push blank onto stack..
20010 p = this.getParentElement();
20014 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20018 a.push(this.doc.body);
20022 lastSelNode : false,
20025 getSelection : function()
20027 this.assignDocWin();
20028 return Roo.isIE ? this.doc.selection : this.win.getSelection();
20031 getSelectedNode: function()
20033 // this may only work on Gecko!!!
20035 // should we cache this!!!!
20040 var range = this.createRange(this.getSelection()).cloneRange();
20043 var parent = range.parentElement();
20045 var testRange = range.duplicate();
20046 testRange.moveToElementText(parent);
20047 if (testRange.inRange(range)) {
20050 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20053 parent = parent.parentElement;
20058 // is ancestor a text element.
20059 var ac = range.commonAncestorContainer;
20060 if (ac.nodeType == 3) {
20061 ac = ac.parentNode;
20064 var ar = ac.childNodes;
20067 var other_nodes = [];
20068 var has_other_nodes = false;
20069 for (var i=0;i<ar.length;i++) {
20070 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
20073 // fullly contained node.
20075 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20080 // probably selected..
20081 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20082 other_nodes.push(ar[i]);
20086 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
20091 has_other_nodes = true;
20093 if (!nodes.length && other_nodes.length) {
20094 nodes= other_nodes;
20096 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20102 createRange: function(sel)
20104 // this has strange effects when using with
20105 // top toolbar - not sure if it's a great idea.
20106 //this.editor.contentWindow.focus();
20107 if (typeof sel != "undefined") {
20109 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20111 return this.doc.createRange();
20114 return this.doc.createRange();
20117 getParentElement: function()
20120 this.assignDocWin();
20121 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20123 var range = this.createRange(sel);
20126 var p = range.commonAncestorContainer;
20127 while (p.nodeType == 3) { // text node
20138 * Range intersection.. the hard stuff...
20142 * [ -- selected range --- ]
20146 * if end is before start or hits it. fail.
20147 * if start is after end or hits it fail.
20149 * if either hits (but other is outside. - then it's not
20155 // @see http://www.thismuchiknow.co.uk/?p=64.
20156 rangeIntersectsNode : function(range, node)
20158 var nodeRange = node.ownerDocument.createRange();
20160 nodeRange.selectNode(node);
20162 nodeRange.selectNodeContents(node);
20165 var rangeStartRange = range.cloneRange();
20166 rangeStartRange.collapse(true);
20168 var rangeEndRange = range.cloneRange();
20169 rangeEndRange.collapse(false);
20171 var nodeStartRange = nodeRange.cloneRange();
20172 nodeStartRange.collapse(true);
20174 var nodeEndRange = nodeRange.cloneRange();
20175 nodeEndRange.collapse(false);
20177 return rangeStartRange.compareBoundaryPoints(
20178 Range.START_TO_START, nodeEndRange) == -1 &&
20179 rangeEndRange.compareBoundaryPoints(
20180 Range.START_TO_START, nodeStartRange) == 1;
20184 rangeCompareNode : function(range, node)
20186 var nodeRange = node.ownerDocument.createRange();
20188 nodeRange.selectNode(node);
20190 nodeRange.selectNodeContents(node);
20194 range.collapse(true);
20196 nodeRange.collapse(true);
20198 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20199 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
20201 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20203 var nodeIsBefore = ss == 1;
20204 var nodeIsAfter = ee == -1;
20206 if (nodeIsBefore && nodeIsAfter) {
20209 if (!nodeIsBefore && nodeIsAfter) {
20210 return 1; //right trailed.
20213 if (nodeIsBefore && !nodeIsAfter) {
20214 return 2; // left trailed.
20220 // private? - in a new class?
20221 cleanUpPaste : function()
20223 // cleans up the whole document..
20224 Roo.log('cleanuppaste');
20226 this.cleanUpChildren(this.doc.body);
20227 var clean = this.cleanWordChars(this.doc.body.innerHTML);
20228 if (clean != this.doc.body.innerHTML) {
20229 this.doc.body.innerHTML = clean;
20234 cleanWordChars : function(input) {// change the chars to hex code
20235 var he = Roo.HtmlEditorCore;
20237 var output = input;
20238 Roo.each(he.swapCodes, function(sw) {
20239 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20241 output = output.replace(swapper, sw[1]);
20248 cleanUpChildren : function (n)
20250 if (!n.childNodes.length) {
20253 for (var i = n.childNodes.length-1; i > -1 ; i--) {
20254 this.cleanUpChild(n.childNodes[i]);
20261 cleanUpChild : function (node)
20264 //console.log(node);
20265 if (node.nodeName == "#text") {
20266 // clean up silly Windows -- stuff?
20269 if (node.nodeName == "#comment") {
20270 node.parentNode.removeChild(node);
20271 // clean up silly Windows -- stuff?
20274 var lcname = node.tagName.toLowerCase();
20275 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20276 // whitelist of tags..
20278 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20280 node.parentNode.removeChild(node);
20285 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20287 // remove <a name=....> as rendering on yahoo mailer is borked with this.
20288 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20290 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20291 // remove_keep_children = true;
20294 if (remove_keep_children) {
20295 this.cleanUpChildren(node);
20296 // inserts everything just before this node...
20297 while (node.childNodes.length) {
20298 var cn = node.childNodes[0];
20299 node.removeChild(cn);
20300 node.parentNode.insertBefore(cn, node);
20302 node.parentNode.removeChild(node);
20306 if (!node.attributes || !node.attributes.length) {
20307 this.cleanUpChildren(node);
20311 function cleanAttr(n,v)
20314 if (v.match(/^\./) || v.match(/^\//)) {
20317 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20320 if (v.match(/^#/)) {
20323 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20324 node.removeAttribute(n);
20328 var cwhite = this.cwhite;
20329 var cblack = this.cblack;
20331 function cleanStyle(n,v)
20333 if (v.match(/expression/)) { //XSS?? should we even bother..
20334 node.removeAttribute(n);
20338 var parts = v.split(/;/);
20341 Roo.each(parts, function(p) {
20342 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20346 var l = p.split(':').shift().replace(/\s+/g,'');
20347 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20349 if ( cwhite.length && cblack.indexOf(l) > -1) {
20350 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20351 //node.removeAttribute(n);
20355 // only allow 'c whitelisted system attributes'
20356 if ( cwhite.length && cwhite.indexOf(l) < 0) {
20357 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20358 //node.removeAttribute(n);
20368 if (clean.length) {
20369 node.setAttribute(n, clean.join(';'));
20371 node.removeAttribute(n);
20377 for (var i = node.attributes.length-1; i > -1 ; i--) {
20378 var a = node.attributes[i];
20381 if (a.name.toLowerCase().substr(0,2)=='on') {
20382 node.removeAttribute(a.name);
20385 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20386 node.removeAttribute(a.name);
20389 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20390 cleanAttr(a.name,a.value); // fixme..
20393 if (a.name == 'style') {
20394 cleanStyle(a.name,a.value);
20397 /// clean up MS crap..
20398 // tecnically this should be a list of valid class'es..
20401 if (a.name == 'class') {
20402 if (a.value.match(/^Mso/)) {
20403 node.className = '';
20406 if (a.value.match(/body/)) {
20407 node.className = '';
20418 this.cleanUpChildren(node);
20424 * Clean up MS wordisms...
20426 cleanWord : function(node)
20431 this.cleanWord(this.doc.body);
20434 if (node.nodeName == "#text") {
20435 // clean up silly Windows -- stuff?
20438 if (node.nodeName == "#comment") {
20439 node.parentNode.removeChild(node);
20440 // clean up silly Windows -- stuff?
20444 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20445 node.parentNode.removeChild(node);
20449 // remove - but keep children..
20450 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20451 while (node.childNodes.length) {
20452 var cn = node.childNodes[0];
20453 node.removeChild(cn);
20454 node.parentNode.insertBefore(cn, node);
20456 node.parentNode.removeChild(node);
20457 this.iterateChildren(node, this.cleanWord);
20461 if (node.className.length) {
20463 var cn = node.className.split(/\W+/);
20465 Roo.each(cn, function(cls) {
20466 if (cls.match(/Mso[a-zA-Z]+/)) {
20471 node.className = cna.length ? cna.join(' ') : '';
20473 node.removeAttribute("class");
20477 if (node.hasAttribute("lang")) {
20478 node.removeAttribute("lang");
20481 if (node.hasAttribute("style")) {
20483 var styles = node.getAttribute("style").split(";");
20485 Roo.each(styles, function(s) {
20486 if (!s.match(/:/)) {
20489 var kv = s.split(":");
20490 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20493 // what ever is left... we allow.
20496 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20497 if (!nstyle.length) {
20498 node.removeAttribute('style');
20501 this.iterateChildren(node, this.cleanWord);
20507 * iterateChildren of a Node, calling fn each time, using this as the scole..
20508 * @param {DomNode} node node to iterate children of.
20509 * @param {Function} fn method of this class to call on each item.
20511 iterateChildren : function(node, fn)
20513 if (!node.childNodes.length) {
20516 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20517 fn.call(this, node.childNodes[i])
20523 * cleanTableWidths.
20525 * Quite often pasting from word etc.. results in tables with column and widths.
20526 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20529 cleanTableWidths : function(node)
20534 this.cleanTableWidths(this.doc.body);
20539 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20542 Roo.log(node.tagName);
20543 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20544 this.iterateChildren(node, this.cleanTableWidths);
20547 if (node.hasAttribute('width')) {
20548 node.removeAttribute('width');
20552 if (node.hasAttribute("style")) {
20555 var styles = node.getAttribute("style").split(";");
20557 Roo.each(styles, function(s) {
20558 if (!s.match(/:/)) {
20561 var kv = s.split(":");
20562 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20565 // what ever is left... we allow.
20568 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20569 if (!nstyle.length) {
20570 node.removeAttribute('style');
20574 this.iterateChildren(node, this.cleanTableWidths);
20582 domToHTML : function(currentElement, depth, nopadtext) {
20584 depth = depth || 0;
20585 nopadtext = nopadtext || false;
20587 if (!currentElement) {
20588 return this.domToHTML(this.doc.body);
20591 //Roo.log(currentElement);
20593 var allText = false;
20594 var nodeName = currentElement.nodeName;
20595 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20597 if (nodeName == '#text') {
20599 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20604 if (nodeName != 'BODY') {
20607 // Prints the node tagName, such as <A>, <IMG>, etc
20610 for(i = 0; i < currentElement.attributes.length;i++) {
20612 var aname = currentElement.attributes.item(i).name;
20613 if (!currentElement.attributes.item(i).value.length) {
20616 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20619 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20628 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20631 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20636 // Traverse the tree
20638 var currentElementChild = currentElement.childNodes.item(i);
20639 var allText = true;
20640 var innerHTML = '';
20642 while (currentElementChild) {
20643 // Formatting code (indent the tree so it looks nice on the screen)
20644 var nopad = nopadtext;
20645 if (lastnode == 'SPAN') {
20649 if (currentElementChild.nodeName == '#text') {
20650 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20651 toadd = nopadtext ? toadd : toadd.trim();
20652 if (!nopad && toadd.length > 80) {
20653 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20655 innerHTML += toadd;
20658 currentElementChild = currentElement.childNodes.item(i);
20664 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20666 // Recursively traverse the tree structure of the child node
20667 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20668 lastnode = currentElementChild.nodeName;
20670 currentElementChild=currentElement.childNodes.item(i);
20676 // The remaining code is mostly for formatting the tree
20677 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20682 ret+= "</"+tagName+">";
20688 applyBlacklists : function()
20690 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20691 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20695 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20696 if (b.indexOf(tag) > -1) {
20699 this.white.push(tag);
20703 Roo.each(w, function(tag) {
20704 if (b.indexOf(tag) > -1) {
20707 if (this.white.indexOf(tag) > -1) {
20710 this.white.push(tag);
20715 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20716 if (w.indexOf(tag) > -1) {
20719 this.black.push(tag);
20723 Roo.each(b, function(tag) {
20724 if (w.indexOf(tag) > -1) {
20727 if (this.black.indexOf(tag) > -1) {
20730 this.black.push(tag);
20735 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20736 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20740 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20741 if (b.indexOf(tag) > -1) {
20744 this.cwhite.push(tag);
20748 Roo.each(w, function(tag) {
20749 if (b.indexOf(tag) > -1) {
20752 if (this.cwhite.indexOf(tag) > -1) {
20755 this.cwhite.push(tag);
20760 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20761 if (w.indexOf(tag) > -1) {
20764 this.cblack.push(tag);
20768 Roo.each(b, function(tag) {
20769 if (w.indexOf(tag) > -1) {
20772 if (this.cblack.indexOf(tag) > -1) {
20775 this.cblack.push(tag);
20780 setStylesheets : function(stylesheets)
20782 if(typeof(stylesheets) == 'string'){
20783 Roo.get(this.iframe.contentDocument.head).createChild({
20785 rel : 'stylesheet',
20794 Roo.each(stylesheets, function(s) {
20799 Roo.get(_this.iframe.contentDocument.head).createChild({
20801 rel : 'stylesheet',
20810 removeStylesheets : function()
20814 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20819 // hide stuff that is not compatible
20833 * @event specialkey
20837 * @cfg {String} fieldClass @hide
20840 * @cfg {String} focusClass @hide
20843 * @cfg {String} autoCreate @hide
20846 * @cfg {String} inputType @hide
20849 * @cfg {String} invalidClass @hide
20852 * @cfg {String} invalidText @hide
20855 * @cfg {String} msgFx @hide
20858 * @cfg {String} validateOnBlur @hide
20862 Roo.HtmlEditorCore.white = [
20863 'area', 'br', 'img', 'input', 'hr', 'wbr',
20865 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
20866 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
20867 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
20868 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
20869 'table', 'ul', 'xmp',
20871 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
20874 'dir', 'menu', 'ol', 'ul', 'dl',
20880 Roo.HtmlEditorCore.black = [
20881 // 'embed', 'object', // enable - backend responsiblity to clean thiese
20883 'base', 'basefont', 'bgsound', 'blink', 'body',
20884 'frame', 'frameset', 'head', 'html', 'ilayer',
20885 'iframe', 'layer', 'link', 'meta', 'object',
20886 'script', 'style' ,'title', 'xml' // clean later..
20888 Roo.HtmlEditorCore.clean = [
20889 'script', 'style', 'title', 'xml'
20891 Roo.HtmlEditorCore.remove = [
20896 Roo.HtmlEditorCore.ablack = [
20900 Roo.HtmlEditorCore.aclean = [
20901 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
20905 Roo.HtmlEditorCore.pwhite= [
20906 'http', 'https', 'mailto'
20909 // white listed style attributes.
20910 Roo.HtmlEditorCore.cwhite= [
20911 // 'text-align', /// default is to allow most things..
20917 // black listed style attributes.
20918 Roo.HtmlEditorCore.cblack= [
20919 // 'font-size' -- this can be set by the project
20923 Roo.HtmlEditorCore.swapCodes =[
20942 * @class Roo.bootstrap.HtmlEditor
20943 * @extends Roo.bootstrap.TextArea
20944 * Bootstrap HtmlEditor class
20947 * Create a new HtmlEditor
20948 * @param {Object} config The config object
20951 Roo.bootstrap.HtmlEditor = function(config){
20952 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20953 if (!this.toolbars) {
20954 this.toolbars = [];
20956 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20959 * @event initialize
20960 * Fires when the editor is fully initialized (including the iframe)
20961 * @param {HtmlEditor} this
20966 * Fires when the editor is first receives the focus. Any insertion must wait
20967 * until after this event.
20968 * @param {HtmlEditor} this
20972 * @event beforesync
20973 * Fires before the textarea is updated with content from the editor iframe. Return false
20974 * to cancel the sync.
20975 * @param {HtmlEditor} this
20976 * @param {String} html
20980 * @event beforepush
20981 * Fires before the iframe editor is updated with content from the textarea. Return false
20982 * to cancel the push.
20983 * @param {HtmlEditor} this
20984 * @param {String} html
20989 * Fires when the textarea is updated with content from the editor iframe.
20990 * @param {HtmlEditor} this
20991 * @param {String} html
20996 * Fires when the iframe editor is updated with content from the textarea.
20997 * @param {HtmlEditor} this
20998 * @param {String} html
21002 * @event editmodechange
21003 * Fires when the editor switches edit modes
21004 * @param {HtmlEditor} this
21005 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21007 editmodechange: true,
21009 * @event editorevent
21010 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21011 * @param {HtmlEditor} this
21015 * @event firstfocus
21016 * Fires when on first focus - needed by toolbars..
21017 * @param {HtmlEditor} this
21022 * Auto save the htmlEditor value as a file into Events
21023 * @param {HtmlEditor} this
21027 * @event savedpreview
21028 * preview the saved version of htmlEditor
21029 * @param {HtmlEditor} this
21036 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
21040 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21045 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21050 * @cfg {Number} height (in pixels)
21054 * @cfg {Number} width (in pixels)
21059 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21062 stylesheets: false,
21067 // private properties
21068 validationEvent : false,
21070 initialized : false,
21073 onFocus : Roo.emptyFn,
21075 hideMode:'offsets',
21078 tbContainer : false,
21080 toolbarContainer :function() {
21081 return this.wrap.select('.x-html-editor-tb',true).first();
21085 * Protected method that will not generally be called directly. It
21086 * is called when the editor creates its toolbar. Override this method if you need to
21087 * add custom toolbar buttons.
21088 * @param {HtmlEditor} editor
21090 createToolbar : function(){
21092 Roo.log("create toolbars");
21094 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21095 this.toolbars[0].render(this.toolbarContainer());
21099 // if (!editor.toolbars || !editor.toolbars.length) {
21100 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21103 // for (var i =0 ; i < editor.toolbars.length;i++) {
21104 // editor.toolbars[i] = Roo.factory(
21105 // typeof(editor.toolbars[i]) == 'string' ?
21106 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
21107 // Roo.bootstrap.HtmlEditor);
21108 // editor.toolbars[i].init(editor);
21114 onRender : function(ct, position)
21116 // Roo.log("Call onRender: " + this.xtype);
21118 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21120 this.wrap = this.inputEl().wrap({
21121 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21124 this.editorcore.onRender(ct, position);
21126 if (this.resizable) {
21127 this.resizeEl = new Roo.Resizable(this.wrap, {
21131 minHeight : this.height,
21132 height: this.height,
21133 handles : this.resizable,
21136 resize : function(r, w, h) {
21137 _t.onResize(w,h); // -something
21143 this.createToolbar(this);
21146 if(!this.width && this.resizable){
21147 this.setSize(this.wrap.getSize());
21149 if (this.resizeEl) {
21150 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21151 // should trigger onReize..
21157 onResize : function(w, h)
21159 Roo.log('resize: ' +w + ',' + h );
21160 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21164 if(this.inputEl() ){
21165 if(typeof w == 'number'){
21166 var aw = w - this.wrap.getFrameWidth('lr');
21167 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21170 if(typeof h == 'number'){
21171 var tbh = -11; // fixme it needs to tool bar size!
21172 for (var i =0; i < this.toolbars.length;i++) {
21173 // fixme - ask toolbars for heights?
21174 tbh += this.toolbars[i].el.getHeight();
21175 //if (this.toolbars[i].footer) {
21176 // tbh += this.toolbars[i].footer.el.getHeight();
21184 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21185 ah -= 5; // knock a few pixes off for look..
21186 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21190 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21191 this.editorcore.onResize(ew,eh);
21196 * Toggles the editor between standard and source edit mode.
21197 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21199 toggleSourceEdit : function(sourceEditMode)
21201 this.editorcore.toggleSourceEdit(sourceEditMode);
21203 if(this.editorcore.sourceEditMode){
21204 Roo.log('editor - showing textarea');
21207 // Roo.log(this.syncValue());
21209 this.inputEl().removeClass(['hide', 'x-hidden']);
21210 this.inputEl().dom.removeAttribute('tabIndex');
21211 this.inputEl().focus();
21213 Roo.log('editor - hiding textarea');
21215 // Roo.log(this.pushValue());
21218 this.inputEl().addClass(['hide', 'x-hidden']);
21219 this.inputEl().dom.setAttribute('tabIndex', -1);
21220 //this.deferFocus();
21223 if(this.resizable){
21224 this.setSize(this.wrap.getSize());
21227 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21230 // private (for BoxComponent)
21231 adjustSize : Roo.BoxComponent.prototype.adjustSize,
21233 // private (for BoxComponent)
21234 getResizeEl : function(){
21238 // private (for BoxComponent)
21239 getPositionEl : function(){
21244 initEvents : function(){
21245 this.originalValue = this.getValue();
21249 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21252 // markInvalid : Roo.emptyFn,
21254 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21257 // clearInvalid : Roo.emptyFn,
21259 setValue : function(v){
21260 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21261 this.editorcore.pushValue();
21266 deferFocus : function(){
21267 this.focus.defer(10, this);
21271 focus : function(){
21272 this.editorcore.focus();
21278 onDestroy : function(){
21284 for (var i =0; i < this.toolbars.length;i++) {
21285 // fixme - ask toolbars for heights?
21286 this.toolbars[i].onDestroy();
21289 this.wrap.dom.innerHTML = '';
21290 this.wrap.remove();
21295 onFirstFocus : function(){
21296 //Roo.log("onFirstFocus");
21297 this.editorcore.onFirstFocus();
21298 for (var i =0; i < this.toolbars.length;i++) {
21299 this.toolbars[i].onFirstFocus();
21305 syncValue : function()
21307 this.editorcore.syncValue();
21310 pushValue : function()
21312 this.editorcore.pushValue();
21316 // hide stuff that is not compatible
21330 * @event specialkey
21334 * @cfg {String} fieldClass @hide
21337 * @cfg {String} focusClass @hide
21340 * @cfg {String} autoCreate @hide
21343 * @cfg {String} inputType @hide
21346 * @cfg {String} invalidClass @hide
21349 * @cfg {String} invalidText @hide
21352 * @cfg {String} msgFx @hide
21355 * @cfg {String} validateOnBlur @hide
21364 Roo.namespace('Roo.bootstrap.htmleditor');
21366 * @class Roo.bootstrap.HtmlEditorToolbar1
21371 new Roo.bootstrap.HtmlEditor({
21374 new Roo.bootstrap.HtmlEditorToolbar1({
21375 disable : { fonts: 1 , format: 1, ..., ... , ...],
21381 * @cfg {Object} disable List of elements to disable..
21382 * @cfg {Array} btns List of additional buttons.
21386 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21389 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21392 Roo.apply(this, config);
21394 // default disabled, based on 'good practice'..
21395 this.disable = this.disable || {};
21396 Roo.applyIf(this.disable, {
21399 specialElements : true
21401 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21403 this.editor = config.editor;
21404 this.editorcore = config.editor.editorcore;
21406 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21408 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21409 // dont call parent... till later.
21411 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
21416 editorcore : false,
21421 "h1","h2","h3","h4","h5","h6",
21423 "abbr", "acronym", "address", "cite", "samp", "var",
21427 onRender : function(ct, position)
21429 // Roo.log("Call onRender: " + this.xtype);
21431 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21433 this.el.dom.style.marginBottom = '0';
21435 var editorcore = this.editorcore;
21436 var editor= this.editor;
21439 var btn = function(id,cmd , toggle, handler){
21441 var event = toggle ? 'toggle' : 'click';
21446 xns: Roo.bootstrap,
21449 enableToggle:toggle !== false,
21451 pressed : toggle ? false : null,
21454 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21455 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21464 xns: Roo.bootstrap,
21465 glyphicon : 'font',
21469 xns: Roo.bootstrap,
21473 Roo.each(this.formats, function(f) {
21474 style.menu.items.push({
21476 xns: Roo.bootstrap,
21477 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21482 editorcore.insertTag(this.tagname);
21489 children.push(style);
21492 btn('bold',false,true);
21493 btn('italic',false,true);
21494 btn('align-left', 'justifyleft',true);
21495 btn('align-center', 'justifycenter',true);
21496 btn('align-right' , 'justifyright',true);
21497 btn('link', false, false, function(btn) {
21498 //Roo.log("create link?");
21499 var url = prompt(this.createLinkText, this.defaultLinkValue);
21500 if(url && url != 'http:/'+'/'){
21501 this.editorcore.relayCmd('createlink', url);
21504 btn('list','insertunorderedlist',true);
21505 btn('pencil', false,true, function(btn){
21508 this.toggleSourceEdit(btn.pressed);
21514 xns: Roo.bootstrap,
21519 xns: Roo.bootstrap,
21524 cog.menu.items.push({
21526 xns: Roo.bootstrap,
21527 html : Clean styles,
21532 editorcore.insertTag(this.tagname);
21541 this.xtype = 'NavSimplebar';
21543 for(var i=0;i< children.length;i++) {
21545 this.buttons.add(this.addxtypeChild(children[i]));
21549 editor.on('editorevent', this.updateToolbar, this);
21551 onBtnClick : function(id)
21553 this.editorcore.relayCmd(id);
21554 this.editorcore.focus();
21558 * Protected method that will not generally be called directly. It triggers
21559 * a toolbar update by reading the markup state of the current selection in the editor.
21561 updateToolbar: function(){
21563 if(!this.editorcore.activated){
21564 this.editor.onFirstFocus(); // is this neeed?
21568 var btns = this.buttons;
21569 var doc = this.editorcore.doc;
21570 btns.get('bold').setActive(doc.queryCommandState('bold'));
21571 btns.get('italic').setActive(doc.queryCommandState('italic'));
21572 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21574 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21575 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21576 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21578 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21579 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21582 var ans = this.editorcore.getAllAncestors();
21583 if (this.formatCombo) {
21586 var store = this.formatCombo.store;
21587 this.formatCombo.setValue("");
21588 for (var i =0; i < ans.length;i++) {
21589 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21591 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21599 // hides menus... - so this cant be on a menu...
21600 Roo.bootstrap.MenuMgr.hideAll();
21602 Roo.bootstrap.MenuMgr.hideAll();
21603 //this.editorsyncValue();
21605 onFirstFocus: function() {
21606 this.buttons.each(function(item){
21610 toggleSourceEdit : function(sourceEditMode){
21613 if(sourceEditMode){
21614 Roo.log("disabling buttons");
21615 this.buttons.each( function(item){
21616 if(item.cmd != 'pencil'){
21622 Roo.log("enabling buttons");
21623 if(this.editorcore.initialized){
21624 this.buttons.each( function(item){
21630 Roo.log("calling toggole on editor");
21631 // tell the editor that it's been pressed..
21632 this.editor.toggleSourceEdit(sourceEditMode);
21642 * @class Roo.bootstrap.Table.AbstractSelectionModel
21643 * @extends Roo.util.Observable
21644 * Abstract base class for grid SelectionModels. It provides the interface that should be
21645 * implemented by descendant classes. This class should not be directly instantiated.
21648 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21649 this.locked = false;
21650 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21654 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21655 /** @ignore Called by the grid automatically. Do not call directly. */
21656 init : function(grid){
21662 * Locks the selections.
21665 this.locked = true;
21669 * Unlocks the selections.
21671 unlock : function(){
21672 this.locked = false;
21676 * Returns true if the selections are locked.
21677 * @return {Boolean}
21679 isLocked : function(){
21680 return this.locked;
21684 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21685 * @class Roo.bootstrap.Table.RowSelectionModel
21686 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21687 * It supports multiple selections and keyboard selection/navigation.
21689 * @param {Object} config
21692 Roo.bootstrap.Table.RowSelectionModel = function(config){
21693 Roo.apply(this, config);
21694 this.selections = new Roo.util.MixedCollection(false, function(o){
21699 this.lastActive = false;
21703 * @event selectionchange
21704 * Fires when the selection changes
21705 * @param {SelectionModel} this
21707 "selectionchange" : true,
21709 * @event afterselectionchange
21710 * Fires after the selection changes (eg. by key press or clicking)
21711 * @param {SelectionModel} this
21713 "afterselectionchange" : true,
21715 * @event beforerowselect
21716 * Fires when a row is selected being selected, return false to cancel.
21717 * @param {SelectionModel} this
21718 * @param {Number} rowIndex The selected index
21719 * @param {Boolean} keepExisting False if other selections will be cleared
21721 "beforerowselect" : true,
21724 * Fires when a row is selected.
21725 * @param {SelectionModel} this
21726 * @param {Number} rowIndex The selected index
21727 * @param {Roo.data.Record} r The record
21729 "rowselect" : true,
21731 * @event rowdeselect
21732 * Fires when a row is deselected.
21733 * @param {SelectionModel} this
21734 * @param {Number} rowIndex The selected index
21736 "rowdeselect" : true
21738 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21739 this.locked = false;
21742 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21744 * @cfg {Boolean} singleSelect
21745 * True to allow selection of only one row at a time (defaults to false)
21747 singleSelect : false,
21750 initEvents : function(){
21752 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21753 this.grid.on("mousedown", this.handleMouseDown, this);
21754 }else{ // allow click to work like normal
21755 this.grid.on("rowclick", this.handleDragableRowClick, this);
21758 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21759 "up" : function(e){
21761 this.selectPrevious(e.shiftKey);
21762 }else if(this.last !== false && this.lastActive !== false){
21763 var last = this.last;
21764 this.selectRange(this.last, this.lastActive-1);
21765 this.grid.getView().focusRow(this.lastActive);
21766 if(last !== false){
21770 this.selectFirstRow();
21772 this.fireEvent("afterselectionchange", this);
21774 "down" : function(e){
21776 this.selectNext(e.shiftKey);
21777 }else if(this.last !== false && this.lastActive !== false){
21778 var last = this.last;
21779 this.selectRange(this.last, this.lastActive+1);
21780 this.grid.getView().focusRow(this.lastActive);
21781 if(last !== false){
21785 this.selectFirstRow();
21787 this.fireEvent("afterselectionchange", this);
21792 var view = this.grid.view;
21793 view.on("refresh", this.onRefresh, this);
21794 view.on("rowupdated", this.onRowUpdated, this);
21795 view.on("rowremoved", this.onRemove, this);
21799 onRefresh : function(){
21800 var ds = this.grid.dataSource, i, v = this.grid.view;
21801 var s = this.selections;
21802 s.each(function(r){
21803 if((i = ds.indexOfId(r.id)) != -1){
21812 onRemove : function(v, index, r){
21813 this.selections.remove(r);
21817 onRowUpdated : function(v, index, r){
21818 if(this.isSelected(r)){
21819 v.onRowSelect(index);
21825 * @param {Array} records The records to select
21826 * @param {Boolean} keepExisting (optional) True to keep existing selections
21828 selectRecords : function(records, keepExisting){
21830 this.clearSelections();
21832 var ds = this.grid.dataSource;
21833 for(var i = 0, len = records.length; i < len; i++){
21834 this.selectRow(ds.indexOf(records[i]), true);
21839 * Gets the number of selected rows.
21842 getCount : function(){
21843 return this.selections.length;
21847 * Selects the first row in the grid.
21849 selectFirstRow : function(){
21854 * Select the last row.
21855 * @param {Boolean} keepExisting (optional) True to keep existing selections
21857 selectLastRow : function(keepExisting){
21858 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21862 * Selects the row immediately following the last selected row.
21863 * @param {Boolean} keepExisting (optional) True to keep existing selections
21865 selectNext : function(keepExisting){
21866 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21867 this.selectRow(this.last+1, keepExisting);
21868 this.grid.getView().focusRow(this.last);
21873 * Selects the row that precedes the last selected row.
21874 * @param {Boolean} keepExisting (optional) True to keep existing selections
21876 selectPrevious : function(keepExisting){
21878 this.selectRow(this.last-1, keepExisting);
21879 this.grid.getView().focusRow(this.last);
21884 * Returns the selected records
21885 * @return {Array} Array of selected records
21887 getSelections : function(){
21888 return [].concat(this.selections.items);
21892 * Returns the first selected record.
21895 getSelected : function(){
21896 return this.selections.itemAt(0);
21901 * Clears all selections.
21903 clearSelections : function(fast){
21908 var ds = this.grid.dataSource;
21909 var s = this.selections;
21910 s.each(function(r){
21911 this.deselectRow(ds.indexOfId(r.id));
21915 this.selections.clear();
21922 * Selects all rows.
21924 selectAll : function(){
21928 this.selections.clear();
21929 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21930 this.selectRow(i, true);
21935 * Returns True if there is a selection.
21936 * @return {Boolean}
21938 hasSelection : function(){
21939 return this.selections.length > 0;
21943 * Returns True if the specified row is selected.
21944 * @param {Number/Record} record The record or index of the record to check
21945 * @return {Boolean}
21947 isSelected : function(index){
21948 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21949 return (r && this.selections.key(r.id) ? true : false);
21953 * Returns True if the specified record id is selected.
21954 * @param {String} id The id of record to check
21955 * @return {Boolean}
21957 isIdSelected : function(id){
21958 return (this.selections.key(id) ? true : false);
21962 handleMouseDown : function(e, t){
21963 var view = this.grid.getView(), rowIndex;
21964 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21967 if(e.shiftKey && this.last !== false){
21968 var last = this.last;
21969 this.selectRange(last, rowIndex, e.ctrlKey);
21970 this.last = last; // reset the last
21971 view.focusRow(rowIndex);
21973 var isSelected = this.isSelected(rowIndex);
21974 if(e.button !== 0 && isSelected){
21975 view.focusRow(rowIndex);
21976 }else if(e.ctrlKey && isSelected){
21977 this.deselectRow(rowIndex);
21978 }else if(!isSelected){
21979 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21980 view.focusRow(rowIndex);
21983 this.fireEvent("afterselectionchange", this);
21986 handleDragableRowClick : function(grid, rowIndex, e)
21988 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21989 this.selectRow(rowIndex, false);
21990 grid.view.focusRow(rowIndex);
21991 this.fireEvent("afterselectionchange", this);
21996 * Selects multiple rows.
21997 * @param {Array} rows Array of the indexes of the row to select
21998 * @param {Boolean} keepExisting (optional) True to keep existing selections
22000 selectRows : function(rows, keepExisting){
22002 this.clearSelections();
22004 for(var i = 0, len = rows.length; i < len; i++){
22005 this.selectRow(rows[i], true);
22010 * Selects a range of rows. All rows in between startRow and endRow are also selected.
22011 * @param {Number} startRow The index of the first row in the range
22012 * @param {Number} endRow The index of the last row in the range
22013 * @param {Boolean} keepExisting (optional) True to retain existing selections
22015 selectRange : function(startRow, endRow, keepExisting){
22020 this.clearSelections();
22022 if(startRow <= endRow){
22023 for(var i = startRow; i <= endRow; i++){
22024 this.selectRow(i, true);
22027 for(var i = startRow; i >= endRow; i--){
22028 this.selectRow(i, true);
22034 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22035 * @param {Number} startRow The index of the first row in the range
22036 * @param {Number} endRow The index of the last row in the range
22038 deselectRange : function(startRow, endRow, preventViewNotify){
22042 for(var i = startRow; i <= endRow; i++){
22043 this.deselectRow(i, preventViewNotify);
22049 * @param {Number} row The index of the row to select
22050 * @param {Boolean} keepExisting (optional) True to keep existing selections
22052 selectRow : function(index, keepExisting, preventViewNotify){
22053 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22056 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22057 if(!keepExisting || this.singleSelect){
22058 this.clearSelections();
22060 var r = this.grid.dataSource.getAt(index);
22061 this.selections.add(r);
22062 this.last = this.lastActive = index;
22063 if(!preventViewNotify){
22064 this.grid.getView().onRowSelect(index);
22066 this.fireEvent("rowselect", this, index, r);
22067 this.fireEvent("selectionchange", this);
22073 * @param {Number} row The index of the row to deselect
22075 deselectRow : function(index, preventViewNotify){
22079 if(this.last == index){
22082 if(this.lastActive == index){
22083 this.lastActive = false;
22085 var r = this.grid.dataSource.getAt(index);
22086 this.selections.remove(r);
22087 if(!preventViewNotify){
22088 this.grid.getView().onRowDeselect(index);
22090 this.fireEvent("rowdeselect", this, index);
22091 this.fireEvent("selectionchange", this);
22095 restoreLast : function(){
22097 this.last = this._last;
22102 acceptsNav : function(row, col, cm){
22103 return !cm.isHidden(col) && cm.isCellEditable(col, row);
22107 onEditorKey : function(field, e){
22108 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22113 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22115 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22117 }else if(k == e.ENTER && !e.ctrlKey){
22121 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22123 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22125 }else if(k == e.ESC){
22129 g.startEditing(newCell[0], newCell[1]);
22134 * Ext JS Library 1.1.1
22135 * Copyright(c) 2006-2007, Ext JS, LLC.
22137 * Originally Released Under LGPL - original licence link has changed is not relivant.
22140 * <script type="text/javascript">
22144 * @class Roo.bootstrap.PagingToolbar
22145 * @extends Roo.bootstrap.NavSimplebar
22146 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22148 * Create a new PagingToolbar
22149 * @param {Object} config The config object
22150 * @param {Roo.data.Store} store
22152 Roo.bootstrap.PagingToolbar = function(config)
22154 // old args format still supported... - xtype is prefered..
22155 // created from xtype...
22157 this.ds = config.dataSource;
22159 if (config.store && !this.ds) {
22160 this.store= Roo.factory(config.store, Roo.data);
22161 this.ds = this.store;
22162 this.ds.xmodule = this.xmodule || false;
22165 this.toolbarItems = [];
22166 if (config.items) {
22167 this.toolbarItems = config.items;
22170 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22175 this.bind(this.ds);
22178 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22182 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22184 * @cfg {Roo.data.Store} dataSource
22185 * The underlying data store providing the paged data
22188 * @cfg {String/HTMLElement/Element} container
22189 * container The id or element that will contain the toolbar
22192 * @cfg {Boolean} displayInfo
22193 * True to display the displayMsg (defaults to false)
22196 * @cfg {Number} pageSize
22197 * The number of records to display per page (defaults to 20)
22201 * @cfg {String} displayMsg
22202 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22204 displayMsg : 'Displaying {0} - {1} of {2}',
22206 * @cfg {String} emptyMsg
22207 * The message to display when no records are found (defaults to "No data to display")
22209 emptyMsg : 'No data to display',
22211 * Customizable piece of the default paging text (defaults to "Page")
22214 beforePageText : "Page",
22216 * Customizable piece of the default paging text (defaults to "of %0")
22219 afterPageText : "of {0}",
22221 * Customizable piece of the default paging text (defaults to "First Page")
22224 firstText : "First Page",
22226 * Customizable piece of the default paging text (defaults to "Previous Page")
22229 prevText : "Previous Page",
22231 * Customizable piece of the default paging text (defaults to "Next Page")
22234 nextText : "Next Page",
22236 * Customizable piece of the default paging text (defaults to "Last Page")
22239 lastText : "Last Page",
22241 * Customizable piece of the default paging text (defaults to "Refresh")
22244 refreshText : "Refresh",
22248 onRender : function(ct, position)
22250 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22251 this.navgroup.parentId = this.id;
22252 this.navgroup.onRender(this.el, null);
22253 // add the buttons to the navgroup
22255 if(this.displayInfo){
22256 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22257 this.displayEl = this.el.select('.x-paging-info', true).first();
22258 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22259 // this.displayEl = navel.el.select('span',true).first();
22265 Roo.each(_this.buttons, function(e){ // this might need to use render????
22266 Roo.factory(e).onRender(_this.el, null);
22270 Roo.each(_this.toolbarItems, function(e) {
22271 _this.navgroup.addItem(e);
22275 this.first = this.navgroup.addItem({
22276 tooltip: this.firstText,
22278 icon : 'fa fa-backward',
22280 preventDefault: true,
22281 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22284 this.prev = this.navgroup.addItem({
22285 tooltip: this.prevText,
22287 icon : 'fa fa-step-backward',
22289 preventDefault: true,
22290 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
22292 //this.addSeparator();
22295 var field = this.navgroup.addItem( {
22297 cls : 'x-paging-position',
22299 html : this.beforePageText +
22300 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22301 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
22304 this.field = field.el.select('input', true).first();
22305 this.field.on("keydown", this.onPagingKeydown, this);
22306 this.field.on("focus", function(){this.dom.select();});
22309 this.afterTextEl = field.el.select('.x-paging-after',true).first();
22310 //this.field.setHeight(18);
22311 //this.addSeparator();
22312 this.next = this.navgroup.addItem({
22313 tooltip: this.nextText,
22315 html : ' <i class="fa fa-step-forward">',
22317 preventDefault: true,
22318 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
22320 this.last = this.navgroup.addItem({
22321 tooltip: this.lastText,
22322 icon : 'fa fa-forward',
22325 preventDefault: true,
22326 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
22328 //this.addSeparator();
22329 this.loading = this.navgroup.addItem({
22330 tooltip: this.refreshText,
22331 icon: 'fa fa-refresh',
22332 preventDefault: true,
22333 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22339 updateInfo : function(){
22340 if(this.displayEl){
22341 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22342 var msg = count == 0 ?
22346 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
22348 this.displayEl.update(msg);
22353 onLoad : function(ds, r, o){
22354 this.cursor = o.params ? o.params.start : 0;
22355 var d = this.getPageData(),
22359 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22360 this.field.dom.value = ap;
22361 this.first.setDisabled(ap == 1);
22362 this.prev.setDisabled(ap == 1);
22363 this.next.setDisabled(ap == ps);
22364 this.last.setDisabled(ap == ps);
22365 this.loading.enable();
22370 getPageData : function(){
22371 var total = this.ds.getTotalCount();
22374 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22375 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22380 onLoadError : function(){
22381 this.loading.enable();
22385 onPagingKeydown : function(e){
22386 var k = e.getKey();
22387 var d = this.getPageData();
22389 var v = this.field.dom.value, pageNum;
22390 if(!v || isNaN(pageNum = parseInt(v, 10))){
22391 this.field.dom.value = d.activePage;
22394 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22395 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22398 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))
22400 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22401 this.field.dom.value = pageNum;
22402 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22405 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22407 var v = this.field.dom.value, pageNum;
22408 var increment = (e.shiftKey) ? 10 : 1;
22409 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22412 if(!v || isNaN(pageNum = parseInt(v, 10))) {
22413 this.field.dom.value = d.activePage;
22416 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22418 this.field.dom.value = parseInt(v, 10) + increment;
22419 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22420 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22427 beforeLoad : function(){
22429 this.loading.disable();
22434 onClick : function(which){
22443 ds.load({params:{start: 0, limit: this.pageSize}});
22446 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22449 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22452 var total = ds.getTotalCount();
22453 var extra = total % this.pageSize;
22454 var lastStart = extra ? (total - extra) : total-this.pageSize;
22455 ds.load({params:{start: lastStart, limit: this.pageSize}});
22458 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22464 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22465 * @param {Roo.data.Store} store The data store to unbind
22467 unbind : function(ds){
22468 ds.un("beforeload", this.beforeLoad, this);
22469 ds.un("load", this.onLoad, this);
22470 ds.un("loadexception", this.onLoadError, this);
22471 ds.un("remove", this.updateInfo, this);
22472 ds.un("add", this.updateInfo, this);
22473 this.ds = undefined;
22477 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22478 * @param {Roo.data.Store} store The data store to bind
22480 bind : function(ds){
22481 ds.on("beforeload", this.beforeLoad, this);
22482 ds.on("load", this.onLoad, this);
22483 ds.on("loadexception", this.onLoadError, this);
22484 ds.on("remove", this.updateInfo, this);
22485 ds.on("add", this.updateInfo, this);
22496 * @class Roo.bootstrap.MessageBar
22497 * @extends Roo.bootstrap.Component
22498 * Bootstrap MessageBar class
22499 * @cfg {String} html contents of the MessageBar
22500 * @cfg {String} weight (info | success | warning | danger) default info
22501 * @cfg {String} beforeClass insert the bar before the given class
22502 * @cfg {Boolean} closable (true | false) default false
22503 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22506 * Create a new Element
22507 * @param {Object} config The config object
22510 Roo.bootstrap.MessageBar = function(config){
22511 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22514 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22520 beforeClass: 'bootstrap-sticky-wrap',
22522 getAutoCreate : function(){
22526 cls: 'alert alert-dismissable alert-' + this.weight,
22531 html: this.html || ''
22537 cfg.cls += ' alert-messages-fixed';
22551 onRender : function(ct, position)
22553 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22556 var cfg = Roo.apply({}, this.getAutoCreate());
22560 cfg.cls += ' ' + this.cls;
22563 cfg.style = this.style;
22565 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22567 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22570 this.el.select('>button.close').on('click', this.hide, this);
22576 if (!this.rendered) {
22582 this.fireEvent('show', this);
22588 if (!this.rendered) {
22594 this.fireEvent('hide', this);
22597 update : function()
22599 // var e = this.el.dom.firstChild;
22601 // if(this.closable){
22602 // e = e.nextSibling;
22605 // e.data = this.html || '';
22607 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22623 * @class Roo.bootstrap.Graph
22624 * @extends Roo.bootstrap.Component
22625 * Bootstrap Graph class
22629 @cfg {String} graphtype bar | vbar | pie
22630 @cfg {number} g_x coodinator | centre x (pie)
22631 @cfg {number} g_y coodinator | centre y (pie)
22632 @cfg {number} g_r radius (pie)
22633 @cfg {number} g_height height of the chart (respected by all elements in the set)
22634 @cfg {number} g_width width of the chart (respected by all elements in the set)
22635 @cfg {Object} title The title of the chart
22638 -opts (object) options for the chart
22640 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22641 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22643 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.
22644 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22646 o stretch (boolean)
22648 -opts (object) options for the pie
22651 o startAngle (number)
22652 o endAngle (number)
22656 * Create a new Input
22657 * @param {Object} config The config object
22660 Roo.bootstrap.Graph = function(config){
22661 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22667 * The img click event for the img.
22668 * @param {Roo.EventObject} e
22674 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22685 //g_colors: this.colors,
22692 getAutoCreate : function(){
22703 onRender : function(ct,position){
22704 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22705 this.raphael = Raphael(this.el.dom);
22707 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22708 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22709 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22710 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22712 r.text(160, 10, "Single Series Chart").attr(txtattr);
22713 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22714 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22715 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22717 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22718 r.barchart(330, 10, 300, 220, data1);
22719 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22720 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22723 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22724 // r.barchart(30, 30, 560, 250, xdata, {
22725 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22726 // axis : "0 0 1 1",
22727 // axisxlabels : xdata
22728 // //yvalues : cols,
22731 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22733 // this.load(null,xdata,{
22734 // axis : "0 0 1 1",
22735 // axisxlabels : xdata
22740 load : function(graphtype,xdata,opts){
22741 this.raphael.clear();
22743 graphtype = this.graphtype;
22748 var r = this.raphael,
22749 fin = function () {
22750 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22752 fout = function () {
22753 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22755 pfin = function() {
22756 this.sector.stop();
22757 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22760 this.label[0].stop();
22761 this.label[0].attr({ r: 7.5 });
22762 this.label[1].attr({ "font-weight": 800 });
22765 pfout = function() {
22766 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22769 this.label[0].animate({ r: 5 }, 500, "bounce");
22770 this.label[1].attr({ "font-weight": 400 });
22776 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22779 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22782 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
22783 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22785 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22792 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22797 setTitle: function(o)
22802 initEvents: function() {
22805 this.el.on('click', this.onClick, this);
22809 onClick : function(e)
22811 Roo.log('img onclick');
22812 this.fireEvent('click', this, e);
22824 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22827 * @class Roo.bootstrap.dash.NumberBox
22828 * @extends Roo.bootstrap.Component
22829 * Bootstrap NumberBox class
22830 * @cfg {String} headline Box headline
22831 * @cfg {String} content Box content
22832 * @cfg {String} icon Box icon
22833 * @cfg {String} footer Footer text
22834 * @cfg {String} fhref Footer href
22837 * Create a new NumberBox
22838 * @param {Object} config The config object
22842 Roo.bootstrap.dash.NumberBox = function(config){
22843 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22847 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
22856 getAutoCreate : function(){
22860 cls : 'small-box ',
22868 cls : 'roo-headline',
22869 html : this.headline
22873 cls : 'roo-content',
22874 html : this.content
22888 cls : 'ion ' + this.icon
22897 cls : 'small-box-footer',
22898 href : this.fhref || '#',
22902 cfg.cn.push(footer);
22909 onRender : function(ct,position){
22910 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22917 setHeadline: function (value)
22919 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22922 setFooter: function (value, href)
22924 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22927 this.el.select('a.small-box-footer',true).first().attr('href', href);
22932 setContent: function (value)
22934 this.el.select('.roo-content',true).first().dom.innerHTML = value;
22937 initEvents: function()
22951 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22954 * @class Roo.bootstrap.dash.TabBox
22955 * @extends Roo.bootstrap.Component
22956 * Bootstrap TabBox class
22957 * @cfg {String} title Title of the TabBox
22958 * @cfg {String} icon Icon of the TabBox
22959 * @cfg {Boolean} showtabs (true|false) show the tabs default true
22960 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22963 * Create a new TabBox
22964 * @param {Object} config The config object
22968 Roo.bootstrap.dash.TabBox = function(config){
22969 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22974 * When a pane is added
22975 * @param {Roo.bootstrap.dash.TabPane} pane
22979 * @event activatepane
22980 * When a pane is activated
22981 * @param {Roo.bootstrap.dash.TabPane} pane
22983 "activatepane" : true
22991 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
22996 tabScrollable : false,
22998 getChildContainer : function()
23000 return this.el.select('.tab-content', true).first();
23003 getAutoCreate : function(){
23007 cls: 'pull-left header',
23015 cls: 'fa ' + this.icon
23021 cls: 'nav nav-tabs pull-right',
23027 if(this.tabScrollable){
23034 cls: 'nav nav-tabs pull-right',
23045 cls: 'nav-tabs-custom',
23050 cls: 'tab-content no-padding',
23058 initEvents : function()
23060 //Roo.log('add add pane handler');
23061 this.on('addpane', this.onAddPane, this);
23064 * Updates the box title
23065 * @param {String} html to set the title to.
23067 setTitle : function(value)
23069 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23071 onAddPane : function(pane)
23073 this.panes.push(pane);
23074 //Roo.log('addpane');
23076 // tabs are rendere left to right..
23077 if(!this.showtabs){
23081 var ctr = this.el.select('.nav-tabs', true).first();
23084 var existing = ctr.select('.nav-tab',true);
23085 var qty = existing.getCount();;
23088 var tab = ctr.createChild({
23090 cls : 'nav-tab' + (qty ? '' : ' active'),
23098 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23101 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23103 pane.el.addClass('active');
23108 onTabClick : function(ev,un,ob,pane)
23110 //Roo.log('tab - prev default');
23111 ev.preventDefault();
23114 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23115 pane.tab.addClass('active');
23116 //Roo.log(pane.title);
23117 this.getChildContainer().select('.tab-pane',true).removeClass('active');
23118 // technically we should have a deactivate event.. but maybe add later.
23119 // and it should not de-activate the selected tab...
23120 this.fireEvent('activatepane', pane);
23121 pane.el.addClass('active');
23122 pane.fireEvent('activate');
23127 getActivePane : function()
23130 Roo.each(this.panes, function(p) {
23131 if(p.el.hasClass('active')){
23152 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23154 * @class Roo.bootstrap.TabPane
23155 * @extends Roo.bootstrap.Component
23156 * Bootstrap TabPane class
23157 * @cfg {Boolean} active (false | true) Default false
23158 * @cfg {String} title title of panel
23162 * Create a new TabPane
23163 * @param {Object} config The config object
23166 Roo.bootstrap.dash.TabPane = function(config){
23167 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23173 * When a pane is activated
23174 * @param {Roo.bootstrap.dash.TabPane} pane
23181 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
23186 // the tabBox that this is attached to.
23189 getAutoCreate : function()
23197 cfg.cls += ' active';
23202 initEvents : function()
23204 //Roo.log('trigger add pane handler');
23205 this.parent().fireEvent('addpane', this)
23209 * Updates the tab title
23210 * @param {String} html to set the title to.
23212 setTitle: function(str)
23218 this.tab.select('a', true).first().dom.innerHTML = str;
23235 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23238 * @class Roo.bootstrap.menu.Menu
23239 * @extends Roo.bootstrap.Component
23240 * Bootstrap Menu class - container for Menu
23241 * @cfg {String} html Text of the menu
23242 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23243 * @cfg {String} icon Font awesome icon
23244 * @cfg {String} pos Menu align to (top | bottom) default bottom
23248 * Create a new Menu
23249 * @param {Object} config The config object
23253 Roo.bootstrap.menu.Menu = function(config){
23254 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23258 * @event beforeshow
23259 * Fires before this menu is displayed
23260 * @param {Roo.bootstrap.menu.Menu} this
23264 * @event beforehide
23265 * Fires before this menu is hidden
23266 * @param {Roo.bootstrap.menu.Menu} this
23271 * Fires after this menu is displayed
23272 * @param {Roo.bootstrap.menu.Menu} this
23277 * Fires after this menu is hidden
23278 * @param {Roo.bootstrap.menu.Menu} this
23283 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23284 * @param {Roo.bootstrap.menu.Menu} this
23285 * @param {Roo.EventObject} e
23292 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
23296 weight : 'default',
23301 getChildContainer : function() {
23302 if(this.isSubMenu){
23306 return this.el.select('ul.dropdown-menu', true).first();
23309 getAutoCreate : function()
23314 cls : 'roo-menu-text',
23322 cls : 'fa ' + this.icon
23333 cls : 'dropdown-button btn btn-' + this.weight,
23338 cls : 'dropdown-toggle btn btn-' + this.weight,
23348 cls : 'dropdown-menu'
23354 if(this.pos == 'top'){
23355 cfg.cls += ' dropup';
23358 if(this.isSubMenu){
23361 cls : 'dropdown-menu'
23368 onRender : function(ct, position)
23370 this.isSubMenu = ct.hasClass('dropdown-submenu');
23372 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23375 initEvents : function()
23377 if(this.isSubMenu){
23381 this.hidden = true;
23383 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23384 this.triggerEl.on('click', this.onTriggerPress, this);
23386 this.buttonEl = this.el.select('button.dropdown-button', true).first();
23387 this.buttonEl.on('click', this.onClick, this);
23393 if(this.isSubMenu){
23397 return this.el.select('ul.dropdown-menu', true).first();
23400 onClick : function(e)
23402 this.fireEvent("click", this, e);
23405 onTriggerPress : function(e)
23407 if (this.isVisible()) {
23414 isVisible : function(){
23415 return !this.hidden;
23420 this.fireEvent("beforeshow", this);
23422 this.hidden = false;
23423 this.el.addClass('open');
23425 Roo.get(document).on("mouseup", this.onMouseUp, this);
23427 this.fireEvent("show", this);
23434 this.fireEvent("beforehide", this);
23436 this.hidden = true;
23437 this.el.removeClass('open');
23439 Roo.get(document).un("mouseup", this.onMouseUp);
23441 this.fireEvent("hide", this);
23444 onMouseUp : function()
23458 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23461 * @class Roo.bootstrap.menu.Item
23462 * @extends Roo.bootstrap.Component
23463 * Bootstrap MenuItem class
23464 * @cfg {Boolean} submenu (true | false) default false
23465 * @cfg {String} html text of the item
23466 * @cfg {String} href the link
23467 * @cfg {Boolean} disable (true | false) default false
23468 * @cfg {Boolean} preventDefault (true | false) default true
23469 * @cfg {String} icon Font awesome icon
23470 * @cfg {String} pos Submenu align to (left | right) default right
23474 * Create a new Item
23475 * @param {Object} config The config object
23479 Roo.bootstrap.menu.Item = function(config){
23480 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23484 * Fires when the mouse is hovering over this menu
23485 * @param {Roo.bootstrap.menu.Item} this
23486 * @param {Roo.EventObject} e
23491 * Fires when the mouse exits this menu
23492 * @param {Roo.bootstrap.menu.Item} this
23493 * @param {Roo.EventObject} e
23499 * The raw click event for the entire grid.
23500 * @param {Roo.EventObject} e
23506 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23511 preventDefault: true,
23516 getAutoCreate : function()
23521 cls : 'roo-menu-item-text',
23529 cls : 'fa ' + this.icon
23538 href : this.href || '#',
23545 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23549 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23551 if(this.pos == 'left'){
23552 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23559 initEvents : function()
23561 this.el.on('mouseover', this.onMouseOver, this);
23562 this.el.on('mouseout', this.onMouseOut, this);
23564 this.el.select('a', true).first().on('click', this.onClick, this);
23568 onClick : function(e)
23570 if(this.preventDefault){
23571 e.preventDefault();
23574 this.fireEvent("click", this, e);
23577 onMouseOver : function(e)
23579 if(this.submenu && this.pos == 'left'){
23580 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23583 this.fireEvent("mouseover", this, e);
23586 onMouseOut : function(e)
23588 this.fireEvent("mouseout", this, e);
23600 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23603 * @class Roo.bootstrap.menu.Separator
23604 * @extends Roo.bootstrap.Component
23605 * Bootstrap Separator class
23608 * Create a new Separator
23609 * @param {Object} config The config object
23613 Roo.bootstrap.menu.Separator = function(config){
23614 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23617 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23619 getAutoCreate : function(){
23640 * @class Roo.bootstrap.Tooltip
23641 * Bootstrap Tooltip class
23642 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23643 * to determine which dom element triggers the tooltip.
23645 * It needs to add support for additional attributes like tooltip-position
23648 * Create a new Toolti
23649 * @param {Object} config The config object
23652 Roo.bootstrap.Tooltip = function(config){
23653 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23656 Roo.apply(Roo.bootstrap.Tooltip, {
23658 * @function init initialize tooltip monitoring.
23662 currentTip : false,
23663 currentRegion : false,
23669 Roo.get(document).on('mouseover', this.enter ,this);
23670 Roo.get(document).on('mouseout', this.leave, this);
23673 this.currentTip = new Roo.bootstrap.Tooltip();
23676 enter : function(ev)
23678 var dom = ev.getTarget();
23680 //Roo.log(['enter',dom]);
23681 var el = Roo.fly(dom);
23682 if (this.currentEl) {
23684 //Roo.log(this.currentEl);
23685 //Roo.log(this.currentEl.contains(dom));
23686 if (this.currentEl == el) {
23689 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23695 if (this.currentTip.el) {
23696 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23701 // you can not look for children, as if el is the body.. then everythign is the child..
23702 if (!el.attr('tooltip')) { //
23703 if (!el.select("[tooltip]").elements.length) {
23706 // is the mouse over this child...?
23707 bindEl = el.select("[tooltip]").first();
23708 var xy = ev.getXY();
23709 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23710 //Roo.log("not in region.");
23713 //Roo.log("child element over..");
23716 this.currentEl = bindEl;
23717 this.currentTip.bind(bindEl);
23718 this.currentRegion = Roo.lib.Region.getRegion(dom);
23719 this.currentTip.enter();
23722 leave : function(ev)
23724 var dom = ev.getTarget();
23725 //Roo.log(['leave',dom]);
23726 if (!this.currentEl) {
23731 if (dom != this.currentEl.dom) {
23734 var xy = ev.getXY();
23735 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23738 // only activate leave if mouse cursor is outside... bounding box..
23743 if (this.currentTip) {
23744 this.currentTip.leave();
23746 //Roo.log('clear currentEl');
23747 this.currentEl = false;
23752 'left' : ['r-l', [-2,0], 'right'],
23753 'right' : ['l-r', [2,0], 'left'],
23754 'bottom' : ['t-b', [0,2], 'top'],
23755 'top' : [ 'b-t', [0,-2], 'bottom']
23761 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
23766 delay : null, // can be { show : 300 , hide: 500}
23770 hoverState : null, //???
23772 placement : 'bottom',
23774 getAutoCreate : function(){
23781 cls : 'tooltip-arrow'
23784 cls : 'tooltip-inner'
23791 bind : function(el)
23797 enter : function () {
23799 if (this.timeout != null) {
23800 clearTimeout(this.timeout);
23803 this.hoverState = 'in';
23804 //Roo.log("enter - show");
23805 if (!this.delay || !this.delay.show) {
23810 this.timeout = setTimeout(function () {
23811 if (_t.hoverState == 'in') {
23814 }, this.delay.show);
23818 clearTimeout(this.timeout);
23820 this.hoverState = 'out';
23821 if (!this.delay || !this.delay.hide) {
23827 this.timeout = setTimeout(function () {
23828 //Roo.log("leave - timeout");
23830 if (_t.hoverState == 'out') {
23832 Roo.bootstrap.Tooltip.currentEl = false;
23840 this.render(document.body);
23843 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23845 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23847 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23849 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23851 var placement = typeof this.placement == 'function' ?
23852 this.placement.call(this, this.el, on_el) :
23855 var autoToken = /\s?auto?\s?/i;
23856 var autoPlace = autoToken.test(placement);
23858 placement = placement.replace(autoToken, '') || 'top';
23862 //this.el.setXY([0,0]);
23864 //this.el.dom.style.display='block';
23866 //this.el.appendTo(on_el);
23868 var p = this.getPosition();
23869 var box = this.el.getBox();
23875 var align = Roo.bootstrap.Tooltip.alignment[placement];
23877 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23879 if(placement == 'top' || placement == 'bottom'){
23881 placement = 'right';
23884 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23885 placement = 'left';
23889 align = Roo.bootstrap.Tooltip.alignment[placement];
23891 this.el.alignTo(this.bindEl, align[0],align[1]);
23892 //var arrow = this.el.select('.arrow',true).first();
23893 //arrow.set(align[2],
23895 this.el.addClass(placement);
23897 this.el.addClass('in fade');
23899 this.hoverState = null;
23901 if (this.el.hasClass('fade')) {
23912 //this.el.setXY([0,0]);
23913 this.el.removeClass('in');
23929 * @class Roo.bootstrap.LocationPicker
23930 * @extends Roo.bootstrap.Component
23931 * Bootstrap LocationPicker class
23932 * @cfg {Number} latitude Position when init default 0
23933 * @cfg {Number} longitude Position when init default 0
23934 * @cfg {Number} zoom default 15
23935 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23936 * @cfg {Boolean} mapTypeControl default false
23937 * @cfg {Boolean} disableDoubleClickZoom default false
23938 * @cfg {Boolean} scrollwheel default true
23939 * @cfg {Boolean} streetViewControl default false
23940 * @cfg {Number} radius default 0
23941 * @cfg {String} locationName
23942 * @cfg {Boolean} draggable default true
23943 * @cfg {Boolean} enableAutocomplete default false
23944 * @cfg {Boolean} enableReverseGeocode default true
23945 * @cfg {String} markerTitle
23948 * Create a new LocationPicker
23949 * @param {Object} config The config object
23953 Roo.bootstrap.LocationPicker = function(config){
23955 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23960 * Fires when the picker initialized.
23961 * @param {Roo.bootstrap.LocationPicker} this
23962 * @param {Google Location} location
23966 * @event positionchanged
23967 * Fires when the picker position changed.
23968 * @param {Roo.bootstrap.LocationPicker} this
23969 * @param {Google Location} location
23971 positionchanged : true,
23974 * Fires when the map resize.
23975 * @param {Roo.bootstrap.LocationPicker} this
23980 * Fires when the map show.
23981 * @param {Roo.bootstrap.LocationPicker} this
23986 * Fires when the map hide.
23987 * @param {Roo.bootstrap.LocationPicker} this
23992 * Fires when click the map.
23993 * @param {Roo.bootstrap.LocationPicker} this
23994 * @param {Map event} e
23998 * @event mapRightClick
23999 * Fires when right click the map.
24000 * @param {Roo.bootstrap.LocationPicker} this
24001 * @param {Map event} e
24003 mapRightClick : true,
24005 * @event markerClick
24006 * Fires when click the marker.
24007 * @param {Roo.bootstrap.LocationPicker} this
24008 * @param {Map event} e
24010 markerClick : true,
24012 * @event markerRightClick
24013 * Fires when right click the marker.
24014 * @param {Roo.bootstrap.LocationPicker} this
24015 * @param {Map event} e
24017 markerRightClick : true,
24019 * @event OverlayViewDraw
24020 * Fires when OverlayView Draw
24021 * @param {Roo.bootstrap.LocationPicker} this
24023 OverlayViewDraw : true,
24025 * @event OverlayViewOnAdd
24026 * Fires when OverlayView Draw
24027 * @param {Roo.bootstrap.LocationPicker} this
24029 OverlayViewOnAdd : true,
24031 * @event OverlayViewOnRemove
24032 * Fires when OverlayView Draw
24033 * @param {Roo.bootstrap.LocationPicker} this
24035 OverlayViewOnRemove : true,
24037 * @event OverlayViewShow
24038 * Fires when OverlayView Draw
24039 * @param {Roo.bootstrap.LocationPicker} this
24040 * @param {Pixel} cpx
24042 OverlayViewShow : true,
24044 * @event OverlayViewHide
24045 * Fires when OverlayView Draw
24046 * @param {Roo.bootstrap.LocationPicker} this
24048 OverlayViewHide : true,
24050 * @event loadexception
24051 * Fires when load google lib failed.
24052 * @param {Roo.bootstrap.LocationPicker} this
24054 loadexception : true
24059 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
24061 gMapContext: false,
24067 mapTypeControl: false,
24068 disableDoubleClickZoom: false,
24070 streetViewControl: false,
24074 enableAutocomplete: false,
24075 enableReverseGeocode: true,
24078 getAutoCreate: function()
24083 cls: 'roo-location-picker'
24089 initEvents: function(ct, position)
24091 if(!this.el.getWidth() || this.isApplied()){
24095 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24100 initial: function()
24102 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24103 this.fireEvent('loadexception', this);
24107 if(!this.mapTypeId){
24108 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24111 this.gMapContext = this.GMapContext();
24113 this.initOverlayView();
24115 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24119 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24120 _this.setPosition(_this.gMapContext.marker.position);
24123 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24124 _this.fireEvent('mapClick', this, event);
24128 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24129 _this.fireEvent('mapRightClick', this, event);
24133 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24134 _this.fireEvent('markerClick', this, event);
24138 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24139 _this.fireEvent('markerRightClick', this, event);
24143 this.setPosition(this.gMapContext.location);
24145 this.fireEvent('initial', this, this.gMapContext.location);
24148 initOverlayView: function()
24152 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24156 _this.fireEvent('OverlayViewDraw', _this);
24161 _this.fireEvent('OverlayViewOnAdd', _this);
24164 onRemove: function()
24166 _this.fireEvent('OverlayViewOnRemove', _this);
24169 show: function(cpx)
24171 _this.fireEvent('OverlayViewShow', _this, cpx);
24176 _this.fireEvent('OverlayViewHide', _this);
24182 fromLatLngToContainerPixel: function(event)
24184 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24187 isApplied: function()
24189 return this.getGmapContext() == false ? false : true;
24192 getGmapContext: function()
24194 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24197 GMapContext: function()
24199 var position = new google.maps.LatLng(this.latitude, this.longitude);
24201 var _map = new google.maps.Map(this.el.dom, {
24204 mapTypeId: this.mapTypeId,
24205 mapTypeControl: this.mapTypeControl,
24206 disableDoubleClickZoom: this.disableDoubleClickZoom,
24207 scrollwheel: this.scrollwheel,
24208 streetViewControl: this.streetViewControl,
24209 locationName: this.locationName,
24210 draggable: this.draggable,
24211 enableAutocomplete: this.enableAutocomplete,
24212 enableReverseGeocode: this.enableReverseGeocode
24215 var _marker = new google.maps.Marker({
24216 position: position,
24218 title: this.markerTitle,
24219 draggable: this.draggable
24226 location: position,
24227 radius: this.radius,
24228 locationName: this.locationName,
24229 addressComponents: {
24230 formatted_address: null,
24231 addressLine1: null,
24232 addressLine2: null,
24234 streetNumber: null,
24238 stateOrProvince: null
24241 domContainer: this.el.dom,
24242 geodecoder: new google.maps.Geocoder()
24246 drawCircle: function(center, radius, options)
24248 if (this.gMapContext.circle != null) {
24249 this.gMapContext.circle.setMap(null);
24253 options = Roo.apply({}, options, {
24254 strokeColor: "#0000FF",
24255 strokeOpacity: .35,
24257 fillColor: "#0000FF",
24261 options.map = this.gMapContext.map;
24262 options.radius = radius;
24263 options.center = center;
24264 this.gMapContext.circle = new google.maps.Circle(options);
24265 return this.gMapContext.circle;
24271 setPosition: function(location)
24273 this.gMapContext.location = location;
24274 this.gMapContext.marker.setPosition(location);
24275 this.gMapContext.map.panTo(location);
24276 this.drawCircle(location, this.gMapContext.radius, {});
24280 if (this.gMapContext.settings.enableReverseGeocode) {
24281 this.gMapContext.geodecoder.geocode({
24282 latLng: this.gMapContext.location
24283 }, function(results, status) {
24285 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24286 _this.gMapContext.locationName = results[0].formatted_address;
24287 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24289 _this.fireEvent('positionchanged', this, location);
24296 this.fireEvent('positionchanged', this, location);
24301 google.maps.event.trigger(this.gMapContext.map, "resize");
24303 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24305 this.fireEvent('resize', this);
24308 setPositionByLatLng: function(latitude, longitude)
24310 this.setPosition(new google.maps.LatLng(latitude, longitude));
24313 getCurrentPosition: function()
24316 latitude: this.gMapContext.location.lat(),
24317 longitude: this.gMapContext.location.lng()
24321 getAddressName: function()
24323 return this.gMapContext.locationName;
24326 getAddressComponents: function()
24328 return this.gMapContext.addressComponents;
24331 address_component_from_google_geocode: function(address_components)
24335 for (var i = 0; i < address_components.length; i++) {
24336 var component = address_components[i];
24337 if (component.types.indexOf("postal_code") >= 0) {
24338 result.postalCode = component.short_name;
24339 } else if (component.types.indexOf("street_number") >= 0) {
24340 result.streetNumber = component.short_name;
24341 } else if (component.types.indexOf("route") >= 0) {
24342 result.streetName = component.short_name;
24343 } else if (component.types.indexOf("neighborhood") >= 0) {
24344 result.city = component.short_name;
24345 } else if (component.types.indexOf("locality") >= 0) {
24346 result.city = component.short_name;
24347 } else if (component.types.indexOf("sublocality") >= 0) {
24348 result.district = component.short_name;
24349 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24350 result.stateOrProvince = component.short_name;
24351 } else if (component.types.indexOf("country") >= 0) {
24352 result.country = component.short_name;
24356 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24357 result.addressLine2 = "";
24361 setZoomLevel: function(zoom)
24363 this.gMapContext.map.setZoom(zoom);
24376 this.fireEvent('show', this);
24387 this.fireEvent('hide', this);
24392 Roo.apply(Roo.bootstrap.LocationPicker, {
24394 OverlayView : function(map, options)
24396 options = options || {};
24410 * @class Roo.bootstrap.Alert
24411 * @extends Roo.bootstrap.Component
24412 * Bootstrap Alert class
24413 * @cfg {String} title The title of alert
24414 * @cfg {String} html The content of alert
24415 * @cfg {String} weight ( success | info | warning | danger )
24416 * @cfg {String} faicon font-awesomeicon
24419 * Create a new alert
24420 * @param {Object} config The config object
24424 Roo.bootstrap.Alert = function(config){
24425 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24429 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
24436 getAutoCreate : function()
24445 cls : 'roo-alert-icon'
24450 cls : 'roo-alert-title',
24455 cls : 'roo-alert-text',
24462 cfg.cn[0].cls += ' fa ' + this.faicon;
24466 cfg.cls += ' alert-' + this.weight;
24472 initEvents: function()
24474 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24477 setTitle : function(str)
24479 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24482 setText : function(str)
24484 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24487 setWeight : function(weight)
24490 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24493 this.weight = weight;
24495 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24498 setIcon : function(icon)
24501 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24504 this.faicon = icon;
24506 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24527 * @class Roo.bootstrap.UploadCropbox
24528 * @extends Roo.bootstrap.Component
24529 * Bootstrap UploadCropbox class
24530 * @cfg {String} emptyText show when image has been loaded
24531 * @cfg {String} rotateNotify show when image too small to rotate
24532 * @cfg {Number} errorTimeout default 3000
24533 * @cfg {Number} minWidth default 300
24534 * @cfg {Number} minHeight default 300
24535 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24536 * @cfg {Boolean} isDocument (true|false) default false
24537 * @cfg {String} url action url
24538 * @cfg {String} paramName default 'imageUpload'
24539 * @cfg {String} method default POST
24540 * @cfg {Boolean} loadMask (true|false) default true
24541 * @cfg {Boolean} loadingText default 'Loading...'
24544 * Create a new UploadCropbox
24545 * @param {Object} config The config object
24548 Roo.bootstrap.UploadCropbox = function(config){
24549 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24553 * @event beforeselectfile
24554 * Fire before select file
24555 * @param {Roo.bootstrap.UploadCropbox} this
24557 "beforeselectfile" : true,
24560 * Fire after initEvent
24561 * @param {Roo.bootstrap.UploadCropbox} this
24566 * Fire after initEvent
24567 * @param {Roo.bootstrap.UploadCropbox} this
24568 * @param {String} data
24573 * Fire when preparing the file data
24574 * @param {Roo.bootstrap.UploadCropbox} this
24575 * @param {Object} file
24580 * Fire when get exception
24581 * @param {Roo.bootstrap.UploadCropbox} this
24582 * @param {XMLHttpRequest} xhr
24584 "exception" : true,
24586 * @event beforeloadcanvas
24587 * Fire before load the canvas
24588 * @param {Roo.bootstrap.UploadCropbox} this
24589 * @param {String} src
24591 "beforeloadcanvas" : true,
24594 * Fire when trash image
24595 * @param {Roo.bootstrap.UploadCropbox} this
24600 * Fire when download the image
24601 * @param {Roo.bootstrap.UploadCropbox} this
24605 * @event footerbuttonclick
24606 * Fire when footerbuttonclick
24607 * @param {Roo.bootstrap.UploadCropbox} this
24608 * @param {String} type
24610 "footerbuttonclick" : true,
24614 * @param {Roo.bootstrap.UploadCropbox} this
24619 * Fire when rotate the image
24620 * @param {Roo.bootstrap.UploadCropbox} this
24621 * @param {String} pos
24626 * Fire when inspect the file
24627 * @param {Roo.bootstrap.UploadCropbox} this
24628 * @param {Object} file
24633 * Fire when xhr upload the file
24634 * @param {Roo.bootstrap.UploadCropbox} this
24635 * @param {Object} data
24640 * Fire when arrange the file data
24641 * @param {Roo.bootstrap.UploadCropbox} this
24642 * @param {Object} formData
24647 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24650 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24652 emptyText : 'Click to upload image',
24653 rotateNotify : 'Image is too small to rotate',
24654 errorTimeout : 3000,
24668 cropType : 'image/jpeg',
24670 canvasLoaded : false,
24671 isDocument : false,
24673 paramName : 'imageUpload',
24675 loadingText : 'Loading...',
24678 getAutoCreate : function()
24682 cls : 'roo-upload-cropbox',
24686 cls : 'roo-upload-cropbox-selector',
24691 cls : 'roo-upload-cropbox-body',
24692 style : 'cursor:pointer',
24696 cls : 'roo-upload-cropbox-preview'
24700 cls : 'roo-upload-cropbox-thumb'
24704 cls : 'roo-upload-cropbox-empty-notify',
24705 html : this.emptyText
24709 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24710 html : this.rotateNotify
24716 cls : 'roo-upload-cropbox-footer',
24719 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24729 onRender : function(ct, position)
24731 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24733 if (this.buttons.length) {
24735 Roo.each(this.buttons, function(bb) {
24737 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24739 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24745 this.maskEl = this.el;
24749 initEvents : function()
24751 this.urlAPI = (window.createObjectURL && window) ||
24752 (window.URL && URL.revokeObjectURL && URL) ||
24753 (window.webkitURL && webkitURL);
24755 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24756 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24758 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24759 this.selectorEl.hide();
24761 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24762 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24764 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24765 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24766 this.thumbEl.hide();
24768 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24769 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24771 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24772 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24773 this.errorEl.hide();
24775 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24776 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24777 this.footerEl.hide();
24779 this.setThumbBoxSize();
24785 this.fireEvent('initial', this);
24792 window.addEventListener("resize", function() { _this.resize(); } );
24794 this.bodyEl.on('click', this.beforeSelectFile, this);
24797 this.bodyEl.on('touchstart', this.onTouchStart, this);
24798 this.bodyEl.on('touchmove', this.onTouchMove, this);
24799 this.bodyEl.on('touchend', this.onTouchEnd, this);
24803 this.bodyEl.on('mousedown', this.onMouseDown, this);
24804 this.bodyEl.on('mousemove', this.onMouseMove, this);
24805 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24806 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24807 Roo.get(document).on('mouseup', this.onMouseUp, this);
24810 this.selectorEl.on('change', this.onFileSelected, this);
24816 this.baseScale = 1;
24818 this.baseRotate = 1;
24819 this.dragable = false;
24820 this.pinching = false;
24823 this.cropData = false;
24824 this.notifyEl.dom.innerHTML = this.emptyText;
24826 this.selectorEl.dom.value = '';
24830 resize : function()
24832 if(this.fireEvent('resize', this) != false){
24833 this.setThumbBoxPosition();
24834 this.setCanvasPosition();
24838 onFooterButtonClick : function(e, el, o, type)
24841 case 'rotate-left' :
24842 this.onRotateLeft(e);
24844 case 'rotate-right' :
24845 this.onRotateRight(e);
24848 this.beforeSelectFile(e);
24863 this.fireEvent('footerbuttonclick', this, type);
24866 beforeSelectFile : function(e)
24868 e.preventDefault();
24870 if(this.fireEvent('beforeselectfile', this) != false){
24871 this.selectorEl.dom.click();
24875 onFileSelected : function(e)
24877 e.preventDefault();
24879 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24883 var file = this.selectorEl.dom.files[0];
24885 if(this.fireEvent('inspect', this, file) != false){
24886 this.prepare(file);
24891 trash : function(e)
24893 this.fireEvent('trash', this);
24896 download : function(e)
24898 this.fireEvent('download', this);
24901 loadCanvas : function(src)
24903 if(this.fireEvent('beforeloadcanvas', this, src) != false){
24907 this.imageEl = document.createElement('img');
24911 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24913 this.imageEl.src = src;
24917 onLoadCanvas : function()
24919 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24920 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24922 this.bodyEl.un('click', this.beforeSelectFile, this);
24924 this.notifyEl.hide();
24925 this.thumbEl.show();
24926 this.footerEl.show();
24928 this.baseRotateLevel();
24930 if(this.isDocument){
24931 this.setThumbBoxSize();
24934 this.setThumbBoxPosition();
24936 this.baseScaleLevel();
24942 this.canvasLoaded = true;
24945 this.maskEl.unmask();
24950 setCanvasPosition : function()
24952 if(!this.canvasEl){
24956 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24957 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24959 this.previewEl.setLeft(pw);
24960 this.previewEl.setTop(ph);
24964 onMouseDown : function(e)
24968 this.dragable = true;
24969 this.pinching = false;
24971 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24972 this.dragable = false;
24976 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24977 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24981 onMouseMove : function(e)
24985 if(!this.canvasLoaded){
24989 if (!this.dragable){
24993 var minX = Math.ceil(this.thumbEl.getLeft(true));
24994 var minY = Math.ceil(this.thumbEl.getTop(true));
24996 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24997 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24999 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25000 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25002 x = x - this.mouseX;
25003 y = y - this.mouseY;
25005 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25006 var bgY = Math.ceil(y + this.previewEl.getTop(true));
25008 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25009 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25011 this.previewEl.setLeft(bgX);
25012 this.previewEl.setTop(bgY);
25014 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25015 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25018 onMouseUp : function(e)
25022 this.dragable = false;
25025 onMouseWheel : function(e)
25029 this.startScale = this.scale;
25031 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25033 if(!this.zoomable()){
25034 this.scale = this.startScale;
25043 zoomable : function()
25045 var minScale = this.thumbEl.getWidth() / this.minWidth;
25047 if(this.minWidth < this.minHeight){
25048 minScale = this.thumbEl.getHeight() / this.minHeight;
25051 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25052 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25056 (this.rotate == 0 || this.rotate == 180) &&
25058 width > this.imageEl.OriginWidth ||
25059 height > this.imageEl.OriginHeight ||
25060 (width < this.minWidth && height < this.minHeight)
25068 (this.rotate == 90 || this.rotate == 270) &&
25070 width > this.imageEl.OriginWidth ||
25071 height > this.imageEl.OriginHeight ||
25072 (width < this.minHeight && height < this.minWidth)
25079 !this.isDocument &&
25080 (this.rotate == 0 || this.rotate == 180) &&
25082 width < this.minWidth ||
25083 width > this.imageEl.OriginWidth ||
25084 height < this.minHeight ||
25085 height > this.imageEl.OriginHeight
25092 !this.isDocument &&
25093 (this.rotate == 90 || this.rotate == 270) &&
25095 width < this.minHeight ||
25096 width > this.imageEl.OriginWidth ||
25097 height < this.minWidth ||
25098 height > this.imageEl.OriginHeight
25108 onRotateLeft : function(e)
25110 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25112 var minScale = this.thumbEl.getWidth() / this.minWidth;
25114 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25115 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25117 this.startScale = this.scale;
25119 while (this.getScaleLevel() < minScale){
25121 this.scale = this.scale + 1;
25123 if(!this.zoomable()){
25128 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25129 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25134 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25141 this.scale = this.startScale;
25143 this.onRotateFail();
25148 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25150 if(this.isDocument){
25151 this.setThumbBoxSize();
25152 this.setThumbBoxPosition();
25153 this.setCanvasPosition();
25158 this.fireEvent('rotate', this, 'left');
25162 onRotateRight : function(e)
25164 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25166 var minScale = this.thumbEl.getWidth() / this.minWidth;
25168 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25169 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25171 this.startScale = this.scale;
25173 while (this.getScaleLevel() < minScale){
25175 this.scale = this.scale + 1;
25177 if(!this.zoomable()){
25182 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25183 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25188 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25195 this.scale = this.startScale;
25197 this.onRotateFail();
25202 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25204 if(this.isDocument){
25205 this.setThumbBoxSize();
25206 this.setThumbBoxPosition();
25207 this.setCanvasPosition();
25212 this.fireEvent('rotate', this, 'right');
25215 onRotateFail : function()
25217 this.errorEl.show(true);
25221 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25226 this.previewEl.dom.innerHTML = '';
25228 var canvasEl = document.createElement("canvas");
25230 var contextEl = canvasEl.getContext("2d");
25232 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25233 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25234 var center = this.imageEl.OriginWidth / 2;
25236 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25237 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25238 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25239 center = this.imageEl.OriginHeight / 2;
25242 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25244 contextEl.translate(center, center);
25245 contextEl.rotate(this.rotate * Math.PI / 180);
25247 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25249 this.canvasEl = document.createElement("canvas");
25251 this.contextEl = this.canvasEl.getContext("2d");
25253 switch (this.rotate) {
25256 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25257 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25259 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25264 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25265 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25267 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25268 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);
25272 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25277 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25278 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25280 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25281 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);
25285 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);
25290 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25291 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25293 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25294 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25298 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);
25305 this.previewEl.appendChild(this.canvasEl);
25307 this.setCanvasPosition();
25312 if(!this.canvasLoaded){
25316 var imageCanvas = document.createElement("canvas");
25318 var imageContext = imageCanvas.getContext("2d");
25320 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25321 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25323 var center = imageCanvas.width / 2;
25325 imageContext.translate(center, center);
25327 imageContext.rotate(this.rotate * Math.PI / 180);
25329 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25331 var canvas = document.createElement("canvas");
25333 var context = canvas.getContext("2d");
25335 canvas.width = this.minWidth;
25336 canvas.height = this.minHeight;
25338 switch (this.rotate) {
25341 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25342 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25344 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25345 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25347 var targetWidth = this.minWidth - 2 * x;
25348 var targetHeight = this.minHeight - 2 * y;
25352 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25353 scale = targetWidth / width;
25356 if(x > 0 && y == 0){
25357 scale = targetHeight / height;
25360 if(x > 0 && y > 0){
25361 scale = targetWidth / width;
25363 if(width < height){
25364 scale = targetHeight / height;
25368 context.scale(scale, scale);
25370 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25371 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25373 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25374 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25376 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25381 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25382 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25384 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25385 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25387 var targetWidth = this.minWidth - 2 * x;
25388 var targetHeight = this.minHeight - 2 * y;
25392 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25393 scale = targetWidth / width;
25396 if(x > 0 && y == 0){
25397 scale = targetHeight / height;
25400 if(x > 0 && y > 0){
25401 scale = targetWidth / width;
25403 if(width < height){
25404 scale = targetHeight / height;
25408 context.scale(scale, scale);
25410 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25411 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25413 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25414 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25416 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25418 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25423 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25424 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25426 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25427 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25429 var targetWidth = this.minWidth - 2 * x;
25430 var targetHeight = this.minHeight - 2 * y;
25434 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25435 scale = targetWidth / width;
25438 if(x > 0 && y == 0){
25439 scale = targetHeight / height;
25442 if(x > 0 && y > 0){
25443 scale = targetWidth / width;
25445 if(width < height){
25446 scale = targetHeight / height;
25450 context.scale(scale, scale);
25452 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25453 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25455 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25456 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25458 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25459 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25461 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25466 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25467 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25469 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25470 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25472 var targetWidth = this.minWidth - 2 * x;
25473 var targetHeight = this.minHeight - 2 * y;
25477 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25478 scale = targetWidth / width;
25481 if(x > 0 && y == 0){
25482 scale = targetHeight / height;
25485 if(x > 0 && y > 0){
25486 scale = targetWidth / width;
25488 if(width < height){
25489 scale = targetHeight / height;
25493 context.scale(scale, scale);
25495 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25496 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25498 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25499 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25501 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25503 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25510 this.cropData = canvas.toDataURL(this.cropType);
25512 if(this.fireEvent('crop', this, this.cropData) !== false){
25513 this.process(this.file, this.cropData);
25520 setThumbBoxSize : function()
25524 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25525 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25526 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25528 this.minWidth = width;
25529 this.minHeight = height;
25531 if(this.rotate == 90 || this.rotate == 270){
25532 this.minWidth = height;
25533 this.minHeight = width;
25538 width = Math.ceil(this.minWidth * height / this.minHeight);
25540 if(this.minWidth > this.minHeight){
25542 height = Math.ceil(this.minHeight * width / this.minWidth);
25545 this.thumbEl.setStyle({
25546 width : width + 'px',
25547 height : height + 'px'
25554 setThumbBoxPosition : function()
25556 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25557 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25559 this.thumbEl.setLeft(x);
25560 this.thumbEl.setTop(y);
25564 baseRotateLevel : function()
25566 this.baseRotate = 1;
25569 typeof(this.exif) != 'undefined' &&
25570 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25571 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25573 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25576 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25580 baseScaleLevel : function()
25584 if(this.isDocument){
25586 if(this.baseRotate == 6 || this.baseRotate == 8){
25588 height = this.thumbEl.getHeight();
25589 this.baseScale = height / this.imageEl.OriginWidth;
25591 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25592 width = this.thumbEl.getWidth();
25593 this.baseScale = width / this.imageEl.OriginHeight;
25599 height = this.thumbEl.getHeight();
25600 this.baseScale = height / this.imageEl.OriginHeight;
25602 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25603 width = this.thumbEl.getWidth();
25604 this.baseScale = width / this.imageEl.OriginWidth;
25610 if(this.baseRotate == 6 || this.baseRotate == 8){
25612 width = this.thumbEl.getHeight();
25613 this.baseScale = width / this.imageEl.OriginHeight;
25615 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25616 height = this.thumbEl.getWidth();
25617 this.baseScale = height / this.imageEl.OriginHeight;
25620 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25621 height = this.thumbEl.getWidth();
25622 this.baseScale = height / this.imageEl.OriginHeight;
25624 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25625 width = this.thumbEl.getHeight();
25626 this.baseScale = width / this.imageEl.OriginWidth;
25633 width = this.thumbEl.getWidth();
25634 this.baseScale = width / this.imageEl.OriginWidth;
25636 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25637 height = this.thumbEl.getHeight();
25638 this.baseScale = height / this.imageEl.OriginHeight;
25641 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25643 height = this.thumbEl.getHeight();
25644 this.baseScale = height / this.imageEl.OriginHeight;
25646 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25647 width = this.thumbEl.getWidth();
25648 this.baseScale = width / this.imageEl.OriginWidth;
25656 getScaleLevel : function()
25658 return this.baseScale * Math.pow(1.1, this.scale);
25661 onTouchStart : function(e)
25663 if(!this.canvasLoaded){
25664 this.beforeSelectFile(e);
25668 var touches = e.browserEvent.touches;
25674 if(touches.length == 1){
25675 this.onMouseDown(e);
25679 if(touches.length != 2){
25685 for(var i = 0, finger; finger = touches[i]; i++){
25686 coords.push(finger.pageX, finger.pageY);
25689 var x = Math.pow(coords[0] - coords[2], 2);
25690 var y = Math.pow(coords[1] - coords[3], 2);
25692 this.startDistance = Math.sqrt(x + y);
25694 this.startScale = this.scale;
25696 this.pinching = true;
25697 this.dragable = false;
25701 onTouchMove : function(e)
25703 if(!this.pinching && !this.dragable){
25707 var touches = e.browserEvent.touches;
25714 this.onMouseMove(e);
25720 for(var i = 0, finger; finger = touches[i]; i++){
25721 coords.push(finger.pageX, finger.pageY);
25724 var x = Math.pow(coords[0] - coords[2], 2);
25725 var y = Math.pow(coords[1] - coords[3], 2);
25727 this.endDistance = Math.sqrt(x + y);
25729 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25731 if(!this.zoomable()){
25732 this.scale = this.startScale;
25740 onTouchEnd : function(e)
25742 this.pinching = false;
25743 this.dragable = false;
25747 process : function(file, crop)
25750 this.maskEl.mask(this.loadingText);
25753 this.xhr = new XMLHttpRequest();
25755 file.xhr = this.xhr;
25757 this.xhr.open(this.method, this.url, true);
25760 "Accept": "application/json",
25761 "Cache-Control": "no-cache",
25762 "X-Requested-With": "XMLHttpRequest"
25765 for (var headerName in headers) {
25766 var headerValue = headers[headerName];
25768 this.xhr.setRequestHeader(headerName, headerValue);
25774 this.xhr.onload = function()
25776 _this.xhrOnLoad(_this.xhr);
25779 this.xhr.onerror = function()
25781 _this.xhrOnError(_this.xhr);
25784 var formData = new FormData();
25786 formData.append('returnHTML', 'NO');
25789 formData.append('crop', crop);
25792 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25793 formData.append(this.paramName, file, file.name);
25796 if(typeof(file.filename) != 'undefined'){
25797 formData.append('filename', file.filename);
25800 if(typeof(file.mimetype) != 'undefined'){
25801 formData.append('mimetype', file.mimetype);
25804 if(this.fireEvent('arrange', this, formData) != false){
25805 this.xhr.send(formData);
25809 xhrOnLoad : function(xhr)
25812 this.maskEl.unmask();
25815 if (xhr.readyState !== 4) {
25816 this.fireEvent('exception', this, xhr);
25820 var response = Roo.decode(xhr.responseText);
25822 if(!response.success){
25823 this.fireEvent('exception', this, xhr);
25827 var response = Roo.decode(xhr.responseText);
25829 this.fireEvent('upload', this, response);
25833 xhrOnError : function()
25836 this.maskEl.unmask();
25839 Roo.log('xhr on error');
25841 var response = Roo.decode(xhr.responseText);
25847 prepare : function(file)
25850 this.maskEl.mask(this.loadingText);
25856 if(typeof(file) === 'string'){
25857 this.loadCanvas(file);
25861 if(!file || !this.urlAPI){
25866 this.cropType = file.type;
25870 if(this.fireEvent('prepare', this, this.file) != false){
25872 var reader = new FileReader();
25874 reader.onload = function (e) {
25875 if (e.target.error) {
25876 Roo.log(e.target.error);
25880 var buffer = e.target.result,
25881 dataView = new DataView(buffer),
25883 maxOffset = dataView.byteLength - 4,
25887 if (dataView.getUint16(0) === 0xffd8) {
25888 while (offset < maxOffset) {
25889 markerBytes = dataView.getUint16(offset);
25891 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25892 markerLength = dataView.getUint16(offset + 2) + 2;
25893 if (offset + markerLength > dataView.byteLength) {
25894 Roo.log('Invalid meta data: Invalid segment size.');
25898 if(markerBytes == 0xffe1){
25899 _this.parseExifData(
25906 offset += markerLength;
25916 var url = _this.urlAPI.createObjectURL(_this.file);
25918 _this.loadCanvas(url);
25923 reader.readAsArrayBuffer(this.file);
25929 parseExifData : function(dataView, offset, length)
25931 var tiffOffset = offset + 10,
25935 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25936 // No Exif data, might be XMP data instead
25940 // Check for the ASCII code for "Exif" (0x45786966):
25941 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25942 // No Exif data, might be XMP data instead
25945 if (tiffOffset + 8 > dataView.byteLength) {
25946 Roo.log('Invalid Exif data: Invalid segment size.');
25949 // Check for the two null bytes:
25950 if (dataView.getUint16(offset + 8) !== 0x0000) {
25951 Roo.log('Invalid Exif data: Missing byte alignment offset.');
25954 // Check the byte alignment:
25955 switch (dataView.getUint16(tiffOffset)) {
25957 littleEndian = true;
25960 littleEndian = false;
25963 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25966 // Check for the TIFF tag marker (0x002A):
25967 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25968 Roo.log('Invalid Exif data: Missing TIFF marker.');
25971 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25972 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25974 this.parseExifTags(
25977 tiffOffset + dirOffset,
25982 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25987 if (dirOffset + 6 > dataView.byteLength) {
25988 Roo.log('Invalid Exif data: Invalid directory offset.');
25991 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25992 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25993 if (dirEndOffset + 4 > dataView.byteLength) {
25994 Roo.log('Invalid Exif data: Invalid directory size.');
25997 for (i = 0; i < tagsNumber; i += 1) {
26001 dirOffset + 2 + 12 * i, // tag offset
26005 // Return the offset to the next directory:
26006 return dataView.getUint32(dirEndOffset, littleEndian);
26009 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
26011 var tag = dataView.getUint16(offset, littleEndian);
26013 this.exif[tag] = this.getExifValue(
26017 dataView.getUint16(offset + 2, littleEndian), // tag type
26018 dataView.getUint32(offset + 4, littleEndian), // tag length
26023 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26025 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26034 Roo.log('Invalid Exif data: Invalid tag type.');
26038 tagSize = tagType.size * length;
26039 // Determine if the value is contained in the dataOffset bytes,
26040 // or if the value at the dataOffset is a pointer to the actual data:
26041 dataOffset = tagSize > 4 ?
26042 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26043 if (dataOffset + tagSize > dataView.byteLength) {
26044 Roo.log('Invalid Exif data: Invalid data offset.');
26047 if (length === 1) {
26048 return tagType.getValue(dataView, dataOffset, littleEndian);
26051 for (i = 0; i < length; i += 1) {
26052 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26055 if (tagType.ascii) {
26057 // Concatenate the chars:
26058 for (i = 0; i < values.length; i += 1) {
26060 // Ignore the terminating NULL byte(s):
26061 if (c === '\u0000') {
26073 Roo.apply(Roo.bootstrap.UploadCropbox, {
26075 'Orientation': 0x0112
26079 1: 0, //'top-left',
26081 3: 180, //'bottom-right',
26082 // 4: 'bottom-left',
26084 6: 90, //'right-top',
26085 // 7: 'right-bottom',
26086 8: 270 //'left-bottom'
26090 // byte, 8-bit unsigned int:
26092 getValue: function (dataView, dataOffset) {
26093 return dataView.getUint8(dataOffset);
26097 // ascii, 8-bit byte:
26099 getValue: function (dataView, dataOffset) {
26100 return String.fromCharCode(dataView.getUint8(dataOffset));
26105 // short, 16 bit int:
26107 getValue: function (dataView, dataOffset, littleEndian) {
26108 return dataView.getUint16(dataOffset, littleEndian);
26112 // long, 32 bit int:
26114 getValue: function (dataView, dataOffset, littleEndian) {
26115 return dataView.getUint32(dataOffset, littleEndian);
26119 // rational = two long values, first is numerator, second is denominator:
26121 getValue: function (dataView, dataOffset, littleEndian) {
26122 return dataView.getUint32(dataOffset, littleEndian) /
26123 dataView.getUint32(dataOffset + 4, littleEndian);
26127 // slong, 32 bit signed int:
26129 getValue: function (dataView, dataOffset, littleEndian) {
26130 return dataView.getInt32(dataOffset, littleEndian);
26134 // srational, two slongs, first is numerator, second is denominator:
26136 getValue: function (dataView, dataOffset, littleEndian) {
26137 return dataView.getInt32(dataOffset, littleEndian) /
26138 dataView.getInt32(dataOffset + 4, littleEndian);
26148 cls : 'btn-group roo-upload-cropbox-rotate-left',
26149 action : 'rotate-left',
26153 cls : 'btn btn-default',
26154 html : '<i class="fa fa-undo"></i>'
26160 cls : 'btn-group roo-upload-cropbox-picture',
26161 action : 'picture',
26165 cls : 'btn btn-default',
26166 html : '<i class="fa fa-picture-o"></i>'
26172 cls : 'btn-group roo-upload-cropbox-rotate-right',
26173 action : 'rotate-right',
26177 cls : 'btn btn-default',
26178 html : '<i class="fa fa-repeat"></i>'
26186 cls : 'btn-group roo-upload-cropbox-rotate-left',
26187 action : 'rotate-left',
26191 cls : 'btn btn-default',
26192 html : '<i class="fa fa-undo"></i>'
26198 cls : 'btn-group roo-upload-cropbox-download',
26199 action : 'download',
26203 cls : 'btn btn-default',
26204 html : '<i class="fa fa-download"></i>'
26210 cls : 'btn-group roo-upload-cropbox-crop',
26215 cls : 'btn btn-default',
26216 html : '<i class="fa fa-crop"></i>'
26222 cls : 'btn-group roo-upload-cropbox-trash',
26227 cls : 'btn btn-default',
26228 html : '<i class="fa fa-trash"></i>'
26234 cls : 'btn-group roo-upload-cropbox-rotate-right',
26235 action : 'rotate-right',
26239 cls : 'btn btn-default',
26240 html : '<i class="fa fa-repeat"></i>'
26248 cls : 'btn-group roo-upload-cropbox-rotate-left',
26249 action : 'rotate-left',
26253 cls : 'btn btn-default',
26254 html : '<i class="fa fa-undo"></i>'
26260 cls : 'btn-group roo-upload-cropbox-rotate-right',
26261 action : 'rotate-right',
26265 cls : 'btn btn-default',
26266 html : '<i class="fa fa-repeat"></i>'
26279 * @class Roo.bootstrap.DocumentManager
26280 * @extends Roo.bootstrap.Component
26281 * Bootstrap DocumentManager class
26282 * @cfg {String} paramName default 'imageUpload'
26283 * @cfg {String} method default POST
26284 * @cfg {String} url action url
26285 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26286 * @cfg {Boolean} multiple multiple upload default true
26287 * @cfg {Number} thumbSize default 300
26288 * @cfg {String} fieldLabel
26289 * @cfg {Number} labelWidth default 4
26290 * @cfg {String} labelAlign (left|top) default left
26291 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26294 * Create a new DocumentManager
26295 * @param {Object} config The config object
26298 Roo.bootstrap.DocumentManager = function(config){
26299 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26304 * Fire when initial the DocumentManager
26305 * @param {Roo.bootstrap.DocumentManager} this
26310 * inspect selected file
26311 * @param {Roo.bootstrap.DocumentManager} this
26312 * @param {File} file
26317 * Fire when xhr load exception
26318 * @param {Roo.bootstrap.DocumentManager} this
26319 * @param {XMLHttpRequest} xhr
26321 "exception" : true,
26324 * prepare the form data
26325 * @param {Roo.bootstrap.DocumentManager} this
26326 * @param {Object} formData
26331 * Fire when remove the file
26332 * @param {Roo.bootstrap.DocumentManager} this
26333 * @param {Object} file
26338 * Fire after refresh the file
26339 * @param {Roo.bootstrap.DocumentManager} this
26344 * Fire after click the image
26345 * @param {Roo.bootstrap.DocumentManager} this
26346 * @param {Object} file
26351 * Fire when upload a image and editable set to true
26352 * @param {Roo.bootstrap.DocumentManager} this
26353 * @param {Object} file
26357 * @event beforeselectfile
26358 * Fire before select file
26359 * @param {Roo.bootstrap.DocumentManager} this
26361 "beforeselectfile" : true,
26364 * Fire before process file
26365 * @param {Roo.bootstrap.DocumentManager} this
26366 * @param {Object} file
26373 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
26382 paramName : 'imageUpload',
26385 labelAlign : 'left',
26392 getAutoCreate : function()
26394 var managerWidget = {
26396 cls : 'roo-document-manager',
26400 cls : 'roo-document-manager-selector',
26405 cls : 'roo-document-manager-uploader',
26409 cls : 'roo-document-manager-upload-btn',
26410 html : '<i class="fa fa-plus"></i>'
26421 cls : 'column col-md-12',
26426 if(this.fieldLabel.length){
26431 cls : 'column col-md-12',
26432 html : this.fieldLabel
26436 cls : 'column col-md-12',
26441 if(this.labelAlign == 'left'){
26445 cls : 'column col-md-' + this.labelWidth,
26446 html : this.fieldLabel
26450 cls : 'column col-md-' + (12 - this.labelWidth),
26460 cls : 'row clearfix',
26468 initEvents : function()
26470 this.managerEl = this.el.select('.roo-document-manager', true).first();
26471 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26473 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26474 this.selectorEl.hide();
26477 this.selectorEl.attr('multiple', 'multiple');
26480 this.selectorEl.on('change', this.onFileSelected, this);
26482 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26483 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26485 this.uploader.on('click', this.onUploaderClick, this);
26487 this.renderProgressDialog();
26491 window.addEventListener("resize", function() { _this.refresh(); } );
26493 this.fireEvent('initial', this);
26496 renderProgressDialog : function()
26500 this.progressDialog = new Roo.bootstrap.Modal({
26501 cls : 'roo-document-manager-progress-dialog',
26502 allow_close : false,
26512 btnclick : function() {
26513 _this.uploadCancel();
26519 this.progressDialog.render(Roo.get(document.body));
26521 this.progress = new Roo.bootstrap.Progress({
26522 cls : 'roo-document-manager-progress',
26527 this.progress.render(this.progressDialog.getChildContainer());
26529 this.progressBar = new Roo.bootstrap.ProgressBar({
26530 cls : 'roo-document-manager-progress-bar',
26533 aria_valuemax : 12,
26537 this.progressBar.render(this.progress.getChildContainer());
26540 onUploaderClick : function(e)
26542 e.preventDefault();
26544 if(this.fireEvent('beforeselectfile', this) != false){
26545 this.selectorEl.dom.click();
26550 onFileSelected : function(e)
26552 e.preventDefault();
26554 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26558 Roo.each(this.selectorEl.dom.files, function(file){
26559 if(this.fireEvent('inspect', this, file) != false){
26560 this.files.push(file);
26570 this.selectorEl.dom.value = '';
26572 if(!this.files.length){
26576 if(this.boxes > 0 && this.files.length > this.boxes){
26577 this.files = this.files.slice(0, this.boxes);
26580 this.uploader.show();
26582 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26583 this.uploader.hide();
26592 Roo.each(this.files, function(file){
26594 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26595 var f = this.renderPreview(file);
26600 if(file.type.indexOf('image') != -1){
26601 this.delegates.push(
26603 _this.process(file);
26604 }).createDelegate(this)
26612 _this.process(file);
26613 }).createDelegate(this)
26618 this.files = files;
26620 this.delegates = this.delegates.concat(docs);
26622 if(!this.delegates.length){
26627 this.progressBar.aria_valuemax = this.delegates.length;
26634 arrange : function()
26636 if(!this.delegates.length){
26637 this.progressDialog.hide();
26642 var delegate = this.delegates.shift();
26644 this.progressDialog.show();
26646 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26648 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26653 refresh : function()
26655 this.uploader.show();
26657 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26658 this.uploader.hide();
26661 Roo.isTouch ? this.closable(false) : this.closable(true);
26663 this.fireEvent('refresh', this);
26666 onRemove : function(e, el, o)
26668 e.preventDefault();
26670 this.fireEvent('remove', this, o);
26674 remove : function(o)
26678 Roo.each(this.files, function(file){
26679 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26688 this.files = files;
26695 Roo.each(this.files, function(file){
26700 file.target.remove();
26709 onClick : function(e, el, o)
26711 e.preventDefault();
26713 this.fireEvent('click', this, o);
26717 closable : function(closable)
26719 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26721 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26733 xhrOnLoad : function(xhr)
26735 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26739 if (xhr.readyState !== 4) {
26741 this.fireEvent('exception', this, xhr);
26745 var response = Roo.decode(xhr.responseText);
26747 if(!response.success){
26749 this.fireEvent('exception', this, xhr);
26753 var file = this.renderPreview(response.data);
26755 this.files.push(file);
26761 xhrOnError : function(xhr)
26763 Roo.log('xhr on error');
26765 var response = Roo.decode(xhr.responseText);
26772 process : function(file)
26774 if(this.fireEvent('process', this, file) !== false){
26775 if(this.editable && file.type.indexOf('image') != -1){
26776 this.fireEvent('edit', this, file);
26780 this.uploadStart(file, false);
26787 uploadStart : function(file, crop)
26789 this.xhr = new XMLHttpRequest();
26791 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26796 file.xhr = this.xhr;
26798 this.managerEl.createChild({
26800 cls : 'roo-document-manager-loading',
26804 tooltip : file.name,
26805 cls : 'roo-document-manager-thumb',
26806 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26812 this.xhr.open(this.method, this.url, true);
26815 "Accept": "application/json",
26816 "Cache-Control": "no-cache",
26817 "X-Requested-With": "XMLHttpRequest"
26820 for (var headerName in headers) {
26821 var headerValue = headers[headerName];
26823 this.xhr.setRequestHeader(headerName, headerValue);
26829 this.xhr.onload = function()
26831 _this.xhrOnLoad(_this.xhr);
26834 this.xhr.onerror = function()
26836 _this.xhrOnError(_this.xhr);
26839 var formData = new FormData();
26841 formData.append('returnHTML', 'NO');
26844 formData.append('crop', crop);
26847 formData.append(this.paramName, file, file.name);
26849 if(this.fireEvent('prepare', this, formData) != false){
26850 this.xhr.send(formData);
26854 uploadCancel : function()
26861 this.delegates = [];
26863 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26870 renderPreview : function(file)
26872 if(typeof(file.target) != 'undefined' && file.target){
26876 var previewEl = this.managerEl.createChild({
26878 cls : 'roo-document-manager-preview',
26882 tooltip : file.filename,
26883 cls : 'roo-document-manager-thumb',
26884 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26889 html : '<i class="fa fa-times-circle"></i>'
26894 var close = previewEl.select('button.close', true).first();
26896 close.on('click', this.onRemove, this, file);
26898 file.target = previewEl;
26900 var image = previewEl.select('img', true).first();
26904 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26906 image.on('click', this.onClick, this, file);
26912 onPreviewLoad : function(file, image)
26914 if(typeof(file.target) == 'undefined' || !file.target){
26918 var width = image.dom.naturalWidth || image.dom.width;
26919 var height = image.dom.naturalHeight || image.dom.height;
26921 if(width > height){
26922 file.target.addClass('wide');
26926 file.target.addClass('tall');
26931 uploadFromSource : function(file, crop)
26933 this.xhr = new XMLHttpRequest();
26935 this.managerEl.createChild({
26937 cls : 'roo-document-manager-loading',
26941 tooltip : file.name,
26942 cls : 'roo-document-manager-thumb',
26943 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26949 this.xhr.open(this.method, this.url, true);
26952 "Accept": "application/json",
26953 "Cache-Control": "no-cache",
26954 "X-Requested-With": "XMLHttpRequest"
26957 for (var headerName in headers) {
26958 var headerValue = headers[headerName];
26960 this.xhr.setRequestHeader(headerName, headerValue);
26966 this.xhr.onload = function()
26968 _this.xhrOnLoad(_this.xhr);
26971 this.xhr.onerror = function()
26973 _this.xhrOnError(_this.xhr);
26976 var formData = new FormData();
26978 formData.append('returnHTML', 'NO');
26980 formData.append('crop', crop);
26982 if(typeof(file.filename) != 'undefined'){
26983 formData.append('filename', file.filename);
26986 if(typeof(file.mimetype) != 'undefined'){
26987 formData.append('mimetype', file.mimetype);
26990 if(this.fireEvent('prepare', this, formData) != false){
26991 this.xhr.send(formData);
27001 * @class Roo.bootstrap.DocumentViewer
27002 * @extends Roo.bootstrap.Component
27003 * Bootstrap DocumentViewer class
27006 * Create a new DocumentViewer
27007 * @param {Object} config The config object
27010 Roo.bootstrap.DocumentViewer = function(config){
27011 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27016 * Fire after initEvent
27017 * @param {Roo.bootstrap.DocumentViewer} this
27023 * @param {Roo.bootstrap.DocumentViewer} this
27028 * Fire after trash button
27029 * @param {Roo.bootstrap.DocumentViewer} this
27036 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
27038 getAutoCreate : function()
27042 cls : 'roo-document-viewer',
27046 cls : 'roo-document-viewer-body',
27050 cls : 'roo-document-viewer-thumb',
27054 cls : 'roo-document-viewer-image'
27062 cls : 'roo-document-viewer-footer',
27065 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27073 cls : 'btn btn-default roo-document-viewer-trash',
27074 html : '<i class="fa fa-trash"></i>'
27087 initEvents : function()
27090 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27091 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27093 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27094 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27096 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27097 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27099 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27100 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27102 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27103 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27105 this.bodyEl.on('click', this.onClick, this);
27107 this.trashBtn.on('click', this.onTrash, this);
27111 initial : function()
27113 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27116 this.fireEvent('initial', this);
27120 onClick : function(e)
27122 e.preventDefault();
27124 this.fireEvent('click', this);
27127 onTrash : function(e)
27129 e.preventDefault();
27131 this.fireEvent('trash', this);
27143 * @class Roo.bootstrap.NavProgressBar
27144 * @extends Roo.bootstrap.Component
27145 * Bootstrap NavProgressBar class
27148 * Create a new nav progress bar
27149 * @param {Object} config The config object
27152 Roo.bootstrap.NavProgressBar = function(config){
27153 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27155 this.bullets = this.bullets || [];
27157 // Roo.bootstrap.NavProgressBar.register(this);
27161 * Fires when the active item changes
27162 * @param {Roo.bootstrap.NavProgressBar} this
27163 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27164 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
27171 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
27176 getAutoCreate : function()
27178 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27182 cls : 'roo-navigation-bar-group',
27186 cls : 'roo-navigation-top-bar'
27190 cls : 'roo-navigation-bullets-bar',
27194 cls : 'roo-navigation-bar'
27201 cls : 'roo-navigation-bottom-bar'
27211 initEvents: function()
27216 onRender : function(ct, position)
27218 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27220 if(this.bullets.length){
27221 Roo.each(this.bullets, function(b){
27230 addItem : function(cfg)
27232 var item = new Roo.bootstrap.NavProgressItem(cfg);
27234 item.parentId = this.id;
27235 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27238 var top = new Roo.bootstrap.Element({
27240 cls : 'roo-navigation-bar-text'
27243 var bottom = new Roo.bootstrap.Element({
27245 cls : 'roo-navigation-bar-text'
27248 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27249 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27251 var topText = new Roo.bootstrap.Element({
27253 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27256 var bottomText = new Roo.bootstrap.Element({
27258 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27261 topText.onRender(top.el, null);
27262 bottomText.onRender(bottom.el, null);
27265 item.bottomEl = bottom;
27268 this.barItems.push(item);
27273 getActive : function()
27275 var active = false;
27277 Roo.each(this.barItems, function(v){
27279 if (!v.isActive()) {
27291 setActiveItem : function(item)
27295 Roo.each(this.barItems, function(v){
27296 if (v.rid == item.rid) {
27300 if (v.isActive()) {
27301 v.setActive(false);
27306 item.setActive(true);
27308 this.fireEvent('changed', this, item, prev);
27311 getBarItem: function(rid)
27315 Roo.each(this.barItems, function(e) {
27316 if (e.rid != rid) {
27327 indexOfItem : function(item)
27331 Roo.each(this.barItems, function(v, i){
27333 if (v.rid != item.rid) {
27344 setActiveNext : function()
27346 var i = this.indexOfItem(this.getActive());
27348 if (i > this.barItems.length) {
27352 this.setActiveItem(this.barItems[i+1]);
27355 setActivePrev : function()
27357 var i = this.indexOfItem(this.getActive());
27363 this.setActiveItem(this.barItems[i-1]);
27366 format : function()
27368 if(!this.barItems.length){
27372 var width = 100 / this.barItems.length;
27374 Roo.each(this.barItems, function(i){
27375 i.el.setStyle('width', width + '%');
27376 i.topEl.el.setStyle('width', width + '%');
27377 i.bottomEl.el.setStyle('width', width + '%');
27386 * Nav Progress Item
27391 * @class Roo.bootstrap.NavProgressItem
27392 * @extends Roo.bootstrap.Component
27393 * Bootstrap NavProgressItem class
27394 * @cfg {String} rid the reference id
27395 * @cfg {Boolean} active (true|false) Is item active default false
27396 * @cfg {Boolean} disabled (true|false) Is item active default false
27397 * @cfg {String} html
27398 * @cfg {String} position (top|bottom) text position default bottom
27399 * @cfg {String} icon show icon instead of number
27402 * Create a new NavProgressItem
27403 * @param {Object} config The config object
27405 Roo.bootstrap.NavProgressItem = function(config){
27406 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27411 * The raw click event for the entire grid.
27412 * @param {Roo.bootstrap.NavProgressItem} this
27413 * @param {Roo.EventObject} e
27420 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
27426 position : 'bottom',
27429 getAutoCreate : function()
27431 var iconCls = 'roo-navigation-bar-item-icon';
27433 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27437 cls: 'roo-navigation-bar-item',
27447 cfg.cls += ' active';
27450 cfg.cls += ' disabled';
27456 disable : function()
27458 this.setDisabled(true);
27461 enable : function()
27463 this.setDisabled(false);
27466 initEvents: function()
27468 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27470 this.iconEl.on('click', this.onClick, this);
27473 onClick : function(e)
27475 e.preventDefault();
27481 if(this.fireEvent('click', this, e) === false){
27485 this.parent().setActiveItem(this);
27488 isActive: function ()
27490 return this.active;
27493 setActive : function(state)
27495 if(this.active == state){
27499 this.active = state;
27502 this.el.addClass('active');
27506 this.el.removeClass('active');
27511 setDisabled : function(state)
27513 if(this.disabled == state){
27517 this.disabled = state;
27520 this.el.addClass('disabled');
27524 this.el.removeClass('disabled');
27527 tooltipEl : function()
27529 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27542 * @class Roo.bootstrap.FieldLabel
27543 * @extends Roo.bootstrap.Component
27544 * Bootstrap FieldLabel class
27545 * @cfg {String} html contents of the element
27546 * @cfg {String} tag tag of the element default label
27547 * @cfg {String} cls class of the element
27548 * @cfg {String} target label target
27549 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27550 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27551 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27552 * @cfg {String} iconTooltip default "This field is required"
27555 * Create a new FieldLabel
27556 * @param {Object} config The config object
27559 Roo.bootstrap.FieldLabel = function(config){
27560 Roo.bootstrap.Element.superclass.constructor.call(this, config);
27565 * Fires after the field has been marked as invalid.
27566 * @param {Roo.form.FieldLabel} this
27567 * @param {String} msg The validation message
27572 * Fires after the field has been validated with no errors.
27573 * @param {Roo.form.FieldLabel} this
27579 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
27586 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27587 validClass : 'text-success fa fa-lg fa-check',
27588 iconTooltip : 'This field is required',
27590 getAutoCreate : function(){
27594 cls : 'roo-bootstrap-field-label ' + this.cls,
27600 tooltip : this.iconTooltip
27612 initEvents: function()
27614 Roo.bootstrap.Element.superclass.initEvents.call(this);
27616 this.iconEl = this.el.select('i', true).first();
27618 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27620 Roo.bootstrap.FieldLabel.register(this);
27624 * Mark this field as valid
27626 markValid : function()
27628 this.iconEl.show();
27630 this.iconEl.removeClass(this.invalidClass);
27632 this.iconEl.addClass(this.validClass);
27634 this.fireEvent('valid', this);
27638 * Mark this field as invalid
27639 * @param {String} msg The validation message
27641 markInvalid : function(msg)
27643 this.iconEl.show();
27645 this.iconEl.removeClass(this.validClass);
27647 this.iconEl.addClass(this.invalidClass);
27649 this.fireEvent('invalid', this, msg);
27655 Roo.apply(Roo.bootstrap.FieldLabel, {
27660 * register a FieldLabel Group
27661 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27663 register : function(label)
27665 if(this.groups.hasOwnProperty(label.target)){
27669 this.groups[label.target] = label;
27673 * fetch a FieldLabel Group based on the target
27674 * @param {string} target
27675 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27677 get: function(target) {
27678 if (typeof(this.groups[target]) == 'undefined') {
27682 return this.groups[target] ;
27691 * page DateSplitField.
27697 * @class Roo.bootstrap.DateSplitField
27698 * @extends Roo.bootstrap.Component
27699 * Bootstrap DateSplitField class
27700 * @cfg {string} fieldLabel - the label associated
27701 * @cfg {Number} labelWidth set the width of label (0-12)
27702 * @cfg {String} labelAlign (top|left)
27703 * @cfg {Boolean} dayAllowBlank (true|false) default false
27704 * @cfg {Boolean} monthAllowBlank (true|false) default false
27705 * @cfg {Boolean} yearAllowBlank (true|false) default false
27706 * @cfg {string} dayPlaceholder
27707 * @cfg {string} monthPlaceholder
27708 * @cfg {string} yearPlaceholder
27709 * @cfg {string} dayFormat default 'd'
27710 * @cfg {string} monthFormat default 'm'
27711 * @cfg {string} yearFormat default 'Y'
27715 * Create a new DateSplitField
27716 * @param {Object} config The config object
27719 Roo.bootstrap.DateSplitField = function(config){
27720 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27726 * getting the data of years
27727 * @param {Roo.bootstrap.DateSplitField} this
27728 * @param {Object} years
27733 * getting the data of days
27734 * @param {Roo.bootstrap.DateSplitField} this
27735 * @param {Object} days
27740 * Fires after the field has been marked as invalid.
27741 * @param {Roo.form.Field} this
27742 * @param {String} msg The validation message
27747 * Fires after the field has been validated with no errors.
27748 * @param {Roo.form.Field} this
27754 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
27757 labelAlign : 'top',
27759 dayAllowBlank : false,
27760 monthAllowBlank : false,
27761 yearAllowBlank : false,
27762 dayPlaceholder : '',
27763 monthPlaceholder : '',
27764 yearPlaceholder : '',
27768 isFormField : true,
27770 getAutoCreate : function()
27774 cls : 'row roo-date-split-field-group',
27779 cls : 'form-hidden-field roo-date-split-field-group-value',
27785 if(this.fieldLabel){
27788 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27792 html : this.fieldLabel
27798 Roo.each(['day', 'month', 'year'], function(t){
27801 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27808 inputEl: function ()
27810 return this.el.select('.roo-date-split-field-group-value', true).first();
27813 onRender : function(ct, position)
27817 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27819 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27821 this.dayField = new Roo.bootstrap.ComboBox({
27822 allowBlank : this.dayAllowBlank,
27823 alwaysQuery : true,
27824 displayField : 'value',
27827 forceSelection : true,
27829 placeholder : this.dayPlaceholder,
27830 selectOnFocus : true,
27831 tpl : '<div class="select2-result"><b>{value}</b></div>',
27832 triggerAction : 'all',
27834 valueField : 'value',
27835 store : new Roo.data.SimpleStore({
27836 data : (function() {
27838 _this.fireEvent('days', _this, days);
27841 fields : [ 'value' ]
27844 select : function (_self, record, index)
27846 _this.setValue(_this.getValue());
27851 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27853 this.monthField = new Roo.bootstrap.MonthField({
27854 after : '<i class=\"fa fa-calendar\"></i>',
27855 allowBlank : this.monthAllowBlank,
27856 placeholder : this.monthPlaceholder,
27859 render : function (_self)
27861 this.el.select('span.input-group-addon', true).first().on('click', function(e){
27862 e.preventDefault();
27866 select : function (_self, oldvalue, newvalue)
27868 _this.setValue(_this.getValue());
27873 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27875 this.yearField = new Roo.bootstrap.ComboBox({
27876 allowBlank : this.yearAllowBlank,
27877 alwaysQuery : true,
27878 displayField : 'value',
27881 forceSelection : true,
27883 placeholder : this.yearPlaceholder,
27884 selectOnFocus : true,
27885 tpl : '<div class="select2-result"><b>{value}</b></div>',
27886 triggerAction : 'all',
27888 valueField : 'value',
27889 store : new Roo.data.SimpleStore({
27890 data : (function() {
27892 _this.fireEvent('years', _this, years);
27895 fields : [ 'value' ]
27898 select : function (_self, record, index)
27900 _this.setValue(_this.getValue());
27905 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
27908 setValue : function(v, format)
27910 this.inputEl.dom.value = v;
27912 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
27914 var d = Date.parseDate(v, f);
27921 this.setDay(d.format(this.dayFormat));
27922 this.setMonth(d.format(this.monthFormat));
27923 this.setYear(d.format(this.yearFormat));
27930 setDay : function(v)
27932 this.dayField.setValue(v);
27933 this.inputEl.dom.value = this.getValue();
27938 setMonth : function(v)
27940 this.monthField.setValue(v, true);
27941 this.inputEl.dom.value = this.getValue();
27946 setYear : function(v)
27948 this.yearField.setValue(v);
27949 this.inputEl.dom.value = this.getValue();
27954 getDay : function()
27956 return this.dayField.getValue();
27959 getMonth : function()
27961 return this.monthField.getValue();
27964 getYear : function()
27966 return this.yearField.getValue();
27969 getValue : function()
27971 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
27973 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
27983 this.inputEl.dom.value = '';
27988 validate : function()
27990 var d = this.dayField.validate();
27991 var m = this.monthField.validate();
27992 var y = this.yearField.validate();
27997 (!this.dayAllowBlank && !d) ||
27998 (!this.monthAllowBlank && !m) ||
27999 (!this.yearAllowBlank && !y)
28004 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28013 this.markInvalid();
28018 markValid : function()
28021 var label = this.el.select('label', true).first();
28022 var icon = this.el.select('i.fa-star', true).first();
28028 this.fireEvent('valid', this);
28032 * Mark this field as invalid
28033 * @param {String} msg The validation message
28035 markInvalid : function(msg)
28038 var label = this.el.select('label', true).first();
28039 var icon = this.el.select('i.fa-star', true).first();
28041 if(label && !icon){
28042 this.el.select('.roo-date-split-field-label', true).createChild({
28044 cls : 'text-danger fa fa-lg fa-star',
28045 tooltip : 'This field is required',
28046 style : 'margin-right:5px;'
28050 this.fireEvent('invalid', this, msg);
28053 clearInvalid : function()
28055 var label = this.el.select('label', true).first();
28056 var icon = this.el.select('i.fa-star', true).first();
28062 this.fireEvent('valid', this);
28065 getName: function()