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
1865 * @cfg {bool} hidden if the menu should be hidden when rendered.
1869 * @param {Object} config The config object
1873 Roo.bootstrap.Menu = function(config){
1874 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1875 if (this.registerMenu && this.type != 'treeview') {
1876 Roo.bootstrap.MenuMgr.register(this);
1881 * Fires before this menu is displayed
1882 * @param {Roo.menu.Menu} this
1887 * Fires before this menu is hidden
1888 * @param {Roo.menu.Menu} this
1893 * Fires after this menu is displayed
1894 * @param {Roo.menu.Menu} this
1899 * Fires after this menu is hidden
1900 * @param {Roo.menu.Menu} this
1905 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1906 * @param {Roo.menu.Menu} this
1907 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1908 * @param {Roo.EventObject} e
1913 * Fires when the mouse is hovering over this menu
1914 * @param {Roo.menu.Menu} this
1915 * @param {Roo.EventObject} e
1916 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1921 * Fires when the mouse exits this menu
1922 * @param {Roo.menu.Menu} this
1923 * @param {Roo.EventObject} e
1924 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1929 * Fires when a menu item contained in this menu is clicked
1930 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1931 * @param {Roo.EventObject} e
1935 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1938 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1942 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1945 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1947 registerMenu : true,
1949 menuItems :false, // stores the menu items..
1956 getChildContainer : function() {
1960 getAutoCreate : function(){
1962 //if (['right'].indexOf(this.align)!==-1) {
1963 // cfg.cn[1].cls += ' pull-right'
1969 cls : 'dropdown-menu' ,
1970 style : 'z-index:1000'
1974 if (this.type === 'submenu') {
1975 cfg.cls = 'submenu active';
1977 if (this.type === 'treeview') {
1978 cfg.cls = 'treeview-menu';
1983 initEvents : function() {
1985 // Roo.log("ADD event");
1986 // Roo.log(this.triggerEl.dom);
1987 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1989 this.triggerEl.addClass('dropdown-toggle');
1993 this.el.on('touchstart' , this.onTouch, this);
1995 this.el.on('click' , this.onClick, this);
1997 this.el.on("mouseover", this.onMouseOver, this);
1998 this.el.on("mouseout", this.onMouseOut, this);
2002 findTargetItem : function(e)
2004 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2008 //Roo.log(t); Roo.log(t.id);
2010 //Roo.log(this.menuitems);
2011 return this.menuitems.get(t.id);
2013 //return this.items.get(t.menuItemId);
2019 onTouch : function(e)
2021 //e.stopEvent(); this make the user popdown broken
2025 onClick : function(e)
2027 Roo.log("menu.onClick");
2028 var t = this.findTargetItem(e);
2029 if(!t || t.isContainer){
2034 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2035 if(t == this.activeItem && t.shouldDeactivate(e)){
2036 this.activeItem.deactivate();
2037 delete this.activeItem;
2041 this.setActiveItem(t, true);
2049 Roo.log('pass click event');
2053 this.fireEvent("click", this, t, e);
2057 onMouseOver : function(e){
2058 var t = this.findTargetItem(e);
2061 // if(t.canActivate && !t.disabled){
2062 // this.setActiveItem(t, true);
2066 this.fireEvent("mouseover", this, e, t);
2068 isVisible : function(){
2069 return !this.hidden;
2071 onMouseOut : function(e){
2072 var t = this.findTargetItem(e);
2075 // if(t == this.activeItem && t.shouldDeactivate(e)){
2076 // this.activeItem.deactivate();
2077 // delete this.activeItem;
2080 this.fireEvent("mouseout", this, e, t);
2085 * Displays this menu relative to another element
2086 * @param {String/HTMLElement/Roo.Element} element The element to align to
2087 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2088 * the element (defaults to this.defaultAlign)
2089 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2091 show : function(el, pos, parentMenu){
2092 this.parentMenu = parentMenu;
2096 this.fireEvent("beforeshow", this);
2097 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2100 * Displays this menu at a specific xy position
2101 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2102 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2104 showAt : function(xy, parentMenu, /* private: */_e){
2105 this.parentMenu = parentMenu;
2110 this.fireEvent("beforeshow", this);
2111 //xy = this.el.adjustForConstraints(xy);
2115 this.hideMenuItems();
2116 this.hidden = false;
2117 this.triggerEl.addClass('open');
2119 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2120 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2125 this.fireEvent("show", this);
2131 this.doFocus.defer(50, this);
2135 doFocus : function(){
2137 this.focusEl.focus();
2142 * Hides this menu and optionally all parent menus
2143 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2145 hide : function(deep)
2148 this.hideMenuItems();
2149 if(this.el && this.isVisible()){
2150 this.fireEvent("beforehide", this);
2151 if(this.activeItem){
2152 this.activeItem.deactivate();
2153 this.activeItem = null;
2155 this.triggerEl.removeClass('open');;
2157 this.fireEvent("hide", this);
2159 if(deep === true && this.parentMenu){
2160 this.parentMenu.hide(true);
2164 onTriggerPress : function(e)
2167 Roo.log('trigger press');
2168 //Roo.log(e.getTarget());
2169 // Roo.log(this.triggerEl.dom);
2171 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2172 var pel = Roo.get(e.getTarget());
2173 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2178 if (this.isVisible()) {
2183 this.show(this.triggerEl, false, false);
2192 hideMenuItems : function()
2194 Roo.log("hide Menu Items");
2198 //$(backdrop).remove()
2199 this.el.select('.open',true).each(function(aa) {
2201 aa.removeClass('open');
2202 //var parent = getParent($(this))
2203 //var relatedTarget = { relatedTarget: this }
2205 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2206 //if (e.isDefaultPrevented()) return
2207 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2210 addxtypeChild : function (tree, cntr) {
2211 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2213 this.menuitems.add(comp);
2234 * @class Roo.bootstrap.MenuItem
2235 * @extends Roo.bootstrap.Component
2236 * Bootstrap MenuItem class
2237 * @cfg {String} html the menu label
2238 * @cfg {String} href the link
2239 * @cfg {Boolean} preventDefault do not trigger A href on clicks.
2240 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2241 * @cfg {Boolean} active used on sidebars to highlight active itesm
2242 * @cfg {String} fa favicon to show on left of menu item.
2246 * Create a new MenuItem
2247 * @param {Object} config The config object
2251 Roo.bootstrap.MenuItem = function(config){
2252 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2257 * The raw click event for the entire grid.
2258 * @param {Roo.bootstrap.MenuItem} this
2259 * @param {Roo.EventObject} e
2265 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2269 preventDefault: true,
2270 isContainer : false,
2274 getAutoCreate : function(){
2276 if(this.isContainer){
2279 cls: 'dropdown-menu-item'
2293 if (this.fa !== false) {
2296 cls : 'fa fa-' + this.fa
2305 cls: 'dropdown-menu-item',
2308 if (this.parent().type == 'treeview') {
2309 cfg.cls = 'treeview-menu';
2312 cfg.cls += ' active';
2317 anc.href = this.href || cfg.cn[0].href ;
2318 ctag.html = this.html || cfg.cn[0].html ;
2322 initEvents: function() {
2323 if (this.parent().type == 'treeview') {
2324 this.el.select('a').on('click', this.onClick, this);
2328 onClick : function(e)
2330 Roo.log('item on click ');
2331 //if(this.preventDefault){
2332 // e.preventDefault();
2334 //this.parent().hideMenuItems();
2336 this.fireEvent('click', this, e);
2355 * @class Roo.bootstrap.MenuSeparator
2356 * @extends Roo.bootstrap.Component
2357 * Bootstrap MenuSeparator class
2360 * Create a new MenuItem
2361 * @param {Object} config The config object
2365 Roo.bootstrap.MenuSeparator = function(config){
2366 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2369 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2371 getAutoCreate : function(){
2390 * @class Roo.bootstrap.Modal
2391 * @extends Roo.bootstrap.Component
2392 * Bootstrap Modal class
2393 * @cfg {String} title Title of dialog
2394 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2395 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2396 * @cfg {Boolean} specificTitle default false
2397 * @cfg {Array} buttons Array of buttons or standard button set..
2398 * @cfg {String} buttonPosition (left|right|center) default right
2399 * @cfg {Boolean} animate default true
2400 * @cfg {Boolean} allow_close default true
2403 * Create a new Modal Dialog
2404 * @param {Object} config The config object
2407 Roo.bootstrap.Modal = function(config){
2408 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2413 * The raw btnclick event for the button
2414 * @param {Roo.EventObject} e
2418 this.buttons = this.buttons || [];
2421 this.tmpl = Roo.factory(this.tmpl);
2426 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2428 title : 'test dialog',
2438 specificTitle: false,
2440 buttonPosition: 'right',
2454 onRender : function(ct, position)
2456 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2459 var cfg = Roo.apply({}, this.getAutoCreate());
2462 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2464 //if (!cfg.name.length) {
2468 cfg.cls += ' ' + this.cls;
2471 cfg.style = this.style;
2473 this.el = Roo.get(document.body).createChild(cfg, position);
2475 //var type = this.el.dom.type;
2478 if(this.tabIndex !== undefined){
2479 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2483 this.bodyEl = this.el.select('.modal-body',true).first();
2484 this.closeEl = this.el.select('.modal-header .close', true).first();
2485 this.footerEl = this.el.select('.modal-footer',true).first();
2486 this.titleEl = this.el.select('.modal-title',true).first();
2490 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2491 this.maskEl.enableDisplayMode("block");
2493 //this.el.addClass("x-dlg-modal");
2495 if (this.buttons.length) {
2496 Roo.each(this.buttons, function(bb) {
2497 var b = Roo.apply({}, bb);
2498 b.xns = b.xns || Roo.bootstrap;
2499 b.xtype = b.xtype || 'Button';
2500 if (typeof(b.listeners) == 'undefined') {
2501 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2504 var btn = Roo.factory(b);
2506 btn.onRender(this.el.select('.modal-footer div').first());
2510 // render the children.
2513 if(typeof(this.items) != 'undefined'){
2514 var items = this.items;
2517 for(var i =0;i < items.length;i++) {
2518 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2522 this.items = nitems;
2524 // where are these used - they used to be body/close/footer
2528 //this.el.addClass([this.fieldClass, this.cls]);
2532 getAutoCreate : function(){
2537 html : this.html || ''
2542 cls : 'modal-title',
2546 if(this.specificTitle){
2552 if (this.allow_close) {
2563 style : 'display: none',
2566 cls: "modal-dialog",
2569 cls : "modal-content",
2572 cls : 'modal-header',
2577 cls : 'modal-footer',
2581 cls: 'btn-' + this.buttonPosition
2598 modal.cls += ' fade';
2604 getChildContainer : function() {
2609 getButtonContainer : function() {
2610 return this.el.select('.modal-footer div',true).first();
2613 initEvents : function()
2615 if (this.allow_close) {
2616 this.closeEl.on('click', this.hide, this);
2621 window.addEventListener("resize", function() { _this.resize(); } );
2627 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2632 if (!this.rendered) {
2636 this.el.setStyle('display', 'block');
2638 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2641 this.el.addClass('in');
2644 this.el.addClass('in');
2648 // not sure how we can show data in here..
2650 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2653 Roo.get(document.body).addClass("x-body-masked");
2654 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2656 this.el.setStyle('zIndex', '10001');
2658 this.fireEvent('show', this);
2666 Roo.get(document.body).removeClass("x-body-masked");
2667 this.el.removeClass('in');
2668 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2670 if(this.animate){ // why
2672 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2674 this.el.setStyle('display', 'none');
2677 this.fireEvent('hide', this);
2680 addButton : function(str, cb)
2684 var b = Roo.apply({}, { html : str } );
2685 b.xns = b.xns || Roo.bootstrap;
2686 b.xtype = b.xtype || 'Button';
2687 if (typeof(b.listeners) == 'undefined') {
2688 b.listeners = { click : cb.createDelegate(this) };
2691 var btn = Roo.factory(b);
2693 btn.onRender(this.el.select('.modal-footer div').first());
2699 setDefaultButton : function(btn)
2701 //this.el.select('.modal-footer').()
2705 resizeTo: function(w,h)
2709 this.el.select('.modal-dialog',true).first().setWidth(w);
2710 if (this.diff === false) {
2711 this.diff = this.el.select('.modal-dialog',true).first().getHeight() - this.el.select('.modal-body',true).first().getHeight();
2714 this.el.select('.modal-body',true).first().setHeight(h-this.diff);
2718 setContentSize : function(w, h)
2722 onButtonClick: function(btn,e)
2725 this.fireEvent('btnclick', btn.name, e);
2728 * Set the title of the Dialog
2729 * @param {String} str new Title
2731 setTitle: function(str) {
2732 this.titleEl.dom.innerHTML = str;
2735 * Set the body of the Dialog
2736 * @param {String} str new Title
2738 setBody: function(str) {
2739 this.bodyEl.dom.innerHTML = str;
2742 * Set the body of the Dialog using the template
2743 * @param {Obj} data - apply this data to the template and replace the body contents.
2745 applyBody: function(obj)
2748 Roo.log("Error - using apply Body without a template");
2751 this.tmpl.overwrite(this.bodyEl, obj);
2757 Roo.apply(Roo.bootstrap.Modal, {
2759 * Button config that displays a single OK button
2768 * Button config that displays Yes and No buttons
2784 * Button config that displays OK and Cancel buttons
2799 * Button config that displays Yes, No and Cancel buttons
2822 * messagebox - can be used as a replace
2826 * @class Roo.MessageBox
2827 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2831 Roo.Msg.alert('Status', 'Changes saved successfully.');
2833 // Prompt for user data:
2834 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2836 // process text value...
2840 // Show a dialog using config options:
2842 title:'Save Changes?',
2843 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2844 buttons: Roo.Msg.YESNOCANCEL,
2851 Roo.bootstrap.MessageBox = function(){
2852 var dlg, opt, mask, waitTimer;
2853 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2854 var buttons, activeTextEl, bwidth;
2858 var handleButton = function(button){
2860 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2864 var handleHide = function(){
2866 dlg.el.removeClass(opt.cls);
2869 // Roo.TaskMgr.stop(waitTimer);
2870 // waitTimer = null;
2875 var updateButtons = function(b){
2878 buttons["ok"].hide();
2879 buttons["cancel"].hide();
2880 buttons["yes"].hide();
2881 buttons["no"].hide();
2882 //dlg.footer.dom.style.display = 'none';
2885 dlg.footerEl.dom.style.display = '';
2886 for(var k in buttons){
2887 if(typeof buttons[k] != "function"){
2890 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2891 width += buttons[k].el.getWidth()+15;
2901 var handleEsc = function(d, k, e){
2902 if(opt && opt.closable !== false){
2912 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2913 * @return {Roo.BasicDialog} The BasicDialog element
2915 getDialog : function(){
2917 dlg = new Roo.bootstrap.Modal( {
2920 //constraintoviewport:false,
2922 //collapsible : false,
2927 //buttonAlign:"center",
2928 closeClick : function(){
2929 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2932 handleButton("cancel");
2937 dlg.on("hide", handleHide);
2939 //dlg.addKeyListener(27, handleEsc);
2941 this.buttons = buttons;
2942 var bt = this.buttonText;
2943 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2944 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2945 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2946 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2948 bodyEl = dlg.bodyEl.createChild({
2950 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2951 '<textarea class="roo-mb-textarea"></textarea>' +
2952 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2954 msgEl = bodyEl.dom.firstChild;
2955 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2956 textboxEl.enableDisplayMode();
2957 textboxEl.addKeyListener([10,13], function(){
2958 if(dlg.isVisible() && opt && opt.buttons){
2961 }else if(opt.buttons.yes){
2962 handleButton("yes");
2966 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2967 textareaEl.enableDisplayMode();
2968 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2969 progressEl.enableDisplayMode();
2970 var pf = progressEl.dom.firstChild;
2972 pp = Roo.get(pf.firstChild);
2973 pp.setHeight(pf.offsetHeight);
2981 * Updates the message box body text
2982 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2983 * the XHTML-compliant non-breaking space character '&#160;')
2984 * @return {Roo.MessageBox} This message box
2986 updateText : function(text){
2987 if(!dlg.isVisible() && !opt.width){
2988 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2990 msgEl.innerHTML = text || ' ';
2992 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2993 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2995 Math.min(opt.width || cw , this.maxWidth),
2996 Math.max(opt.minWidth || this.minWidth, bwidth)
2999 activeTextEl.setWidth(w);
3001 if(dlg.isVisible()){
3002 dlg.fixedcenter = false;
3004 // to big, make it scroll. = But as usual stupid IE does not support
3007 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3008 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3009 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3011 bodyEl.dom.style.height = '';
3012 bodyEl.dom.style.overflowY = '';
3015 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3017 bodyEl.dom.style.overflowX = '';
3020 dlg.setContentSize(w, bodyEl.getHeight());
3021 if(dlg.isVisible()){
3022 dlg.fixedcenter = true;
3028 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3029 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3030 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3031 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3032 * @return {Roo.MessageBox} This message box
3034 updateProgress : function(value, text){
3036 this.updateText(text);
3038 if (pp) { // weird bug on my firefox - for some reason this is not defined
3039 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3045 * Returns true if the message box is currently displayed
3046 * @return {Boolean} True if the message box is visible, else false
3048 isVisible : function(){
3049 return dlg && dlg.isVisible();
3053 * Hides the message box if it is displayed
3056 if(this.isVisible()){
3062 * Displays a new message box, or reinitializes an existing message box, based on the config options
3063 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3064 * The following config object properties are supported:
3066 Property Type Description
3067 ---------- --------------- ------------------------------------------------------------------------------------
3068 animEl String/Element An id or Element from which the message box should animate as it opens and
3069 closes (defaults to undefined)
3070 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3071 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3072 closable Boolean False to hide the top-right close button (defaults to true). Note that
3073 progress and wait dialogs will ignore this property and always hide the
3074 close button as they can only be closed programmatically.
3075 cls String A custom CSS class to apply to the message box element
3076 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3077 displayed (defaults to 75)
3078 fn Function A callback function to execute after closing the dialog. The arguments to the
3079 function will be btn (the name of the button that was clicked, if applicable,
3080 e.g. "ok"), and text (the value of the active text field, if applicable).
3081 Progress and wait dialogs will ignore this option since they do not respond to
3082 user actions and can only be closed programmatically, so any required function
3083 should be called by the same code after it closes the dialog.
3084 icon String A CSS class that provides a background image to be used as an icon for
3085 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3086 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3087 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3088 modal Boolean False to allow user interaction with the page while the message box is
3089 displayed (defaults to true)
3090 msg String A string that will replace the existing message box body text (defaults
3091 to the XHTML-compliant non-breaking space character ' ')
3092 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3093 progress Boolean True to display a progress bar (defaults to false)
3094 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3095 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3096 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3097 title String The title text
3098 value String The string value to set into the active textbox element if displayed
3099 wait Boolean True to display a progress bar (defaults to false)
3100 width Number The width of the dialog in pixels
3107 msg: 'Please enter your address:',
3109 buttons: Roo.MessageBox.OKCANCEL,
3112 animEl: 'addAddressBtn'
3115 * @param {Object} config Configuration options
3116 * @return {Roo.MessageBox} This message box
3118 show : function(options)
3121 // this causes nightmares if you show one dialog after another
3122 // especially on callbacks..
3124 if(this.isVisible()){
3127 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3128 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3129 Roo.log("New Dialog Message:" + options.msg )
3130 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3131 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3134 var d = this.getDialog();
3136 d.setTitle(opt.title || " ");
3137 d.closeEl.setDisplayed(opt.closable !== false);
3138 activeTextEl = textboxEl;
3139 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3144 textareaEl.setHeight(typeof opt.multiline == "number" ?
3145 opt.multiline : this.defaultTextHeight);
3146 activeTextEl = textareaEl;
3155 progressEl.setDisplayed(opt.progress === true);
3156 this.updateProgress(0);
3157 activeTextEl.dom.value = opt.value || "";
3159 dlg.setDefaultButton(activeTextEl);
3161 var bs = opt.buttons;
3165 }else if(bs && bs.yes){
3166 db = buttons["yes"];
3168 dlg.setDefaultButton(db);
3170 bwidth = updateButtons(opt.buttons);
3171 this.updateText(opt.msg);
3173 d.el.addClass(opt.cls);
3175 d.proxyDrag = opt.proxyDrag === true;
3176 d.modal = opt.modal !== false;
3177 d.mask = opt.modal !== false ? mask : false;
3179 // force it to the end of the z-index stack so it gets a cursor in FF
3180 document.body.appendChild(dlg.el.dom);
3181 d.animateTarget = null;
3182 d.show(options.animEl);
3188 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3189 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3190 * and closing the message box when the process is complete.
3191 * @param {String} title The title bar text
3192 * @param {String} msg The message box body text
3193 * @return {Roo.MessageBox} This message box
3195 progress : function(title, msg){
3202 minWidth: this.minProgressWidth,
3209 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3210 * If a callback function is passed it will be called after the user clicks the button, and the
3211 * id of the button that was clicked will be passed as the only parameter to the callback
3212 * (could also be the top-right close button).
3213 * @param {String} title The title bar text
3214 * @param {String} msg The message box body text
3215 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3216 * @param {Object} scope (optional) The scope of the callback function
3217 * @return {Roo.MessageBox} This message box
3219 alert : function(title, msg, fn, scope){
3232 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3233 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3234 * You are responsible for closing the message box when the process is complete.
3235 * @param {String} msg The message box body text
3236 * @param {String} title (optional) The title bar text
3237 * @return {Roo.MessageBox} This message box
3239 wait : function(msg, title){
3250 waitTimer = Roo.TaskMgr.start({
3252 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3260 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3261 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3262 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3263 * @param {String} title The title bar text
3264 * @param {String} msg The message box body text
3265 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3266 * @param {Object} scope (optional) The scope of the callback function
3267 * @return {Roo.MessageBox} This message box
3269 confirm : function(title, msg, fn, scope){
3273 buttons: this.YESNO,
3282 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3283 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3284 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3285 * (could also be the top-right close button) and the text that was entered will be passed as the two
3286 * parameters to the callback.
3287 * @param {String} title The title bar text
3288 * @param {String} msg The message box body text
3289 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3290 * @param {Object} scope (optional) The scope of the callback function
3291 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3292 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3293 * @return {Roo.MessageBox} This message box
3295 prompt : function(title, msg, fn, scope, multiline){
3299 buttons: this.OKCANCEL,
3304 multiline: multiline,
3311 * Button config that displays a single OK button
3316 * Button config that displays Yes and No buttons
3319 YESNO : {yes:true, no:true},
3321 * Button config that displays OK and Cancel buttons
3324 OKCANCEL : {ok:true, cancel:true},
3326 * Button config that displays Yes, No and Cancel buttons
3329 YESNOCANCEL : {yes:true, no:true, cancel:true},
3332 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3335 defaultTextHeight : 75,
3337 * The maximum width in pixels of the message box (defaults to 600)
3342 * The minimum width in pixels of the message box (defaults to 100)
3347 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3348 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3351 minProgressWidth : 250,
3353 * An object containing the default button text strings that can be overriden for localized language support.
3354 * Supported properties are: ok, cancel, yes and no.
3355 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3368 * Shorthand for {@link Roo.MessageBox}
3370 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3371 Roo.Msg = Roo.Msg || Roo.MessageBox;
3380 * @class Roo.bootstrap.Navbar
3381 * @extends Roo.bootstrap.Component
3382 * Bootstrap Navbar class
3385 * Create a new Navbar
3386 * @param {Object} config The config object
3390 Roo.bootstrap.Navbar = function(config){
3391 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3395 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3404 getAutoCreate : function(){
3407 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3411 initEvents :function ()
3413 //Roo.log(this.el.select('.navbar-toggle',true));
3414 this.el.select('.navbar-toggle',true).on('click', function() {
3415 // Roo.log('click');
3416 this.el.select('.navbar-collapse',true).toggleClass('in');
3424 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3426 var size = this.el.getSize();
3427 this.maskEl.setSize(size.width, size.height);
3428 this.maskEl.enableDisplayMode("block");
3437 getChildContainer : function()
3439 if (this.el.select('.collapse').getCount()) {
3440 return this.el.select('.collapse',true).first();
3473 * @class Roo.bootstrap.NavSimplebar
3474 * @extends Roo.bootstrap.Navbar
3475 * Bootstrap Sidebar class
3477 * @cfg {Boolean} inverse is inverted color
3479 * @cfg {String} type (nav | pills | tabs)
3480 * @cfg {Boolean} arrangement stacked | justified
3481 * @cfg {String} align (left | right) alignment
3483 * @cfg {Boolean} main (true|false) main nav bar? default false
3484 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3486 * @cfg {String} tag (header|footer|nav|div) default is nav
3492 * Create a new Sidebar
3493 * @param {Object} config The config object
3497 Roo.bootstrap.NavSimplebar = function(config){
3498 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3501 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3517 getAutoCreate : function(){
3521 tag : this.tag || 'div',
3534 this.type = this.type || 'nav';
3535 if (['tabs','pills'].indexOf(this.type)!==-1) {
3536 cfg.cn[0].cls += ' nav-' + this.type
3540 if (this.type!=='nav') {
3541 Roo.log('nav type must be nav/tabs/pills')
3543 cfg.cn[0].cls += ' navbar-nav'
3549 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3550 cfg.cn[0].cls += ' nav-' + this.arrangement;
3554 if (this.align === 'right') {
3555 cfg.cn[0].cls += ' navbar-right';
3559 cfg.cls += ' navbar-inverse';
3586 * @class Roo.bootstrap.NavHeaderbar
3587 * @extends Roo.bootstrap.NavSimplebar
3588 * Bootstrap Sidebar class
3590 * @cfg {String} brand what is brand
3591 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3592 * @cfg {String} brand_href href of the brand
3593 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3594 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3595 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3596 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3599 * Create a new Sidebar
3600 * @param {Object} config The config object
3604 Roo.bootstrap.NavHeaderbar = function(config){
3605 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3609 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3616 desktopCenter : false,
3619 getAutoCreate : function(){
3622 tag: this.nav || 'nav',
3629 if (this.desktopCenter) {
3630 cn.push({cls : 'container', cn : []});
3637 cls: 'navbar-header',
3642 cls: 'navbar-toggle',
3643 'data-toggle': 'collapse',
3648 html: 'Toggle navigation'
3670 cls: 'collapse navbar-collapse',
3674 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3676 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3677 cfg.cls += ' navbar-' + this.position;
3679 // tag can override this..
3681 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3684 if (this.brand !== '') {
3687 href: this.brand_href ? this.brand_href : '#',
3688 cls: 'navbar-brand',
3696 cfg.cls += ' main-nav';
3704 getHeaderChildContainer : function()
3706 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3707 return this.el.select('.navbar-header',true).first();
3710 return this.getChildContainer();
3714 initEvents : function()
3716 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3718 if (this.autohide) {
3723 Roo.get(document).on('scroll',function(e) {
3724 var ns = Roo.get(document).getScroll().top;
3725 var os = prevScroll;
3729 ft.removeClass('slideDown');
3730 ft.addClass('slideUp');
3733 ft.removeClass('slideUp');
3734 ft.addClass('slideDown');
3755 * @class Roo.bootstrap.NavSidebar
3756 * @extends Roo.bootstrap.Navbar
3757 * Bootstrap Sidebar class
3760 * Create a new Sidebar
3761 * @param {Object} config The config object
3765 Roo.bootstrap.NavSidebar = function(config){
3766 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3769 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3771 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3773 getAutoCreate : function(){
3778 cls: 'sidebar sidebar-nav'
3800 * @class Roo.bootstrap.NavGroup
3801 * @extends Roo.bootstrap.Component
3802 * Bootstrap NavGroup class
3803 * @cfg {String} align (left|right)
3804 * @cfg {Boolean} inverse
3805 * @cfg {String} type (nav|pills|tab) default nav
3806 * @cfg {String} navId - reference Id for navbar.
3810 * Create a new nav group
3811 * @param {Object} config The config object
3814 Roo.bootstrap.NavGroup = function(config){
3815 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3818 Roo.bootstrap.NavGroup.register(this);
3822 * Fires when the active item changes
3823 * @param {Roo.bootstrap.NavGroup} this
3824 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3825 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3832 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3843 getAutoCreate : function()
3845 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3852 if (['tabs','pills'].indexOf(this.type)!==-1) {
3853 cfg.cls += ' nav-' + this.type
3855 if (this.type!=='nav') {
3856 Roo.log('nav type must be nav/tabs/pills')
3858 cfg.cls += ' navbar-nav'
3861 if (this.parent().sidebar) {
3864 cls: 'dashboard-menu sidebar-menu'
3870 if (this.form === true) {
3876 if (this.align === 'right') {
3877 cfg.cls += ' navbar-right';
3879 cfg.cls += ' navbar-left';
3883 if (this.align === 'right') {
3884 cfg.cls += ' navbar-right';
3888 cfg.cls += ' navbar-inverse';
3896 * sets the active Navigation item
3897 * @param {Roo.bootstrap.NavItem} the new current navitem
3899 setActiveItem : function(item)
3902 Roo.each(this.navItems, function(v){
3907 v.setActive(false, true);
3914 item.setActive(true, true);
3915 this.fireEvent('changed', this, item, prev);
3920 * gets the active Navigation item
3921 * @return {Roo.bootstrap.NavItem} the current navitem
3923 getActive : function()
3927 Roo.each(this.navItems, function(v){
3938 indexOfNav : function()
3942 Roo.each(this.navItems, function(v,i){
3953 * adds a Navigation item
3954 * @param {Roo.bootstrap.NavItem} the navitem to add
3956 addItem : function(cfg)
3958 var cn = new Roo.bootstrap.NavItem(cfg);
3960 cn.parentId = this.id;
3961 cn.onRender(this.el, null);
3965 * register a Navigation item
3966 * @param {Roo.bootstrap.NavItem} the navitem to add
3968 register : function(item)
3970 this.navItems.push( item);
3971 item.navId = this.navId;
3976 * clear all the Navigation item
3979 clearAll : function()
3982 this.el.dom.innerHTML = '';
3985 getNavItem: function(tabId)
3988 Roo.each(this.navItems, function(e) {
3989 if (e.tabId == tabId) {
3999 setActiveNext : function()
4001 var i = this.indexOfNav(this.getActive());
4002 if (i > this.navItems.length) {
4005 this.setActiveItem(this.navItems[i+1]);
4007 setActivePrev : function()
4009 var i = this.indexOfNav(this.getActive());
4013 this.setActiveItem(this.navItems[i-1]);
4015 clearWasActive : function(except) {
4016 Roo.each(this.navItems, function(e) {
4017 if (e.tabId != except.tabId && e.was_active) {
4018 e.was_active = false;
4025 getWasActive : function ()
4028 Roo.each(this.navItems, function(e) {
4043 Roo.apply(Roo.bootstrap.NavGroup, {
4047 * register a Navigation Group
4048 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4050 register : function(navgrp)
4052 this.groups[navgrp.navId] = navgrp;
4056 * fetch a Navigation Group based on the navigation ID
4057 * @param {string} the navgroup to add
4058 * @returns {Roo.bootstrap.NavGroup} the navgroup
4060 get: function(navId) {
4061 if (typeof(this.groups[navId]) == 'undefined') {
4063 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4065 return this.groups[navId] ;
4080 * @class Roo.bootstrap.NavItem
4081 * @extends Roo.bootstrap.Component
4082 * Bootstrap Navbar.NavItem class
4083 * @cfg {String} href link to
4084 * @cfg {String} html content of button
4085 * @cfg {String} badge text inside badge
4086 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4087 * @cfg {String} glyphicon name of glyphicon
4088 * @cfg {String} icon name of font awesome icon
4089 * @cfg {Boolean} active Is item active
4090 * @cfg {Boolean} disabled Is item disabled
4092 * @cfg {Boolean} preventDefault (true | false) default false
4093 * @cfg {String} tabId the tab that this item activates.
4094 * @cfg {String} tagtype (a|span) render as a href or span?
4095 * @cfg {Boolean} animateRef (true|false) link to element default false
4098 * Create a new Navbar Item
4099 * @param {Object} config The config object
4101 Roo.bootstrap.NavItem = function(config){
4102 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4107 * The raw click event for the entire grid.
4108 * @param {Roo.EventObject} e
4113 * Fires when the active item active state changes
4114 * @param {Roo.bootstrap.NavItem} this
4115 * @param {boolean} state the new state
4121 * Fires when scroll to element
4122 * @param {Roo.bootstrap.NavItem} this
4123 * @param {Object} options
4124 * @param {Roo.EventObject} e
4132 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4140 preventDefault : false,
4147 getAutoCreate : function(){
4156 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4158 if (this.disabled) {
4159 cfg.cls += ' disabled';
4162 if (this.href || this.html || this.glyphicon || this.icon) {
4166 href : this.href || "#",
4167 html: this.html || ''
4172 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4175 if(this.glyphicon) {
4176 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4181 cfg.cn[0].html += " <span class='caret'></span>";
4185 if (this.badge !== '') {
4187 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4195 initEvents: function()
4197 if (typeof (this.menu) != 'undefined') {
4198 this.menu.parentType = this.xtype;
4199 this.menu.triggerEl = this.el;
4200 this.menu = this.addxtype(Roo.apply({}, this.menu));
4203 this.el.select('a',true).on('click', this.onClick, this);
4205 if(this.tagtype == 'span'){
4206 this.el.select('span',true).on('click', this.onClick, this);
4209 // at this point parent should be available..
4210 this.parent().register(this);
4213 onClick : function(e)
4216 this.preventDefault ||
4223 if (this.disabled) {
4227 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4228 if (tg && tg.transition) {
4229 Roo.log("waiting for the transitionend");
4235 //Roo.log("fire event clicked");
4236 if(this.fireEvent('click', this, e) === false){
4240 if(this.tagtype == 'span'){
4244 //Roo.log(this.href);
4245 var ael = this.el.select('a',true).first();
4248 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4249 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4250 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4251 return; // ignore... - it's a 'hash' to another page.
4255 this.scrollToElement(e);
4259 var p = this.parent();
4261 if (['tabs','pills'].indexOf(p.type)!==-1) {
4262 if (typeof(p.setActiveItem) !== 'undefined') {
4263 p.setActiveItem(this);
4267 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4268 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4269 // remove the collapsed menu expand...
4270 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4274 isActive: function () {
4277 setActive : function(state, fire, is_was_active)
4279 if (this.active && !state && this.navId) {
4280 this.was_active = true;
4281 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4283 nv.clearWasActive(this);
4287 this.active = state;
4290 this.el.removeClass('active');
4291 } else if (!this.el.hasClass('active')) {
4292 this.el.addClass('active');
4295 this.fireEvent('changed', this, state);
4298 // show a panel if it's registered and related..
4300 if (!this.navId || !this.tabId || !state || is_was_active) {
4304 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4308 var pan = tg.getPanelByName(this.tabId);
4312 // if we can not flip to new panel - go back to old nav highlight..
4313 if (false == tg.showPanel(pan)) {
4314 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4316 var onav = nv.getWasActive();
4318 onav.setActive(true, false, true);
4327 // this should not be here...
4328 setDisabled : function(state)
4330 this.disabled = state;
4332 this.el.removeClass('disabled');
4333 } else if (!this.el.hasClass('disabled')) {
4334 this.el.addClass('disabled');
4340 * Fetch the element to display the tooltip on.
4341 * @return {Roo.Element} defaults to this.el
4343 tooltipEl : function()
4345 return this.el.select('' + this.tagtype + '', true).first();
4348 scrollToElement : function(e)
4350 var c = document.body;
4353 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4355 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4356 c = document.documentElement;
4359 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4365 var o = target.calcOffsetsTo(c);
4372 this.fireEvent('scrollto', this, options, e);
4374 Roo.get(c).scrollTo('top', options.value, true);
4387 * <span> icon </span>
4388 * <span> text </span>
4389 * <span>badge </span>
4393 * @class Roo.bootstrap.NavSidebarItem
4394 * @extends Roo.bootstrap.NavItem
4395 * Bootstrap Navbar.NavSidebarItem class
4396 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4397 * {bool} open is the menu open
4399 * Create a new Navbar Button
4400 * @param {Object} config The config object
4402 Roo.bootstrap.NavSidebarItem = function(config){
4403 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4408 * The raw click event for the entire grid.
4409 * @param {Roo.EventObject} e
4414 * Fires when the active item active state changes
4415 * @param {Roo.bootstrap.NavSidebarItem} this
4416 * @param {boolean} state the new state
4424 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4426 badgeWeight : 'default',
4430 getAutoCreate : function(){
4435 href : this.href || '#',
4447 html : this.html || ''
4452 cfg.cls += ' active';
4455 if (this.disabled) {
4456 cfg.cls += ' disabled';
4459 cfg.cls += ' open x-open';
4462 if (this.glyphicon || this.icon) {
4463 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4464 a.cn.push({ tag : 'i', cls : c }) ;
4469 if (this.badge !== '') {
4471 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4475 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4476 a.cls += 'dropdown-toggle treeview' ;
4487 initEvents : function()
4489 if (typeof (this.menu) != 'undefined') {
4490 this.menu.parentType = this.xtype;
4491 this.menu.triggerEl = this.el;
4492 this.menu = this.addxtype(Roo.apply({}, this.menu));
4495 this.el.on('click', this.onClick, this);
4498 if(this.badge !== ''){
4500 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4505 onClick : function(e)
4512 if(this.preventDefault){
4516 this.fireEvent('click', this);
4519 disable : function()
4521 this.setDisabled(true);
4526 this.setDisabled(false);
4529 setDisabled : function(state)
4531 if(this.disabled == state){
4535 this.disabled = state;
4538 this.el.addClass('disabled');
4542 this.el.removeClass('disabled');
4547 setActive : function(state)
4549 if(this.active == state){
4553 this.active = state;
4556 this.el.addClass('active');
4560 this.el.removeClass('active');
4565 isActive: function ()
4570 setBadge : function(str)
4576 this.badgeEl.dom.innerHTML = str;
4593 * @class Roo.bootstrap.Row
4594 * @extends Roo.bootstrap.Component
4595 * Bootstrap Row class (contains columns...)
4599 * @param {Object} config The config object
4602 Roo.bootstrap.Row = function(config){
4603 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4606 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4608 getAutoCreate : function(){
4627 * @class Roo.bootstrap.Element
4628 * @extends Roo.bootstrap.Component
4629 * Bootstrap Element class
4630 * @cfg {String} html contents of the element
4631 * @cfg {String} tag tag of the element
4632 * @cfg {String} cls class of the element
4633 * @cfg {Boolean} preventDefault (true|false) default false
4634 * @cfg {Boolean} clickable (true|false) default false
4637 * Create a new Element
4638 * @param {Object} config The config object
4641 Roo.bootstrap.Element = function(config){
4642 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4648 * When a element is chick
4649 * @param {Roo.bootstrap.Element} this
4650 * @param {Roo.EventObject} e
4656 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4661 preventDefault: false,
4664 getAutoCreate : function(){
4675 initEvents: function()
4677 Roo.bootstrap.Element.superclass.initEvents.call(this);
4680 this.el.on('click', this.onClick, this);
4685 onClick : function(e)
4687 if(this.preventDefault){
4691 this.fireEvent('click', this, e);
4694 getValue : function()
4696 return this.el.dom.innerHTML;
4699 setValue : function(value)
4701 this.el.dom.innerHTML = value;
4716 * @class Roo.bootstrap.Pagination
4717 * @extends Roo.bootstrap.Component
4718 * Bootstrap Pagination class
4719 * @cfg {String} size xs | sm | md | lg
4720 * @cfg {Boolean} inverse false | true
4723 * Create a new Pagination
4724 * @param {Object} config The config object
4727 Roo.bootstrap.Pagination = function(config){
4728 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4731 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4737 getAutoCreate : function(){
4743 cfg.cls += ' inverse';
4749 cfg.cls += " " + this.cls;
4767 * @class Roo.bootstrap.PaginationItem
4768 * @extends Roo.bootstrap.Component
4769 * Bootstrap PaginationItem class
4770 * @cfg {String} html text
4771 * @cfg {String} href the link
4772 * @cfg {Boolean} preventDefault (true | false) default true
4773 * @cfg {Boolean} active (true | false) default false
4774 * @cfg {Boolean} disabled default false
4778 * Create a new PaginationItem
4779 * @param {Object} config The config object
4783 Roo.bootstrap.PaginationItem = function(config){
4784 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4789 * The raw click event for the entire grid.
4790 * @param {Roo.EventObject} e
4796 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4800 preventDefault: true,
4805 getAutoCreate : function(){
4811 href : this.href ? this.href : '#',
4812 html : this.html ? this.html : ''
4822 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4826 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4832 initEvents: function() {
4834 this.el.on('click', this.onClick, this);
4837 onClick : function(e)
4839 Roo.log('PaginationItem on click ');
4840 if(this.preventDefault){
4848 this.fireEvent('click', this, e);
4864 * @class Roo.bootstrap.Slider
4865 * @extends Roo.bootstrap.Component
4866 * Bootstrap Slider class
4869 * Create a new Slider
4870 * @param {Object} config The config object
4873 Roo.bootstrap.Slider = function(config){
4874 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4877 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4879 getAutoCreate : function(){
4883 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4887 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4899 * Ext JS Library 1.1.1
4900 * Copyright(c) 2006-2007, Ext JS, LLC.
4902 * Originally Released Under LGPL - original licence link has changed is not relivant.
4905 * <script type="text/javascript">
4910 * @class Roo.grid.ColumnModel
4911 * @extends Roo.util.Observable
4912 * This is the default implementation of a ColumnModel used by the Grid. It defines
4913 * the columns in the grid.
4916 var colModel = new Roo.grid.ColumnModel([
4917 {header: "Ticker", width: 60, sortable: true, locked: true},
4918 {header: "Company Name", width: 150, sortable: true},
4919 {header: "Market Cap.", width: 100, sortable: true},
4920 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4921 {header: "Employees", width: 100, sortable: true, resizable: false}
4926 * The config options listed for this class are options which may appear in each
4927 * individual column definition.
4928 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4930 * @param {Object} config An Array of column config objects. See this class's
4931 * config objects for details.
4933 Roo.grid.ColumnModel = function(config){
4935 * The config passed into the constructor
4937 this.config = config;
4940 // if no id, create one
4941 // if the column does not have a dataIndex mapping,
4942 // map it to the order it is in the config
4943 for(var i = 0, len = config.length; i < len; i++){
4945 if(typeof c.dataIndex == "undefined"){
4948 if(typeof c.renderer == "string"){
4949 c.renderer = Roo.util.Format[c.renderer];
4951 if(typeof c.id == "undefined"){
4954 if(c.editor && c.editor.xtype){
4955 c.editor = Roo.factory(c.editor, Roo.grid);
4957 if(c.editor && c.editor.isFormField){
4958 c.editor = new Roo.grid.GridEditor(c.editor);
4960 this.lookup[c.id] = c;
4964 * The width of columns which have no width specified (defaults to 100)
4967 this.defaultWidth = 100;
4970 * Default sortable of columns which have no sortable specified (defaults to false)
4973 this.defaultSortable = false;
4977 * @event widthchange
4978 * Fires when the width of a column changes.
4979 * @param {ColumnModel} this
4980 * @param {Number} columnIndex The column index
4981 * @param {Number} newWidth The new width
4983 "widthchange": true,
4985 * @event headerchange
4986 * Fires when the text of a header changes.
4987 * @param {ColumnModel} this
4988 * @param {Number} columnIndex The column index
4989 * @param {Number} newText The new header text
4991 "headerchange": true,
4993 * @event hiddenchange
4994 * Fires when a column is hidden or "unhidden".
4995 * @param {ColumnModel} this
4996 * @param {Number} columnIndex The column index
4997 * @param {Boolean} hidden true if hidden, false otherwise
4999 "hiddenchange": true,
5001 * @event columnmoved
5002 * Fires when a column is moved.
5003 * @param {ColumnModel} this
5004 * @param {Number} oldIndex
5005 * @param {Number} newIndex
5007 "columnmoved" : true,
5009 * @event columlockchange
5010 * Fires when a column's locked state is changed
5011 * @param {ColumnModel} this
5012 * @param {Number} colIndex
5013 * @param {Boolean} locked true if locked
5015 "columnlockchange" : true
5017 Roo.grid.ColumnModel.superclass.constructor.call(this);
5019 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5021 * @cfg {String} header The header text to display in the Grid view.
5024 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5025 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5026 * specified, the column's index is used as an index into the Record's data Array.
5029 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5030 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5033 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5034 * Defaults to the value of the {@link #defaultSortable} property.
5035 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5038 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5041 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5044 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5047 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5050 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5051 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5052 * default renderer uses the raw data value. If an object is returned (bootstrap only)
5053 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5056 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5059 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5062 * @cfg {String} cursor (Optional)
5065 * @cfg {String} tooltip (Optional)
5068 * @cfg {Number} xs (Optional)
5071 * @cfg {Number} sm (Optional)
5074 * @cfg {Number} md (Optional)
5077 * @cfg {Number} lg (Optional)
5080 * Returns the id of the column at the specified index.
5081 * @param {Number} index The column index
5082 * @return {String} the id
5084 getColumnId : function(index){
5085 return this.config[index].id;
5089 * Returns the column for a specified id.
5090 * @param {String} id The column id
5091 * @return {Object} the column
5093 getColumnById : function(id){
5094 return this.lookup[id];
5099 * Returns the column for a specified dataIndex.
5100 * @param {String} dataIndex The column dataIndex
5101 * @return {Object|Boolean} the column or false if not found
5103 getColumnByDataIndex: function(dataIndex){
5104 var index = this.findColumnIndex(dataIndex);
5105 return index > -1 ? this.config[index] : false;
5109 * Returns the index for a specified column id.
5110 * @param {String} id The column id
5111 * @return {Number} the index, or -1 if not found
5113 getIndexById : function(id){
5114 for(var i = 0, len = this.config.length; i < len; i++){
5115 if(this.config[i].id == id){
5123 * Returns the index for a specified column dataIndex.
5124 * @param {String} dataIndex The column dataIndex
5125 * @return {Number} the index, or -1 if not found
5128 findColumnIndex : function(dataIndex){
5129 for(var i = 0, len = this.config.length; i < len; i++){
5130 if(this.config[i].dataIndex == dataIndex){
5138 moveColumn : function(oldIndex, newIndex){
5139 var c = this.config[oldIndex];
5140 this.config.splice(oldIndex, 1);
5141 this.config.splice(newIndex, 0, c);
5142 this.dataMap = null;
5143 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5146 isLocked : function(colIndex){
5147 return this.config[colIndex].locked === true;
5150 setLocked : function(colIndex, value, suppressEvent){
5151 if(this.isLocked(colIndex) == value){
5154 this.config[colIndex].locked = value;
5156 this.fireEvent("columnlockchange", this, colIndex, value);
5160 getTotalLockedWidth : function(){
5162 for(var i = 0; i < this.config.length; i++){
5163 if(this.isLocked(i) && !this.isHidden(i)){
5164 this.totalWidth += this.getColumnWidth(i);
5170 getLockedCount : function(){
5171 for(var i = 0, len = this.config.length; i < len; i++){
5172 if(!this.isLocked(i)){
5177 return this.config.length;
5181 * Returns the number of columns.
5184 getColumnCount : function(visibleOnly){
5185 if(visibleOnly === true){
5187 for(var i = 0, len = this.config.length; i < len; i++){
5188 if(!this.isHidden(i)){
5194 return this.config.length;
5198 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5199 * @param {Function} fn
5200 * @param {Object} scope (optional)
5201 * @return {Array} result
5203 getColumnsBy : function(fn, scope){
5205 for(var i = 0, len = this.config.length; i < len; i++){
5206 var c = this.config[i];
5207 if(fn.call(scope||this, c, i) === true){
5215 * Returns true if the specified column is sortable.
5216 * @param {Number} col The column index
5219 isSortable : function(col){
5220 if(typeof this.config[col].sortable == "undefined"){
5221 return this.defaultSortable;
5223 return this.config[col].sortable;
5227 * Returns the rendering (formatting) function defined for the column.
5228 * @param {Number} col The column index.
5229 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5231 getRenderer : function(col){
5232 if(!this.config[col].renderer){
5233 return Roo.grid.ColumnModel.defaultRenderer;
5235 return this.config[col].renderer;
5239 * Sets the rendering (formatting) function for a column.
5240 * @param {Number} col The column index
5241 * @param {Function} fn The function to use to process the cell's raw data
5242 * to return HTML markup for the grid view. The render function is called with
5243 * the following parameters:<ul>
5244 * <li>Data value.</li>
5245 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5246 * <li>css A CSS style string to apply to the table cell.</li>
5247 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5248 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5249 * <li>Row index</li>
5250 * <li>Column index</li>
5251 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5253 setRenderer : function(col, fn){
5254 this.config[col].renderer = fn;
5258 * Returns the width for the specified column.
5259 * @param {Number} col The column index
5262 getColumnWidth : function(col){
5263 return this.config[col].width * 1 || this.defaultWidth;
5267 * Sets the width for a column.
5268 * @param {Number} col The column index
5269 * @param {Number} width The new width
5271 setColumnWidth : function(col, width, suppressEvent){
5272 this.config[col].width = width;
5273 this.totalWidth = null;
5275 this.fireEvent("widthchange", this, col, width);
5280 * Returns the total width of all columns.
5281 * @param {Boolean} includeHidden True to include hidden column widths
5284 getTotalWidth : function(includeHidden){
5285 if(!this.totalWidth){
5286 this.totalWidth = 0;
5287 for(var i = 0, len = this.config.length; i < len; i++){
5288 if(includeHidden || !this.isHidden(i)){
5289 this.totalWidth += this.getColumnWidth(i);
5293 return this.totalWidth;
5297 * Returns the header for the specified column.
5298 * @param {Number} col The column index
5301 getColumnHeader : function(col){
5302 return this.config[col].header;
5306 * Sets the header for a column.
5307 * @param {Number} col The column index
5308 * @param {String} header The new header
5310 setColumnHeader : function(col, header){
5311 this.config[col].header = header;
5312 this.fireEvent("headerchange", this, col, header);
5316 * Returns the tooltip for the specified column.
5317 * @param {Number} col The column index
5320 getColumnTooltip : function(col){
5321 return this.config[col].tooltip;
5324 * Sets the tooltip for a column.
5325 * @param {Number} col The column index
5326 * @param {String} tooltip The new tooltip
5328 setColumnTooltip : function(col, tooltip){
5329 this.config[col].tooltip = tooltip;
5333 * Returns the dataIndex for the specified column.
5334 * @param {Number} col The column index
5337 getDataIndex : function(col){
5338 return this.config[col].dataIndex;
5342 * Sets the dataIndex for a column.
5343 * @param {Number} col The column index
5344 * @param {Number} dataIndex The new dataIndex
5346 setDataIndex : function(col, dataIndex){
5347 this.config[col].dataIndex = dataIndex;
5353 * Returns true if the cell is editable.
5354 * @param {Number} colIndex The column index
5355 * @param {Number} rowIndex The row index - this is nto actually used..?
5358 isCellEditable : function(colIndex, rowIndex){
5359 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5363 * Returns the editor defined for the cell/column.
5364 * return false or null to disable editing.
5365 * @param {Number} colIndex The column index
5366 * @param {Number} rowIndex The row index
5369 getCellEditor : function(colIndex, rowIndex){
5370 return this.config[colIndex].editor;
5374 * Sets if a column is editable.
5375 * @param {Number} col The column index
5376 * @param {Boolean} editable True if the column is editable
5378 setEditable : function(col, editable){
5379 this.config[col].editable = editable;
5384 * Returns true if the column is hidden.
5385 * @param {Number} colIndex The column index
5388 isHidden : function(colIndex){
5389 return this.config[colIndex].hidden;
5394 * Returns true if the column width cannot be changed
5396 isFixed : function(colIndex){
5397 return this.config[colIndex].fixed;
5401 * Returns true if the column can be resized
5404 isResizable : function(colIndex){
5405 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5408 * Sets if a column is hidden.
5409 * @param {Number} colIndex The column index
5410 * @param {Boolean} hidden True if the column is hidden
5412 setHidden : function(colIndex, hidden){
5413 this.config[colIndex].hidden = hidden;
5414 this.totalWidth = null;
5415 this.fireEvent("hiddenchange", this, colIndex, hidden);
5419 * Sets the editor for a column.
5420 * @param {Number} col The column index
5421 * @param {Object} editor The editor object
5423 setEditor : function(col, editor){
5424 this.config[col].editor = editor;
5428 Roo.grid.ColumnModel.defaultRenderer = function(value){
5429 if(typeof value == "string" && value.length < 1){
5435 // Alias for backwards compatibility
5436 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5439 * Ext JS Library 1.1.1
5440 * Copyright(c) 2006-2007, Ext JS, LLC.
5442 * Originally Released Under LGPL - original licence link has changed is not relivant.
5445 * <script type="text/javascript">
5449 * @class Roo.LoadMask
5450 * A simple utility class for generically masking elements while loading data. If the element being masked has
5451 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5452 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5453 * element's UpdateManager load indicator and will be destroyed after the initial load.
5455 * Create a new LoadMask
5456 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5457 * @param {Object} config The config object
5459 Roo.LoadMask = function(el, config){
5460 this.el = Roo.get(el);
5461 Roo.apply(this, config);
5463 this.store.on('beforeload', this.onBeforeLoad, this);
5464 this.store.on('load', this.onLoad, this);
5465 this.store.on('loadexception', this.onLoadException, this);
5466 this.removeMask = false;
5468 var um = this.el.getUpdateManager();
5469 um.showLoadIndicator = false; // disable the default indicator
5470 um.on('beforeupdate', this.onBeforeLoad, this);
5471 um.on('update', this.onLoad, this);
5472 um.on('failure', this.onLoad, this);
5473 this.removeMask = true;
5477 Roo.LoadMask.prototype = {
5479 * @cfg {Boolean} removeMask
5480 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5481 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5485 * The text to display in a centered loading message box (defaults to 'Loading...')
5489 * @cfg {String} msgCls
5490 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5492 msgCls : 'x-mask-loading',
5495 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5501 * Disables the mask to prevent it from being displayed
5503 disable : function(){
5504 this.disabled = true;
5508 * Enables the mask so that it can be displayed
5510 enable : function(){
5511 this.disabled = false;
5514 onLoadException : function()
5518 if (typeof(arguments[3]) != 'undefined') {
5519 Roo.MessageBox.alert("Error loading",arguments[3]);
5523 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5524 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5533 this.el.unmask(this.removeMask);
5538 this.el.unmask(this.removeMask);
5542 onBeforeLoad : function(){
5544 this.el.mask(this.msg, this.msgCls);
5549 destroy : function(){
5551 this.store.un('beforeload', this.onBeforeLoad, this);
5552 this.store.un('load', this.onLoad, this);
5553 this.store.un('loadexception', this.onLoadException, this);
5555 var um = this.el.getUpdateManager();
5556 um.un('beforeupdate', this.onBeforeLoad, this);
5557 um.un('update', this.onLoad, this);
5558 um.un('failure', this.onLoad, this);
5569 * @class Roo.bootstrap.Table
5570 * @extends Roo.bootstrap.Component
5571 * Bootstrap Table class
5572 * @cfg {String} cls table class
5573 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5574 * @cfg {String} bgcolor Specifies the background color for a table
5575 * @cfg {Number} border Specifies whether the table cells should have borders or not
5576 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5577 * @cfg {Number} cellspacing Specifies the space between cells
5578 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5579 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5580 * @cfg {String} sortable Specifies that the table should be sortable
5581 * @cfg {String} summary Specifies a summary of the content of a table
5582 * @cfg {Number} width Specifies the width of a table
5583 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5585 * @cfg {boolean} striped Should the rows be alternative striped
5586 * @cfg {boolean} bordered Add borders to the table
5587 * @cfg {boolean} hover Add hover highlighting
5588 * @cfg {boolean} condensed Format condensed
5589 * @cfg {boolean} responsive Format condensed
5590 * @cfg {Boolean} loadMask (true|false) default false
5591 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5592 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5593 * @cfg {Boolean} rowSelection (true|false) default false
5594 * @cfg {Boolean} cellSelection (true|false) default false
5595 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5599 * Create a new Table
5600 * @param {Object} config The config object
5603 Roo.bootstrap.Table = function(config){
5604 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5607 this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5608 this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5609 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5610 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5614 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5615 this.sm = this.selModel;
5616 this.sm.xmodule = this.xmodule || false;
5618 if (this.cm && typeof(this.cm.config) == 'undefined') {
5619 this.colModel = new Roo.grid.ColumnModel(this.cm);
5620 this.cm = this.colModel;
5621 this.cm.xmodule = this.xmodule || false;
5624 this.store= Roo.factory(this.store, Roo.data);
5625 this.ds = this.store;
5626 this.ds.xmodule = this.xmodule || false;
5629 if (this.footer && this.store) {
5630 this.footer.dataSource = this.ds;
5631 this.footer = Roo.factory(this.footer);
5638 * Fires when a cell is clicked
5639 * @param {Roo.bootstrap.Table} this
5640 * @param {Roo.Element} el
5641 * @param {Number} rowIndex
5642 * @param {Number} columnIndex
5643 * @param {Roo.EventObject} e
5647 * @event celldblclick
5648 * Fires when a cell is double clicked
5649 * @param {Roo.bootstrap.Table} this
5650 * @param {Roo.Element} el
5651 * @param {Number} rowIndex
5652 * @param {Number} columnIndex
5653 * @param {Roo.EventObject} e
5655 "celldblclick" : true,
5658 * Fires when a row is clicked
5659 * @param {Roo.bootstrap.Table} this
5660 * @param {Roo.Element} el
5661 * @param {Number} rowIndex
5662 * @param {Roo.EventObject} e
5666 * @event rowdblclick
5667 * Fires when a row is double clicked
5668 * @param {Roo.bootstrap.Table} this
5669 * @param {Roo.Element} el
5670 * @param {Number} rowIndex
5671 * @param {Roo.EventObject} e
5673 "rowdblclick" : true,
5676 * Fires when a mouseover occur
5677 * @param {Roo.bootstrap.Table} this
5678 * @param {Roo.Element} el
5679 * @param {Number} rowIndex
5680 * @param {Number} columnIndex
5681 * @param {Roo.EventObject} e
5686 * Fires when a mouseout occur
5687 * @param {Roo.bootstrap.Table} this
5688 * @param {Roo.Element} el
5689 * @param {Number} rowIndex
5690 * @param {Number} columnIndex
5691 * @param {Roo.EventObject} e
5696 * Fires when a row is rendered, so you can change add a style to it.
5697 * @param {Roo.bootstrap.Table} this
5698 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5702 * @event rowsrendered
5703 * Fires when all the rows have been rendered
5704 * @param {Roo.bootstrap.Table} this
5706 'rowsrendered' : true
5711 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5736 rowSelection : false,
5737 cellSelection : false,
5740 // Roo.Element - the tbody
5743 getAutoCreate : function(){
5744 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5753 cfg.cls += ' table-striped';
5757 cfg.cls += ' table-hover';
5759 if (this.bordered) {
5760 cfg.cls += ' table-bordered';
5762 if (this.condensed) {
5763 cfg.cls += ' table-condensed';
5765 if (this.responsive) {
5766 cfg.cls += ' table-responsive';
5770 cfg.cls+= ' ' +this.cls;
5773 // this lot should be simplifed...
5776 cfg.align=this.align;
5779 cfg.bgcolor=this.bgcolor;
5782 cfg.border=this.border;
5784 if (this.cellpadding) {
5785 cfg.cellpadding=this.cellpadding;
5787 if (this.cellspacing) {
5788 cfg.cellspacing=this.cellspacing;
5791 cfg.frame=this.frame;
5794 cfg.rules=this.rules;
5796 if (this.sortable) {
5797 cfg.sortable=this.sortable;
5800 cfg.summary=this.summary;
5803 cfg.width=this.width;
5806 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5809 if(this.store || this.cm){
5810 if(this.headerShow){
5811 cfg.cn.push(this.renderHeader());
5814 cfg.cn.push(this.renderBody());
5816 if(this.footerShow){
5817 cfg.cn.push(this.renderFooter());
5820 cfg.cls+= ' TableGrid';
5823 return { cn : [ cfg ] };
5826 initEvents : function()
5828 if(!this.store || !this.cm){
5832 //Roo.log('initEvents with ds!!!!');
5834 this.mainBody = this.el.select('tbody', true).first();
5839 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5840 e.on('click', _this.sort, _this);
5843 this.el.on("click", this.onClick, this);
5844 this.el.on("dblclick", this.onDblClick, this);
5846 // why is this done????? = it breaks dialogs??
5847 //this.parent().el.setStyle('position', 'relative');
5851 this.footer.parentId = this.id;
5852 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5855 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5857 this.store.on('load', this.onLoad, this);
5858 this.store.on('beforeload', this.onBeforeLoad, this);
5859 this.store.on('update', this.onUpdate, this);
5860 this.store.on('add', this.onAdd, this);
5864 onMouseover : function(e, el)
5866 var cell = Roo.get(el);
5872 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5873 cell = cell.findParent('td', false, true);
5876 var row = cell.findParent('tr', false, true);
5877 var cellIndex = cell.dom.cellIndex;
5878 var rowIndex = row.dom.rowIndex - 1; // start from 0
5880 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5884 onMouseout : function(e, el)
5886 var cell = Roo.get(el);
5892 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5893 cell = cell.findParent('td', false, true);
5896 var row = cell.findParent('tr', false, true);
5897 var cellIndex = cell.dom.cellIndex;
5898 var rowIndex = row.dom.rowIndex - 1; // start from 0
5900 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5904 onClick : function(e, el)
5906 var cell = Roo.get(el);
5908 if(!cell || (!this.cellSelection && !this.rowSelection)){
5912 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5913 cell = cell.findParent('td', false, true);
5916 if(!cell || typeof(cell) == 'undefined'){
5920 var row = cell.findParent('tr', false, true);
5922 if(!row || typeof(row) == 'undefined'){
5926 var cellIndex = cell.dom.cellIndex;
5927 var rowIndex = this.getRowIndex(row);
5929 // why??? - should these not be based on SelectionModel?
5930 if(this.cellSelection){
5931 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5934 if(this.rowSelection){
5935 this.fireEvent('rowclick', this, row, rowIndex, e);
5941 onDblClick : function(e,el)
5943 var cell = Roo.get(el);
5945 if(!cell || (!this.CellSelection && !this.RowSelection)){
5949 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5950 cell = cell.findParent('td', false, true);
5953 if(!cell || typeof(cell) == 'undefined'){
5957 var row = cell.findParent('tr', false, true);
5959 if(!row || typeof(row) == 'undefined'){
5963 var cellIndex = cell.dom.cellIndex;
5964 var rowIndex = this.getRowIndex(row);
5966 if(this.CellSelection){
5967 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5970 if(this.RowSelection){
5971 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5975 sort : function(e,el)
5977 var col = Roo.get(el);
5979 if(!col.hasClass('sortable')){
5983 var sort = col.attr('sort');
5986 if(col.hasClass('glyphicon-arrow-up')){
5990 this.store.sortInfo = {field : sort, direction : dir};
5993 Roo.log("calling footer first");
5994 this.footer.onClick('first');
5997 this.store.load({ params : { start : 0 } });
6001 renderHeader : function()
6010 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6012 var config = cm.config[i];
6017 html: cm.getColumnHeader(i)
6022 if(typeof(config.lgHeader) != 'undefined'){
6023 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6026 if(typeof(config.mdHeader) != 'undefined'){
6027 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6030 if(typeof(config.smHeader) != 'undefined'){
6031 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6034 if(typeof(config.xsHeader) != 'undefined'){
6035 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6042 if(typeof(config.tooltip) != 'undefined'){
6043 c.tooltip = config.tooltip;
6046 if(typeof(config.colspan) != 'undefined'){
6047 c.colspan = config.colspan;
6050 if(typeof(config.hidden) != 'undefined' && config.hidden){
6051 c.style += ' display:none;';
6054 if(typeof(config.dataIndex) != 'undefined'){
6055 c.sort = config.dataIndex;
6058 if(typeof(config.sortable) != 'undefined' && config.sortable){
6062 if(typeof(config.align) != 'undefined' && config.align.length){
6063 c.style += ' text-align:' + config.align + ';';
6066 if(typeof(config.width) != 'undefined'){
6067 c.style += ' width:' + config.width + 'px;';
6070 if(typeof(config.cls) != 'undefined'){
6071 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6074 ['xs','sm','md','lg'].map(function(size){
6076 if(typeof(config[size]) == 'undefined'){
6080 if (!config[size]) { // 0 = hidden
6081 c.cls += ' hidden-' + size;
6085 c.cls += ' col-' + size + '-' + config[size];
6095 renderBody : function()
6105 colspan : this.cm.getColumnCount()
6115 renderFooter : function()
6125 colspan : this.cm.getColumnCount()
6139 // Roo.log('ds onload');
6144 var ds = this.store;
6146 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6147 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6149 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6150 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6153 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6154 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6158 var tbody = this.mainBody;
6160 if(ds.getCount() > 0){
6161 ds.data.each(function(d,rowIndex){
6162 var row = this.renderRow(cm, ds, rowIndex);
6164 tbody.createChild(row);
6168 if(row.cellObjects.length){
6169 Roo.each(row.cellObjects, function(r){
6170 _this.renderCellObject(r);
6177 Roo.each(this.el.select('tbody td', true).elements, function(e){
6178 e.on('mouseover', _this.onMouseover, _this);
6181 Roo.each(this.el.select('tbody td', true).elements, function(e){
6182 e.on('mouseout', _this.onMouseout, _this);
6184 this.fireEvent('rowsrendered', this);
6185 //if(this.loadMask){
6186 // this.maskEl.hide();
6191 onUpdate : function(ds,record)
6193 this.refreshRow(record);
6196 onRemove : function(ds, record, index, isUpdate){
6197 if(isUpdate !== true){
6198 this.fireEvent("beforerowremoved", this, index, record);
6200 var bt = this.mainBody.dom;
6202 var rows = this.el.select('tbody > tr', true).elements;
6204 if(typeof(rows[index]) != 'undefined'){
6205 bt.removeChild(rows[index].dom);
6208 // if(bt.rows[index]){
6209 // bt.removeChild(bt.rows[index]);
6212 if(isUpdate !== true){
6213 //this.stripeRows(index);
6214 //this.syncRowHeights(index, index);
6216 this.fireEvent("rowremoved", this, index, record);
6220 onAdd : function(ds, records, rowIndex)
6222 //Roo.log('on Add called');
6223 // - note this does not handle multiple adding very well..
6224 var bt = this.mainBody.dom;
6225 for (var i =0 ; i < records.length;i++) {
6226 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6227 //Roo.log(records[i]);
6228 //Roo.log(this.store.getAt(rowIndex+i));
6229 this.insertRow(this.store, rowIndex + i, false);
6236 refreshRow : function(record){
6237 var ds = this.store, index;
6238 if(typeof record == 'number'){
6240 record = ds.getAt(index);
6242 index = ds.indexOf(record);
6244 this.insertRow(ds, index, true);
6245 this.onRemove(ds, record, index+1, true);
6246 //this.syncRowHeights(index, index);
6248 this.fireEvent("rowupdated", this, index, record);
6251 insertRow : function(dm, rowIndex, isUpdate){
6254 this.fireEvent("beforerowsinserted", this, rowIndex);
6256 //var s = this.getScrollState();
6257 var row = this.renderRow(this.cm, this.store, rowIndex);
6258 // insert before rowIndex..
6259 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6263 if(row.cellObjects.length){
6264 Roo.each(row.cellObjects, function(r){
6265 _this.renderCellObject(r);
6270 this.fireEvent("rowsinserted", this, rowIndex);
6271 //this.syncRowHeights(firstRow, lastRow);
6272 //this.stripeRows(firstRow);
6279 getRowDom : function(rowIndex)
6281 var rows = this.el.select('tbody > tr', true).elements;
6283 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6286 // returns the object tree for a tr..
6289 renderRow : function(cm, ds, rowIndex)
6292 var d = ds.getAt(rowIndex);
6299 var cellObjects = [];
6301 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6302 var config = cm.config[i];
6304 var renderer = cm.getRenderer(i);
6308 if(typeof(renderer) !== 'undefined'){
6309 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6311 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6312 // and are rendered into the cells after the row is rendered - using the id for the element.
6314 if(typeof(value) === 'object'){
6324 rowIndex : rowIndex,
6329 this.fireEvent('rowclass', this, rowcfg);
6333 cls : rowcfg.rowClass,
6335 html: (typeof(value) === 'object') ? '' : value
6342 if(typeof(config.colspan) != 'undefined'){
6343 td.colspan = config.colspan;
6346 if(typeof(config.hidden) != 'undefined' && config.hidden){
6347 td.style += ' display:none;';
6350 if(typeof(config.align) != 'undefined' && config.align.length){
6351 td.style += ' text-align:' + config.align + ';';
6354 if(typeof(config.width) != 'undefined'){
6355 td.style += ' width:' + config.width + 'px;';
6358 if(typeof(config.cursor) != 'undefined'){
6359 td.style += ' cursor:' + config.cursor + ';';
6362 if(typeof(config.cls) != 'undefined'){
6363 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6366 ['xs','sm','md','lg'].map(function(size){
6368 if(typeof(config[size]) == 'undefined'){
6372 if (!config[size]) { // 0 = hidden
6373 td.cls += ' hidden-' + size;
6377 td.cls += ' col-' + size + '-' + config[size];
6385 row.cellObjects = cellObjects;
6393 onBeforeLoad : function()
6395 //Roo.log('ds onBeforeLoad');
6399 //if(this.loadMask){
6400 // this.maskEl.show();
6408 this.el.select('tbody', true).first().dom.innerHTML = '';
6411 * Show or hide a row.
6412 * @param {Number} rowIndex to show or hide
6413 * @param {Boolean} state hide
6415 setRowVisibility : function(rowIndex, state)
6417 var bt = this.mainBody.dom;
6419 var rows = this.el.select('tbody > tr', true).elements;
6421 if(typeof(rows[rowIndex]) == 'undefined'){
6424 rows[rowIndex].dom.style.display = state ? '' : 'none';
6428 getSelectionModel : function(){
6430 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6432 return this.selModel;
6435 * Render the Roo.bootstrap object from renderder
6437 renderCellObject : function(r)
6441 var t = r.cfg.render(r.container);
6444 Roo.each(r.cfg.cn, function(c){
6446 container: t.getChildContainer(),
6449 _this.renderCellObject(child);
6454 getRowIndex : function(row)
6458 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6481 * @class Roo.bootstrap.TableCell
6482 * @extends Roo.bootstrap.Component
6483 * Bootstrap TableCell class
6484 * @cfg {String} html cell contain text
6485 * @cfg {String} cls cell class
6486 * @cfg {String} tag cell tag (td|th) default td
6487 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6488 * @cfg {String} align Aligns the content in a cell
6489 * @cfg {String} axis Categorizes cells
6490 * @cfg {String} bgcolor Specifies the background color of a cell
6491 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6492 * @cfg {Number} colspan Specifies the number of columns a cell should span
6493 * @cfg {String} headers Specifies one or more header cells a cell is related to
6494 * @cfg {Number} height Sets the height of a cell
6495 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6496 * @cfg {Number} rowspan Sets the number of rows a cell should span
6497 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6498 * @cfg {String} valign Vertical aligns the content in a cell
6499 * @cfg {Number} width Specifies the width of a cell
6502 * Create a new TableCell
6503 * @param {Object} config The config object
6506 Roo.bootstrap.TableCell = function(config){
6507 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6510 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6530 getAutoCreate : function(){
6531 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6551 cfg.align=this.align
6557 cfg.bgcolor=this.bgcolor
6560 cfg.charoff=this.charoff
6563 cfg.colspan=this.colspan
6566 cfg.headers=this.headers
6569 cfg.height=this.height
6572 cfg.nowrap=this.nowrap
6575 cfg.rowspan=this.rowspan
6578 cfg.scope=this.scope
6581 cfg.valign=this.valign
6584 cfg.width=this.width
6603 * @class Roo.bootstrap.TableRow
6604 * @extends Roo.bootstrap.Component
6605 * Bootstrap TableRow class
6606 * @cfg {String} cls row class
6607 * @cfg {String} align Aligns the content in a table row
6608 * @cfg {String} bgcolor Specifies a background color for a table row
6609 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6610 * @cfg {String} valign Vertical aligns the content in a table row
6613 * Create a new TableRow
6614 * @param {Object} config The config object
6617 Roo.bootstrap.TableRow = function(config){
6618 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6621 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6629 getAutoCreate : function(){
6630 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6640 cfg.align = this.align;
6643 cfg.bgcolor = this.bgcolor;
6646 cfg.charoff = this.charoff;
6649 cfg.valign = this.valign;
6667 * @class Roo.bootstrap.TableBody
6668 * @extends Roo.bootstrap.Component
6669 * Bootstrap TableBody class
6670 * @cfg {String} cls element class
6671 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6672 * @cfg {String} align Aligns the content inside the element
6673 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6674 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6677 * Create a new TableBody
6678 * @param {Object} config The config object
6681 Roo.bootstrap.TableBody = function(config){
6682 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6685 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6693 getAutoCreate : function(){
6694 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6708 cfg.align = this.align;
6711 cfg.charoff = this.charoff;
6714 cfg.valign = this.valign;
6721 // initEvents : function()
6728 // this.store = Roo.factory(this.store, Roo.data);
6729 // this.store.on('load', this.onLoad, this);
6731 // this.store.load();
6735 // onLoad: function ()
6737 // this.fireEvent('load', this);
6747 * Ext JS Library 1.1.1
6748 * Copyright(c) 2006-2007, Ext JS, LLC.
6750 * Originally Released Under LGPL - original licence link has changed is not relivant.
6753 * <script type="text/javascript">
6756 // as we use this in bootstrap.
6757 Roo.namespace('Roo.form');
6759 * @class Roo.form.Action
6760 * Internal Class used to handle form actions
6762 * @param {Roo.form.BasicForm} el The form element or its id
6763 * @param {Object} config Configuration options
6768 // define the action interface
6769 Roo.form.Action = function(form, options){
6771 this.options = options || {};
6774 * Client Validation Failed
6777 Roo.form.Action.CLIENT_INVALID = 'client';
6779 * Server Validation Failed
6782 Roo.form.Action.SERVER_INVALID = 'server';
6784 * Connect to Server Failed
6787 Roo.form.Action.CONNECT_FAILURE = 'connect';
6789 * Reading Data from Server Failed
6792 Roo.form.Action.LOAD_FAILURE = 'load';
6794 Roo.form.Action.prototype = {
6796 failureType : undefined,
6797 response : undefined,
6801 run : function(options){
6806 success : function(response){
6811 handleResponse : function(response){
6815 // default connection failure
6816 failure : function(response){
6818 this.response = response;
6819 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6820 this.form.afterAction(this, false);
6823 processResponse : function(response){
6824 this.response = response;
6825 if(!response.responseText){
6828 this.result = this.handleResponse(response);
6832 // utility functions used internally
6833 getUrl : function(appendParams){
6834 var url = this.options.url || this.form.url || this.form.el.dom.action;
6836 var p = this.getParams();
6838 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6844 getMethod : function(){
6845 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6848 getParams : function(){
6849 var bp = this.form.baseParams;
6850 var p = this.options.params;
6852 if(typeof p == "object"){
6853 p = Roo.urlEncode(Roo.applyIf(p, bp));
6854 }else if(typeof p == 'string' && bp){
6855 p += '&' + Roo.urlEncode(bp);
6858 p = Roo.urlEncode(bp);
6863 createCallback : function(){
6865 success: this.success,
6866 failure: this.failure,
6868 timeout: (this.form.timeout*1000),
6869 upload: this.form.fileUpload ? this.success : undefined
6874 Roo.form.Action.Submit = function(form, options){
6875 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6878 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6881 haveProgress : false,
6882 uploadComplete : false,
6884 // uploadProgress indicator.
6885 uploadProgress : function()
6887 if (!this.form.progressUrl) {
6891 if (!this.haveProgress) {
6892 Roo.MessageBox.progress("Uploading", "Uploading");
6894 if (this.uploadComplete) {
6895 Roo.MessageBox.hide();
6899 this.haveProgress = true;
6901 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6903 var c = new Roo.data.Connection();
6905 url : this.form.progressUrl,
6910 success : function(req){
6911 //console.log(data);
6915 rdata = Roo.decode(req.responseText)
6917 Roo.log("Invalid data from server..");
6921 if (!rdata || !rdata.success) {
6923 Roo.MessageBox.alert(Roo.encode(rdata));
6926 var data = rdata.data;
6928 if (this.uploadComplete) {
6929 Roo.MessageBox.hide();
6934 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6935 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6938 this.uploadProgress.defer(2000,this);
6941 failure: function(data) {
6942 Roo.log('progress url failed ');
6953 // run get Values on the form, so it syncs any secondary forms.
6954 this.form.getValues();
6956 var o = this.options;
6957 var method = this.getMethod();
6958 var isPost = method == 'POST';
6959 if(o.clientValidation === false || this.form.isValid()){
6961 if (this.form.progressUrl) {
6962 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6963 (new Date() * 1) + '' + Math.random());
6968 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6969 form:this.form.el.dom,
6970 url:this.getUrl(!isPost),
6972 params:isPost ? this.getParams() : null,
6973 isUpload: this.form.fileUpload
6976 this.uploadProgress();
6978 }else if (o.clientValidation !== false){ // client validation failed
6979 this.failureType = Roo.form.Action.CLIENT_INVALID;
6980 this.form.afterAction(this, false);
6984 success : function(response)
6986 this.uploadComplete= true;
6987 if (this.haveProgress) {
6988 Roo.MessageBox.hide();
6992 var result = this.processResponse(response);
6993 if(result === true || result.success){
6994 this.form.afterAction(this, true);
6998 this.form.markInvalid(result.errors);
6999 this.failureType = Roo.form.Action.SERVER_INVALID;
7001 this.form.afterAction(this, false);
7003 failure : function(response)
7005 this.uploadComplete= true;
7006 if (this.haveProgress) {
7007 Roo.MessageBox.hide();
7010 this.response = response;
7011 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7012 this.form.afterAction(this, false);
7015 handleResponse : function(response){
7016 if(this.form.errorReader){
7017 var rs = this.form.errorReader.read(response);
7020 for(var i = 0, len = rs.records.length; i < len; i++) {
7021 var r = rs.records[i];
7025 if(errors.length < 1){
7029 success : rs.success,
7035 ret = Roo.decode(response.responseText);
7039 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7049 Roo.form.Action.Load = function(form, options){
7050 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7051 this.reader = this.form.reader;
7054 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7059 Roo.Ajax.request(Roo.apply(
7060 this.createCallback(), {
7061 method:this.getMethod(),
7062 url:this.getUrl(false),
7063 params:this.getParams()
7067 success : function(response){
7069 var result = this.processResponse(response);
7070 if(result === true || !result.success || !result.data){
7071 this.failureType = Roo.form.Action.LOAD_FAILURE;
7072 this.form.afterAction(this, false);
7075 this.form.clearInvalid();
7076 this.form.setValues(result.data);
7077 this.form.afterAction(this, true);
7080 handleResponse : function(response){
7081 if(this.form.reader){
7082 var rs = this.form.reader.read(response);
7083 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7085 success : rs.success,
7089 return Roo.decode(response.responseText);
7093 Roo.form.Action.ACTION_TYPES = {
7094 'load' : Roo.form.Action.Load,
7095 'submit' : Roo.form.Action.Submit
7104 * @class Roo.bootstrap.Form
7105 * @extends Roo.bootstrap.Component
7106 * Bootstrap Form class
7107 * @cfg {String} method GET | POST (default POST)
7108 * @cfg {String} labelAlign top | left (default top)
7109 * @cfg {String} align left | right - for navbars
7110 * @cfg {Boolean} loadMask load mask when submit (default true)
7115 * @param {Object} config The config object
7119 Roo.bootstrap.Form = function(config){
7120 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7123 * @event clientvalidation
7124 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7125 * @param {Form} this
7126 * @param {Boolean} valid true if the form has passed client-side validation
7128 clientvalidation: true,
7130 * @event beforeaction
7131 * Fires before any action is performed. Return false to cancel the action.
7132 * @param {Form} this
7133 * @param {Action} action The action to be performed
7137 * @event actionfailed
7138 * Fires when an action fails.
7139 * @param {Form} this
7140 * @param {Action} action The action that failed
7142 actionfailed : true,
7144 * @event actioncomplete
7145 * Fires when an action is completed.
7146 * @param {Form} this
7147 * @param {Action} action The action that completed
7149 actioncomplete : true
7154 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7157 * @cfg {String} method
7158 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7163 * The URL to use for form actions if one isn't supplied in the action options.
7166 * @cfg {Boolean} fileUpload
7167 * Set to true if this form is a file upload.
7171 * @cfg {Object} baseParams
7172 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7176 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7180 * @cfg {Sting} align (left|right) for navbar forms
7185 activeAction : null,
7188 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7189 * element by passing it or its id or mask the form itself by passing in true.
7192 waitMsgTarget : false,
7196 getAutoCreate : function(){
7200 method : this.method || 'POST',
7201 id : this.id || Roo.id(),
7204 if (this.parent().xtype.match(/^Nav/)) {
7205 cfg.cls = 'navbar-form navbar-' + this.align;
7209 if (this.labelAlign == 'left' ) {
7210 cfg.cls += ' form-horizontal';
7216 initEvents : function()
7218 this.el.on('submit', this.onSubmit, this);
7219 // this was added as random key presses on the form where triggering form submit.
7220 this.el.on('keypress', function(e) {
7221 if (e.getCharCode() != 13) {
7224 // we might need to allow it for textareas.. and some other items.
7225 // check e.getTarget().
7227 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7231 Roo.log("keypress blocked");
7239 onSubmit : function(e){
7244 * Returns true if client-side validation on the form is successful.
7247 isValid : function(){
7248 var items = this.getItems();
7250 items.each(function(f){
7259 * Returns true if any fields in this form have changed since their original load.
7262 isDirty : function(){
7264 var items = this.getItems();
7265 items.each(function(f){
7275 * Performs a predefined action (submit or load) or custom actions you define on this form.
7276 * @param {String} actionName The name of the action type
7277 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7278 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7279 * accept other config options):
7281 Property Type Description
7282 ---------------- --------------- ----------------------------------------------------------------------------------
7283 url String The url for the action (defaults to the form's url)
7284 method String The form method to use (defaults to the form's method, or POST if not defined)
7285 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7286 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7287 validate the form on the client (defaults to false)
7289 * @return {BasicForm} this
7291 doAction : function(action, options){
7292 if(typeof action == 'string'){
7293 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7295 if(this.fireEvent('beforeaction', this, action) !== false){
7296 this.beforeAction(action);
7297 action.run.defer(100, action);
7303 beforeAction : function(action){
7304 var o = action.options;
7307 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7309 // not really supported yet.. ??
7311 //if(this.waitMsgTarget === true){
7312 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7313 //}else if(this.waitMsgTarget){
7314 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7315 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7317 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7323 afterAction : function(action, success){
7324 this.activeAction = null;
7325 var o = action.options;
7327 //if(this.waitMsgTarget === true){
7329 //}else if(this.waitMsgTarget){
7330 // this.waitMsgTarget.unmask();
7332 // Roo.MessageBox.updateProgress(1);
7333 // Roo.MessageBox.hide();
7340 Roo.callback(o.success, o.scope, [this, action]);
7341 this.fireEvent('actioncomplete', this, action);
7345 // failure condition..
7346 // we have a scenario where updates need confirming.
7347 // eg. if a locking scenario exists..
7348 // we look for { errors : { needs_confirm : true }} in the response.
7350 (typeof(action.result) != 'undefined') &&
7351 (typeof(action.result.errors) != 'undefined') &&
7352 (typeof(action.result.errors.needs_confirm) != 'undefined')
7355 Roo.log("not supported yet");
7358 Roo.MessageBox.confirm(
7359 "Change requires confirmation",
7360 action.result.errorMsg,
7365 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7375 Roo.callback(o.failure, o.scope, [this, action]);
7376 // show an error message if no failed handler is set..
7377 if (!this.hasListener('actionfailed')) {
7378 Roo.log("need to add dialog support");
7380 Roo.MessageBox.alert("Error",
7381 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7382 action.result.errorMsg :
7383 "Saving Failed, please check your entries or try again"
7388 this.fireEvent('actionfailed', this, action);
7393 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7394 * @param {String} id The value to search for
7397 findField : function(id){
7398 var items = this.getItems();
7399 var field = items.get(id);
7401 items.each(function(f){
7402 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7409 return field || null;
7412 * Mark fields in this form invalid in bulk.
7413 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7414 * @return {BasicForm} this
7416 markInvalid : function(errors){
7417 if(errors instanceof Array){
7418 for(var i = 0, len = errors.length; i < len; i++){
7419 var fieldError = errors[i];
7420 var f = this.findField(fieldError.id);
7422 f.markInvalid(fieldError.msg);
7428 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7429 field.markInvalid(errors[id]);
7433 //Roo.each(this.childForms || [], function (f) {
7434 // f.markInvalid(errors);
7441 * Set values for fields in this form in bulk.
7442 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7443 * @return {BasicForm} this
7445 setValues : function(values){
7446 if(values instanceof Array){ // array of objects
7447 for(var i = 0, len = values.length; i < len; i++){
7449 var f = this.findField(v.id);
7451 f.setValue(v.value);
7452 if(this.trackResetOnLoad){
7453 f.originalValue = f.getValue();
7457 }else{ // object hash
7460 if(typeof values[id] != 'function' && (field = this.findField(id))){
7462 if (field.setFromData &&
7464 field.displayField &&
7465 // combos' with local stores can
7466 // be queried via setValue()
7467 // to set their value..
7468 (field.store && !field.store.isLocal)
7472 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7473 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7474 field.setFromData(sd);
7477 field.setValue(values[id]);
7481 if(this.trackResetOnLoad){
7482 field.originalValue = field.getValue();
7488 //Roo.each(this.childForms || [], function (f) {
7489 // f.setValues(values);
7496 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7497 * they are returned as an array.
7498 * @param {Boolean} asString
7501 getValues : function(asString){
7502 //if (this.childForms) {
7503 // copy values from the child forms
7504 // Roo.each(this.childForms, function (f) {
7505 // this.setValues(f.getValues());
7511 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7512 if(asString === true){
7515 return Roo.urlDecode(fs);
7519 * Returns the fields in this form as an object with key/value pairs.
7520 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7523 getFieldValues : function(with_hidden)
7525 var items = this.getItems();
7527 items.each(function(f){
7531 var v = f.getValue();
7532 if (f.inputType =='radio') {
7533 if (typeof(ret[f.getName()]) == 'undefined') {
7534 ret[f.getName()] = ''; // empty..
7537 if (!f.el.dom.checked) {
7545 // not sure if this supported any more..
7546 if ((typeof(v) == 'object') && f.getRawValue) {
7547 v = f.getRawValue() ; // dates..
7549 // combo boxes where name != hiddenName...
7550 if (f.name != f.getName()) {
7551 ret[f.name] = f.getRawValue();
7553 ret[f.getName()] = v;
7560 * Clears all invalid messages in this form.
7561 * @return {BasicForm} this
7563 clearInvalid : function(){
7564 var items = this.getItems();
7566 items.each(function(f){
7577 * @return {BasicForm} this
7580 var items = this.getItems();
7581 items.each(function(f){
7585 Roo.each(this.childForms || [], function (f) {
7592 getItems : function()
7594 var r=new Roo.util.MixedCollection(false, function(o){
7595 return o.id || (o.id = Roo.id());
7597 var iter = function(el) {
7604 Roo.each(el.items,function(e) {
7624 * Ext JS Library 1.1.1
7625 * Copyright(c) 2006-2007, Ext JS, LLC.
7627 * Originally Released Under LGPL - original licence link has changed is not relivant.
7630 * <script type="text/javascript">
7633 * @class Roo.form.VTypes
7634 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7637 Roo.form.VTypes = function(){
7638 // closure these in so they are only created once.
7639 var alpha = /^[a-zA-Z_]+$/;
7640 var alphanum = /^[a-zA-Z0-9_]+$/;
7641 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7642 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7644 // All these messages and functions are configurable
7647 * The function used to validate email addresses
7648 * @param {String} value The email address
7650 'email' : function(v){
7651 return email.test(v);
7654 * The error text to display when the email validation function returns false
7657 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7659 * The keystroke filter mask to be applied on email input
7662 'emailMask' : /[a-z0-9_\.\-@]/i,
7665 * The function used to validate URLs
7666 * @param {String} value The URL
7668 'url' : function(v){
7672 * The error text to display when the url validation function returns false
7675 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7678 * The function used to validate alpha values
7679 * @param {String} value The value
7681 'alpha' : function(v){
7682 return alpha.test(v);
7685 * The error text to display when the alpha validation function returns false
7688 'alphaText' : 'This field should only contain letters and _',
7690 * The keystroke filter mask to be applied on alpha input
7693 'alphaMask' : /[a-z_]/i,
7696 * The function used to validate alphanumeric values
7697 * @param {String} value The value
7699 'alphanum' : function(v){
7700 return alphanum.test(v);
7703 * The error text to display when the alphanumeric validation function returns false
7706 'alphanumText' : 'This field should only contain letters, numbers and _',
7708 * The keystroke filter mask to be applied on alphanumeric input
7711 'alphanumMask' : /[a-z0-9_]/i
7721 * @class Roo.bootstrap.Input
7722 * @extends Roo.bootstrap.Component
7723 * Bootstrap Input class
7724 * @cfg {Boolean} disabled is it disabled
7725 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7726 * @cfg {String} name name of the input
7727 * @cfg {string} fieldLabel - the label associated
7728 * @cfg {string} placeholder - placeholder to put in text.
7729 * @cfg {string} before - input group add on before
7730 * @cfg {string} after - input group add on after
7731 * @cfg {string} size - (lg|sm) or leave empty..
7732 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7733 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7734 * @cfg {Number} md colspan out of 12 for computer-sized screens
7735 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7736 * @cfg {string} value default value of the input
7737 * @cfg {Number} labelWidth set the width of label (0-12)
7738 * @cfg {String} labelAlign (top|left)
7739 * @cfg {Boolean} readOnly Specifies that the field should be read-only
7740 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7742 * @cfg {String} align (left|center|right) Default left
7743 * @cfg {Boolean} forceFeedback (true|false) Default false
7749 * Create a new Input
7750 * @param {Object} config The config object
7753 Roo.bootstrap.Input = function(config){
7754 Roo.bootstrap.Input.superclass.constructor.call(this, config);
7759 * Fires when this field receives input focus.
7760 * @param {Roo.form.Field} this
7765 * Fires when this field loses input focus.
7766 * @param {Roo.form.Field} this
7771 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
7772 * {@link Roo.EventObject#getKey} to determine which key was pressed.
7773 * @param {Roo.form.Field} this
7774 * @param {Roo.EventObject} e The event object
7779 * Fires just before the field blurs if the field value has changed.
7780 * @param {Roo.form.Field} this
7781 * @param {Mixed} newValue The new value
7782 * @param {Mixed} oldValue The original value
7787 * Fires after the field has been marked as invalid.
7788 * @param {Roo.form.Field} this
7789 * @param {String} msg The validation message
7794 * Fires after the field has been validated with no errors.
7795 * @param {Roo.form.Field} this
7800 * Fires after the key up
7801 * @param {Roo.form.Field} this
7802 * @param {Roo.EventObject} e The event Object
7808 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
7810 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7811 automatic validation (defaults to "keyup").
7813 validationEvent : "keyup",
7815 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7817 validateOnBlur : true,
7819 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7821 validationDelay : 250,
7823 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7825 focusClass : "x-form-focus", // not needed???
7829 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7831 invalidClass : "has-warning",
7834 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7836 validClass : "has-success",
7839 * @cfg {Boolean} hasFeedback (true|false) default true
7844 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7846 invalidFeedbackClass : "glyphicon-warning-sign",
7849 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7851 validFeedbackClass : "glyphicon-ok",
7854 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7856 selectOnFocus : false,
7859 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7863 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7868 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7870 disableKeyFilter : false,
7873 * @cfg {Boolean} disabled True to disable the field (defaults to false).
7877 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7881 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7883 blankText : "This field is required",
7886 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7890 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7892 maxLength : Number.MAX_VALUE,
7894 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7896 minLengthText : "The minimum length for this field is {0}",
7898 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7900 maxLengthText : "The maximum length for this field is {0}",
7904 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7905 * If available, this function will be called only after the basic validators all return true, and will be passed the
7906 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7910 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7911 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7912 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
7916 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7920 autocomplete: false,
7939 formatedValue : false,
7940 forceFeedback : false,
7942 parentLabelAlign : function()
7945 while (parent.parent()) {
7946 parent = parent.parent();
7947 if (typeof(parent.labelAlign) !='undefined') {
7948 return parent.labelAlign;
7955 getAutoCreate : function(){
7957 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7963 if(this.inputType != 'hidden'){
7964 cfg.cls = 'form-group' //input-group
7970 type : this.inputType,
7972 cls : 'form-control',
7973 placeholder : this.placeholder || '',
7974 autocomplete : this.autocomplete || 'new-password'
7979 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7982 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7983 input.maxLength = this.maxLength;
7986 if (this.disabled) {
7987 input.disabled=true;
7990 if (this.readOnly) {
7991 input.readonly=true;
7995 input.name = this.name;
7998 input.cls += ' input-' + this.size;
8001 ['xs','sm','md','lg'].map(function(size){
8002 if (settings[size]) {
8003 cfg.cls += ' col-' + size + '-' + settings[size];
8007 var inputblock = input;
8011 cls: 'glyphicon form-control-feedback'
8014 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8017 cls : 'has-feedback',
8025 if (this.before || this.after) {
8028 cls : 'input-group',
8032 if (this.before && typeof(this.before) == 'string') {
8034 inputblock.cn.push({
8036 cls : 'roo-input-before input-group-addon',
8040 if (this.before && typeof(this.before) == 'object') {
8041 this.before = Roo.factory(this.before);
8043 inputblock.cn.push({
8045 cls : 'roo-input-before input-group-' +
8046 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8050 inputblock.cn.push(input);
8052 if (this.after && typeof(this.after) == 'string') {
8053 inputblock.cn.push({
8055 cls : 'roo-input-after input-group-addon',
8059 if (this.after && typeof(this.after) == 'object') {
8060 this.after = Roo.factory(this.after);
8062 inputblock.cn.push({
8064 cls : 'roo-input-after input-group-' +
8065 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8069 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8070 inputblock.cls += ' has-feedback';
8071 inputblock.cn.push(feedback);
8075 if (align ==='left' && this.fieldLabel.length) {
8082 cls : 'control-label col-sm-' + this.labelWidth,
8083 html : this.fieldLabel
8087 cls : "col-sm-" + (12 - this.labelWidth),
8094 } else if ( this.fieldLabel.length) {
8100 //cls : 'input-group-addon',
8101 html : this.fieldLabel
8120 if (this.parentType === 'Navbar' && this.parent().bar) {
8121 cfg.cls += ' navbar-form';
8128 * return the real input element.
8130 inputEl: function ()
8132 return this.el.select('input.form-control',true).first();
8135 tooltipEl : function()
8137 return this.inputEl();
8140 setDisabled : function(v)
8142 var i = this.inputEl().dom;
8144 i.removeAttribute('disabled');
8148 i.setAttribute('disabled','true');
8150 initEvents : function()
8153 this.inputEl().on("keydown" , this.fireKey, this);
8154 this.inputEl().on("focus", this.onFocus, this);
8155 this.inputEl().on("blur", this.onBlur, this);
8157 this.inputEl().relayEvent('keyup', this);
8159 // reference to original value for reset
8160 this.originalValue = this.getValue();
8161 //Roo.form.TextField.superclass.initEvents.call(this);
8162 if(this.validationEvent == 'keyup'){
8163 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8164 this.inputEl().on('keyup', this.filterValidation, this);
8166 else if(this.validationEvent !== false){
8167 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8170 if(this.selectOnFocus){
8171 this.on("focus", this.preFocus, this);
8174 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8175 this.inputEl().on("keypress", this.filterKeys, this);
8178 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8179 this.el.on("click", this.autoSize, this);
8182 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8183 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8186 if (typeof(this.before) == 'object') {
8187 this.before.render(this.el.select('.roo-input-before',true).first());
8189 if (typeof(this.after) == 'object') {
8190 this.after.render(this.el.select('.roo-input-after',true).first());
8195 filterValidation : function(e){
8196 if(!e.isNavKeyPress()){
8197 this.validationTask.delay(this.validationDelay);
8201 * Validates the field value
8202 * @return {Boolean} True if the value is valid, else false
8204 validate : function(){
8205 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8206 if(this.disabled || this.validateValue(this.getRawValue())){
8217 * Validates a value according to the field's validation rules and marks the field as invalid
8218 * if the validation fails
8219 * @param {Mixed} value The value to validate
8220 * @return {Boolean} True if the value is valid, else false
8222 validateValue : function(value){
8223 if(value.length < 1) { // if it's blank
8224 if(this.allowBlank){
8230 if(value.length < this.minLength){
8233 if(value.length > this.maxLength){
8237 var vt = Roo.form.VTypes;
8238 if(!vt[this.vtype](value, this)){
8242 if(typeof this.validator == "function"){
8243 var msg = this.validator(value);
8249 if(this.regex && !this.regex.test(value)){
8259 fireKey : function(e){
8260 //Roo.log('field ' + e.getKey());
8261 if(e.isNavKeyPress()){
8262 this.fireEvent("specialkey", this, e);
8265 focus : function (selectText){
8267 this.inputEl().focus();
8268 if(selectText === true){
8269 this.inputEl().dom.select();
8275 onFocus : function(){
8276 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8277 // this.el.addClass(this.focusClass);
8280 this.hasFocus = true;
8281 this.startValue = this.getValue();
8282 this.fireEvent("focus", this);
8286 beforeBlur : Roo.emptyFn,
8290 onBlur : function(){
8292 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8293 //this.el.removeClass(this.focusClass);
8295 this.hasFocus = false;
8296 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8299 var v = this.getValue();
8300 if(String(v) !== String(this.startValue)){
8301 this.fireEvent('change', this, v, this.startValue);
8303 this.fireEvent("blur", this);
8307 * Resets the current field value to the originally loaded value and clears any validation messages
8310 this.setValue(this.originalValue);
8314 * Returns the name of the field
8315 * @return {Mixed} name The name field
8317 getName: function(){
8321 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8322 * @return {Mixed} value The field value
8324 getValue : function(){
8326 var v = this.inputEl().getValue();
8331 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8332 * @return {Mixed} value The field value
8334 getRawValue : function(){
8335 var v = this.inputEl().getValue();
8341 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8342 * @param {Mixed} value The value to set
8344 setRawValue : function(v){
8345 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8348 selectText : function(start, end){
8349 var v = this.getRawValue();
8351 start = start === undefined ? 0 : start;
8352 end = end === undefined ? v.length : end;
8353 var d = this.inputEl().dom;
8354 if(d.setSelectionRange){
8355 d.setSelectionRange(start, end);
8356 }else if(d.createTextRange){
8357 var range = d.createTextRange();
8358 range.moveStart("character", start);
8359 range.moveEnd("character", v.length-end);
8366 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8367 * @param {Mixed} value The value to set
8369 setValue : function(v){
8372 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8378 processValue : function(value){
8379 if(this.stripCharsRe){
8380 var newValue = value.replace(this.stripCharsRe, '');
8381 if(newValue !== value){
8382 this.setRawValue(newValue);
8389 preFocus : function(){
8391 if(this.selectOnFocus){
8392 this.inputEl().dom.select();
8395 filterKeys : function(e){
8397 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8400 var c = e.getCharCode(), cc = String.fromCharCode(c);
8401 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8404 if(!this.maskRe.test(cc)){
8409 * Clear any invalid styles/messages for this field
8411 clearInvalid : function(){
8413 if(!this.el || this.preventMark){ // not rendered
8417 var label = this.el.select('label', true).first();
8418 var icon = this.el.select('i.fa-star', true).first();
8424 this.el.removeClass(this.invalidClass);
8426 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8428 var feedback = this.el.select('.form-control-feedback', true).first();
8431 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8436 this.fireEvent('valid', this);
8440 * Mark this field as valid
8442 markValid : function()
8444 if(!this.el || this.preventMark){ // not rendered
8448 this.el.removeClass([this.invalidClass, this.validClass]);
8450 var feedback = this.el.select('.form-control-feedback', true).first();
8453 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8456 if(this.disabled || this.allowBlank){
8460 var formGroup = this.el.findParent('.form-group', false, true);
8464 var label = formGroup.select('label', true).first();
8465 var icon = formGroup.select('i.fa-star', true).first();
8472 this.el.addClass(this.validClass);
8474 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8476 var feedback = this.el.select('.form-control-feedback', true).first();
8479 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8480 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8485 this.fireEvent('valid', this);
8489 * Mark this field as invalid
8490 * @param {String} msg The validation message
8492 markInvalid : function(msg)
8494 if(!this.el || this.preventMark){ // not rendered
8498 this.el.removeClass([this.invalidClass, this.validClass]);
8500 var feedback = this.el.select('.form-control-feedback', true).first();
8503 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8506 if(this.disabled || this.allowBlank){
8510 var formGroup = this.el.findParent('.form-group', false, true);
8513 var label = formGroup.select('label', true).first();
8514 var icon = formGroup.select('i.fa-star', true).first();
8516 if(!this.getValue().length && label && !icon){
8517 this.el.findParent('.form-group', false, true).createChild({
8519 cls : 'text-danger fa fa-lg fa-star',
8520 tooltip : 'This field is required',
8521 style : 'margin-right:5px;'
8527 this.el.addClass(this.invalidClass);
8529 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8531 var feedback = this.el.select('.form-control-feedback', true).first();
8534 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8536 if(this.getValue().length || this.forceFeedback){
8537 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8544 this.fireEvent('invalid', this, msg);
8547 SafariOnKeyDown : function(event)
8549 // this is a workaround for a password hang bug on chrome/ webkit.
8551 var isSelectAll = false;
8553 if(this.inputEl().dom.selectionEnd > 0){
8554 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8556 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8557 event.preventDefault();
8562 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8564 event.preventDefault();
8565 // this is very hacky as keydown always get's upper case.
8567 var cc = String.fromCharCode(event.getCharCode());
8568 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8572 adjustWidth : function(tag, w){
8573 tag = tag.toLowerCase();
8574 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8575 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8579 if(tag == 'textarea'){
8582 }else if(Roo.isOpera){
8586 if(tag == 'textarea'){
8605 * @class Roo.bootstrap.TextArea
8606 * @extends Roo.bootstrap.Input
8607 * Bootstrap TextArea class
8608 * @cfg {Number} cols Specifies the visible width of a text area
8609 * @cfg {Number} rows Specifies the visible number of lines in a text area
8610 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8611 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8612 * @cfg {string} html text
8615 * Create a new TextArea
8616 * @param {Object} config The config object
8619 Roo.bootstrap.TextArea = function(config){
8620 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8624 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8634 getAutoCreate : function(){
8636 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8647 value : this.value || '',
8648 html: this.html || '',
8649 cls : 'form-control',
8650 placeholder : this.placeholder || ''
8654 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8655 input.maxLength = this.maxLength;
8659 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8663 input.cols = this.cols;
8666 if (this.readOnly) {
8667 input.readonly = true;
8671 input.name = this.name;
8675 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8679 ['xs','sm','md','lg'].map(function(size){
8680 if (settings[size]) {
8681 cfg.cls += ' col-' + size + '-' + settings[size];
8685 var inputblock = input;
8687 if(this.hasFeedback && !this.allowBlank){
8691 cls: 'glyphicon form-control-feedback'
8695 cls : 'has-feedback',
8704 if (this.before || this.after) {
8707 cls : 'input-group',
8711 inputblock.cn.push({
8713 cls : 'input-group-addon',
8718 inputblock.cn.push(input);
8720 if(this.hasFeedback && !this.allowBlank){
8721 inputblock.cls += ' has-feedback';
8722 inputblock.cn.push(feedback);
8726 inputblock.cn.push({
8728 cls : 'input-group-addon',
8735 if (align ==='left' && this.fieldLabel.length) {
8736 // Roo.log("left and has label");
8742 cls : 'control-label col-sm-' + this.labelWidth,
8743 html : this.fieldLabel
8747 cls : "col-sm-" + (12 - this.labelWidth),
8754 } else if ( this.fieldLabel.length) {
8755 // Roo.log(" label");
8760 //cls : 'input-group-addon',
8761 html : this.fieldLabel
8771 // Roo.log(" no label && no align");
8781 if (this.disabled) {
8782 input.disabled=true;
8789 * return the real textarea element.
8791 inputEl: function ()
8793 return this.el.select('textarea.form-control',true).first();
8797 * Clear any invalid styles/messages for this field
8799 clearInvalid : function()
8802 if(!this.el || this.preventMark){ // not rendered
8806 var label = this.el.select('label', true).first();
8807 var icon = this.el.select('i.fa-star', true).first();
8813 this.el.removeClass(this.invalidClass);
8815 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8817 var feedback = this.el.select('.form-control-feedback', true).first();
8820 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8825 this.fireEvent('valid', this);
8829 * Mark this field as valid
8831 markValid : function()
8833 if(!this.el || this.preventMark){ // not rendered
8837 this.el.removeClass([this.invalidClass, this.validClass]);
8839 var feedback = this.el.select('.form-control-feedback', true).first();
8842 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8845 if(this.disabled || this.allowBlank){
8849 var label = this.el.select('label', true).first();
8850 var icon = this.el.select('i.fa-star', true).first();
8856 this.el.addClass(this.validClass);
8858 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8860 var feedback = this.el.select('.form-control-feedback', true).first();
8863 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8864 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8869 this.fireEvent('valid', this);
8873 * Mark this field as invalid
8874 * @param {String} msg The validation message
8876 markInvalid : function(msg)
8878 if(!this.el || this.preventMark){ // not rendered
8882 this.el.removeClass([this.invalidClass, this.validClass]);
8884 var feedback = this.el.select('.form-control-feedback', true).first();
8887 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8890 if(this.disabled || this.allowBlank){
8894 var label = this.el.select('label', true).first();
8895 var icon = this.el.select('i.fa-star', true).first();
8897 if(!this.getValue().length && label && !icon){
8898 this.el.createChild({
8900 cls : 'text-danger fa fa-lg fa-star',
8901 tooltip : 'This field is required',
8902 style : 'margin-right:5px;'
8906 this.el.addClass(this.invalidClass);
8908 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8910 var feedback = this.el.select('.form-control-feedback', true).first();
8913 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8915 if(this.getValue().length || this.forceFeedback){
8916 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8923 this.fireEvent('invalid', this, msg);
8931 * trigger field - base class for combo..
8936 * @class Roo.bootstrap.TriggerField
8937 * @extends Roo.bootstrap.Input
8938 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8939 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8940 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8941 * for which you can provide a custom implementation. For example:
8943 var trigger = new Roo.bootstrap.TriggerField();
8944 trigger.onTriggerClick = myTriggerFn;
8945 trigger.applyTo('my-field');
8948 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8949 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8950 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
8951 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8952 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8955 * Create a new TriggerField.
8956 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8957 * to the base TextField)
8959 Roo.bootstrap.TriggerField = function(config){
8960 this.mimicing = false;
8961 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8964 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
8966 * @cfg {String} triggerClass A CSS class to apply to the trigger
8969 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8974 * @cfg {Boolean} removable (true|false) special filter default false
8978 /** @cfg {Boolean} grow @hide */
8979 /** @cfg {Number} growMin @hide */
8980 /** @cfg {Number} growMax @hide */
8986 autoSize: Roo.emptyFn,
8993 actionMode : 'wrap',
8998 getAutoCreate : function(){
9000 var align = this.labelAlign || this.parentLabelAlign();
9005 cls: 'form-group' //input-group
9012 type : this.inputType,
9013 cls : 'form-control',
9014 autocomplete: 'new-password',
9015 placeholder : this.placeholder || ''
9019 input.name = this.name;
9022 input.cls += ' input-' + this.size;
9025 if (this.disabled) {
9026 input.disabled=true;
9029 var inputblock = input;
9031 if(this.hasFeedback && !this.allowBlank){
9035 cls: 'glyphicon form-control-feedback'
9038 if(this.removable && !this.editable && !this.tickable){
9040 cls : 'has-feedback',
9046 cls : 'roo-combo-removable-btn close'
9053 cls : 'has-feedback',
9062 if(this.removable && !this.editable && !this.tickable){
9064 cls : 'roo-removable',
9070 cls : 'roo-combo-removable-btn close'
9077 if (this.before || this.after) {
9080 cls : 'input-group',
9084 inputblock.cn.push({
9086 cls : 'input-group-addon',
9091 inputblock.cn.push(input);
9093 if(this.hasFeedback && !this.allowBlank){
9094 inputblock.cls += ' has-feedback';
9095 inputblock.cn.push(feedback);
9099 inputblock.cn.push({
9101 cls : 'input-group-addon',
9114 cls: 'form-hidden-field'
9128 cls: 'form-hidden-field'
9132 cls: 'select2-choices',
9136 cls: 'select2-search-field',
9149 cls: 'select2-container input-group',
9154 // cls: 'typeahead typeahead-long dropdown-menu',
9155 // style: 'display:none'
9160 if(!this.multiple && this.showToggleBtn){
9166 if (this.caret != false) {
9169 cls: 'fa fa-' + this.caret
9176 cls : 'input-group-addon btn dropdown-toggle',
9181 cls: 'combobox-clear',
9195 combobox.cls += ' select2-container-multi';
9198 if (align ==='left' && this.fieldLabel.length) {
9200 // Roo.log("left and has label");
9206 cls : 'control-label col-sm-' + this.labelWidth,
9207 html : this.fieldLabel
9211 cls : "col-sm-" + (12 - this.labelWidth),
9218 } else if ( this.fieldLabel.length) {
9219 // Roo.log(" label");
9224 //cls : 'input-group-addon',
9225 html : this.fieldLabel
9235 // Roo.log(" no label && no align");
9242 ['xs','sm','md','lg'].map(function(size){
9243 if (settings[size]) {
9244 cfg.cls += ' col-' + size + '-' + settings[size];
9255 onResize : function(w, h){
9256 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9257 // if(typeof w == 'number'){
9258 // var x = w - this.trigger.getWidth();
9259 // this.inputEl().setWidth(this.adjustWidth('input', x));
9260 // this.trigger.setStyle('left', x+'px');
9265 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9268 getResizeEl : function(){
9269 return this.inputEl();
9273 getPositionEl : function(){
9274 return this.inputEl();
9278 alignErrorIcon : function(){
9279 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9283 initEvents : function(){
9287 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9288 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9289 if(!this.multiple && this.showToggleBtn){
9290 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9291 if(this.hideTrigger){
9292 this.trigger.setDisplayed(false);
9294 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9298 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9301 if(this.removable && !this.editable && !this.tickable){
9302 var close = this.closeTriggerEl();
9305 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9306 close.on('click', this.removeBtnClick, this, close);
9310 //this.trigger.addClassOnOver('x-form-trigger-over');
9311 //this.trigger.addClassOnClick('x-form-trigger-click');
9314 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9318 closeTriggerEl : function()
9320 var close = this.el.select('.roo-combo-removable-btn', true).first();
9321 return close ? close : false;
9324 removeBtnClick : function(e, h, el)
9328 if(this.fireEvent("remove", this) !== false){
9333 createList : function()
9335 this.list = Roo.get(document.body).createChild({
9337 cls: 'typeahead typeahead-long dropdown-menu',
9338 style: 'display:none'
9341 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9346 initTrigger : function(){
9351 onDestroy : function(){
9353 this.trigger.removeAllListeners();
9354 // this.trigger.remove();
9357 // this.wrap.remove();
9359 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9363 onFocus : function(){
9364 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9367 this.wrap.addClass('x-trigger-wrap-focus');
9368 this.mimicing = true;
9369 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9370 if(this.monitorTab){
9371 this.el.on("keydown", this.checkTab, this);
9378 checkTab : function(e){
9379 if(e.getKey() == e.TAB){
9385 onBlur : function(){
9390 mimicBlur : function(e, t){
9392 if(!this.wrap.contains(t) && this.validateBlur()){
9399 triggerBlur : function(){
9400 this.mimicing = false;
9401 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9402 if(this.monitorTab){
9403 this.el.un("keydown", this.checkTab, this);
9405 //this.wrap.removeClass('x-trigger-wrap-focus');
9406 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9410 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9411 validateBlur : function(e, t){
9416 onDisable : function(){
9417 this.inputEl().dom.disabled = true;
9418 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9420 // this.wrap.addClass('x-item-disabled');
9425 onEnable : function(){
9426 this.inputEl().dom.disabled = false;
9427 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9429 // this.el.removeClass('x-item-disabled');
9434 onShow : function(){
9435 var ae = this.getActionEl();
9438 ae.dom.style.display = '';
9439 ae.dom.style.visibility = 'visible';
9445 onHide : function(){
9446 var ae = this.getActionEl();
9447 ae.dom.style.display = 'none';
9451 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9452 * by an implementing function.
9454 * @param {EventObject} e
9456 onTriggerClick : Roo.emptyFn
9460 * Ext JS Library 1.1.1
9461 * Copyright(c) 2006-2007, Ext JS, LLC.
9463 * Originally Released Under LGPL - original licence link has changed is not relivant.
9466 * <script type="text/javascript">
9471 * @class Roo.data.SortTypes
9473 * Defines the default sorting (casting?) comparison functions used when sorting data.
9475 Roo.data.SortTypes = {
9477 * Default sort that does nothing
9478 * @param {Mixed} s The value being converted
9479 * @return {Mixed} The comparison value
9486 * The regular expression used to strip tags
9490 stripTagsRE : /<\/?[^>]+>/gi,
9493 * Strips all HTML tags to sort on text only
9494 * @param {Mixed} s The value being converted
9495 * @return {String} The comparison value
9497 asText : function(s){
9498 return String(s).replace(this.stripTagsRE, "");
9502 * Strips all HTML tags to sort on text only - Case insensitive
9503 * @param {Mixed} s The value being converted
9504 * @return {String} The comparison value
9506 asUCText : function(s){
9507 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9511 * Case insensitive string
9512 * @param {Mixed} s The value being converted
9513 * @return {String} The comparison value
9515 asUCString : function(s) {
9516 return String(s).toUpperCase();
9521 * @param {Mixed} s The value being converted
9522 * @return {Number} The comparison value
9524 asDate : function(s) {
9528 if(s instanceof Date){
9531 return Date.parse(String(s));
9536 * @param {Mixed} s The value being converted
9537 * @return {Float} The comparison value
9539 asFloat : function(s) {
9540 var val = parseFloat(String(s).replace(/,/g, ""));
9549 * @param {Mixed} s The value being converted
9550 * @return {Number} The comparison value
9552 asInt : function(s) {
9553 var val = parseInt(String(s).replace(/,/g, ""));
9561 * Ext JS Library 1.1.1
9562 * Copyright(c) 2006-2007, Ext JS, LLC.
9564 * Originally Released Under LGPL - original licence link has changed is not relivant.
9567 * <script type="text/javascript">
9571 * @class Roo.data.Record
9572 * Instances of this class encapsulate both record <em>definition</em> information, and record
9573 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9574 * to access Records cached in an {@link Roo.data.Store} object.<br>
9576 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9577 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9580 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9582 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9583 * {@link #create}. The parameters are the same.
9584 * @param {Array} data An associative Array of data values keyed by the field name.
9585 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9586 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9587 * not specified an integer id is generated.
9589 Roo.data.Record = function(data, id){
9590 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9595 * Generate a constructor for a specific record layout.
9596 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9597 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9598 * Each field definition object may contain the following properties: <ul>
9599 * <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,
9600 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9601 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9602 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9603 * is being used, then this is a string containing the javascript expression to reference the data relative to
9604 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9605 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9606 * this may be omitted.</p></li>
9607 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9608 * <ul><li>auto (Default, implies no conversion)</li>
9613 * <li>date</li></ul></p></li>
9614 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9615 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9616 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9617 * by the Reader into an object that will be stored in the Record. It is passed the
9618 * following parameters:<ul>
9619 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9621 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9623 * <br>usage:<br><pre><code>
9624 var TopicRecord = Roo.data.Record.create(
9625 {name: 'title', mapping: 'topic_title'},
9626 {name: 'author', mapping: 'username'},
9627 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9628 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9629 {name: 'lastPoster', mapping: 'user2'},
9630 {name: 'excerpt', mapping: 'post_text'}
9633 var myNewRecord = new TopicRecord({
9634 title: 'Do my job please',
9637 lastPost: new Date(),
9638 lastPoster: 'Animal',
9639 excerpt: 'No way dude!'
9641 myStore.add(myNewRecord);
9646 Roo.data.Record.create = function(o){
9648 f.superclass.constructor.apply(this, arguments);
9650 Roo.extend(f, Roo.data.Record);
9651 var p = f.prototype;
9652 p.fields = new Roo.util.MixedCollection(false, function(field){
9655 for(var i = 0, len = o.length; i < len; i++){
9656 p.fields.add(new Roo.data.Field(o[i]));
9658 f.getField = function(name){
9659 return p.fields.get(name);
9664 Roo.data.Record.AUTO_ID = 1000;
9665 Roo.data.Record.EDIT = 'edit';
9666 Roo.data.Record.REJECT = 'reject';
9667 Roo.data.Record.COMMIT = 'commit';
9669 Roo.data.Record.prototype = {
9671 * Readonly flag - true if this record has been modified.
9680 join : function(store){
9685 * Set the named field to the specified value.
9686 * @param {String} name The name of the field to set.
9687 * @param {Object} value The value to set the field to.
9689 set : function(name, value){
9690 if(this.data[name] == value){
9697 if(typeof this.modified[name] == 'undefined'){
9698 this.modified[name] = this.data[name];
9700 this.data[name] = value;
9701 if(!this.editing && this.store){
9702 this.store.afterEdit(this);
9707 * Get the value of the named field.
9708 * @param {String} name The name of the field to get the value of.
9709 * @return {Object} The value of the field.
9711 get : function(name){
9712 return this.data[name];
9716 beginEdit : function(){
9717 this.editing = true;
9722 cancelEdit : function(){
9723 this.editing = false;
9724 delete this.modified;
9728 endEdit : function(){
9729 this.editing = false;
9730 if(this.dirty && this.store){
9731 this.store.afterEdit(this);
9736 * Usually called by the {@link Roo.data.Store} which owns the Record.
9737 * Rejects all changes made to the Record since either creation, or the last commit operation.
9738 * Modified fields are reverted to their original values.
9740 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9741 * of reject operations.
9743 reject : function(){
9744 var m = this.modified;
9746 if(typeof m[n] != "function"){
9747 this.data[n] = m[n];
9751 delete this.modified;
9752 this.editing = false;
9754 this.store.afterReject(this);
9759 * Usually called by the {@link Roo.data.Store} which owns the Record.
9760 * Commits all changes made to the Record since either creation, or the last commit operation.
9762 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9763 * of commit operations.
9765 commit : function(){
9767 delete this.modified;
9768 this.editing = false;
9770 this.store.afterCommit(this);
9775 hasError : function(){
9776 return this.error != null;
9780 clearError : function(){
9785 * Creates a copy of this record.
9786 * @param {String} id (optional) A new record id if you don't want to use this record's id
9789 copy : function(newId) {
9790 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9794 * Ext JS Library 1.1.1
9795 * Copyright(c) 2006-2007, Ext JS, LLC.
9797 * Originally Released Under LGPL - original licence link has changed is not relivant.
9800 * <script type="text/javascript">
9806 * @class Roo.data.Store
9807 * @extends Roo.util.Observable
9808 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9809 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9811 * 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
9812 * has no knowledge of the format of the data returned by the Proxy.<br>
9814 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9815 * instances from the data object. These records are cached and made available through accessor functions.
9817 * Creates a new Store.
9818 * @param {Object} config A config object containing the objects needed for the Store to access data,
9819 * and read the data into Records.
9821 Roo.data.Store = function(config){
9822 this.data = new Roo.util.MixedCollection(false);
9823 this.data.getKey = function(o){
9826 this.baseParams = {};
9833 "multisort" : "_multisort"
9836 if(config && config.data){
9837 this.inlineData = config.data;
9841 Roo.apply(this, config);
9843 if(this.reader){ // reader passed
9844 this.reader = Roo.factory(this.reader, Roo.data);
9845 this.reader.xmodule = this.xmodule || false;
9846 if(!this.recordType){
9847 this.recordType = this.reader.recordType;
9849 if(this.reader.onMetaChange){
9850 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9854 if(this.recordType){
9855 this.fields = this.recordType.prototype.fields;
9861 * @event datachanged
9862 * Fires when the data cache has changed, and a widget which is using this Store
9863 * as a Record cache should refresh its view.
9864 * @param {Store} this
9869 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9870 * @param {Store} this
9871 * @param {Object} meta The JSON metadata
9876 * Fires when Records have been added to the Store
9877 * @param {Store} this
9878 * @param {Roo.data.Record[]} records The array of Records added
9879 * @param {Number} index The index at which the record(s) were added
9884 * Fires when a Record has been removed from the Store
9885 * @param {Store} this
9886 * @param {Roo.data.Record} record The Record that was removed
9887 * @param {Number} index The index at which the record was removed
9892 * Fires when a Record has been updated
9893 * @param {Store} this
9894 * @param {Roo.data.Record} record The Record that was updated
9895 * @param {String} operation The update operation being performed. Value may be one of:
9897 Roo.data.Record.EDIT
9898 Roo.data.Record.REJECT
9899 Roo.data.Record.COMMIT
9905 * Fires when the data cache has been cleared.
9906 * @param {Store} this
9911 * Fires before a request is made for a new data object. If the beforeload handler returns false
9912 * the load action will be canceled.
9913 * @param {Store} this
9914 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9918 * @event beforeloadadd
9919 * Fires after a new set of Records has been loaded.
9920 * @param {Store} this
9921 * @param {Roo.data.Record[]} records The Records that were loaded
9922 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9924 beforeloadadd : true,
9927 * Fires after a new set of Records has been loaded, before they are added to the store.
9928 * @param {Store} this
9929 * @param {Roo.data.Record[]} records The Records that were loaded
9930 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9931 * @params {Object} return from reader
9935 * @event loadexception
9936 * Fires if an exception occurs in the Proxy during loading.
9937 * Called with the signature of the Proxy's "loadexception" event.
9938 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9941 * @param {Object} return from JsonData.reader() - success, totalRecords, records
9942 * @param {Object} load options
9943 * @param {Object} jsonData from your request (normally this contains the Exception)
9945 loadexception : true
9949 this.proxy = Roo.factory(this.proxy, Roo.data);
9950 this.proxy.xmodule = this.xmodule || false;
9951 this.relayEvents(this.proxy, ["loadexception"]);
9953 this.sortToggle = {};
9954 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9956 Roo.data.Store.superclass.constructor.call(this);
9958 if(this.inlineData){
9959 this.loadData(this.inlineData);
9960 delete this.inlineData;
9964 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9966 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
9967 * without a remote query - used by combo/forms at present.
9971 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9974 * @cfg {Array} data Inline data to be loaded when the store is initialized.
9977 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9978 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9981 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9982 * on any HTTP request
9985 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9988 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9992 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9993 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9998 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9999 * loaded or when a record is removed. (defaults to false).
10001 pruneModifiedRecords : false,
10004 lastOptions : null,
10007 * Add Records to the Store and fires the add event.
10008 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10010 add : function(records){
10011 records = [].concat(records);
10012 for(var i = 0, len = records.length; i < len; i++){
10013 records[i].join(this);
10015 var index = this.data.length;
10016 this.data.addAll(records);
10017 this.fireEvent("add", this, records, index);
10021 * Remove a Record from the Store and fires the remove event.
10022 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10024 remove : function(record){
10025 var index = this.data.indexOf(record);
10026 this.data.removeAt(index);
10027 if(this.pruneModifiedRecords){
10028 this.modified.remove(record);
10030 this.fireEvent("remove", this, record, index);
10034 * Remove all Records from the Store and fires the clear event.
10036 removeAll : function(){
10038 if(this.pruneModifiedRecords){
10039 this.modified = [];
10041 this.fireEvent("clear", this);
10045 * Inserts Records to the Store at the given index and fires the add event.
10046 * @param {Number} index The start index at which to insert the passed Records.
10047 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10049 insert : function(index, records){
10050 records = [].concat(records);
10051 for(var i = 0, len = records.length; i < len; i++){
10052 this.data.insert(index, records[i]);
10053 records[i].join(this);
10055 this.fireEvent("add", this, records, index);
10059 * Get the index within the cache of the passed Record.
10060 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10061 * @return {Number} The index of the passed Record. Returns -1 if not found.
10063 indexOf : function(record){
10064 return this.data.indexOf(record);
10068 * Get the index within the cache of the Record with the passed id.
10069 * @param {String} id The id of the Record to find.
10070 * @return {Number} The index of the Record. Returns -1 if not found.
10072 indexOfId : function(id){
10073 return this.data.indexOfKey(id);
10077 * Get the Record with the specified id.
10078 * @param {String} id The id of the Record to find.
10079 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10081 getById : function(id){
10082 return this.data.key(id);
10086 * Get the Record at the specified index.
10087 * @param {Number} index The index of the Record to find.
10088 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10090 getAt : function(index){
10091 return this.data.itemAt(index);
10095 * Returns a range of Records between specified indices.
10096 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10097 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10098 * @return {Roo.data.Record[]} An array of Records
10100 getRange : function(start, end){
10101 return this.data.getRange(start, end);
10105 storeOptions : function(o){
10106 o = Roo.apply({}, o);
10109 this.lastOptions = o;
10113 * Loads the Record cache from the configured Proxy using the configured Reader.
10115 * If using remote paging, then the first load call must specify the <em>start</em>
10116 * and <em>limit</em> properties in the options.params property to establish the initial
10117 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10119 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10120 * and this call will return before the new data has been loaded. Perform any post-processing
10121 * in a callback function, or in a "load" event handler.</strong>
10123 * @param {Object} options An object containing properties which control loading options:<ul>
10124 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10125 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10126 * passed the following arguments:<ul>
10127 * <li>r : Roo.data.Record[]</li>
10128 * <li>options: Options object from the load call</li>
10129 * <li>success: Boolean success indicator</li></ul></li>
10130 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10131 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10134 load : function(options){
10135 options = options || {};
10136 if(this.fireEvent("beforeload", this, options) !== false){
10137 this.storeOptions(options);
10138 var p = Roo.apply(options.params || {}, this.baseParams);
10139 // if meta was not loaded from remote source.. try requesting it.
10140 if (!this.reader.metaFromRemote) {
10141 p._requestMeta = 1;
10143 if(this.sortInfo && this.remoteSort){
10144 var pn = this.paramNames;
10145 p[pn["sort"]] = this.sortInfo.field;
10146 p[pn["dir"]] = this.sortInfo.direction;
10148 if (this.multiSort) {
10149 var pn = this.paramNames;
10150 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10153 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10158 * Reloads the Record cache from the configured Proxy using the configured Reader and
10159 * the options from the last load operation performed.
10160 * @param {Object} options (optional) An object containing properties which may override the options
10161 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10162 * the most recently used options are reused).
10164 reload : function(options){
10165 this.load(Roo.applyIf(options||{}, this.lastOptions));
10169 // Called as a callback by the Reader during a load operation.
10170 loadRecords : function(o, options, success){
10171 if(!o || success === false){
10172 if(success !== false){
10173 this.fireEvent("load", this, [], options, o);
10175 if(options.callback){
10176 options.callback.call(options.scope || this, [], options, false);
10180 // if data returned failure - throw an exception.
10181 if (o.success === false) {
10182 // show a message if no listener is registered.
10183 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10184 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10186 // loadmask wil be hooked into this..
10187 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10190 var r = o.records, t = o.totalRecords || r.length;
10192 this.fireEvent("beforeloadadd", this, r, options, o);
10194 if(!options || options.add !== true){
10195 if(this.pruneModifiedRecords){
10196 this.modified = [];
10198 for(var i = 0, len = r.length; i < len; i++){
10202 this.data = this.snapshot;
10203 delete this.snapshot;
10206 this.data.addAll(r);
10207 this.totalLength = t;
10209 this.fireEvent("datachanged", this);
10211 this.totalLength = Math.max(t, this.data.length+r.length);
10214 this.fireEvent("load", this, r, options, o);
10215 if(options.callback){
10216 options.callback.call(options.scope || this, r, options, true);
10222 * Loads data from a passed data block. A Reader which understands the format of the data
10223 * must have been configured in the constructor.
10224 * @param {Object} data The data block from which to read the Records. The format of the data expected
10225 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10226 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10228 loadData : function(o, append){
10229 var r = this.reader.readRecords(o);
10230 this.loadRecords(r, {add: append}, true);
10234 * Gets the number of cached records.
10236 * <em>If using paging, this may not be the total size of the dataset. If the data object
10237 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10238 * the data set size</em>
10240 getCount : function(){
10241 return this.data.length || 0;
10245 * Gets the total number of records in the dataset as returned by the server.
10247 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10248 * the dataset size</em>
10250 getTotalCount : function(){
10251 return this.totalLength || 0;
10255 * Returns the sort state of the Store as an object with two properties:
10257 field {String} The name of the field by which the Records are sorted
10258 direction {String} The sort order, "ASC" or "DESC"
10261 getSortState : function(){
10262 return this.sortInfo;
10266 applySort : function(){
10267 if(this.sortInfo && !this.remoteSort){
10268 var s = this.sortInfo, f = s.field;
10269 var st = this.fields.get(f).sortType;
10270 var fn = function(r1, r2){
10271 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10272 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10274 this.data.sort(s.direction, fn);
10275 if(this.snapshot && this.snapshot != this.data){
10276 this.snapshot.sort(s.direction, fn);
10282 * Sets the default sort column and order to be used by the next load operation.
10283 * @param {String} fieldName The name of the field to sort by.
10284 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10286 setDefaultSort : function(field, dir){
10287 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10291 * Sort the Records.
10292 * If remote sorting is used, the sort is performed on the server, and the cache is
10293 * reloaded. If local sorting is used, the cache is sorted internally.
10294 * @param {String} fieldName The name of the field to sort by.
10295 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10297 sort : function(fieldName, dir){
10298 var f = this.fields.get(fieldName);
10300 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10302 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10303 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10308 this.sortToggle[f.name] = dir;
10309 this.sortInfo = {field: f.name, direction: dir};
10310 if(!this.remoteSort){
10312 this.fireEvent("datachanged", this);
10314 this.load(this.lastOptions);
10319 * Calls the specified function for each of the Records in the cache.
10320 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10321 * Returning <em>false</em> aborts and exits the iteration.
10322 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10324 each : function(fn, scope){
10325 this.data.each(fn, scope);
10329 * Gets all records modified since the last commit. Modified records are persisted across load operations
10330 * (e.g., during paging).
10331 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10333 getModifiedRecords : function(){
10334 return this.modified;
10338 createFilterFn : function(property, value, anyMatch){
10339 if(!value.exec){ // not a regex
10340 value = String(value);
10341 if(value.length == 0){
10344 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10346 return function(r){
10347 return value.test(r.data[property]);
10352 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10353 * @param {String} property A field on your records
10354 * @param {Number} start The record index to start at (defaults to 0)
10355 * @param {Number} end The last record index to include (defaults to length - 1)
10356 * @return {Number} The sum
10358 sum : function(property, start, end){
10359 var rs = this.data.items, v = 0;
10360 start = start || 0;
10361 end = (end || end === 0) ? end : rs.length-1;
10363 for(var i = start; i <= end; i++){
10364 v += (rs[i].data[property] || 0);
10370 * Filter the records by a specified property.
10371 * @param {String} field A field on your records
10372 * @param {String/RegExp} value Either a string that the field
10373 * should start with or a RegExp to test against the field
10374 * @param {Boolean} anyMatch True to match any part not just the beginning
10376 filter : function(property, value, anyMatch){
10377 var fn = this.createFilterFn(property, value, anyMatch);
10378 return fn ? this.filterBy(fn) : this.clearFilter();
10382 * Filter by a function. The specified function will be called with each
10383 * record in this data source. If the function returns true the record is included,
10384 * otherwise it is filtered.
10385 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10386 * @param {Object} scope (optional) The scope of the function (defaults to this)
10388 filterBy : function(fn, scope){
10389 this.snapshot = this.snapshot || this.data;
10390 this.data = this.queryBy(fn, scope||this);
10391 this.fireEvent("datachanged", this);
10395 * Query the records by a specified property.
10396 * @param {String} field A field on your records
10397 * @param {String/RegExp} value Either a string that the field
10398 * should start with or a RegExp to test against the field
10399 * @param {Boolean} anyMatch True to match any part not just the beginning
10400 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10402 query : function(property, value, anyMatch){
10403 var fn = this.createFilterFn(property, value, anyMatch);
10404 return fn ? this.queryBy(fn) : this.data.clone();
10408 * Query by a function. The specified function will be called with each
10409 * record in this data source. If the function returns true the record is included
10411 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10412 * @param {Object} scope (optional) The scope of the function (defaults to this)
10413 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10415 queryBy : function(fn, scope){
10416 var data = this.snapshot || this.data;
10417 return data.filterBy(fn, scope||this);
10421 * Collects unique values for a particular dataIndex from this store.
10422 * @param {String} dataIndex The property to collect
10423 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10424 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10425 * @return {Array} An array of the unique values
10427 collect : function(dataIndex, allowNull, bypassFilter){
10428 var d = (bypassFilter === true && this.snapshot) ?
10429 this.snapshot.items : this.data.items;
10430 var v, sv, r = [], l = {};
10431 for(var i = 0, len = d.length; i < len; i++){
10432 v = d[i].data[dataIndex];
10434 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10443 * Revert to a view of the Record cache with no filtering applied.
10444 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10446 clearFilter : function(suppressEvent){
10447 if(this.snapshot && this.snapshot != this.data){
10448 this.data = this.snapshot;
10449 delete this.snapshot;
10450 if(suppressEvent !== true){
10451 this.fireEvent("datachanged", this);
10457 afterEdit : function(record){
10458 if(this.modified.indexOf(record) == -1){
10459 this.modified.push(record);
10461 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10465 afterReject : function(record){
10466 this.modified.remove(record);
10467 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10471 afterCommit : function(record){
10472 this.modified.remove(record);
10473 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10477 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10478 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10480 commitChanges : function(){
10481 var m = this.modified.slice(0);
10482 this.modified = [];
10483 for(var i = 0, len = m.length; i < len; i++){
10489 * Cancel outstanding changes on all changed records.
10491 rejectChanges : function(){
10492 var m = this.modified.slice(0);
10493 this.modified = [];
10494 for(var i = 0, len = m.length; i < len; i++){
10499 onMetaChange : function(meta, rtype, o){
10500 this.recordType = rtype;
10501 this.fields = rtype.prototype.fields;
10502 delete this.snapshot;
10503 this.sortInfo = meta.sortInfo || this.sortInfo;
10504 this.modified = [];
10505 this.fireEvent('metachange', this, this.reader.meta);
10508 moveIndex : function(data, type)
10510 var index = this.indexOf(data);
10512 var newIndex = index + type;
10516 this.insert(newIndex, data);
10521 * Ext JS Library 1.1.1
10522 * Copyright(c) 2006-2007, Ext JS, LLC.
10524 * Originally Released Under LGPL - original licence link has changed is not relivant.
10527 * <script type="text/javascript">
10531 * @class Roo.data.SimpleStore
10532 * @extends Roo.data.Store
10533 * Small helper class to make creating Stores from Array data easier.
10534 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10535 * @cfg {Array} fields An array of field definition objects, or field name strings.
10536 * @cfg {Array} data The multi-dimensional array of data
10538 * @param {Object} config
10540 Roo.data.SimpleStore = function(config){
10541 Roo.data.SimpleStore.superclass.constructor.call(this, {
10543 reader: new Roo.data.ArrayReader({
10546 Roo.data.Record.create(config.fields)
10548 proxy : new Roo.data.MemoryProxy(config.data)
10552 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10554 * Ext JS Library 1.1.1
10555 * Copyright(c) 2006-2007, Ext JS, LLC.
10557 * Originally Released Under LGPL - original licence link has changed is not relivant.
10560 * <script type="text/javascript">
10565 * @extends Roo.data.Store
10566 * @class Roo.data.JsonStore
10567 * Small helper class to make creating Stores for JSON data easier. <br/>
10569 var store = new Roo.data.JsonStore({
10570 url: 'get-images.php',
10572 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10575 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10576 * JsonReader and HttpProxy (unless inline data is provided).</b>
10577 * @cfg {Array} fields An array of field definition objects, or field name strings.
10579 * @param {Object} config
10581 Roo.data.JsonStore = function(c){
10582 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10583 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10584 reader: new Roo.data.JsonReader(c, c.fields)
10587 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10589 * Ext JS Library 1.1.1
10590 * Copyright(c) 2006-2007, Ext JS, LLC.
10592 * Originally Released Under LGPL - original licence link has changed is not relivant.
10595 * <script type="text/javascript">
10599 Roo.data.Field = function(config){
10600 if(typeof config == "string"){
10601 config = {name: config};
10603 Roo.apply(this, config);
10606 this.type = "auto";
10609 var st = Roo.data.SortTypes;
10610 // named sortTypes are supported, here we look them up
10611 if(typeof this.sortType == "string"){
10612 this.sortType = st[this.sortType];
10615 // set default sortType for strings and dates
10616 if(!this.sortType){
10619 this.sortType = st.asUCString;
10622 this.sortType = st.asDate;
10625 this.sortType = st.none;
10630 var stripRe = /[\$,%]/g;
10632 // prebuilt conversion function for this field, instead of
10633 // switching every time we're reading a value
10635 var cv, dateFormat = this.dateFormat;
10640 cv = function(v){ return v; };
10643 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10647 return v !== undefined && v !== null && v !== '' ?
10648 parseInt(String(v).replace(stripRe, ""), 10) : '';
10653 return v !== undefined && v !== null && v !== '' ?
10654 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10659 cv = function(v){ return v === true || v === "true" || v == 1; };
10666 if(v instanceof Date){
10670 if(dateFormat == "timestamp"){
10671 return new Date(v*1000);
10673 return Date.parseDate(v, dateFormat);
10675 var parsed = Date.parse(v);
10676 return parsed ? new Date(parsed) : null;
10685 Roo.data.Field.prototype = {
10693 * Ext JS Library 1.1.1
10694 * Copyright(c) 2006-2007, Ext JS, LLC.
10696 * Originally Released Under LGPL - original licence link has changed is not relivant.
10699 * <script type="text/javascript">
10702 // Base class for reading structured data from a data source. This class is intended to be
10703 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10706 * @class Roo.data.DataReader
10707 * Base class for reading structured data from a data source. This class is intended to be
10708 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10711 Roo.data.DataReader = function(meta, recordType){
10715 this.recordType = recordType instanceof Array ?
10716 Roo.data.Record.create(recordType) : recordType;
10719 Roo.data.DataReader.prototype = {
10721 * Create an empty record
10722 * @param {Object} data (optional) - overlay some values
10723 * @return {Roo.data.Record} record created.
10725 newRow : function(d) {
10727 this.recordType.prototype.fields.each(function(c) {
10729 case 'int' : da[c.name] = 0; break;
10730 case 'date' : da[c.name] = new Date(); break;
10731 case 'float' : da[c.name] = 0.0; break;
10732 case 'boolean' : da[c.name] = false; break;
10733 default : da[c.name] = ""; break;
10737 return new this.recordType(Roo.apply(da, d));
10742 * Ext JS Library 1.1.1
10743 * Copyright(c) 2006-2007, Ext JS, LLC.
10745 * Originally Released Under LGPL - original licence link has changed is not relivant.
10748 * <script type="text/javascript">
10752 * @class Roo.data.DataProxy
10753 * @extends Roo.data.Observable
10754 * This class is an abstract base class for implementations which provide retrieval of
10755 * unformatted data objects.<br>
10757 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10758 * (of the appropriate type which knows how to parse the data object) to provide a block of
10759 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10761 * Custom implementations must implement the load method as described in
10762 * {@link Roo.data.HttpProxy#load}.
10764 Roo.data.DataProxy = function(){
10767 * @event beforeload
10768 * Fires before a network request is made to retrieve a data object.
10769 * @param {Object} This DataProxy object.
10770 * @param {Object} params The params parameter to the load function.
10775 * Fires before the load method's callback is called.
10776 * @param {Object} This DataProxy object.
10777 * @param {Object} o The data object.
10778 * @param {Object} arg The callback argument object passed to the load function.
10782 * @event loadexception
10783 * Fires if an Exception occurs during data retrieval.
10784 * @param {Object} This DataProxy object.
10785 * @param {Object} o The data object.
10786 * @param {Object} arg The callback argument object passed to the load function.
10787 * @param {Object} e The Exception.
10789 loadexception : true
10791 Roo.data.DataProxy.superclass.constructor.call(this);
10794 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10797 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10801 * Ext JS Library 1.1.1
10802 * Copyright(c) 2006-2007, Ext JS, LLC.
10804 * Originally Released Under LGPL - original licence link has changed is not relivant.
10807 * <script type="text/javascript">
10810 * @class Roo.data.MemoryProxy
10811 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10812 * to the Reader when its load method is called.
10814 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10816 Roo.data.MemoryProxy = function(data){
10820 Roo.data.MemoryProxy.superclass.constructor.call(this);
10824 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10826 * Load data from the requested source (in this case an in-memory
10827 * data object passed to the constructor), read the data object into
10828 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10829 * process that block using the passed callback.
10830 * @param {Object} params This parameter is not used by the MemoryProxy class.
10831 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10832 * object into a block of Roo.data.Records.
10833 * @param {Function} callback The function into which to pass the block of Roo.data.records.
10834 * The function must be passed <ul>
10835 * <li>The Record block object</li>
10836 * <li>The "arg" argument from the load function</li>
10837 * <li>A boolean success indicator</li>
10839 * @param {Object} scope The scope in which to call the callback
10840 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10842 load : function(params, reader, callback, scope, arg){
10843 params = params || {};
10846 result = reader.readRecords(this.data);
10848 this.fireEvent("loadexception", this, arg, null, e);
10849 callback.call(scope, null, arg, false);
10852 callback.call(scope, result, arg, true);
10856 update : function(params, records){
10861 * Ext JS Library 1.1.1
10862 * Copyright(c) 2006-2007, Ext JS, LLC.
10864 * Originally Released Under LGPL - original licence link has changed is not relivant.
10867 * <script type="text/javascript">
10870 * @class Roo.data.HttpProxy
10871 * @extends Roo.data.DataProxy
10872 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10873 * configured to reference a certain URL.<br><br>
10875 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10876 * from which the running page was served.<br><br>
10878 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10880 * Be aware that to enable the browser to parse an XML document, the server must set
10881 * the Content-Type header in the HTTP response to "text/xml".
10883 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10884 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
10885 * will be used to make the request.
10887 Roo.data.HttpProxy = function(conn){
10888 Roo.data.HttpProxy.superclass.constructor.call(this);
10889 // is conn a conn config or a real conn?
10891 this.useAjax = !conn || !conn.events;
10895 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10896 // thse are take from connection...
10899 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10902 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10903 * extra parameters to each request made by this object. (defaults to undefined)
10906 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10907 * to each request made by this object. (defaults to undefined)
10910 * @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)
10913 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10916 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10922 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10926 * Return the {@link Roo.data.Connection} object being used by this Proxy.
10927 * @return {Connection} The Connection object. This object may be used to subscribe to events on
10928 * a finer-grained basis than the DataProxy events.
10930 getConnection : function(){
10931 return this.useAjax ? Roo.Ajax : this.conn;
10935 * Load data from the configured {@link Roo.data.Connection}, read the data object into
10936 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10937 * process that block using the passed callback.
10938 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10939 * for the request to the remote server.
10940 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10941 * object into a block of Roo.data.Records.
10942 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10943 * The function must be passed <ul>
10944 * <li>The Record block object</li>
10945 * <li>The "arg" argument from the load function</li>
10946 * <li>A boolean success indicator</li>
10948 * @param {Object} scope The scope in which to call the callback
10949 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10951 load : function(params, reader, callback, scope, arg){
10952 if(this.fireEvent("beforeload", this, params) !== false){
10954 params : params || {},
10956 callback : callback,
10961 callback : this.loadResponse,
10965 Roo.applyIf(o, this.conn);
10966 if(this.activeRequest){
10967 Roo.Ajax.abort(this.activeRequest);
10969 this.activeRequest = Roo.Ajax.request(o);
10971 this.conn.request(o);
10974 callback.call(scope||this, null, arg, false);
10979 loadResponse : function(o, success, response){
10980 delete this.activeRequest;
10982 this.fireEvent("loadexception", this, o, response);
10983 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10988 result = o.reader.read(response);
10990 this.fireEvent("loadexception", this, o, response, e);
10991 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10995 this.fireEvent("load", this, o, o.request.arg);
10996 o.request.callback.call(o.request.scope, result, o.request.arg, true);
11000 update : function(dataSet){
11005 updateResponse : function(dataSet){
11010 * Ext JS Library 1.1.1
11011 * Copyright(c) 2006-2007, Ext JS, LLC.
11013 * Originally Released Under LGPL - original licence link has changed is not relivant.
11016 * <script type="text/javascript">
11020 * @class Roo.data.ScriptTagProxy
11021 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11022 * other than the originating domain of the running page.<br><br>
11024 * <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
11025 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11027 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11028 * source code that is used as the source inside a <script> tag.<br><br>
11030 * In order for the browser to process the returned data, the server must wrap the data object
11031 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11032 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11033 * depending on whether the callback name was passed:
11036 boolean scriptTag = false;
11037 String cb = request.getParameter("callback");
11040 response.setContentType("text/javascript");
11042 response.setContentType("application/x-json");
11044 Writer out = response.getWriter();
11046 out.write(cb + "(");
11048 out.print(dataBlock.toJsonString());
11055 * @param {Object} config A configuration object.
11057 Roo.data.ScriptTagProxy = function(config){
11058 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11059 Roo.apply(this, config);
11060 this.head = document.getElementsByTagName("head")[0];
11063 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11065 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11067 * @cfg {String} url The URL from which to request the data object.
11070 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11074 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11075 * the server the name of the callback function set up by the load call to process the returned data object.
11076 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11077 * javascript output which calls this named function passing the data object as its only parameter.
11079 callbackParam : "callback",
11081 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11082 * name to the request.
11087 * Load data from the configured URL, read the data object into
11088 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11089 * process that block using the passed callback.
11090 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11091 * for the request to the remote server.
11092 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11093 * object into a block of Roo.data.Records.
11094 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11095 * The function must be passed <ul>
11096 * <li>The Record block object</li>
11097 * <li>The "arg" argument from the load function</li>
11098 * <li>A boolean success indicator</li>
11100 * @param {Object} scope The scope in which to call the callback
11101 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11103 load : function(params, reader, callback, scope, arg){
11104 if(this.fireEvent("beforeload", this, params) !== false){
11106 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11108 var url = this.url;
11109 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11111 url += "&_dc=" + (new Date().getTime());
11113 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11116 cb : "stcCallback"+transId,
11117 scriptId : "stcScript"+transId,
11121 callback : callback,
11127 window[trans.cb] = function(o){
11128 conn.handleResponse(o, trans);
11131 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11133 if(this.autoAbort !== false){
11137 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11139 var script = document.createElement("script");
11140 script.setAttribute("src", url);
11141 script.setAttribute("type", "text/javascript");
11142 script.setAttribute("id", trans.scriptId);
11143 this.head.appendChild(script);
11145 this.trans = trans;
11147 callback.call(scope||this, null, arg, false);
11152 isLoading : function(){
11153 return this.trans ? true : false;
11157 * Abort the current server request.
11159 abort : function(){
11160 if(this.isLoading()){
11161 this.destroyTrans(this.trans);
11166 destroyTrans : function(trans, isLoaded){
11167 this.head.removeChild(document.getElementById(trans.scriptId));
11168 clearTimeout(trans.timeoutId);
11170 window[trans.cb] = undefined;
11172 delete window[trans.cb];
11175 // if hasn't been loaded, wait for load to remove it to prevent script error
11176 window[trans.cb] = function(){
11177 window[trans.cb] = undefined;
11179 delete window[trans.cb];
11186 handleResponse : function(o, trans){
11187 this.trans = false;
11188 this.destroyTrans(trans, true);
11191 result = trans.reader.readRecords(o);
11193 this.fireEvent("loadexception", this, o, trans.arg, e);
11194 trans.callback.call(trans.scope||window, null, trans.arg, false);
11197 this.fireEvent("load", this, o, trans.arg);
11198 trans.callback.call(trans.scope||window, result, trans.arg, true);
11202 handleFailure : function(trans){
11203 this.trans = false;
11204 this.destroyTrans(trans, false);
11205 this.fireEvent("loadexception", this, null, trans.arg);
11206 trans.callback.call(trans.scope||window, null, trans.arg, false);
11210 * Ext JS Library 1.1.1
11211 * Copyright(c) 2006-2007, Ext JS, LLC.
11213 * Originally Released Under LGPL - original licence link has changed is not relivant.
11216 * <script type="text/javascript">
11220 * @class Roo.data.JsonReader
11221 * @extends Roo.data.DataReader
11222 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11223 * based on mappings in a provided Roo.data.Record constructor.
11225 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11226 * in the reply previously.
11231 var RecordDef = Roo.data.Record.create([
11232 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11233 {name: 'occupation'} // This field will use "occupation" as the mapping.
11235 var myReader = new Roo.data.JsonReader({
11236 totalProperty: "results", // The property which contains the total dataset size (optional)
11237 root: "rows", // The property which contains an Array of row objects
11238 id: "id" // The property within each row object that provides an ID for the record (optional)
11242 * This would consume a JSON file like this:
11244 { 'results': 2, 'rows': [
11245 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11246 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11249 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11250 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11251 * paged from the remote server.
11252 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11253 * @cfg {String} root name of the property which contains the Array of row objects.
11254 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11255 * @cfg {Array} fields Array of field definition objects
11257 * Create a new JsonReader
11258 * @param {Object} meta Metadata configuration options
11259 * @param {Object} recordType Either an Array of field definition objects,
11260 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11262 Roo.data.JsonReader = function(meta, recordType){
11265 // set some defaults:
11266 Roo.applyIf(meta, {
11267 totalProperty: 'total',
11268 successProperty : 'success',
11273 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11275 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11278 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11279 * Used by Store query builder to append _requestMeta to params.
11282 metaFromRemote : false,
11284 * This method is only used by a DataProxy which has retrieved data from a remote server.
11285 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11286 * @return {Object} data A data block which is used by an Roo.data.Store object as
11287 * a cache of Roo.data.Records.
11289 read : function(response){
11290 var json = response.responseText;
11292 var o = /* eval:var:o */ eval("("+json+")");
11294 throw {message: "JsonReader.read: Json object not found"};
11300 this.metaFromRemote = true;
11301 this.meta = o.metaData;
11302 this.recordType = Roo.data.Record.create(o.metaData.fields);
11303 this.onMetaChange(this.meta, this.recordType, o);
11305 return this.readRecords(o);
11308 // private function a store will implement
11309 onMetaChange : function(meta, recordType, o){
11316 simpleAccess: function(obj, subsc) {
11323 getJsonAccessor: function(){
11325 return function(expr) {
11327 return(re.test(expr))
11328 ? new Function("obj", "return obj." + expr)
11333 return Roo.emptyFn;
11338 * Create a data block containing Roo.data.Records from an XML document.
11339 * @param {Object} o An object which contains an Array of row objects in the property specified
11340 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11341 * which contains the total size of the dataset.
11342 * @return {Object} data A data block which is used by an Roo.data.Store object as
11343 * a cache of Roo.data.Records.
11345 readRecords : function(o){
11347 * After any data loads, the raw JSON data is available for further custom processing.
11351 var s = this.meta, Record = this.recordType,
11352 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11354 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11356 if(s.totalProperty) {
11357 this.getTotal = this.getJsonAccessor(s.totalProperty);
11359 if(s.successProperty) {
11360 this.getSuccess = this.getJsonAccessor(s.successProperty);
11362 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11364 var g = this.getJsonAccessor(s.id);
11365 this.getId = function(rec) {
11367 return (r === undefined || r === "") ? null : r;
11370 this.getId = function(){return null;};
11373 for(var jj = 0; jj < fl; jj++){
11375 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11376 this.ef[jj] = this.getJsonAccessor(map);
11380 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11381 if(s.totalProperty){
11382 var vt = parseInt(this.getTotal(o), 10);
11387 if(s.successProperty){
11388 var vs = this.getSuccess(o);
11389 if(vs === false || vs === 'false'){
11394 for(var i = 0; i < c; i++){
11397 var id = this.getId(n);
11398 for(var j = 0; j < fl; j++){
11400 var v = this.ef[j](n);
11402 Roo.log('missing convert for ' + f.name);
11406 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11408 var record = new Record(values, id);
11410 records[i] = record;
11416 totalRecords : totalRecords
11421 * Ext JS Library 1.1.1
11422 * Copyright(c) 2006-2007, Ext JS, LLC.
11424 * Originally Released Under LGPL - original licence link has changed is not relivant.
11427 * <script type="text/javascript">
11431 * @class Roo.data.ArrayReader
11432 * @extends Roo.data.DataReader
11433 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11434 * Each element of that Array represents a row of data fields. The
11435 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11436 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11440 var RecordDef = Roo.data.Record.create([
11441 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11442 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11444 var myReader = new Roo.data.ArrayReader({
11445 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11449 * This would consume an Array like this:
11451 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11453 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11455 * Create a new JsonReader
11456 * @param {Object} meta Metadata configuration options.
11457 * @param {Object} recordType Either an Array of field definition objects
11458 * as specified to {@link Roo.data.Record#create},
11459 * or an {@link Roo.data.Record} object
11460 * created using {@link Roo.data.Record#create}.
11462 Roo.data.ArrayReader = function(meta, recordType){
11463 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11466 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11468 * Create a data block containing Roo.data.Records from an XML document.
11469 * @param {Object} o An Array of row objects which represents the dataset.
11470 * @return {Object} data A data block which is used by an Roo.data.Store object as
11471 * a cache of Roo.data.Records.
11473 readRecords : function(o){
11474 var sid = this.meta ? this.meta.id : null;
11475 var recordType = this.recordType, fields = recordType.prototype.fields;
11478 for(var i = 0; i < root.length; i++){
11481 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11482 for(var j = 0, jlen = fields.length; j < jlen; j++){
11483 var f = fields.items[j];
11484 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11485 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11487 values[f.name] = v;
11489 var record = new recordType(values, id);
11491 records[records.length] = record;
11495 totalRecords : records.length
11504 * @class Roo.bootstrap.ComboBox
11505 * @extends Roo.bootstrap.TriggerField
11506 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11507 * @cfg {Boolean} append (true|false) default false
11508 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11509 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11510 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11511 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11512 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11513 * @cfg {Boolean} animate default true
11514 * @cfg {Boolean} emptyResultText only for touch device
11515 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11517 * Create a new ComboBox.
11518 * @param {Object} config Configuration options
11520 Roo.bootstrap.ComboBox = function(config){
11521 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11525 * Fires when the dropdown list is expanded
11526 * @param {Roo.bootstrap.ComboBox} combo This combo box
11531 * Fires when the dropdown list is collapsed
11532 * @param {Roo.bootstrap.ComboBox} combo This combo box
11536 * @event beforeselect
11537 * Fires before a list item is selected. Return false to cancel the selection.
11538 * @param {Roo.bootstrap.ComboBox} combo This combo box
11539 * @param {Roo.data.Record} record The data record returned from the underlying store
11540 * @param {Number} index The index of the selected item in the dropdown list
11542 'beforeselect' : true,
11545 * Fires when a list item is selected
11546 * @param {Roo.bootstrap.ComboBox} combo This combo box
11547 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11548 * @param {Number} index The index of the selected item in the dropdown list
11552 * @event beforequery
11553 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11554 * The event object passed has these properties:
11555 * @param {Roo.bootstrap.ComboBox} combo This combo box
11556 * @param {String} query The query
11557 * @param {Boolean} forceAll true to force "all" query
11558 * @param {Boolean} cancel true to cancel the query
11559 * @param {Object} e The query event object
11561 'beforequery': true,
11564 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11565 * @param {Roo.bootstrap.ComboBox} combo This combo box
11570 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11571 * @param {Roo.bootstrap.ComboBox} combo This combo box
11572 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11577 * Fires when the remove value from the combobox array
11578 * @param {Roo.bootstrap.ComboBox} combo This combo box
11582 * @event specialfilter
11583 * Fires when specialfilter
11584 * @param {Roo.bootstrap.ComboBox} combo This combo box
11586 'specialfilter' : true,
11589 * Fires when tick the element
11590 * @param {Roo.bootstrap.ComboBox} combo This combo box
11594 * @event touchviewdisplay
11595 * Fires when touch view require special display (default is using displayField)
11596 * @param {Roo.bootstrap.ComboBox} combo This combo box
11597 * @param {Object} cfg set html .
11599 'touchviewdisplay' : true
11604 this.tickItems = [];
11606 this.selectedIndex = -1;
11607 if(this.mode == 'local'){
11608 if(config.queryDelay === undefined){
11609 this.queryDelay = 10;
11611 if(config.minChars === undefined){
11617 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11620 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11621 * rendering into an Roo.Editor, defaults to false)
11624 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11625 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11628 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11631 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11632 * the dropdown list (defaults to undefined, with no header element)
11636 * @cfg {String/Roo.Template} tpl The template to use to render the output
11640 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11642 listWidth: undefined,
11644 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11645 * mode = 'remote' or 'text' if mode = 'local')
11647 displayField: undefined,
11650 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11651 * mode = 'remote' or 'value' if mode = 'local').
11652 * Note: use of a valueField requires the user make a selection
11653 * in order for a value to be mapped.
11655 valueField: undefined,
11659 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11660 * field's data value (defaults to the underlying DOM element's name)
11662 hiddenName: undefined,
11664 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11668 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11670 selectedClass: 'active',
11673 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11677 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11678 * anchor positions (defaults to 'tl-bl')
11680 listAlign: 'tl-bl?',
11682 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11686 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11687 * query specified by the allQuery config option (defaults to 'query')
11689 triggerAction: 'query',
11691 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11692 * (defaults to 4, does not apply if editable = false)
11696 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11697 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11701 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11702 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11706 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11707 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11711 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11712 * when editable = true (defaults to false)
11714 selectOnFocus:false,
11716 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11718 queryParam: 'query',
11720 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
11721 * when mode = 'remote' (defaults to 'Loading...')
11723 loadingText: 'Loading...',
11725 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11729 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11733 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11734 * traditional select (defaults to true)
11738 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11742 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11746 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11747 * listWidth has a higher value)
11751 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11752 * allow the user to set arbitrary text into the field (defaults to false)
11754 forceSelection:false,
11756 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11757 * if typeAhead = true (defaults to 250)
11759 typeAheadDelay : 250,
11761 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11762 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11764 valueNotFoundText : undefined,
11766 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11768 blockFocus : false,
11771 * @cfg {Boolean} disableClear Disable showing of clear button.
11773 disableClear : false,
11775 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
11777 alwaysQuery : false,
11780 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
11785 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11787 invalidClass : "has-warning",
11790 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11792 validClass : "has-success",
11795 * @cfg {Boolean} specialFilter (true|false) special filter default false
11797 specialFilter : false,
11800 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11802 mobileTouchView : true,
11814 btnPosition : 'right',
11815 triggerList : true,
11816 showToggleBtn : true,
11818 emptyResultText: 'Empty',
11819 triggerText : 'Select',
11821 // element that contains real text value.. (when hidden is used..)
11823 getAutoCreate : function()
11831 if(Roo.isTouch && this.mobileTouchView){
11832 cfg = this.getAutoCreateTouchView();
11839 if(!this.tickable){
11840 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11845 * ComboBox with tickable selections
11848 var align = this.labelAlign || this.parentLabelAlign();
11851 cls : 'form-group roo-combobox-tickable' //input-group
11856 cls : 'tickable-buttons',
11861 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11862 html : this.triggerText
11868 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11875 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11882 buttons.cn.unshift({
11884 cls: 'select2-search-field-input'
11890 Roo.each(buttons.cn, function(c){
11892 c.cls += ' btn-' + _this.size;
11895 if (_this.disabled) {
11906 cls: 'form-hidden-field'
11910 cls: 'select2-choices',
11914 cls: 'select2-search-field',
11926 cls: 'select2-container input-group select2-container-multi',
11931 // cls: 'typeahead typeahead-long dropdown-menu',
11932 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
11937 if(this.hasFeedback && !this.allowBlank){
11941 cls: 'glyphicon form-control-feedback'
11944 combobox.cn.push(feedback);
11947 if (align ==='left' && this.fieldLabel.length) {
11949 // Roo.log("left and has label");
11955 cls : 'control-label col-sm-' + this.labelWidth,
11956 html : this.fieldLabel
11960 cls : "col-sm-" + (12 - this.labelWidth),
11967 } else if ( this.fieldLabel.length) {
11968 // Roo.log(" label");
11973 //cls : 'input-group-addon',
11974 html : this.fieldLabel
11984 // Roo.log(" no label && no align");
11991 ['xs','sm','md','lg'].map(function(size){
11992 if (settings[size]) {
11993 cfg.cls += ' col-' + size + '-' + settings[size];
12001 _initEventsCalled : false,
12004 initEvents: function()
12007 if (this._initEventsCalled) { // as we call render... prevent looping...
12010 this._initEventsCalled = true;
12013 throw "can not find store for combo";
12016 this.store = Roo.factory(this.store, Roo.data);
12018 // if we are building from html. then this element is so complex, that we can not really
12019 // use the rendered HTML.
12020 // so we have to trash and replace the previous code.
12021 if (Roo.XComponent.build_from_html) {
12023 // remove this element....
12024 var e = this.el.dom, k=0;
12025 while (e ) { e = e.previousSibling; ++k;}
12030 this.rendered = false;
12032 this.render(this.parent().getChildContainer(true), k);
12043 if(Roo.isTouch && this.mobileTouchView){
12044 this.initTouchView();
12049 this.initTickableEvents();
12053 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12055 if(this.hiddenName){
12057 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12059 this.hiddenField.dom.value =
12060 this.hiddenValue !== undefined ? this.hiddenValue :
12061 this.value !== undefined ? this.value : '';
12063 // prevent input submission
12064 this.el.dom.removeAttribute('name');
12065 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12070 // this.el.dom.setAttribute('autocomplete', 'off');
12073 var cls = 'x-combo-list';
12075 //this.list = new Roo.Layer({
12076 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12082 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12083 _this.list.setWidth(lw);
12086 this.list.on('mouseover', this.onViewOver, this);
12087 this.list.on('mousemove', this.onViewMove, this);
12089 this.list.on('scroll', this.onViewScroll, this);
12092 this.list.swallowEvent('mousewheel');
12093 this.assetHeight = 0;
12096 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12097 this.assetHeight += this.header.getHeight();
12100 this.innerList = this.list.createChild({cls:cls+'-inner'});
12101 this.innerList.on('mouseover', this.onViewOver, this);
12102 this.innerList.on('mousemove', this.onViewMove, this);
12103 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12105 if(this.allowBlank && !this.pageSize && !this.disableClear){
12106 this.footer = this.list.createChild({cls:cls+'-ft'});
12107 this.pageTb = new Roo.Toolbar(this.footer);
12111 this.footer = this.list.createChild({cls:cls+'-ft'});
12112 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12113 {pageSize: this.pageSize});
12117 if (this.pageTb && this.allowBlank && !this.disableClear) {
12119 this.pageTb.add(new Roo.Toolbar.Fill(), {
12120 cls: 'x-btn-icon x-btn-clear',
12122 handler: function()
12125 _this.clearValue();
12126 _this.onSelect(false, -1);
12131 this.assetHeight += this.footer.getHeight();
12136 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12139 this.view = new Roo.View(this.list, this.tpl, {
12140 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12142 //this.view.wrapEl.setDisplayed(false);
12143 this.view.on('click', this.onViewClick, this);
12147 this.store.on('beforeload', this.onBeforeLoad, this);
12148 this.store.on('load', this.onLoad, this);
12149 this.store.on('loadexception', this.onLoadException, this);
12151 if(this.resizable){
12152 this.resizer = new Roo.Resizable(this.list, {
12153 pinned:true, handles:'se'
12155 this.resizer.on('resize', function(r, w, h){
12156 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12157 this.listWidth = w;
12158 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12159 this.restrictHeight();
12161 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12164 if(!this.editable){
12165 this.editable = true;
12166 this.setEditable(false);
12171 if (typeof(this.events.add.listeners) != 'undefined') {
12173 this.addicon = this.wrap.createChild(
12174 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12176 this.addicon.on('click', function(e) {
12177 this.fireEvent('add', this);
12180 if (typeof(this.events.edit.listeners) != 'undefined') {
12182 this.editicon = this.wrap.createChild(
12183 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12184 if (this.addicon) {
12185 this.editicon.setStyle('margin-left', '40px');
12187 this.editicon.on('click', function(e) {
12189 // we fire even if inothing is selected..
12190 this.fireEvent('edit', this, this.lastData );
12196 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12197 "up" : function(e){
12198 this.inKeyMode = true;
12202 "down" : function(e){
12203 if(!this.isExpanded()){
12204 this.onTriggerClick();
12206 this.inKeyMode = true;
12211 "enter" : function(e){
12212 // this.onViewClick();
12216 if(this.fireEvent("specialkey", this, e)){
12217 this.onViewClick(false);
12223 "esc" : function(e){
12227 "tab" : function(e){
12230 if(this.fireEvent("specialkey", this, e)){
12231 this.onViewClick(false);
12239 doRelay : function(foo, bar, hname){
12240 if(hname == 'down' || this.scope.isExpanded()){
12241 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12250 this.queryDelay = Math.max(this.queryDelay || 10,
12251 this.mode == 'local' ? 10 : 250);
12254 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12256 if(this.typeAhead){
12257 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12259 if(this.editable !== false){
12260 this.inputEl().on("keyup", this.onKeyUp, this);
12262 if(this.forceSelection){
12263 this.inputEl().on('blur', this.doForce, this);
12267 this.choices = this.el.select('ul.select2-choices', true).first();
12268 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12272 initTickableEvents: function()
12276 if(this.hiddenName){
12278 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12280 this.hiddenField.dom.value =
12281 this.hiddenValue !== undefined ? this.hiddenValue :
12282 this.value !== undefined ? this.value : '';
12284 // prevent input submission
12285 this.el.dom.removeAttribute('name');
12286 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12291 // this.list = this.el.select('ul.dropdown-menu',true).first();
12293 this.choices = this.el.select('ul.select2-choices', true).first();
12294 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12295 if(this.triggerList){
12296 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12299 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12300 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12302 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12303 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12305 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12306 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12308 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12309 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12310 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12313 this.cancelBtn.hide();
12318 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12319 _this.list.setWidth(lw);
12322 this.list.on('mouseover', this.onViewOver, this);
12323 this.list.on('mousemove', this.onViewMove, this);
12325 this.list.on('scroll', this.onViewScroll, this);
12328 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>';
12331 this.view = new Roo.View(this.list, this.tpl, {
12332 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12335 //this.view.wrapEl.setDisplayed(false);
12336 this.view.on('click', this.onViewClick, this);
12340 this.store.on('beforeload', this.onBeforeLoad, this);
12341 this.store.on('load', this.onLoad, this);
12342 this.store.on('loadexception', this.onLoadException, this);
12345 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12346 "up" : function(e){
12347 this.inKeyMode = true;
12351 "down" : function(e){
12352 this.inKeyMode = true;
12356 "enter" : function(e){
12357 if(this.fireEvent("specialkey", this, e)){
12358 this.onViewClick(false);
12364 "esc" : function(e){
12365 this.onTickableFooterButtonClick(e, false, false);
12368 "tab" : function(e){
12369 this.fireEvent("specialkey", this, e);
12371 this.onTickableFooterButtonClick(e, false, false);
12378 doRelay : function(e, fn, key){
12379 if(this.scope.isExpanded()){
12380 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12389 this.queryDelay = Math.max(this.queryDelay || 10,
12390 this.mode == 'local' ? 10 : 250);
12393 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12395 if(this.typeAhead){
12396 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12399 if(this.editable !== false){
12400 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12405 onDestroy : function(){
12407 this.view.setStore(null);
12408 this.view.el.removeAllListeners();
12409 this.view.el.remove();
12410 this.view.purgeListeners();
12413 this.list.dom.innerHTML = '';
12417 this.store.un('beforeload', this.onBeforeLoad, this);
12418 this.store.un('load', this.onLoad, this);
12419 this.store.un('loadexception', this.onLoadException, this);
12421 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12425 fireKey : function(e){
12426 if(e.isNavKeyPress() && !this.list.isVisible()){
12427 this.fireEvent("specialkey", this, e);
12432 onResize: function(w, h){
12433 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12435 // if(typeof w != 'number'){
12436 // // we do not handle it!?!?
12439 // var tw = this.trigger.getWidth();
12440 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12441 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12443 // this.inputEl().setWidth( this.adjustWidth('input', x));
12445 // //this.trigger.setStyle('left', x+'px');
12447 // if(this.list && this.listWidth === undefined){
12448 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12449 // this.list.setWidth(lw);
12450 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12458 * Allow or prevent the user from directly editing the field text. If false is passed,
12459 * the user will only be able to select from the items defined in the dropdown list. This method
12460 * is the runtime equivalent of setting the 'editable' config option at config time.
12461 * @param {Boolean} value True to allow the user to directly edit the field text
12463 setEditable : function(value){
12464 if(value == this.editable){
12467 this.editable = value;
12469 this.inputEl().dom.setAttribute('readOnly', true);
12470 this.inputEl().on('mousedown', this.onTriggerClick, this);
12471 this.inputEl().addClass('x-combo-noedit');
12473 this.inputEl().dom.setAttribute('readOnly', false);
12474 this.inputEl().un('mousedown', this.onTriggerClick, this);
12475 this.inputEl().removeClass('x-combo-noedit');
12481 onBeforeLoad : function(combo,opts){
12482 if(!this.hasFocus){
12486 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12488 this.restrictHeight();
12489 this.selectedIndex = -1;
12493 onLoad : function(){
12495 this.hasQuery = false;
12497 if(!this.hasFocus){
12501 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12502 this.loading.hide();
12505 if(this.store.getCount() > 0){
12507 this.restrictHeight();
12508 if(this.lastQuery == this.allQuery){
12509 if(this.editable && !this.tickable){
12510 this.inputEl().dom.select();
12514 !this.selectByValue(this.value, true) &&
12517 !this.store.lastOptions ||
12518 typeof(this.store.lastOptions.add) == 'undefined' ||
12519 this.store.lastOptions.add != true
12522 this.select(0, true);
12525 if(this.autoFocus){
12528 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12529 this.taTask.delay(this.typeAheadDelay);
12533 this.onEmptyResults();
12539 onLoadException : function()
12541 this.hasQuery = false;
12543 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12544 this.loading.hide();
12547 if(this.tickable && this.editable){
12552 // only causes errors at present
12553 //Roo.log(this.store.reader.jsonData);
12554 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12556 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12562 onTypeAhead : function(){
12563 if(this.store.getCount() > 0){
12564 var r = this.store.getAt(0);
12565 var newValue = r.data[this.displayField];
12566 var len = newValue.length;
12567 var selStart = this.getRawValue().length;
12569 if(selStart != len){
12570 this.setRawValue(newValue);
12571 this.selectText(selStart, newValue.length);
12577 onSelect : function(record, index){
12579 if(this.fireEvent('beforeselect', this, record, index) !== false){
12581 this.setFromData(index > -1 ? record.data : false);
12584 this.fireEvent('select', this, record, index);
12589 * Returns the currently selected field value or empty string if no value is set.
12590 * @return {String} value The selected value
12592 getValue : function(){
12595 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12598 if(this.valueField){
12599 return typeof this.value != 'undefined' ? this.value : '';
12601 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12606 * Clears any text/value currently set in the field
12608 clearValue : function(){
12609 if(this.hiddenField){
12610 this.hiddenField.dom.value = '';
12613 this.setRawValue('');
12614 this.lastSelectionText = '';
12615 this.lastData = false;
12617 var close = this.closeTriggerEl();
12626 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12627 * will be displayed in the field. If the value does not match the data value of an existing item,
12628 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12629 * Otherwise the field will be blank (although the value will still be set).
12630 * @param {String} value The value to match
12632 setValue : function(v){
12639 if(this.valueField){
12640 var r = this.findRecord(this.valueField, v);
12642 text = r.data[this.displayField];
12643 }else if(this.valueNotFoundText !== undefined){
12644 text = this.valueNotFoundText;
12647 this.lastSelectionText = text;
12648 if(this.hiddenField){
12649 this.hiddenField.dom.value = v;
12651 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12654 var close = this.closeTriggerEl();
12657 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12661 * @property {Object} the last set data for the element
12666 * Sets the value of the field based on a object which is related to the record format for the store.
12667 * @param {Object} value the value to set as. or false on reset?
12669 setFromData : function(o){
12676 var dv = ''; // display value
12677 var vv = ''; // value value..
12679 if (this.displayField) {
12680 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12682 // this is an error condition!!!
12683 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12686 if(this.valueField){
12687 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12690 var close = this.closeTriggerEl();
12693 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12696 if(this.hiddenField){
12697 this.hiddenField.dom.value = vv;
12699 this.lastSelectionText = dv;
12700 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12704 // no hidden field.. - we store the value in 'value', but still display
12705 // display field!!!!
12706 this.lastSelectionText = dv;
12707 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12714 reset : function(){
12715 // overridden so that last data is reset..
12722 this.setValue(this.originalValue);
12723 this.clearInvalid();
12724 this.lastData = false;
12726 this.view.clearSelections();
12730 findRecord : function(prop, value){
12732 if(this.store.getCount() > 0){
12733 this.store.each(function(r){
12734 if(r.data[prop] == value){
12744 getName: function()
12746 // returns hidden if it's set..
12747 if (!this.rendered) {return ''};
12748 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
12752 onViewMove : function(e, t){
12753 this.inKeyMode = false;
12757 onViewOver : function(e, t){
12758 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12761 var item = this.view.findItemFromChild(t);
12764 var index = this.view.indexOf(item);
12765 this.select(index, false);
12770 onViewClick : function(view, doFocus, el, e)
12772 var index = this.view.getSelectedIndexes()[0];
12774 var r = this.store.getAt(index);
12778 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12785 Roo.each(this.tickItems, function(v,k){
12787 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12789 _this.tickItems.splice(k, 1);
12791 if(typeof(e) == 'undefined' && view == false){
12792 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12804 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
12805 this.tickItems.push(r.data);
12808 if(typeof(e) == 'undefined' && view == false){
12809 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12816 this.onSelect(r, index);
12818 if(doFocus !== false && !this.blockFocus){
12819 this.inputEl().focus();
12824 restrictHeight : function(){
12825 //this.innerList.dom.style.height = '';
12826 //var inner = this.innerList.dom;
12827 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12828 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12829 //this.list.beginUpdate();
12830 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12831 this.list.alignTo(this.inputEl(), this.listAlign);
12832 this.list.alignTo(this.inputEl(), this.listAlign);
12833 //this.list.endUpdate();
12837 onEmptyResults : function(){
12839 if(this.tickable && this.editable){
12840 this.restrictHeight();
12848 * Returns true if the dropdown list is expanded, else false.
12850 isExpanded : function(){
12851 return this.list.isVisible();
12855 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12856 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12857 * @param {String} value The data value of the item to select
12858 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12859 * selected item if it is not currently in view (defaults to true)
12860 * @return {Boolean} True if the value matched an item in the list, else false
12862 selectByValue : function(v, scrollIntoView){
12863 if(v !== undefined && v !== null){
12864 var r = this.findRecord(this.valueField || this.displayField, v);
12866 this.select(this.store.indexOf(r), scrollIntoView);
12874 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12875 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12876 * @param {Number} index The zero-based index of the list item to select
12877 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12878 * selected item if it is not currently in view (defaults to true)
12880 select : function(index, scrollIntoView){
12881 this.selectedIndex = index;
12882 this.view.select(index);
12883 if(scrollIntoView !== false){
12884 var el = this.view.getNode(index);
12886 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12889 this.list.scrollChildIntoView(el, false);
12895 selectNext : function(){
12896 var ct = this.store.getCount();
12898 if(this.selectedIndex == -1){
12900 }else if(this.selectedIndex < ct-1){
12901 this.select(this.selectedIndex+1);
12907 selectPrev : function(){
12908 var ct = this.store.getCount();
12910 if(this.selectedIndex == -1){
12912 }else if(this.selectedIndex != 0){
12913 this.select(this.selectedIndex-1);
12919 onKeyUp : function(e){
12920 if(this.editable !== false && !e.isSpecialKey()){
12921 this.lastKey = e.getKey();
12922 this.dqTask.delay(this.queryDelay);
12927 validateBlur : function(){
12928 return !this.list || !this.list.isVisible();
12932 initQuery : function(){
12934 var v = this.getRawValue();
12936 if(this.tickable && this.editable){
12937 v = this.tickableInputEl().getValue();
12944 doForce : function(){
12945 if(this.inputEl().dom.value.length > 0){
12946 this.inputEl().dom.value =
12947 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12953 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
12954 * query allowing the query action to be canceled if needed.
12955 * @param {String} query The SQL query to execute
12956 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12957 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
12958 * saved in the current store (defaults to false)
12960 doQuery : function(q, forceAll){
12962 if(q === undefined || q === null){
12967 forceAll: forceAll,
12971 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12976 forceAll = qe.forceAll;
12977 if(forceAll === true || (q.length >= this.minChars)){
12979 this.hasQuery = true;
12981 if(this.lastQuery != q || this.alwaysQuery){
12982 this.lastQuery = q;
12983 if(this.mode == 'local'){
12984 this.selectedIndex = -1;
12986 this.store.clearFilter();
12989 if(this.specialFilter){
12990 this.fireEvent('specialfilter', this);
12995 this.store.filter(this.displayField, q);
12998 this.store.fireEvent("datachanged", this.store);
13005 this.store.baseParams[this.queryParam] = q;
13007 var options = {params : this.getParams(q)};
13010 options.add = true;
13011 options.params.start = this.page * this.pageSize;
13014 this.store.load(options);
13017 * this code will make the page width larger, at the beginning, the list not align correctly,
13018 * we should expand the list on onLoad
13019 * so command out it
13024 this.selectedIndex = -1;
13029 this.loadNext = false;
13033 getParams : function(q){
13035 //p[this.queryParam] = q;
13039 p.limit = this.pageSize;
13045 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13047 collapse : function(){
13048 if(!this.isExpanded()){
13055 this.hasFocus = false;
13057 this.cancelBtn.hide();
13058 this.trigger.show();
13061 this.tickableInputEl().dom.value = '';
13062 this.tickableInputEl().blur();
13067 Roo.get(document).un('mousedown', this.collapseIf, this);
13068 Roo.get(document).un('mousewheel', this.collapseIf, this);
13069 if (!this.editable) {
13070 Roo.get(document).un('keydown', this.listKeyPress, this);
13072 this.fireEvent('collapse', this);
13076 collapseIf : function(e){
13077 var in_combo = e.within(this.el);
13078 var in_list = e.within(this.list);
13079 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13081 if (in_combo || in_list || is_list) {
13082 //e.stopPropagation();
13087 this.onTickableFooterButtonClick(e, false, false);
13095 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13097 expand : function(){
13099 if(this.isExpanded() || !this.hasFocus){
13103 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13104 this.list.setWidth(lw);
13111 this.restrictHeight();
13115 this.tickItems = Roo.apply([], this.item);
13118 this.cancelBtn.show();
13119 this.trigger.hide();
13122 this.tickableInputEl().focus();
13127 Roo.get(document).on('mousedown', this.collapseIf, this);
13128 Roo.get(document).on('mousewheel', this.collapseIf, this);
13129 if (!this.editable) {
13130 Roo.get(document).on('keydown', this.listKeyPress, this);
13133 this.fireEvent('expand', this);
13137 // Implements the default empty TriggerField.onTriggerClick function
13138 onTriggerClick : function(e)
13140 Roo.log('trigger click');
13142 if(this.disabled || !this.triggerList){
13147 this.loadNext = false;
13149 if(this.isExpanded()){
13151 if (!this.blockFocus) {
13152 this.inputEl().focus();
13156 this.hasFocus = true;
13157 if(this.triggerAction == 'all') {
13158 this.doQuery(this.allQuery, true);
13160 this.doQuery(this.getRawValue());
13162 if (!this.blockFocus) {
13163 this.inputEl().focus();
13168 onTickableTriggerClick : function(e)
13175 this.loadNext = false;
13176 this.hasFocus = true;
13178 if(this.triggerAction == 'all') {
13179 this.doQuery(this.allQuery, true);
13181 this.doQuery(this.getRawValue());
13185 onSearchFieldClick : function(e)
13187 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13188 this.onTickableFooterButtonClick(e, false, false);
13192 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13197 this.loadNext = false;
13198 this.hasFocus = true;
13200 if(this.triggerAction == 'all') {
13201 this.doQuery(this.allQuery, true);
13203 this.doQuery(this.getRawValue());
13207 listKeyPress : function(e)
13209 //Roo.log('listkeypress');
13210 // scroll to first matching element based on key pres..
13211 if (e.isSpecialKey()) {
13214 var k = String.fromCharCode(e.getKey()).toUpperCase();
13217 var csel = this.view.getSelectedNodes();
13218 var cselitem = false;
13220 var ix = this.view.indexOf(csel[0]);
13221 cselitem = this.store.getAt(ix);
13222 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13228 this.store.each(function(v) {
13230 // start at existing selection.
13231 if (cselitem.id == v.id) {
13237 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13238 match = this.store.indexOf(v);
13244 if (match === false) {
13245 return true; // no more action?
13248 this.view.select(match);
13249 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13250 sn.scrollIntoView(sn.dom.parentNode, false);
13253 onViewScroll : function(e, t){
13255 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){
13259 this.hasQuery = true;
13261 this.loading = this.list.select('.loading', true).first();
13263 if(this.loading === null){
13264 this.list.createChild({
13266 cls: 'loading select2-more-results select2-active',
13267 html: 'Loading more results...'
13270 this.loading = this.list.select('.loading', true).first();
13272 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13274 this.loading.hide();
13277 this.loading.show();
13282 this.loadNext = true;
13284 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13289 addItem : function(o)
13291 var dv = ''; // display value
13293 if (this.displayField) {
13294 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13296 // this is an error condition!!!
13297 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13304 var choice = this.choices.createChild({
13306 cls: 'select2-search-choice',
13315 cls: 'select2-search-choice-close',
13320 }, this.searchField);
13322 var close = choice.select('a.select2-search-choice-close', true).first();
13324 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13332 this.inputEl().dom.value = '';
13337 onRemoveItem : function(e, _self, o)
13339 e.preventDefault();
13341 this.lastItem = Roo.apply([], this.item);
13343 var index = this.item.indexOf(o.data) * 1;
13346 Roo.log('not this item?!');
13350 this.item.splice(index, 1);
13355 this.fireEvent('remove', this, e);
13361 syncValue : function()
13363 if(!this.item.length){
13370 Roo.each(this.item, function(i){
13371 if(_this.valueField){
13372 value.push(i[_this.valueField]);
13379 this.value = value.join(',');
13381 if(this.hiddenField){
13382 this.hiddenField.dom.value = this.value;
13385 this.store.fireEvent("datachanged", this.store);
13388 clearItem : function()
13390 if(!this.multiple){
13396 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13404 if(this.tickable && !Roo.isTouch){
13405 this.view.refresh();
13409 inputEl: function ()
13411 if(Roo.isTouch && this.mobileTouchView){
13412 return this.el.select('input.form-control',true).first();
13416 return this.searchField;
13419 return this.el.select('input.form-control',true).first();
13423 onTickableFooterButtonClick : function(e, btn, el)
13425 e.preventDefault();
13427 this.lastItem = Roo.apply([], this.item);
13429 if(btn && btn.name == 'cancel'){
13430 this.tickItems = Roo.apply([], this.item);
13439 Roo.each(this.tickItems, function(o){
13447 validate : function()
13449 var v = this.getRawValue();
13452 v = this.getValue();
13455 if(this.disabled || this.allowBlank || v.length){
13460 this.markInvalid();
13464 tickableInputEl : function()
13466 if(!this.tickable || !this.editable){
13467 return this.inputEl();
13470 return this.inputEl().select('.select2-search-field-input', true).first();
13474 getAutoCreateTouchView : function()
13479 cls: 'form-group' //input-group
13485 type : this.inputType,
13486 cls : 'form-control x-combo-noedit',
13487 autocomplete: 'new-password',
13488 placeholder : this.placeholder || '',
13493 input.name = this.name;
13497 input.cls += ' input-' + this.size;
13500 if (this.disabled) {
13501 input.disabled = true;
13512 inputblock.cls += ' input-group';
13514 inputblock.cn.unshift({
13516 cls : 'input-group-addon',
13521 if(this.removable && !this.multiple){
13522 inputblock.cls += ' roo-removable';
13524 inputblock.cn.push({
13527 cls : 'roo-combo-removable-btn close'
13531 if(this.hasFeedback && !this.allowBlank){
13533 inputblock.cls += ' has-feedback';
13535 inputblock.cn.push({
13537 cls: 'glyphicon form-control-feedback'
13544 inputblock.cls += (this.before) ? '' : ' input-group';
13546 inputblock.cn.push({
13548 cls : 'input-group-addon',
13559 cls: 'form-hidden-field'
13573 cls: 'form-hidden-field'
13577 cls: 'select2-choices',
13581 cls: 'select2-search-field',
13594 cls: 'select2-container input-group',
13601 combobox.cls += ' select2-container-multi';
13604 var align = this.labelAlign || this.parentLabelAlign();
13608 if(this.fieldLabel.length){
13610 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13611 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13616 cls : 'control-label ' + lw,
13617 html : this.fieldLabel
13629 var settings = this;
13631 ['xs','sm','md','lg'].map(function(size){
13632 if (settings[size]) {
13633 cfg.cls += ' col-' + size + '-' + settings[size];
13640 initTouchView : function()
13642 this.renderTouchView();
13644 this.touchViewEl.on('scroll', function(){
13645 this.el.dom.scrollTop = 0;
13648 this.originalValue = this.getValue();
13650 this.inputEl().on("click", this.showTouchView, this);
13652 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13653 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13655 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13657 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13658 this.store.on('load', this.onTouchViewLoad, this);
13659 this.store.on('loadexception', this.onTouchViewLoadException, this);
13661 if(this.hiddenName){
13663 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13665 this.hiddenField.dom.value =
13666 this.hiddenValue !== undefined ? this.hiddenValue :
13667 this.value !== undefined ? this.value : '';
13669 this.el.dom.removeAttribute('name');
13670 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13674 this.choices = this.el.select('ul.select2-choices', true).first();
13675 this.searchField = this.el.select('ul li.select2-search-field', true).first();
13678 if(this.removable && !this.multiple){
13679 var close = this.closeTriggerEl();
13681 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13682 close.on('click', this.removeBtnClick, this, close);
13686 * fix the bug in Safari iOS8
13688 this.inputEl().on("focus", function(e){
13689 document.activeElement.blur();
13697 renderTouchView : function()
13699 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13700 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13702 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13703 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13705 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13706 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13707 this.touchViewBodyEl.setStyle('overflow', 'auto');
13709 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13710 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13712 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13713 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13717 showTouchView : function()
13723 this.touchViewHeaderEl.hide();
13725 if(this.fieldLabel.length){
13726 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13727 this.touchViewHeaderEl.show();
13730 this.touchViewEl.show();
13732 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13733 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13735 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13737 if(this.fieldLabel.length){
13738 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13741 this.touchViewBodyEl.setHeight(bodyHeight);
13745 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13747 this.touchViewEl.addClass('in');
13750 this.doTouchViewQuery();
13754 hideTouchView : function()
13756 this.touchViewEl.removeClass('in');
13760 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13762 this.touchViewEl.setStyle('display', 'none');
13767 setTouchViewValue : function()
13774 Roo.each(this.tickItems, function(o){
13779 this.hideTouchView();
13782 doTouchViewQuery : function()
13791 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13795 if(!this.alwaysQuery || this.mode == 'local'){
13796 this.onTouchViewLoad();
13803 onTouchViewBeforeLoad : function(combo,opts)
13809 onTouchViewLoad : function()
13811 if(this.store.getCount() < 1){
13812 this.onTouchViewEmptyResults();
13816 this.clearTouchView();
13818 var rawValue = this.getRawValue();
13820 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13822 this.tickItems = [];
13824 this.store.data.each(function(d, rowIndex){
13825 var row = this.touchViewListGroup.createChild(template);
13827 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13830 html : d.data[this.displayField]
13833 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
13834 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
13838 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13839 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13842 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13843 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13844 this.tickItems.push(d.data);
13847 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13851 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13853 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13855 if(this.fieldLabel.length){
13856 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13859 var listHeight = this.touchViewListGroup.getHeight();
13863 if(firstChecked && listHeight > bodyHeight){
13864 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13869 onTouchViewLoadException : function()
13871 this.hideTouchView();
13874 onTouchViewEmptyResults : function()
13876 this.clearTouchView();
13878 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13880 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13884 clearTouchView : function()
13886 this.touchViewListGroup.dom.innerHTML = '';
13889 onTouchViewClick : function(e, el, o)
13891 e.preventDefault();
13894 var rowIndex = o.rowIndex;
13896 var r = this.store.getAt(rowIndex);
13898 if(!this.multiple){
13899 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13900 c.dom.removeAttribute('checked');
13903 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13905 this.setFromData(r.data);
13907 var close = this.closeTriggerEl();
13913 this.hideTouchView();
13915 this.fireEvent('select', this, r, rowIndex);
13920 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13921 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13922 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13926 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13927 this.addItem(r.data);
13928 this.tickItems.push(r.data);
13934 * @cfg {Boolean} grow
13938 * @cfg {Number} growMin
13942 * @cfg {Number} growMax
13951 Roo.apply(Roo.bootstrap.ComboBox, {
13955 cls: 'modal-header',
13977 cls: 'list-group-item',
13981 cls: 'roo-combobox-list-group-item-value'
13985 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13999 listItemCheckbox : {
14001 cls: 'list-group-item',
14005 cls: 'roo-combobox-list-group-item-value'
14009 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14025 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14030 cls: 'modal-footer',
14038 cls: 'col-xs-6 text-left',
14041 cls: 'btn btn-danger roo-touch-view-cancel',
14047 cls: 'col-xs-6 text-right',
14050 cls: 'btn btn-success roo-touch-view-ok',
14061 Roo.apply(Roo.bootstrap.ComboBox, {
14063 touchViewTemplate : {
14065 cls: 'modal fade roo-combobox-touch-view',
14069 cls: 'modal-dialog',
14070 style : 'position:fixed', // we have to fix position....
14074 cls: 'modal-content',
14076 Roo.bootstrap.ComboBox.header,
14077 Roo.bootstrap.ComboBox.body,
14078 Roo.bootstrap.ComboBox.footer
14087 * Ext JS Library 1.1.1
14088 * Copyright(c) 2006-2007, Ext JS, LLC.
14090 * Originally Released Under LGPL - original licence link has changed is not relivant.
14093 * <script type="text/javascript">
14098 * @extends Roo.util.Observable
14099 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14100 * This class also supports single and multi selection modes. <br>
14101 * Create a data model bound view:
14103 var store = new Roo.data.Store(...);
14105 var view = new Roo.View({
14107 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14109 singleSelect: true,
14110 selectedClass: "ydataview-selected",
14114 // listen for node click?
14115 view.on("click", function(vw, index, node, e){
14116 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14120 dataModel.load("foobar.xml");
14122 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14124 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14125 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14127 * Note: old style constructor is still suported (container, template, config)
14130 * Create a new View
14131 * @param {Object} config The config object
14134 Roo.View = function(config, depreciated_tpl, depreciated_config){
14136 this.parent = false;
14138 if (typeof(depreciated_tpl) == 'undefined') {
14139 // new way.. - universal constructor.
14140 Roo.apply(this, config);
14141 this.el = Roo.get(this.el);
14144 this.el = Roo.get(config);
14145 this.tpl = depreciated_tpl;
14146 Roo.apply(this, depreciated_config);
14148 this.wrapEl = this.el.wrap().wrap();
14149 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14152 if(typeof(this.tpl) == "string"){
14153 this.tpl = new Roo.Template(this.tpl);
14155 // support xtype ctors..
14156 this.tpl = new Roo.factory(this.tpl, Roo);
14160 this.tpl.compile();
14165 * @event beforeclick
14166 * Fires before a click is processed. Returns false to cancel the default action.
14167 * @param {Roo.View} this
14168 * @param {Number} index The index of the target node
14169 * @param {HTMLElement} node The target node
14170 * @param {Roo.EventObject} e The raw event object
14172 "beforeclick" : true,
14175 * Fires when a template node is clicked.
14176 * @param {Roo.View} this
14177 * @param {Number} index The index of the target node
14178 * @param {HTMLElement} node The target node
14179 * @param {Roo.EventObject} e The raw event object
14184 * Fires when a template node is double clicked.
14185 * @param {Roo.View} this
14186 * @param {Number} index The index of the target node
14187 * @param {HTMLElement} node The target node
14188 * @param {Roo.EventObject} e The raw event object
14192 * @event contextmenu
14193 * Fires when a template node is right clicked.
14194 * @param {Roo.View} this
14195 * @param {Number} index The index of the target node
14196 * @param {HTMLElement} node The target node
14197 * @param {Roo.EventObject} e The raw event object
14199 "contextmenu" : true,
14201 * @event selectionchange
14202 * Fires when the selected nodes change.
14203 * @param {Roo.View} this
14204 * @param {Array} selections Array of the selected nodes
14206 "selectionchange" : true,
14209 * @event beforeselect
14210 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14211 * @param {Roo.View} this
14212 * @param {HTMLElement} node The node to be selected
14213 * @param {Array} selections Array of currently selected nodes
14215 "beforeselect" : true,
14217 * @event preparedata
14218 * Fires on every row to render, to allow you to change the data.
14219 * @param {Roo.View} this
14220 * @param {Object} data to be rendered (change this)
14222 "preparedata" : true
14230 "click": this.onClick,
14231 "dblclick": this.onDblClick,
14232 "contextmenu": this.onContextMenu,
14236 this.selections = [];
14238 this.cmp = new Roo.CompositeElementLite([]);
14240 this.store = Roo.factory(this.store, Roo.data);
14241 this.setStore(this.store, true);
14244 if ( this.footer && this.footer.xtype) {
14246 var fctr = this.wrapEl.appendChild(document.createElement("div"));
14248 this.footer.dataSource = this.store;
14249 this.footer.container = fctr;
14250 this.footer = Roo.factory(this.footer, Roo);
14251 fctr.insertFirst(this.el);
14253 // this is a bit insane - as the paging toolbar seems to detach the el..
14254 // dom.parentNode.parentNode.parentNode
14255 // they get detached?
14259 Roo.View.superclass.constructor.call(this);
14264 Roo.extend(Roo.View, Roo.util.Observable, {
14267 * @cfg {Roo.data.Store} store Data store to load data from.
14272 * @cfg {String|Roo.Element} el The container element.
14277 * @cfg {String|Roo.Template} tpl The template used by this View
14281 * @cfg {String} dataName the named area of the template to use as the data area
14282 * Works with domtemplates roo-name="name"
14286 * @cfg {String} selectedClass The css class to add to selected nodes
14288 selectedClass : "x-view-selected",
14290 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14295 * @cfg {String} text to display on mask (default Loading)
14299 * @cfg {Boolean} multiSelect Allow multiple selection
14301 multiSelect : false,
14303 * @cfg {Boolean} singleSelect Allow single selection
14305 singleSelect: false,
14308 * @cfg {Boolean} toggleSelect - selecting
14310 toggleSelect : false,
14313 * @cfg {Boolean} tickable - selecting
14318 * Returns the element this view is bound to.
14319 * @return {Roo.Element}
14321 getEl : function(){
14322 return this.wrapEl;
14328 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14330 refresh : function(){
14331 //Roo.log('refresh');
14334 // if we are using something like 'domtemplate', then
14335 // the what gets used is:
14336 // t.applySubtemplate(NAME, data, wrapping data..)
14337 // the outer template then get' applied with
14338 // the store 'extra data'
14339 // and the body get's added to the
14340 // roo-name="data" node?
14341 // <span class='roo-tpl-{name}'></span> ?????
14345 this.clearSelections();
14346 this.el.update("");
14348 var records = this.store.getRange();
14349 if(records.length < 1) {
14351 // is this valid?? = should it render a template??
14353 this.el.update(this.emptyText);
14357 if (this.dataName) {
14358 this.el.update(t.apply(this.store.meta)); //????
14359 el = this.el.child('.roo-tpl-' + this.dataName);
14362 for(var i = 0, len = records.length; i < len; i++){
14363 var data = this.prepareData(records[i].data, i, records[i]);
14364 this.fireEvent("preparedata", this, data, i, records[i]);
14366 var d = Roo.apply({}, data);
14369 Roo.apply(d, {'roo-id' : Roo.id()});
14373 Roo.each(this.parent.item, function(item){
14374 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14377 Roo.apply(d, {'roo-data-checked' : 'checked'});
14381 html[html.length] = Roo.util.Format.trim(
14383 t.applySubtemplate(this.dataName, d, this.store.meta) :
14390 el.update(html.join(""));
14391 this.nodes = el.dom.childNodes;
14392 this.updateIndexes(0);
14397 * Function to override to reformat the data that is sent to
14398 * the template for each node.
14399 * DEPRICATED - use the preparedata event handler.
14400 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14401 * a JSON object for an UpdateManager bound view).
14403 prepareData : function(data, index, record)
14405 this.fireEvent("preparedata", this, data, index, record);
14409 onUpdate : function(ds, record){
14410 // Roo.log('on update');
14411 this.clearSelections();
14412 var index = this.store.indexOf(record);
14413 var n = this.nodes[index];
14414 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14415 n.parentNode.removeChild(n);
14416 this.updateIndexes(index, index);
14422 onAdd : function(ds, records, index)
14424 //Roo.log(['on Add', ds, records, index] );
14425 this.clearSelections();
14426 if(this.nodes.length == 0){
14430 var n = this.nodes[index];
14431 for(var i = 0, len = records.length; i < len; i++){
14432 var d = this.prepareData(records[i].data, i, records[i]);
14434 this.tpl.insertBefore(n, d);
14437 this.tpl.append(this.el, d);
14440 this.updateIndexes(index);
14443 onRemove : function(ds, record, index){
14444 // Roo.log('onRemove');
14445 this.clearSelections();
14446 var el = this.dataName ?
14447 this.el.child('.roo-tpl-' + this.dataName) :
14450 el.dom.removeChild(this.nodes[index]);
14451 this.updateIndexes(index);
14455 * Refresh an individual node.
14456 * @param {Number} index
14458 refreshNode : function(index){
14459 this.onUpdate(this.store, this.store.getAt(index));
14462 updateIndexes : function(startIndex, endIndex){
14463 var ns = this.nodes;
14464 startIndex = startIndex || 0;
14465 endIndex = endIndex || ns.length - 1;
14466 for(var i = startIndex; i <= endIndex; i++){
14467 ns[i].nodeIndex = i;
14472 * Changes the data store this view uses and refresh the view.
14473 * @param {Store} store
14475 setStore : function(store, initial){
14476 if(!initial && this.store){
14477 this.store.un("datachanged", this.refresh);
14478 this.store.un("add", this.onAdd);
14479 this.store.un("remove", this.onRemove);
14480 this.store.un("update", this.onUpdate);
14481 this.store.un("clear", this.refresh);
14482 this.store.un("beforeload", this.onBeforeLoad);
14483 this.store.un("load", this.onLoad);
14484 this.store.un("loadexception", this.onLoad);
14488 store.on("datachanged", this.refresh, this);
14489 store.on("add", this.onAdd, this);
14490 store.on("remove", this.onRemove, this);
14491 store.on("update", this.onUpdate, this);
14492 store.on("clear", this.refresh, this);
14493 store.on("beforeload", this.onBeforeLoad, this);
14494 store.on("load", this.onLoad, this);
14495 store.on("loadexception", this.onLoad, this);
14503 * onbeforeLoad - masks the loading area.
14506 onBeforeLoad : function(store,opts)
14508 //Roo.log('onBeforeLoad');
14510 this.el.update("");
14512 this.el.mask(this.mask ? this.mask : "Loading" );
14514 onLoad : function ()
14521 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14522 * @param {HTMLElement} node
14523 * @return {HTMLElement} The template node
14525 findItemFromChild : function(node){
14526 var el = this.dataName ?
14527 this.el.child('.roo-tpl-' + this.dataName,true) :
14530 if(!node || node.parentNode == el){
14533 var p = node.parentNode;
14534 while(p && p != el){
14535 if(p.parentNode == el){
14544 onClick : function(e){
14545 var item = this.findItemFromChild(e.getTarget());
14547 var index = this.indexOf(item);
14548 if(this.onItemClick(item, index, e) !== false){
14549 this.fireEvent("click", this, index, item, e);
14552 this.clearSelections();
14557 onContextMenu : function(e){
14558 var item = this.findItemFromChild(e.getTarget());
14560 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14565 onDblClick : function(e){
14566 var item = this.findItemFromChild(e.getTarget());
14568 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14572 onItemClick : function(item, index, e)
14574 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14577 if (this.toggleSelect) {
14578 var m = this.isSelected(item) ? 'unselect' : 'select';
14581 _t[m](item, true, false);
14584 if(this.multiSelect || this.singleSelect){
14585 if(this.multiSelect && e.shiftKey && this.lastSelection){
14586 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14588 this.select(item, this.multiSelect && e.ctrlKey);
14589 this.lastSelection = item;
14592 if(!this.tickable){
14593 e.preventDefault();
14601 * Get the number of selected nodes.
14604 getSelectionCount : function(){
14605 return this.selections.length;
14609 * Get the currently selected nodes.
14610 * @return {Array} An array of HTMLElements
14612 getSelectedNodes : function(){
14613 return this.selections;
14617 * Get the indexes of the selected nodes.
14620 getSelectedIndexes : function(){
14621 var indexes = [], s = this.selections;
14622 for(var i = 0, len = s.length; i < len; i++){
14623 indexes.push(s[i].nodeIndex);
14629 * Clear all selections
14630 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14632 clearSelections : function(suppressEvent){
14633 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14634 this.cmp.elements = this.selections;
14635 this.cmp.removeClass(this.selectedClass);
14636 this.selections = [];
14637 if(!suppressEvent){
14638 this.fireEvent("selectionchange", this, this.selections);
14644 * Returns true if the passed node is selected
14645 * @param {HTMLElement/Number} node The node or node index
14646 * @return {Boolean}
14648 isSelected : function(node){
14649 var s = this.selections;
14653 node = this.getNode(node);
14654 return s.indexOf(node) !== -1;
14659 * @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
14660 * @param {Boolean} keepExisting (optional) true to keep existing selections
14661 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14663 select : function(nodeInfo, keepExisting, suppressEvent){
14664 if(nodeInfo instanceof Array){
14666 this.clearSelections(true);
14668 for(var i = 0, len = nodeInfo.length; i < len; i++){
14669 this.select(nodeInfo[i], true, true);
14673 var node = this.getNode(nodeInfo);
14674 if(!node || this.isSelected(node)){
14675 return; // already selected.
14678 this.clearSelections(true);
14681 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14682 Roo.fly(node).addClass(this.selectedClass);
14683 this.selections.push(node);
14684 if(!suppressEvent){
14685 this.fireEvent("selectionchange", this, this.selections);
14693 * @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
14694 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14695 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14697 unselect : function(nodeInfo, keepExisting, suppressEvent)
14699 if(nodeInfo instanceof Array){
14700 Roo.each(this.selections, function(s) {
14701 this.unselect(s, nodeInfo);
14705 var node = this.getNode(nodeInfo);
14706 if(!node || !this.isSelected(node)){
14707 //Roo.log("not selected");
14708 return; // not selected.
14712 Roo.each(this.selections, function(s) {
14714 Roo.fly(node).removeClass(this.selectedClass);
14721 this.selections= ns;
14722 this.fireEvent("selectionchange", this, this.selections);
14726 * Gets a template node.
14727 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14728 * @return {HTMLElement} The node or null if it wasn't found
14730 getNode : function(nodeInfo){
14731 if(typeof nodeInfo == "string"){
14732 return document.getElementById(nodeInfo);
14733 }else if(typeof nodeInfo == "number"){
14734 return this.nodes[nodeInfo];
14740 * Gets a range template nodes.
14741 * @param {Number} startIndex
14742 * @param {Number} endIndex
14743 * @return {Array} An array of nodes
14745 getNodes : function(start, end){
14746 var ns = this.nodes;
14747 start = start || 0;
14748 end = typeof end == "undefined" ? ns.length - 1 : end;
14751 for(var i = start; i <= end; i++){
14755 for(var i = start; i >= end; i--){
14763 * Finds the index of the passed node
14764 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14765 * @return {Number} The index of the node or -1
14767 indexOf : function(node){
14768 node = this.getNode(node);
14769 if(typeof node.nodeIndex == "number"){
14770 return node.nodeIndex;
14772 var ns = this.nodes;
14773 for(var i = 0, len = ns.length; i < len; i++){
14784 * based on jquery fullcalendar
14788 Roo.bootstrap = Roo.bootstrap || {};
14790 * @class Roo.bootstrap.Calendar
14791 * @extends Roo.bootstrap.Component
14792 * Bootstrap Calendar class
14793 * @cfg {Boolean} loadMask (true|false) default false
14794 * @cfg {Object} header generate the user specific header of the calendar, default false
14797 * Create a new Container
14798 * @param {Object} config The config object
14803 Roo.bootstrap.Calendar = function(config){
14804 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14808 * Fires when a date is selected
14809 * @param {DatePicker} this
14810 * @param {Date} date The selected date
14814 * @event monthchange
14815 * Fires when the displayed month changes
14816 * @param {DatePicker} this
14817 * @param {Date} date The selected month
14819 'monthchange': true,
14821 * @event evententer
14822 * Fires when mouse over an event
14823 * @param {Calendar} this
14824 * @param {event} Event
14826 'evententer': true,
14828 * @event eventleave
14829 * Fires when the mouse leaves an
14830 * @param {Calendar} this
14833 'eventleave': true,
14835 * @event eventclick
14836 * Fires when the mouse click an
14837 * @param {Calendar} this
14846 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
14849 * @cfg {Number} startDay
14850 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14858 getAutoCreate : function(){
14861 var fc_button = function(name, corner, style, content ) {
14862 return Roo.apply({},{
14864 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
14866 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14869 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14880 style : 'width:100%',
14887 cls : 'fc-header-left',
14889 fc_button('prev', 'left', 'arrow', '‹' ),
14890 fc_button('next', 'right', 'arrow', '›' ),
14891 { tag: 'span', cls: 'fc-header-space' },
14892 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
14900 cls : 'fc-header-center',
14904 cls: 'fc-header-title',
14907 html : 'month / year'
14915 cls : 'fc-header-right',
14917 /* fc_button('month', 'left', '', 'month' ),
14918 fc_button('week', '', '', 'week' ),
14919 fc_button('day', 'right', '', 'day' )
14931 header = this.header;
14934 var cal_heads = function() {
14936 // fixme - handle this.
14938 for (var i =0; i < Date.dayNames.length; i++) {
14939 var d = Date.dayNames[i];
14942 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14943 html : d.substring(0,3)
14947 ret[0].cls += ' fc-first';
14948 ret[6].cls += ' fc-last';
14951 var cal_cell = function(n) {
14954 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14959 cls: 'fc-day-number',
14963 cls: 'fc-day-content',
14967 style: 'position: relative;' // height: 17px;
14979 var cal_rows = function() {
14982 for (var r = 0; r < 6; r++) {
14989 for (var i =0; i < Date.dayNames.length; i++) {
14990 var d = Date.dayNames[i];
14991 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14994 row.cn[0].cls+=' fc-first';
14995 row.cn[0].cn[0].style = 'min-height:90px';
14996 row.cn[6].cls+=' fc-last';
15000 ret[0].cls += ' fc-first';
15001 ret[4].cls += ' fc-prev-last';
15002 ret[5].cls += ' fc-last';
15009 cls: 'fc-border-separate',
15010 style : 'width:100%',
15018 cls : 'fc-first fc-last',
15036 cls : 'fc-content',
15037 style : "position: relative;",
15040 cls : 'fc-view fc-view-month fc-grid',
15041 style : 'position: relative',
15042 unselectable : 'on',
15045 cls : 'fc-event-container',
15046 style : 'position:absolute;z-index:8;top:0;left:0;'
15064 initEvents : function()
15067 throw "can not find store for calendar";
15073 style: "text-align:center",
15077 style: "background-color:white;width:50%;margin:250 auto",
15081 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15092 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15094 var size = this.el.select('.fc-content', true).first().getSize();
15095 this.maskEl.setSize(size.width, size.height);
15096 this.maskEl.enableDisplayMode("block");
15097 if(!this.loadMask){
15098 this.maskEl.hide();
15101 this.store = Roo.factory(this.store, Roo.data);
15102 this.store.on('load', this.onLoad, this);
15103 this.store.on('beforeload', this.onBeforeLoad, this);
15107 this.cells = this.el.select('.fc-day',true);
15108 //Roo.log(this.cells);
15109 this.textNodes = this.el.query('.fc-day-number');
15110 this.cells.addClassOnOver('fc-state-hover');
15112 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15113 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15114 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15115 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15117 this.on('monthchange', this.onMonthChange, this);
15119 this.update(new Date().clearTime());
15122 resize : function() {
15123 var sz = this.el.getSize();
15125 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15126 this.el.select('.fc-day-content div',true).setHeight(34);
15131 showPrevMonth : function(e){
15132 this.update(this.activeDate.add("mo", -1));
15134 showToday : function(e){
15135 this.update(new Date().clearTime());
15138 showNextMonth : function(e){
15139 this.update(this.activeDate.add("mo", 1));
15143 showPrevYear : function(){
15144 this.update(this.activeDate.add("y", -1));
15148 showNextYear : function(){
15149 this.update(this.activeDate.add("y", 1));
15154 update : function(date)
15156 var vd = this.activeDate;
15157 this.activeDate = date;
15158 // if(vd && this.el){
15159 // var t = date.getTime();
15160 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15161 // Roo.log('using add remove');
15163 // this.fireEvent('monthchange', this, date);
15165 // this.cells.removeClass("fc-state-highlight");
15166 // this.cells.each(function(c){
15167 // if(c.dateValue == t){
15168 // c.addClass("fc-state-highlight");
15169 // setTimeout(function(){
15170 // try{c.dom.firstChild.focus();}catch(e){}
15180 var days = date.getDaysInMonth();
15182 var firstOfMonth = date.getFirstDateOfMonth();
15183 var startingPos = firstOfMonth.getDay()-this.startDay;
15185 if(startingPos < this.startDay){
15189 var pm = date.add(Date.MONTH, -1);
15190 var prevStart = pm.getDaysInMonth()-startingPos;
15192 this.cells = this.el.select('.fc-day',true);
15193 this.textNodes = this.el.query('.fc-day-number');
15194 this.cells.addClassOnOver('fc-state-hover');
15196 var cells = this.cells.elements;
15197 var textEls = this.textNodes;
15199 Roo.each(cells, function(cell){
15200 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15203 days += startingPos;
15205 // convert everything to numbers so it's fast
15206 var day = 86400000;
15207 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15210 //Roo.log(prevStart);
15212 var today = new Date().clearTime().getTime();
15213 var sel = date.clearTime().getTime();
15214 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15215 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15216 var ddMatch = this.disabledDatesRE;
15217 var ddText = this.disabledDatesText;
15218 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15219 var ddaysText = this.disabledDaysText;
15220 var format = this.format;
15222 var setCellClass = function(cal, cell){
15226 //Roo.log('set Cell Class');
15228 var t = d.getTime();
15232 cell.dateValue = t;
15234 cell.className += " fc-today";
15235 cell.className += " fc-state-highlight";
15236 cell.title = cal.todayText;
15239 // disable highlight in other month..
15240 //cell.className += " fc-state-highlight";
15245 cell.className = " fc-state-disabled";
15246 cell.title = cal.minText;
15250 cell.className = " fc-state-disabled";
15251 cell.title = cal.maxText;
15255 if(ddays.indexOf(d.getDay()) != -1){
15256 cell.title = ddaysText;
15257 cell.className = " fc-state-disabled";
15260 if(ddMatch && format){
15261 var fvalue = d.dateFormat(format);
15262 if(ddMatch.test(fvalue)){
15263 cell.title = ddText.replace("%0", fvalue);
15264 cell.className = " fc-state-disabled";
15268 if (!cell.initialClassName) {
15269 cell.initialClassName = cell.dom.className;
15272 cell.dom.className = cell.initialClassName + ' ' + cell.className;
15277 for(; i < startingPos; i++) {
15278 textEls[i].innerHTML = (++prevStart);
15279 d.setDate(d.getDate()+1);
15281 cells[i].className = "fc-past fc-other-month";
15282 setCellClass(this, cells[i]);
15287 for(; i < days; i++){
15288 intDay = i - startingPos + 1;
15289 textEls[i].innerHTML = (intDay);
15290 d.setDate(d.getDate()+1);
15292 cells[i].className = ''; // "x-date-active";
15293 setCellClass(this, cells[i]);
15297 for(; i < 42; i++) {
15298 textEls[i].innerHTML = (++extraDays);
15299 d.setDate(d.getDate()+1);
15301 cells[i].className = "fc-future fc-other-month";
15302 setCellClass(this, cells[i]);
15305 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15307 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15309 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15310 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15312 if(totalRows != 6){
15313 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15314 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15317 this.fireEvent('monthchange', this, date);
15321 if(!this.internalRender){
15322 var main = this.el.dom.firstChild;
15323 var w = main.offsetWidth;
15324 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15325 Roo.fly(main).setWidth(w);
15326 this.internalRender = true;
15327 // opera does not respect the auto grow header center column
15328 // then, after it gets a width opera refuses to recalculate
15329 // without a second pass
15330 if(Roo.isOpera && !this.secondPass){
15331 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15332 this.secondPass = true;
15333 this.update.defer(10, this, [date]);
15340 findCell : function(dt) {
15341 dt = dt.clearTime().getTime();
15343 this.cells.each(function(c){
15344 //Roo.log("check " +c.dateValue + '?=' + dt);
15345 if(c.dateValue == dt){
15355 findCells : function(ev) {
15356 var s = ev.start.clone().clearTime().getTime();
15358 var e= ev.end.clone().clearTime().getTime();
15361 this.cells.each(function(c){
15362 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15364 if(c.dateValue > e){
15367 if(c.dateValue < s){
15376 // findBestRow: function(cells)
15380 // for (var i =0 ; i < cells.length;i++) {
15381 // ret = Math.max(cells[i].rows || 0,ret);
15388 addItem : function(ev)
15390 // look for vertical location slot in
15391 var cells = this.findCells(ev);
15393 // ev.row = this.findBestRow(cells);
15395 // work out the location.
15399 for(var i =0; i < cells.length; i++) {
15401 cells[i].row = cells[0].row;
15404 cells[i].row = cells[i].row + 1;
15414 if (crow.start.getY() == cells[i].getY()) {
15416 crow.end = cells[i];
15433 cells[0].events.push(ev);
15435 this.calevents.push(ev);
15438 clearEvents: function() {
15440 if(!this.calevents){
15444 Roo.each(this.cells.elements, function(c){
15450 Roo.each(this.calevents, function(e) {
15451 Roo.each(e.els, function(el) {
15452 el.un('mouseenter' ,this.onEventEnter, this);
15453 el.un('mouseleave' ,this.onEventLeave, this);
15458 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15464 renderEvents: function()
15468 this.cells.each(function(c) {
15477 if(c.row != c.events.length){
15478 r = 4 - (4 - (c.row - c.events.length));
15481 c.events = ev.slice(0, r);
15482 c.more = ev.slice(r);
15484 if(c.more.length && c.more.length == 1){
15485 c.events.push(c.more.pop());
15488 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15492 this.cells.each(function(c) {
15494 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15497 for (var e = 0; e < c.events.length; e++){
15498 var ev = c.events[e];
15499 var rows = ev.rows;
15501 for(var i = 0; i < rows.length; i++) {
15503 // how many rows should it span..
15506 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15507 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15509 unselectable : "on",
15512 cls: 'fc-event-inner',
15516 // cls: 'fc-event-time',
15517 // html : cells.length > 1 ? '' : ev.time
15521 cls: 'fc-event-title',
15522 html : String.format('{0}', ev.title)
15529 cls: 'ui-resizable-handle ui-resizable-e',
15530 html : '  '
15537 cfg.cls += ' fc-event-start';
15539 if ((i+1) == rows.length) {
15540 cfg.cls += ' fc-event-end';
15543 var ctr = _this.el.select('.fc-event-container',true).first();
15544 var cg = ctr.createChild(cfg);
15546 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15547 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15549 var r = (c.more.length) ? 1 : 0;
15550 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15551 cg.setWidth(ebox.right - sbox.x -2);
15553 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15554 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15555 cg.on('click', _this.onEventClick, _this, ev);
15566 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15567 style : 'position: absolute',
15568 unselectable : "on",
15571 cls: 'fc-event-inner',
15575 cls: 'fc-event-title',
15583 cls: 'ui-resizable-handle ui-resizable-e',
15584 html : '  '
15590 var ctr = _this.el.select('.fc-event-container',true).first();
15591 var cg = ctr.createChild(cfg);
15593 var sbox = c.select('.fc-day-content',true).first().getBox();
15594 var ebox = c.select('.fc-day-content',true).first().getBox();
15596 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15597 cg.setWidth(ebox.right - sbox.x -2);
15599 cg.on('click', _this.onMoreEventClick, _this, c.more);
15609 onEventEnter: function (e, el,event,d) {
15610 this.fireEvent('evententer', this, el, event);
15613 onEventLeave: function (e, el,event,d) {
15614 this.fireEvent('eventleave', this, el, event);
15617 onEventClick: function (e, el,event,d) {
15618 this.fireEvent('eventclick', this, el, event);
15621 onMonthChange: function () {
15625 onMoreEventClick: function(e, el, more)
15629 this.calpopover.placement = 'right';
15630 this.calpopover.setTitle('More');
15632 this.calpopover.setContent('');
15634 var ctr = this.calpopover.el.select('.popover-content', true).first();
15636 Roo.each(more, function(m){
15638 cls : 'fc-event-hori fc-event-draggable',
15641 var cg = ctr.createChild(cfg);
15643 cg.on('click', _this.onEventClick, _this, m);
15646 this.calpopover.show(el);
15651 onLoad: function ()
15653 this.calevents = [];
15656 if(this.store.getCount() > 0){
15657 this.store.data.each(function(d){
15660 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15661 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15662 time : d.data.start_time,
15663 title : d.data.title,
15664 description : d.data.description,
15665 venue : d.data.venue
15670 this.renderEvents();
15672 if(this.calevents.length && this.loadMask){
15673 this.maskEl.hide();
15677 onBeforeLoad: function()
15679 this.clearEvents();
15681 this.maskEl.show();
15695 * @class Roo.bootstrap.Popover
15696 * @extends Roo.bootstrap.Component
15697 * Bootstrap Popover class
15698 * @cfg {String} html contents of the popover (or false to use children..)
15699 * @cfg {String} title of popover (or false to hide)
15700 * @cfg {String} placement how it is placed
15701 * @cfg {String} trigger click || hover (or false to trigger manually)
15702 * @cfg {String} over what (parent or false to trigger manually.)
15703 * @cfg {Number} delay - delay before showing
15706 * Create a new Popover
15707 * @param {Object} config The config object
15710 Roo.bootstrap.Popover = function(config){
15711 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15717 * After the popover show
15719 * @param {Roo.bootstrap.Popover} this
15724 * After the popover hide
15726 * @param {Roo.bootstrap.Popover} this
15732 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
15734 title: 'Fill in a title',
15737 placement : 'right',
15738 trigger : 'hover', // hover
15744 can_build_overlaid : false,
15746 getChildContainer : function()
15748 return this.el.select('.popover-content',true).first();
15751 getAutoCreate : function(){
15754 cls : 'popover roo-dynamic',
15755 style: 'display:block',
15761 cls : 'popover-inner',
15765 cls: 'popover-title',
15769 cls : 'popover-content',
15780 setTitle: function(str)
15783 this.el.select('.popover-title',true).first().dom.innerHTML = str;
15785 setContent: function(str)
15788 this.el.select('.popover-content',true).first().dom.innerHTML = str;
15790 // as it get's added to the bottom of the page.
15791 onRender : function(ct, position)
15793 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15795 var cfg = Roo.apply({}, this.getAutoCreate());
15799 cfg.cls += ' ' + this.cls;
15802 cfg.style = this.style;
15804 //Roo.log("adding to ");
15805 this.el = Roo.get(document.body).createChild(cfg, position);
15806 // Roo.log(this.el);
15811 initEvents : function()
15813 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15814 this.el.enableDisplayMode('block');
15816 if (this.over === false) {
15819 if (this.triggers === false) {
15822 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15823 var triggers = this.trigger ? this.trigger.split(' ') : [];
15824 Roo.each(triggers, function(trigger) {
15826 if (trigger == 'click') {
15827 on_el.on('click', this.toggle, this);
15828 } else if (trigger != 'manual') {
15829 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
15830 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15832 on_el.on(eventIn ,this.enter, this);
15833 on_el.on(eventOut, this.leave, this);
15844 toggle : function () {
15845 this.hoverState == 'in' ? this.leave() : this.enter();
15848 enter : function () {
15851 clearTimeout(this.timeout);
15853 this.hoverState = 'in';
15855 if (!this.delay || !this.delay.show) {
15860 this.timeout = setTimeout(function () {
15861 if (_t.hoverState == 'in') {
15864 }, this.delay.show)
15866 leave : function() {
15867 clearTimeout(this.timeout);
15869 this.hoverState = 'out';
15871 if (!this.delay || !this.delay.hide) {
15876 this.timeout = setTimeout(function () {
15877 if (_t.hoverState == 'out') {
15880 }, this.delay.hide)
15883 show : function (on_el)
15886 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15889 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15890 if (this.html !== false) {
15891 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15893 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15894 if (!this.title.length) {
15895 this.el.select('.popover-title',true).hide();
15898 var placement = typeof this.placement == 'function' ?
15899 this.placement.call(this, this.el, on_el) :
15902 var autoToken = /\s?auto?\s?/i;
15903 var autoPlace = autoToken.test(placement);
15905 placement = placement.replace(autoToken, '') || 'top';
15909 //this.el.setXY([0,0]);
15911 this.el.dom.style.display='block';
15912 this.el.addClass(placement);
15914 //this.el.appendTo(on_el);
15916 var p = this.getPosition();
15917 var box = this.el.getBox();
15922 var align = Roo.bootstrap.Popover.alignment[placement];
15923 this.el.alignTo(on_el, align[0],align[1]);
15924 //var arrow = this.el.select('.arrow',true).first();
15925 //arrow.set(align[2],
15927 this.el.addClass('in');
15930 if (this.el.hasClass('fade')) {
15934 this.fireEvent('show', this);
15939 this.el.setXY([0,0]);
15940 this.el.removeClass('in');
15942 this.hoverState = null;
15944 this.fireEvent('hide', this);
15949 Roo.bootstrap.Popover.alignment = {
15950 'left' : ['r-l', [-10,0], 'right'],
15951 'right' : ['l-r', [10,0], 'left'],
15952 'bottom' : ['t-b', [0,10], 'top'],
15953 'top' : [ 'b-t', [0,-10], 'bottom']
15964 * @class Roo.bootstrap.Progress
15965 * @extends Roo.bootstrap.Component
15966 * Bootstrap Progress class
15967 * @cfg {Boolean} striped striped of the progress bar
15968 * @cfg {Boolean} active animated of the progress bar
15972 * Create a new Progress
15973 * @param {Object} config The config object
15976 Roo.bootstrap.Progress = function(config){
15977 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15980 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
15985 getAutoCreate : function(){
15993 cfg.cls += ' progress-striped';
15997 cfg.cls += ' active';
16016 * @class Roo.bootstrap.ProgressBar
16017 * @extends Roo.bootstrap.Component
16018 * Bootstrap ProgressBar class
16019 * @cfg {Number} aria_valuenow aria-value now
16020 * @cfg {Number} aria_valuemin aria-value min
16021 * @cfg {Number} aria_valuemax aria-value max
16022 * @cfg {String} label label for the progress bar
16023 * @cfg {String} panel (success | info | warning | danger )
16024 * @cfg {String} role role of the progress bar
16025 * @cfg {String} sr_only text
16029 * Create a new ProgressBar
16030 * @param {Object} config The config object
16033 Roo.bootstrap.ProgressBar = function(config){
16034 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16037 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
16041 aria_valuemax : 100,
16047 getAutoCreate : function()
16052 cls: 'progress-bar',
16053 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16065 cfg.role = this.role;
16068 if(this.aria_valuenow){
16069 cfg['aria-valuenow'] = this.aria_valuenow;
16072 if(this.aria_valuemin){
16073 cfg['aria-valuemin'] = this.aria_valuemin;
16076 if(this.aria_valuemax){
16077 cfg['aria-valuemax'] = this.aria_valuemax;
16080 if(this.label && !this.sr_only){
16081 cfg.html = this.label;
16085 cfg.cls += ' progress-bar-' + this.panel;
16091 update : function(aria_valuenow)
16093 this.aria_valuenow = aria_valuenow;
16095 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16110 * @class Roo.bootstrap.TabGroup
16111 * @extends Roo.bootstrap.Column
16112 * Bootstrap Column class
16113 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16114 * @cfg {Boolean} carousel true to make the group behave like a carousel
16115 * @cfg {Boolean} bullets show bullets for the panels
16116 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16117 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16118 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16121 * Create a new TabGroup
16122 * @param {Object} config The config object
16125 Roo.bootstrap.TabGroup = function(config){
16126 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16128 this.navId = Roo.id();
16131 Roo.bootstrap.TabGroup.register(this);
16135 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16138 transition : false,
16143 slideOnTouch : false,
16145 getAutoCreate : function()
16147 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16149 cfg.cls += ' tab-content';
16151 if (this.carousel) {
16152 cfg.cls += ' carousel slide';
16155 cls : 'carousel-inner'
16158 if(this.bullets && !Roo.isTouch){
16161 cls : 'carousel-bullets',
16165 if(this.bullets_cls){
16166 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16169 for (var i = 0; i < this.bullets; i++){
16171 cls : 'bullet bullet-' + i
16179 cfg.cn[0].cn = bullets;
16186 initEvents: function()
16188 if(Roo.isTouch && this.slideOnTouch){
16189 this.el.on("touchstart", this.onTouchStart, this);
16192 if(this.autoslide){
16195 this.slideFn = window.setInterval(function() {
16196 _this.showPanelNext();
16202 onTouchStart : function(e, el, o)
16204 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16208 this.showPanelNext();
16211 getChildContainer : function()
16213 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16217 * register a Navigation item
16218 * @param {Roo.bootstrap.NavItem} the navitem to add
16220 register : function(item)
16222 this.tabs.push( item);
16223 item.navId = this.navId; // not really needed..
16228 getActivePanel : function()
16231 Roo.each(this.tabs, function(t) {
16241 getPanelByName : function(n)
16244 Roo.each(this.tabs, function(t) {
16245 if (t.tabId == n) {
16253 indexOfPanel : function(p)
16256 Roo.each(this.tabs, function(t,i) {
16257 if (t.tabId == p.tabId) {
16266 * show a specific panel
16267 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16268 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16270 showPanel : function (pan)
16272 if(this.transition || typeof(pan) == 'undefined'){
16273 Roo.log("waiting for the transitionend");
16277 if (typeof(pan) == 'number') {
16278 pan = this.tabs[pan];
16281 if (typeof(pan) == 'string') {
16282 pan = this.getPanelByName(pan);
16285 var cur = this.getActivePanel();
16288 Roo.log('pan or acitve pan is undefined');
16292 if (pan.tabId == this.getActivePanel().tabId) {
16296 if (false === cur.fireEvent('beforedeactivate')) {
16300 if(this.bullets > 0 && !Roo.isTouch){
16301 this.setActiveBullet(this.indexOfPanel(pan));
16304 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16306 this.transition = true;
16307 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16308 var lr = dir == 'next' ? 'left' : 'right';
16309 pan.el.addClass(dir); // or prev
16310 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16311 cur.el.addClass(lr); // or right
16312 pan.el.addClass(lr);
16315 cur.el.on('transitionend', function() {
16316 Roo.log("trans end?");
16318 pan.el.removeClass([lr,dir]);
16319 pan.setActive(true);
16321 cur.el.removeClass([lr]);
16322 cur.setActive(false);
16324 _this.transition = false;
16326 }, this, { single: true } );
16331 cur.setActive(false);
16332 pan.setActive(true);
16337 showPanelNext : function()
16339 var i = this.indexOfPanel(this.getActivePanel());
16341 if (i >= this.tabs.length - 1 && !this.autoslide) {
16345 if (i >= this.tabs.length - 1 && this.autoslide) {
16349 this.showPanel(this.tabs[i+1]);
16352 showPanelPrev : function()
16354 var i = this.indexOfPanel(this.getActivePanel());
16356 if (i < 1 && !this.autoslide) {
16360 if (i < 1 && this.autoslide) {
16361 i = this.tabs.length;
16364 this.showPanel(this.tabs[i-1]);
16368 addBullet: function()
16370 if(!this.bullets || Roo.isTouch){
16373 var ctr = this.el.select('.carousel-bullets',true).first();
16374 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16375 var bullet = ctr.createChild({
16376 cls : 'bullet bullet-' + i
16377 },ctr.dom.lastChild);
16382 bullet.on('click', (function(e, el, o, ii, t){
16384 e.preventDefault();
16386 this.showPanel(ii);
16388 if(this.autoslide && this.slideFn){
16389 clearInterval(this.slideFn);
16390 this.slideFn = window.setInterval(function() {
16391 _this.showPanelNext();
16395 }).createDelegate(this, [i, bullet], true));
16400 setActiveBullet : function(i)
16406 Roo.each(this.el.select('.bullet', true).elements, function(el){
16407 el.removeClass('selected');
16410 var bullet = this.el.select('.bullet-' + i, true).first();
16416 bullet.addClass('selected');
16427 Roo.apply(Roo.bootstrap.TabGroup, {
16431 * register a Navigation Group
16432 * @param {Roo.bootstrap.NavGroup} the navgroup to add
16434 register : function(navgrp)
16436 this.groups[navgrp.navId] = navgrp;
16440 * fetch a Navigation Group based on the navigation ID
16441 * if one does not exist , it will get created.
16442 * @param {string} the navgroup to add
16443 * @returns {Roo.bootstrap.NavGroup} the navgroup
16445 get: function(navId) {
16446 if (typeof(this.groups[navId]) == 'undefined') {
16447 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16449 return this.groups[navId] ;
16464 * @class Roo.bootstrap.TabPanel
16465 * @extends Roo.bootstrap.Component
16466 * Bootstrap TabPanel class
16467 * @cfg {Boolean} active panel active
16468 * @cfg {String} html panel content
16469 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16470 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16474 * Create a new TabPanel
16475 * @param {Object} config The config object
16478 Roo.bootstrap.TabPanel = function(config){
16479 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16483 * Fires when the active status changes
16484 * @param {Roo.bootstrap.TabPanel} this
16485 * @param {Boolean} state the new state
16490 * @event beforedeactivate
16491 * Fires before a tab is de-activated - can be used to do validation on a form.
16492 * @param {Roo.bootstrap.TabPanel} this
16493 * @return {Boolean} false if there is an error
16496 'beforedeactivate': true
16499 this.tabId = this.tabId || Roo.id();
16503 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16510 getAutoCreate : function(){
16513 // item is needed for carousel - not sure if it has any effect otherwise
16514 cls: 'tab-pane item',
16515 html: this.html || ''
16519 cfg.cls += ' active';
16523 cfg.tabId = this.tabId;
16530 initEvents: function()
16532 var p = this.parent();
16533 this.navId = this.navId || p.navId;
16535 if (typeof(this.navId) != 'undefined') {
16536 // not really needed.. but just in case.. parent should be a NavGroup.
16537 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16541 var i = tg.tabs.length - 1;
16543 if(this.active && tg.bullets > 0 && i < tg.bullets){
16544 tg.setActiveBullet(i);
16551 onRender : function(ct, position)
16553 // Roo.log("Call onRender: " + this.xtype);
16555 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16563 setActive: function(state)
16565 Roo.log("panel - set active " + this.tabId + "=" + state);
16567 this.active = state;
16569 this.el.removeClass('active');
16571 } else if (!this.el.hasClass('active')) {
16572 this.el.addClass('active');
16575 this.fireEvent('changed', this, state);
16592 * @class Roo.bootstrap.DateField
16593 * @extends Roo.bootstrap.Input
16594 * Bootstrap DateField class
16595 * @cfg {Number} weekStart default 0
16596 * @cfg {String} viewMode default empty, (months|years)
16597 * @cfg {String} minViewMode default empty, (months|years)
16598 * @cfg {Number} startDate default -Infinity
16599 * @cfg {Number} endDate default Infinity
16600 * @cfg {Boolean} todayHighlight default false
16601 * @cfg {Boolean} todayBtn default false
16602 * @cfg {Boolean} calendarWeeks default false
16603 * @cfg {Object} daysOfWeekDisabled default empty
16604 * @cfg {Boolean} singleMode default false (true | false)
16606 * @cfg {Boolean} keyboardNavigation default true
16607 * @cfg {String} language default en
16610 * Create a new DateField
16611 * @param {Object} config The config object
16614 Roo.bootstrap.DateField = function(config){
16615 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16619 * Fires when this field show.
16620 * @param {Roo.bootstrap.DateField} this
16621 * @param {Mixed} date The date value
16626 * Fires when this field hide.
16627 * @param {Roo.bootstrap.DateField} this
16628 * @param {Mixed} date The date value
16633 * Fires when select a date.
16634 * @param {Roo.bootstrap.DateField} this
16635 * @param {Mixed} date The date value
16639 * @event beforeselect
16640 * Fires when before select a date.
16641 * @param {Roo.bootstrap.DateField} this
16642 * @param {Mixed} date The date value
16644 beforeselect : true
16648 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16651 * @cfg {String} format
16652 * The default date format string which can be overriden for localization support. The format must be
16653 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16657 * @cfg {String} altFormats
16658 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16659 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16661 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16669 todayHighlight : false,
16675 keyboardNavigation: true,
16677 calendarWeeks: false,
16679 startDate: -Infinity,
16683 daysOfWeekDisabled: [],
16687 singleMode : false,
16689 UTCDate: function()
16691 return new Date(Date.UTC.apply(Date, arguments));
16694 UTCToday: function()
16696 var today = new Date();
16697 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16700 getDate: function() {
16701 var d = this.getUTCDate();
16702 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16705 getUTCDate: function() {
16709 setDate: function(d) {
16710 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16713 setUTCDate: function(d) {
16715 this.setValue(this.formatDate(this.date));
16718 onRender: function(ct, position)
16721 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16723 this.language = this.language || 'en';
16724 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16725 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16727 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16728 this.format = this.format || 'm/d/y';
16729 this.isInline = false;
16730 this.isInput = true;
16731 this.component = this.el.select('.add-on', true).first() || false;
16732 this.component = (this.component && this.component.length === 0) ? false : this.component;
16733 this.hasInput = this.component && this.inputEL().length;
16735 if (typeof(this.minViewMode === 'string')) {
16736 switch (this.minViewMode) {
16738 this.minViewMode = 1;
16741 this.minViewMode = 2;
16744 this.minViewMode = 0;
16749 if (typeof(this.viewMode === 'string')) {
16750 switch (this.viewMode) {
16763 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16765 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16767 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16769 this.picker().on('mousedown', this.onMousedown, this);
16770 this.picker().on('click', this.onClick, this);
16772 this.picker().addClass('datepicker-dropdown');
16774 this.startViewMode = this.viewMode;
16776 if(this.singleMode){
16777 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16778 v.setVisibilityMode(Roo.Element.DISPLAY);
16782 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16783 v.setStyle('width', '189px');
16787 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16788 if(!this.calendarWeeks){
16793 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16794 v.attr('colspan', function(i, val){
16795 return parseInt(val) + 1;
16800 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16802 this.setStartDate(this.startDate);
16803 this.setEndDate(this.endDate);
16805 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16812 if(this.isInline) {
16817 picker : function()
16819 return this.pickerEl;
16820 // return this.el.select('.datepicker', true).first();
16823 fillDow: function()
16825 var dowCnt = this.weekStart;
16834 if(this.calendarWeeks){
16842 while (dowCnt < this.weekStart + 7) {
16846 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16850 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16853 fillMonths: function()
16856 var months = this.picker().select('>.datepicker-months td', true).first();
16858 months.dom.innerHTML = '';
16864 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16867 months.createChild(month);
16874 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;
16876 if (this.date < this.startDate) {
16877 this.viewDate = new Date(this.startDate);
16878 } else if (this.date > this.endDate) {
16879 this.viewDate = new Date(this.endDate);
16881 this.viewDate = new Date(this.date);
16889 var d = new Date(this.viewDate),
16890 year = d.getUTCFullYear(),
16891 month = d.getUTCMonth(),
16892 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16893 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16894 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16895 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16896 currentDate = this.date && this.date.valueOf(),
16897 today = this.UTCToday();
16899 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16901 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16903 // this.picker.select('>tfoot th.today').
16904 // .text(dates[this.language].today)
16905 // .toggle(this.todayBtn !== false);
16907 this.updateNavArrows();
16910 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16912 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16914 prevMonth.setUTCDate(day);
16916 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16918 var nextMonth = new Date(prevMonth);
16920 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16922 nextMonth = nextMonth.valueOf();
16924 var fillMonths = false;
16926 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16928 while(prevMonth.valueOf() < nextMonth) {
16931 if (prevMonth.getUTCDay() === this.weekStart) {
16933 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16941 if(this.calendarWeeks){
16942 // ISO 8601: First week contains first thursday.
16943 // ISO also states week starts on Monday, but we can be more abstract here.
16945 // Start of current week: based on weekstart/current date
16946 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16947 // Thursday of this week
16948 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16949 // First Thursday of year, year from thursday
16950 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16951 // Calendar week: ms between thursdays, div ms per day, div 7 days
16952 calWeek = (th - yth) / 864e5 / 7 + 1;
16954 fillMonths.cn.push({
16962 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16964 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16967 if (this.todayHighlight &&
16968 prevMonth.getUTCFullYear() == today.getFullYear() &&
16969 prevMonth.getUTCMonth() == today.getMonth() &&
16970 prevMonth.getUTCDate() == today.getDate()) {
16971 clsName += ' today';
16974 if (currentDate && prevMonth.valueOf() === currentDate) {
16975 clsName += ' active';
16978 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16979 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16980 clsName += ' disabled';
16983 fillMonths.cn.push({
16985 cls: 'day ' + clsName,
16986 html: prevMonth.getDate()
16989 prevMonth.setDate(prevMonth.getDate()+1);
16992 var currentYear = this.date && this.date.getUTCFullYear();
16993 var currentMonth = this.date && this.date.getUTCMonth();
16995 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16997 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16998 v.removeClass('active');
17000 if(currentYear === year && k === currentMonth){
17001 v.addClass('active');
17004 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17005 v.addClass('disabled');
17011 year = parseInt(year/10, 10) * 10;
17013 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17015 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17018 for (var i = -1; i < 11; i++) {
17019 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17021 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17029 showMode: function(dir)
17032 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17035 Roo.each(this.picker().select('>div',true).elements, function(v){
17036 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17039 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17044 if(this.isInline) {
17048 this.picker().removeClass(['bottom', 'top']);
17050 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17052 * place to the top of element!
17056 this.picker().addClass('top');
17057 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17062 this.picker().addClass('bottom');
17064 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17067 parseDate : function(value)
17069 if(!value || value instanceof Date){
17072 var v = Date.parseDate(value, this.format);
17073 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17074 v = Date.parseDate(value, 'Y-m-d');
17076 if(!v && this.altFormats){
17077 if(!this.altFormatsArray){
17078 this.altFormatsArray = this.altFormats.split("|");
17080 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17081 v = Date.parseDate(value, this.altFormatsArray[i]);
17087 formatDate : function(date, fmt)
17089 return (!date || !(date instanceof Date)) ?
17090 date : date.dateFormat(fmt || this.format);
17093 onFocus : function()
17095 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17099 onBlur : function()
17101 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17103 var d = this.inputEl().getValue();
17112 this.picker().show();
17116 this.fireEvent('show', this, this.date);
17121 if(this.isInline) {
17124 this.picker().hide();
17125 this.viewMode = this.startViewMode;
17128 this.fireEvent('hide', this, this.date);
17132 onMousedown: function(e)
17134 e.stopPropagation();
17135 e.preventDefault();
17140 Roo.bootstrap.DateField.superclass.keyup.call(this);
17144 setValue: function(v)
17146 if(this.fireEvent('beforeselect', this, v) !== false){
17147 var d = new Date(this.parseDate(v) ).clearTime();
17149 if(isNaN(d.getTime())){
17150 this.date = this.viewDate = '';
17151 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17155 v = this.formatDate(d);
17157 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17159 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17163 this.fireEvent('select', this, this.date);
17167 getValue: function()
17169 return this.formatDate(this.date);
17172 fireKey: function(e)
17174 if (!this.picker().isVisible()){
17175 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17181 var dateChanged = false,
17183 newDate, newViewDate;
17188 e.preventDefault();
17192 if (!this.keyboardNavigation) {
17195 dir = e.keyCode == 37 ? -1 : 1;
17198 newDate = this.moveYear(this.date, dir);
17199 newViewDate = this.moveYear(this.viewDate, dir);
17200 } else if (e.shiftKey){
17201 newDate = this.moveMonth(this.date, dir);
17202 newViewDate = this.moveMonth(this.viewDate, dir);
17204 newDate = new Date(this.date);
17205 newDate.setUTCDate(this.date.getUTCDate() + dir);
17206 newViewDate = new Date(this.viewDate);
17207 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17209 if (this.dateWithinRange(newDate)){
17210 this.date = newDate;
17211 this.viewDate = newViewDate;
17212 this.setValue(this.formatDate(this.date));
17214 e.preventDefault();
17215 dateChanged = true;
17220 if (!this.keyboardNavigation) {
17223 dir = e.keyCode == 38 ? -1 : 1;
17225 newDate = this.moveYear(this.date, dir);
17226 newViewDate = this.moveYear(this.viewDate, dir);
17227 } else if (e.shiftKey){
17228 newDate = this.moveMonth(this.date, dir);
17229 newViewDate = this.moveMonth(this.viewDate, dir);
17231 newDate = new Date(this.date);
17232 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17233 newViewDate = new Date(this.viewDate);
17234 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17236 if (this.dateWithinRange(newDate)){
17237 this.date = newDate;
17238 this.viewDate = newViewDate;
17239 this.setValue(this.formatDate(this.date));
17241 e.preventDefault();
17242 dateChanged = true;
17246 this.setValue(this.formatDate(this.date));
17248 e.preventDefault();
17251 this.setValue(this.formatDate(this.date));
17265 onClick: function(e)
17267 e.stopPropagation();
17268 e.preventDefault();
17270 var target = e.getTarget();
17272 if(target.nodeName.toLowerCase() === 'i'){
17273 target = Roo.get(target).dom.parentNode;
17276 var nodeName = target.nodeName;
17277 var className = target.className;
17278 var html = target.innerHTML;
17279 //Roo.log(nodeName);
17281 switch(nodeName.toLowerCase()) {
17283 switch(className) {
17289 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17290 switch(this.viewMode){
17292 this.viewDate = this.moveMonth(this.viewDate, dir);
17296 this.viewDate = this.moveYear(this.viewDate, dir);
17302 var date = new Date();
17303 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17305 this.setValue(this.formatDate(this.date));
17312 if (className.indexOf('disabled') < 0) {
17313 this.viewDate.setUTCDate(1);
17314 if (className.indexOf('month') > -1) {
17315 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17317 var year = parseInt(html, 10) || 0;
17318 this.viewDate.setUTCFullYear(year);
17322 if(this.singleMode){
17323 this.setValue(this.formatDate(this.viewDate));
17334 //Roo.log(className);
17335 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17336 var day = parseInt(html, 10) || 1;
17337 var year = this.viewDate.getUTCFullYear(),
17338 month = this.viewDate.getUTCMonth();
17340 if (className.indexOf('old') > -1) {
17347 } else if (className.indexOf('new') > -1) {
17355 //Roo.log([year,month,day]);
17356 this.date = this.UTCDate(year, month, day,0,0,0,0);
17357 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17359 //Roo.log(this.formatDate(this.date));
17360 this.setValue(this.formatDate(this.date));
17367 setStartDate: function(startDate)
17369 this.startDate = startDate || -Infinity;
17370 if (this.startDate !== -Infinity) {
17371 this.startDate = this.parseDate(this.startDate);
17374 this.updateNavArrows();
17377 setEndDate: function(endDate)
17379 this.endDate = endDate || Infinity;
17380 if (this.endDate !== Infinity) {
17381 this.endDate = this.parseDate(this.endDate);
17384 this.updateNavArrows();
17387 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17389 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17390 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17391 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17393 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17394 return parseInt(d, 10);
17397 this.updateNavArrows();
17400 updateNavArrows: function()
17402 if(this.singleMode){
17406 var d = new Date(this.viewDate),
17407 year = d.getUTCFullYear(),
17408 month = d.getUTCMonth();
17410 Roo.each(this.picker().select('.prev', true).elements, function(v){
17412 switch (this.viewMode) {
17415 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17421 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17428 Roo.each(this.picker().select('.next', true).elements, function(v){
17430 switch (this.viewMode) {
17433 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17439 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17447 moveMonth: function(date, dir)
17452 var new_date = new Date(date.valueOf()),
17453 day = new_date.getUTCDate(),
17454 month = new_date.getUTCMonth(),
17455 mag = Math.abs(dir),
17457 dir = dir > 0 ? 1 : -1;
17460 // If going back one month, make sure month is not current month
17461 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17463 return new_date.getUTCMonth() == month;
17465 // If going forward one month, make sure month is as expected
17466 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17468 return new_date.getUTCMonth() != new_month;
17470 new_month = month + dir;
17471 new_date.setUTCMonth(new_month);
17472 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17473 if (new_month < 0 || new_month > 11) {
17474 new_month = (new_month + 12) % 12;
17477 // For magnitudes >1, move one month at a time...
17478 for (var i=0; i<mag; i++) {
17479 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17480 new_date = this.moveMonth(new_date, dir);
17482 // ...then reset the day, keeping it in the new month
17483 new_month = new_date.getUTCMonth();
17484 new_date.setUTCDate(day);
17486 return new_month != new_date.getUTCMonth();
17489 // Common date-resetting loop -- if date is beyond end of month, make it
17492 new_date.setUTCDate(--day);
17493 new_date.setUTCMonth(new_month);
17498 moveYear: function(date, dir)
17500 return this.moveMonth(date, dir*12);
17503 dateWithinRange: function(date)
17505 return date >= this.startDate && date <= this.endDate;
17511 this.picker().remove();
17516 Roo.apply(Roo.bootstrap.DateField, {
17527 html: '<i class="fa fa-arrow-left"/>'
17537 html: '<i class="fa fa-arrow-right"/>'
17579 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17580 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17581 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17582 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17583 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17596 navFnc: 'FullYear',
17601 navFnc: 'FullYear',
17606 Roo.apply(Roo.bootstrap.DateField, {
17610 cls: 'datepicker dropdown-menu roo-dynamic',
17614 cls: 'datepicker-days',
17618 cls: 'table-condensed',
17620 Roo.bootstrap.DateField.head,
17624 Roo.bootstrap.DateField.footer
17631 cls: 'datepicker-months',
17635 cls: 'table-condensed',
17637 Roo.bootstrap.DateField.head,
17638 Roo.bootstrap.DateField.content,
17639 Roo.bootstrap.DateField.footer
17646 cls: 'datepicker-years',
17650 cls: 'table-condensed',
17652 Roo.bootstrap.DateField.head,
17653 Roo.bootstrap.DateField.content,
17654 Roo.bootstrap.DateField.footer
17673 * @class Roo.bootstrap.TimeField
17674 * @extends Roo.bootstrap.Input
17675 * Bootstrap DateField class
17679 * Create a new TimeField
17680 * @param {Object} config The config object
17683 Roo.bootstrap.TimeField = function(config){
17684 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17688 * Fires when this field show.
17689 * @param {Roo.bootstrap.DateField} thisthis
17690 * @param {Mixed} date The date value
17695 * Fires when this field hide.
17696 * @param {Roo.bootstrap.DateField} this
17697 * @param {Mixed} date The date value
17702 * Fires when select a date.
17703 * @param {Roo.bootstrap.DateField} this
17704 * @param {Mixed} date The date value
17710 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17713 * @cfg {String} format
17714 * The default time format string which can be overriden for localization support. The format must be
17715 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17719 onRender: function(ct, position)
17722 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17724 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17726 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17728 this.pop = this.picker().select('>.datepicker-time',true).first();
17729 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17731 this.picker().on('mousedown', this.onMousedown, this);
17732 this.picker().on('click', this.onClick, this);
17734 this.picker().addClass('datepicker-dropdown');
17739 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17740 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17741 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17742 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17743 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17744 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17748 fireKey: function(e){
17749 if (!this.picker().isVisible()){
17750 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17756 e.preventDefault();
17764 this.onTogglePeriod();
17767 this.onIncrementMinutes();
17770 this.onDecrementMinutes();
17779 onClick: function(e) {
17780 e.stopPropagation();
17781 e.preventDefault();
17784 picker : function()
17786 return this.el.select('.datepicker', true).first();
17789 fillTime: function()
17791 var time = this.pop.select('tbody', true).first();
17793 time.dom.innerHTML = '';
17808 cls: 'hours-up glyphicon glyphicon-chevron-up'
17828 cls: 'minutes-up glyphicon glyphicon-chevron-up'
17849 cls: 'timepicker-hour',
17864 cls: 'timepicker-minute',
17879 cls: 'btn btn-primary period',
17901 cls: 'hours-down glyphicon glyphicon-chevron-down'
17921 cls: 'minutes-down glyphicon glyphicon-chevron-down'
17939 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17946 var hours = this.time.getHours();
17947 var minutes = this.time.getMinutes();
17960 hours = hours - 12;
17964 hours = '0' + hours;
17968 minutes = '0' + minutes;
17971 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17972 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17973 this.pop.select('button', true).first().dom.innerHTML = period;
17979 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17981 var cls = ['bottom'];
17983 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17990 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17995 this.picker().addClass(cls.join('-'));
17999 Roo.each(cls, function(c){
18001 _this.picker().setTop(_this.inputEl().getHeight());
18005 _this.picker().setTop(0 - _this.picker().getHeight());
18010 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18014 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18021 onFocus : function()
18023 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18027 onBlur : function()
18029 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18035 this.picker().show();
18040 this.fireEvent('show', this, this.date);
18045 this.picker().hide();
18048 this.fireEvent('hide', this, this.date);
18051 setTime : function()
18054 this.setValue(this.time.format(this.format));
18056 this.fireEvent('select', this, this.date);
18061 onMousedown: function(e){
18062 e.stopPropagation();
18063 e.preventDefault();
18066 onIncrementHours: function()
18068 Roo.log('onIncrementHours');
18069 this.time = this.time.add(Date.HOUR, 1);
18074 onDecrementHours: function()
18076 Roo.log('onDecrementHours');
18077 this.time = this.time.add(Date.HOUR, -1);
18081 onIncrementMinutes: function()
18083 Roo.log('onIncrementMinutes');
18084 this.time = this.time.add(Date.MINUTE, 1);
18088 onDecrementMinutes: function()
18090 Roo.log('onDecrementMinutes');
18091 this.time = this.time.add(Date.MINUTE, -1);
18095 onTogglePeriod: function()
18097 Roo.log('onTogglePeriod');
18098 this.time = this.time.add(Date.HOUR, 12);
18105 Roo.apply(Roo.bootstrap.TimeField, {
18135 cls: 'btn btn-info ok',
18147 Roo.apply(Roo.bootstrap.TimeField, {
18151 cls: 'datepicker dropdown-menu',
18155 cls: 'datepicker-time',
18159 cls: 'table-condensed',
18161 Roo.bootstrap.TimeField.content,
18162 Roo.bootstrap.TimeField.footer
18181 * @class Roo.bootstrap.MonthField
18182 * @extends Roo.bootstrap.Input
18183 * Bootstrap MonthField class
18185 * @cfg {String} language default en
18188 * Create a new MonthField
18189 * @param {Object} config The config object
18192 Roo.bootstrap.MonthField = function(config){
18193 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18198 * Fires when this field show.
18199 * @param {Roo.bootstrap.MonthField} this
18200 * @param {Mixed} date The date value
18205 * Fires when this field hide.
18206 * @param {Roo.bootstrap.MonthField} this
18207 * @param {Mixed} date The date value
18212 * Fires when select a date.
18213 * @param {Roo.bootstrap.MonthField} this
18214 * @param {String} oldvalue The old value
18215 * @param {String} newvalue The new value
18221 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
18223 onRender: function(ct, position)
18226 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18228 this.language = this.language || 'en';
18229 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18230 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18232 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18233 this.isInline = false;
18234 this.isInput = true;
18235 this.component = this.el.select('.add-on', true).first() || false;
18236 this.component = (this.component && this.component.length === 0) ? false : this.component;
18237 this.hasInput = this.component && this.inputEL().length;
18239 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18241 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18243 this.picker().on('mousedown', this.onMousedown, this);
18244 this.picker().on('click', this.onClick, this);
18246 this.picker().addClass('datepicker-dropdown');
18248 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18249 v.setStyle('width', '189px');
18256 if(this.isInline) {
18262 setValue: function(v, suppressEvent)
18264 var o = this.getValue();
18266 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18270 if(suppressEvent !== true){
18271 this.fireEvent('select', this, o, v);
18276 getValue: function()
18281 onClick: function(e)
18283 e.stopPropagation();
18284 e.preventDefault();
18286 var target = e.getTarget();
18288 if(target.nodeName.toLowerCase() === 'i'){
18289 target = Roo.get(target).dom.parentNode;
18292 var nodeName = target.nodeName;
18293 var className = target.className;
18294 var html = target.innerHTML;
18296 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18300 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18302 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18308 picker : function()
18310 return this.pickerEl;
18313 fillMonths: function()
18316 var months = this.picker().select('>.datepicker-months td', true).first();
18318 months.dom.innerHTML = '';
18324 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18327 months.createChild(month);
18336 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18337 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18340 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18341 e.removeClass('active');
18343 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18344 e.addClass('active');
18351 if(this.isInline) {
18355 this.picker().removeClass(['bottom', 'top']);
18357 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18359 * place to the top of element!
18363 this.picker().addClass('top');
18364 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18369 this.picker().addClass('bottom');
18371 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18374 onFocus : function()
18376 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18380 onBlur : function()
18382 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18384 var d = this.inputEl().getValue();
18393 this.picker().show();
18394 this.picker().select('>.datepicker-months', true).first().show();
18398 this.fireEvent('show', this, this.date);
18403 if(this.isInline) {
18406 this.picker().hide();
18407 this.fireEvent('hide', this, this.date);
18411 onMousedown: function(e)
18413 e.stopPropagation();
18414 e.preventDefault();
18419 Roo.bootstrap.MonthField.superclass.keyup.call(this);
18423 fireKey: function(e)
18425 if (!this.picker().isVisible()){
18426 if (e.keyCode == 27) {// allow escape to hide and re-show picker
18437 e.preventDefault();
18441 dir = e.keyCode == 37 ? -1 : 1;
18443 this.vIndex = this.vIndex + dir;
18445 if(this.vIndex < 0){
18449 if(this.vIndex > 11){
18453 if(isNaN(this.vIndex)){
18457 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18463 dir = e.keyCode == 38 ? -1 : 1;
18465 this.vIndex = this.vIndex + dir * 4;
18467 if(this.vIndex < 0){
18471 if(this.vIndex > 11){
18475 if(isNaN(this.vIndex)){
18479 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18484 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18485 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18489 e.preventDefault();
18492 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18493 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18509 this.picker().remove();
18514 Roo.apply(Roo.bootstrap.MonthField, {
18533 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18534 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18539 Roo.apply(Roo.bootstrap.MonthField, {
18543 cls: 'datepicker dropdown-menu roo-dynamic',
18547 cls: 'datepicker-months',
18551 cls: 'table-condensed',
18553 Roo.bootstrap.DateField.content
18573 * @class Roo.bootstrap.CheckBox
18574 * @extends Roo.bootstrap.Input
18575 * Bootstrap CheckBox class
18577 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18578 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18579 * @cfg {String} boxLabel The text that appears beside the checkbox
18580 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18581 * @cfg {Boolean} checked initnal the element
18582 * @cfg {Boolean} inline inline the element (default false)
18583 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18586 * Create a new CheckBox
18587 * @param {Object} config The config object
18590 Roo.bootstrap.CheckBox = function(config){
18591 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18596 * Fires when the element is checked or unchecked.
18597 * @param {Roo.bootstrap.CheckBox} this This input
18598 * @param {Boolean} checked The new checked value
18605 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18607 inputType: 'checkbox',
18615 getAutoCreate : function()
18617 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18623 cfg.cls = 'form-group ' + this.inputType; //input-group
18626 cfg.cls += ' ' + this.inputType + '-inline';
18632 type : this.inputType,
18633 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18634 cls : 'roo-' + this.inputType, //'form-box',
18635 placeholder : this.placeholder || ''
18639 if (this.weight) { // Validity check?
18640 cfg.cls += " " + this.inputType + "-" + this.weight;
18643 if (this.disabled) {
18644 input.disabled=true;
18648 input.checked = this.checked;
18652 input.name = this.name;
18656 input.cls += ' input-' + this.size;
18661 ['xs','sm','md','lg'].map(function(size){
18662 if (settings[size]) {
18663 cfg.cls += ' col-' + size + '-' + settings[size];
18667 var inputblock = input;
18669 if (this.before || this.after) {
18672 cls : 'input-group',
18677 inputblock.cn.push({
18679 cls : 'input-group-addon',
18684 inputblock.cn.push(input);
18687 inputblock.cn.push({
18689 cls : 'input-group-addon',
18696 if (align ==='left' && this.fieldLabel.length) {
18697 // Roo.log("left and has label");
18703 cls : 'control-label col-md-' + this.labelWidth,
18704 html : this.fieldLabel
18708 cls : "col-md-" + (12 - this.labelWidth),
18715 } else if ( this.fieldLabel.length) {
18716 // Roo.log(" label");
18720 tag: this.boxLabel ? 'span' : 'label',
18722 cls: 'control-label box-input-label',
18723 //cls : 'input-group-addon',
18724 html : this.fieldLabel
18734 // Roo.log(" no label && no align");
18735 cfg.cn = [ inputblock ] ;
18740 var boxLabelCfg = {
18742 //'for': id, // box label is handled by onclick - so no for...
18744 html: this.boxLabel
18748 boxLabelCfg.tooltip = this.tooltip;
18751 cfg.cn.push(boxLabelCfg);
18761 * return the real input element.
18763 inputEl: function ()
18765 return this.el.select('input.roo-' + this.inputType,true).first();
18768 labelEl: function()
18770 return this.el.select('label.control-label',true).first();
18772 /* depricated... */
18776 return this.labelEl();
18779 initEvents : function()
18781 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18783 this.inputEl().on('click', this.onClick, this);
18785 if (this.boxLabel) {
18786 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18789 this.startValue = this.getValue();
18792 Roo.bootstrap.CheckBox.register(this);
18796 onClick : function()
18798 this.setChecked(!this.checked);
18801 setChecked : function(state,suppressEvent)
18803 this.startValue = this.getValue();
18805 if(this.inputType == 'radio'){
18807 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18808 e.dom.checked = false;
18811 this.inputEl().dom.checked = true;
18813 this.inputEl().dom.value = this.inputValue;
18815 if(suppressEvent !== true){
18816 this.fireEvent('check', this, true);
18824 this.checked = state;
18826 this.inputEl().dom.checked = state;
18828 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18830 if(suppressEvent !== true){
18831 this.fireEvent('check', this, state);
18837 getValue : function()
18839 if(this.inputType == 'radio'){
18840 return this.getGroupValue();
18843 return this.inputEl().getValue();
18847 getGroupValue : function()
18849 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18853 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18856 setValue : function(v,suppressEvent)
18858 if(this.inputType == 'radio'){
18859 this.setGroupValue(v, suppressEvent);
18863 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18868 setGroupValue : function(v, suppressEvent)
18870 this.startValue = this.getValue();
18872 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18873 e.dom.checked = false;
18875 if(e.dom.value == v){
18876 e.dom.checked = true;
18880 if(suppressEvent !== true){
18881 this.fireEvent('check', this, true);
18889 validate : function()
18893 (this.inputType == 'radio' && this.validateRadio()) ||
18894 (this.inputType == 'checkbox' && this.validateCheckbox())
18900 this.markInvalid();
18904 validateRadio : function()
18908 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18909 if(!e.dom.checked){
18921 validateCheckbox : function()
18924 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18927 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18935 for(var i in group){
18940 r = (group[i].getValue() == group[i].inputValue) ? true : false;
18947 * Mark this field as valid
18949 markValid : function()
18951 if(this.allowBlank){
18957 this.fireEvent('valid', this);
18959 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18962 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18969 if(this.inputType == 'radio'){
18970 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18971 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18972 e.findParent('.form-group', false, true).addClass(_this.validClass);
18979 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18980 this.el.findParent('.form-group', false, true).addClass(this.validClass);
18984 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18990 for(var i in group){
18991 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18992 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18997 * Mark this field as invalid
18998 * @param {String} msg The validation message
19000 markInvalid : function(msg)
19002 if(this.allowBlank){
19008 this.fireEvent('invalid', this, msg);
19010 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19013 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19017 label.markInvalid();
19020 if(this.inputType == 'radio'){
19021 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19022 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19023 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19030 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19031 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19035 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19041 for(var i in group){
19042 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19043 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19050 Roo.apply(Roo.bootstrap.CheckBox, {
19055 * register a CheckBox Group
19056 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19058 register : function(checkbox)
19060 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19061 this.groups[checkbox.groupId] = {};
19064 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19068 this.groups[checkbox.groupId][checkbox.name] = checkbox;
19072 * fetch a CheckBox Group based on the group ID
19073 * @param {string} the group ID
19074 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19076 get: function(groupId) {
19077 if (typeof(this.groups[groupId]) == 'undefined') {
19081 return this.groups[groupId] ;
19093 *<div class="radio">
19095 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19096 Option one is this and that—be sure to include why it's great
19103 *<label class="radio-inline">fieldLabel</label>
19104 *<label class="radio-inline">
19105 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19113 * @class Roo.bootstrap.Radio
19114 * @extends Roo.bootstrap.CheckBox
19115 * Bootstrap Radio class
19118 * Create a new Radio
19119 * @param {Object} config The config object
19122 Roo.bootstrap.Radio = function(config){
19123 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19127 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
19129 inputType: 'radio',
19133 getAutoCreate : function()
19135 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19136 align = align || 'left'; // default...
19143 tag : this.inline ? 'span' : 'div',
19148 var inline = this.inline ? ' radio-inline' : '';
19152 // does not need for, as we wrap the input with it..
19154 cls : 'control-label box-label' + inline,
19157 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19161 //cls : 'control-label' + inline,
19162 html : this.fieldLabel,
19163 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19172 type : this.inputType,
19173 //value : (!this.checked) ? this.valueOff : this.inputValue,
19174 value : this.inputValue,
19176 placeholder : this.placeholder || '' // ?? needed????
19179 if (this.weight) { // Validity check?
19180 input.cls += " radio-" + this.weight;
19182 if (this.disabled) {
19183 input.disabled=true;
19187 input.checked = this.checked;
19191 input.name = this.name;
19195 input.cls += ' input-' + this.size;
19198 //?? can span's inline have a width??
19201 ['xs','sm','md','lg'].map(function(size){
19202 if (settings[size]) {
19203 cfg.cls += ' col-' + size + '-' + settings[size];
19207 var inputblock = input;
19209 if (this.before || this.after) {
19212 cls : 'input-group',
19217 inputblock.cn.push({
19219 cls : 'input-group-addon',
19223 inputblock.cn.push(input);
19225 inputblock.cn.push({
19227 cls : 'input-group-addon',
19235 if (this.fieldLabel && this.fieldLabel.length) {
19236 cfg.cn.push(fieldLabel);
19239 // normal bootstrap puts the input inside the label.
19240 // however with our styled version - it has to go after the input.
19242 //lbl.cn.push(inputblock);
19246 cls: 'radio' + inline,
19253 cfg.cn.push( lblwrap);
19258 html: this.boxLabel
19267 initEvents : function()
19269 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19271 this.inputEl().on('click', this.onClick, this);
19272 if (this.boxLabel) {
19273 //Roo.log('find label');
19274 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
19279 inputEl: function ()
19281 return this.el.select('input.roo-radio',true).first();
19283 onClick : function()
19286 this.setChecked(true);
19289 setChecked : function(state,suppressEvent)
19292 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19293 v.dom.checked = false;
19296 Roo.log(this.inputEl().dom);
19297 this.checked = state;
19298 this.inputEl().dom.checked = state;
19300 if(suppressEvent !== true){
19301 this.fireEvent('check', this, state);
19304 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19308 getGroupValue : function()
19311 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19312 if(v.dom.checked == true){
19313 value = v.dom.value;
19321 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
19322 * @return {Mixed} value The field value
19324 getValue : function(){
19325 return this.getGroupValue();
19331 //<script type="text/javascript">
19334 * Based Ext JS Library 1.1.1
19335 * Copyright(c) 2006-2007, Ext JS, LLC.
19341 * @class Roo.HtmlEditorCore
19342 * @extends Roo.Component
19343 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19345 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19348 Roo.HtmlEditorCore = function(config){
19351 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19356 * @event initialize
19357 * Fires when the editor is fully initialized (including the iframe)
19358 * @param {Roo.HtmlEditorCore} this
19363 * Fires when the editor is first receives the focus. Any insertion must wait
19364 * until after this event.
19365 * @param {Roo.HtmlEditorCore} this
19369 * @event beforesync
19370 * Fires before the textarea is updated with content from the editor iframe. Return false
19371 * to cancel the sync.
19372 * @param {Roo.HtmlEditorCore} this
19373 * @param {String} html
19377 * @event beforepush
19378 * Fires before the iframe editor is updated with content from the textarea. Return false
19379 * to cancel the push.
19380 * @param {Roo.HtmlEditorCore} this
19381 * @param {String} html
19386 * Fires when the textarea is updated with content from the editor iframe.
19387 * @param {Roo.HtmlEditorCore} this
19388 * @param {String} html
19393 * Fires when the iframe editor is updated with content from the textarea.
19394 * @param {Roo.HtmlEditorCore} this
19395 * @param {String} html
19400 * @event editorevent
19401 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19402 * @param {Roo.HtmlEditorCore} this
19408 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19410 // defaults : white / black...
19411 this.applyBlacklists();
19418 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
19422 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
19428 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
19433 * @cfg {Number} height (in pixels)
19437 * @cfg {Number} width (in pixels)
19442 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19445 stylesheets: false,
19450 // private properties
19451 validationEvent : false,
19453 initialized : false,
19455 sourceEditMode : false,
19456 onFocus : Roo.emptyFn,
19458 hideMode:'offsets',
19462 // blacklist + whitelisted elements..
19469 * Protected method that will not generally be called directly. It
19470 * is called when the editor initializes the iframe with HTML contents. Override this method if you
19471 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19473 getDocMarkup : function(){
19477 // inherit styels from page...??
19478 if (this.stylesheets === false) {
19480 Roo.get(document.head).select('style').each(function(node) {
19481 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19484 Roo.get(document.head).select('link').each(function(node) {
19485 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19488 } else if (!this.stylesheets.length) {
19490 st = '<style type="text/css">' +
19491 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19497 st += '<style type="text/css">' +
19498 'IMG { cursor: pointer } ' +
19502 return '<html><head>' + st +
19503 //<style type="text/css">' +
19504 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19506 ' </head><body class="roo-htmleditor-body"></body></html>';
19510 onRender : function(ct, position)
19513 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19514 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19517 this.el.dom.style.border = '0 none';
19518 this.el.dom.setAttribute('tabIndex', -1);
19519 this.el.addClass('x-hidden hide');
19523 if(Roo.isIE){ // fix IE 1px bogus margin
19524 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19528 this.frameId = Roo.id();
19532 var iframe = this.owner.wrap.createChild({
19534 cls: 'form-control', // bootstrap..
19536 name: this.frameId,
19537 frameBorder : 'no',
19538 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19543 this.iframe = iframe.dom;
19545 this.assignDocWin();
19547 this.doc.designMode = 'on';
19550 this.doc.write(this.getDocMarkup());
19554 var task = { // must defer to wait for browser to be ready
19556 //console.log("run task?" + this.doc.readyState);
19557 this.assignDocWin();
19558 if(this.doc.body || this.doc.readyState == 'complete'){
19560 this.doc.designMode="on";
19564 Roo.TaskMgr.stop(task);
19565 this.initEditor.defer(10, this);
19572 Roo.TaskMgr.start(task);
19577 onResize : function(w, h)
19579 Roo.log('resize: ' +w + ',' + h );
19580 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19584 if(typeof w == 'number'){
19586 this.iframe.style.width = w + 'px';
19588 if(typeof h == 'number'){
19590 this.iframe.style.height = h + 'px';
19592 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19599 * Toggles the editor between standard and source edit mode.
19600 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19602 toggleSourceEdit : function(sourceEditMode){
19604 this.sourceEditMode = sourceEditMode === true;
19606 if(this.sourceEditMode){
19608 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19611 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19612 //this.iframe.className = '';
19615 //this.setSize(this.owner.wrap.getSize());
19616 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19623 * Protected method that will not generally be called directly. If you need/want
19624 * custom HTML cleanup, this is the method you should override.
19625 * @param {String} html The HTML to be cleaned
19626 * return {String} The cleaned HTML
19628 cleanHtml : function(html){
19629 html = String(html);
19630 if(html.length > 5){
19631 if(Roo.isSafari){ // strip safari nonsense
19632 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19635 if(html == ' '){
19642 * HTML Editor -> Textarea
19643 * Protected method that will not generally be called directly. Syncs the contents
19644 * of the editor iframe with the textarea.
19646 syncValue : function(){
19647 if(this.initialized){
19648 var bd = (this.doc.body || this.doc.documentElement);
19649 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19650 var html = bd.innerHTML;
19652 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19653 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19655 html = '<div style="'+m[0]+'">' + html + '</div>';
19658 html = this.cleanHtml(html);
19659 // fix up the special chars.. normaly like back quotes in word...
19660 // however we do not want to do this with chinese..
19661 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19662 var cc = b.charCodeAt();
19664 (cc >= 0x4E00 && cc < 0xA000 ) ||
19665 (cc >= 0x3400 && cc < 0x4E00 ) ||
19666 (cc >= 0xf900 && cc < 0xfb00 )
19672 if(this.owner.fireEvent('beforesync', this, html) !== false){
19673 this.el.dom.value = html;
19674 this.owner.fireEvent('sync', this, html);
19680 * Protected method that will not generally be called directly. Pushes the value of the textarea
19681 * into the iframe editor.
19683 pushValue : function(){
19684 if(this.initialized){
19685 var v = this.el.dom.value.trim();
19687 // if(v.length < 1){
19691 if(this.owner.fireEvent('beforepush', this, v) !== false){
19692 var d = (this.doc.body || this.doc.documentElement);
19694 this.cleanUpPaste();
19695 this.el.dom.value = d.innerHTML;
19696 this.owner.fireEvent('push', this, v);
19702 deferFocus : function(){
19703 this.focus.defer(10, this);
19707 focus : function(){
19708 if(this.win && !this.sourceEditMode){
19715 assignDocWin: function()
19717 var iframe = this.iframe;
19720 this.doc = iframe.contentWindow.document;
19721 this.win = iframe.contentWindow;
19723 // if (!Roo.get(this.frameId)) {
19726 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19727 // this.win = Roo.get(this.frameId).dom.contentWindow;
19729 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19733 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19734 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19739 initEditor : function(){
19740 //console.log("INIT EDITOR");
19741 this.assignDocWin();
19745 this.doc.designMode="on";
19747 this.doc.write(this.getDocMarkup());
19750 var dbody = (this.doc.body || this.doc.documentElement);
19751 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19752 // this copies styles from the containing element into thsi one..
19753 // not sure why we need all of this..
19754 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19756 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19757 //ss['background-attachment'] = 'fixed'; // w3c
19758 dbody.bgProperties = 'fixed'; // ie
19759 //Roo.DomHelper.applyStyles(dbody, ss);
19760 Roo.EventManager.on(this.doc, {
19761 //'mousedown': this.onEditorEvent,
19762 'mouseup': this.onEditorEvent,
19763 'dblclick': this.onEditorEvent,
19764 'click': this.onEditorEvent,
19765 'keyup': this.onEditorEvent,
19770 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19772 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19773 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19775 this.initialized = true;
19777 this.owner.fireEvent('initialize', this);
19782 onDestroy : function(){
19788 //for (var i =0; i < this.toolbars.length;i++) {
19789 // // fixme - ask toolbars for heights?
19790 // this.toolbars[i].onDestroy();
19793 //this.wrap.dom.innerHTML = '';
19794 //this.wrap.remove();
19799 onFirstFocus : function(){
19801 this.assignDocWin();
19804 this.activated = true;
19807 if(Roo.isGecko){ // prevent silly gecko errors
19809 var s = this.win.getSelection();
19810 if(!s.focusNode || s.focusNode.nodeType != 3){
19811 var r = s.getRangeAt(0);
19812 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19817 this.execCmd('useCSS', true);
19818 this.execCmd('styleWithCSS', false);
19821 this.owner.fireEvent('activate', this);
19825 adjustFont: function(btn){
19826 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19827 //if(Roo.isSafari){ // safari
19830 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19831 if(Roo.isSafari){ // safari
19832 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19833 v = (v < 10) ? 10 : v;
19834 v = (v > 48) ? 48 : v;
19835 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19840 v = Math.max(1, v+adjust);
19842 this.execCmd('FontSize', v );
19845 onEditorEvent : function(e)
19847 this.owner.fireEvent('editorevent', this, e);
19848 // this.updateToolbar();
19849 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19852 insertTag : function(tg)
19854 // could be a bit smarter... -> wrap the current selected tRoo..
19855 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19857 range = this.createRange(this.getSelection());
19858 var wrappingNode = this.doc.createElement(tg.toLowerCase());
19859 wrappingNode.appendChild(range.extractContents());
19860 range.insertNode(wrappingNode);
19867 this.execCmd("formatblock", tg);
19871 insertText : function(txt)
19875 var range = this.createRange();
19876 range.deleteContents();
19877 //alert(Sender.getAttribute('label'));
19879 range.insertNode(this.doc.createTextNode(txt));
19885 * Executes a Midas editor command on the editor document and performs necessary focus and
19886 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19887 * @param {String} cmd The Midas command
19888 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19890 relayCmd : function(cmd, value){
19892 this.execCmd(cmd, value);
19893 this.owner.fireEvent('editorevent', this);
19894 //this.updateToolbar();
19895 this.owner.deferFocus();
19899 * Executes a Midas editor command directly on the editor document.
19900 * For visual commands, you should use {@link #relayCmd} instead.
19901 * <b>This should only be called after the editor is initialized.</b>
19902 * @param {String} cmd The Midas command
19903 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19905 execCmd : function(cmd, value){
19906 this.doc.execCommand(cmd, false, value === undefined ? null : value);
19913 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19915 * @param {String} text | dom node..
19917 insertAtCursor : function(text)
19922 if(!this.activated){
19928 var r = this.doc.selection.createRange();
19939 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19943 // from jquery ui (MIT licenced)
19945 var win = this.win;
19947 if (win.getSelection && win.getSelection().getRangeAt) {
19948 range = win.getSelection().getRangeAt(0);
19949 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19950 range.insertNode(node);
19951 } else if (win.document.selection && win.document.selection.createRange) {
19952 // no firefox support
19953 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19954 win.document.selection.createRange().pasteHTML(txt);
19956 // no firefox support
19957 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19958 this.execCmd('InsertHTML', txt);
19967 mozKeyPress : function(e){
19969 var c = e.getCharCode(), cmd;
19972 c = String.fromCharCode(c).toLowerCase();
19986 this.cleanUpPaste.defer(100, this);
19994 e.preventDefault();
20002 fixKeys : function(){ // load time branching for fastest keydown performance
20004 return function(e){
20005 var k = e.getKey(), r;
20008 r = this.doc.selection.createRange();
20011 r.pasteHTML('    ');
20018 r = this.doc.selection.createRange();
20020 var target = r.parentElement();
20021 if(!target || target.tagName.toLowerCase() != 'li'){
20023 r.pasteHTML('<br />');
20029 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20030 this.cleanUpPaste.defer(100, this);
20036 }else if(Roo.isOpera){
20037 return function(e){
20038 var k = e.getKey();
20042 this.execCmd('InsertHTML','    ');
20045 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20046 this.cleanUpPaste.defer(100, this);
20051 }else if(Roo.isSafari){
20052 return function(e){
20053 var k = e.getKey();
20057 this.execCmd('InsertText','\t');
20061 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20062 this.cleanUpPaste.defer(100, this);
20070 getAllAncestors: function()
20072 var p = this.getSelectedNode();
20075 a.push(p); // push blank onto stack..
20076 p = this.getParentElement();
20080 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20084 a.push(this.doc.body);
20088 lastSelNode : false,
20091 getSelection : function()
20093 this.assignDocWin();
20094 return Roo.isIE ? this.doc.selection : this.win.getSelection();
20097 getSelectedNode: function()
20099 // this may only work on Gecko!!!
20101 // should we cache this!!!!
20106 var range = this.createRange(this.getSelection()).cloneRange();
20109 var parent = range.parentElement();
20111 var testRange = range.duplicate();
20112 testRange.moveToElementText(parent);
20113 if (testRange.inRange(range)) {
20116 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20119 parent = parent.parentElement;
20124 // is ancestor a text element.
20125 var ac = range.commonAncestorContainer;
20126 if (ac.nodeType == 3) {
20127 ac = ac.parentNode;
20130 var ar = ac.childNodes;
20133 var other_nodes = [];
20134 var has_other_nodes = false;
20135 for (var i=0;i<ar.length;i++) {
20136 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
20139 // fullly contained node.
20141 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20146 // probably selected..
20147 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20148 other_nodes.push(ar[i]);
20152 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
20157 has_other_nodes = true;
20159 if (!nodes.length && other_nodes.length) {
20160 nodes= other_nodes;
20162 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20168 createRange: function(sel)
20170 // this has strange effects when using with
20171 // top toolbar - not sure if it's a great idea.
20172 //this.editor.contentWindow.focus();
20173 if (typeof sel != "undefined") {
20175 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20177 return this.doc.createRange();
20180 return this.doc.createRange();
20183 getParentElement: function()
20186 this.assignDocWin();
20187 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20189 var range = this.createRange(sel);
20192 var p = range.commonAncestorContainer;
20193 while (p.nodeType == 3) { // text node
20204 * Range intersection.. the hard stuff...
20208 * [ -- selected range --- ]
20212 * if end is before start or hits it. fail.
20213 * if start is after end or hits it fail.
20215 * if either hits (but other is outside. - then it's not
20221 // @see http://www.thismuchiknow.co.uk/?p=64.
20222 rangeIntersectsNode : function(range, node)
20224 var nodeRange = node.ownerDocument.createRange();
20226 nodeRange.selectNode(node);
20228 nodeRange.selectNodeContents(node);
20231 var rangeStartRange = range.cloneRange();
20232 rangeStartRange.collapse(true);
20234 var rangeEndRange = range.cloneRange();
20235 rangeEndRange.collapse(false);
20237 var nodeStartRange = nodeRange.cloneRange();
20238 nodeStartRange.collapse(true);
20240 var nodeEndRange = nodeRange.cloneRange();
20241 nodeEndRange.collapse(false);
20243 return rangeStartRange.compareBoundaryPoints(
20244 Range.START_TO_START, nodeEndRange) == -1 &&
20245 rangeEndRange.compareBoundaryPoints(
20246 Range.START_TO_START, nodeStartRange) == 1;
20250 rangeCompareNode : function(range, node)
20252 var nodeRange = node.ownerDocument.createRange();
20254 nodeRange.selectNode(node);
20256 nodeRange.selectNodeContents(node);
20260 range.collapse(true);
20262 nodeRange.collapse(true);
20264 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20265 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
20267 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20269 var nodeIsBefore = ss == 1;
20270 var nodeIsAfter = ee == -1;
20272 if (nodeIsBefore && nodeIsAfter) {
20275 if (!nodeIsBefore && nodeIsAfter) {
20276 return 1; //right trailed.
20279 if (nodeIsBefore && !nodeIsAfter) {
20280 return 2; // left trailed.
20286 // private? - in a new class?
20287 cleanUpPaste : function()
20289 // cleans up the whole document..
20290 Roo.log('cleanuppaste');
20292 this.cleanUpChildren(this.doc.body);
20293 var clean = this.cleanWordChars(this.doc.body.innerHTML);
20294 if (clean != this.doc.body.innerHTML) {
20295 this.doc.body.innerHTML = clean;
20300 cleanWordChars : function(input) {// change the chars to hex code
20301 var he = Roo.HtmlEditorCore;
20303 var output = input;
20304 Roo.each(he.swapCodes, function(sw) {
20305 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20307 output = output.replace(swapper, sw[1]);
20314 cleanUpChildren : function (n)
20316 if (!n.childNodes.length) {
20319 for (var i = n.childNodes.length-1; i > -1 ; i--) {
20320 this.cleanUpChild(n.childNodes[i]);
20327 cleanUpChild : function (node)
20330 //console.log(node);
20331 if (node.nodeName == "#text") {
20332 // clean up silly Windows -- stuff?
20335 if (node.nodeName == "#comment") {
20336 node.parentNode.removeChild(node);
20337 // clean up silly Windows -- stuff?
20340 var lcname = node.tagName.toLowerCase();
20341 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20342 // whitelist of tags..
20344 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20346 node.parentNode.removeChild(node);
20351 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20353 // remove <a name=....> as rendering on yahoo mailer is borked with this.
20354 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20356 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20357 // remove_keep_children = true;
20360 if (remove_keep_children) {
20361 this.cleanUpChildren(node);
20362 // inserts everything just before this node...
20363 while (node.childNodes.length) {
20364 var cn = node.childNodes[0];
20365 node.removeChild(cn);
20366 node.parentNode.insertBefore(cn, node);
20368 node.parentNode.removeChild(node);
20372 if (!node.attributes || !node.attributes.length) {
20373 this.cleanUpChildren(node);
20377 function cleanAttr(n,v)
20380 if (v.match(/^\./) || v.match(/^\//)) {
20383 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20386 if (v.match(/^#/)) {
20389 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20390 node.removeAttribute(n);
20394 var cwhite = this.cwhite;
20395 var cblack = this.cblack;
20397 function cleanStyle(n,v)
20399 if (v.match(/expression/)) { //XSS?? should we even bother..
20400 node.removeAttribute(n);
20404 var parts = v.split(/;/);
20407 Roo.each(parts, function(p) {
20408 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20412 var l = p.split(':').shift().replace(/\s+/g,'');
20413 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20415 if ( cwhite.length && cblack.indexOf(l) > -1) {
20416 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20417 //node.removeAttribute(n);
20421 // only allow 'c whitelisted system attributes'
20422 if ( cwhite.length && cwhite.indexOf(l) < 0) {
20423 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20424 //node.removeAttribute(n);
20434 if (clean.length) {
20435 node.setAttribute(n, clean.join(';'));
20437 node.removeAttribute(n);
20443 for (var i = node.attributes.length-1; i > -1 ; i--) {
20444 var a = node.attributes[i];
20447 if (a.name.toLowerCase().substr(0,2)=='on') {
20448 node.removeAttribute(a.name);
20451 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20452 node.removeAttribute(a.name);
20455 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20456 cleanAttr(a.name,a.value); // fixme..
20459 if (a.name == 'style') {
20460 cleanStyle(a.name,a.value);
20463 /// clean up MS crap..
20464 // tecnically this should be a list of valid class'es..
20467 if (a.name == 'class') {
20468 if (a.value.match(/^Mso/)) {
20469 node.className = '';
20472 if (a.value.match(/body/)) {
20473 node.className = '';
20484 this.cleanUpChildren(node);
20490 * Clean up MS wordisms...
20492 cleanWord : function(node)
20497 this.cleanWord(this.doc.body);
20500 if (node.nodeName == "#text") {
20501 // clean up silly Windows -- stuff?
20504 if (node.nodeName == "#comment") {
20505 node.parentNode.removeChild(node);
20506 // clean up silly Windows -- stuff?
20510 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20511 node.parentNode.removeChild(node);
20515 // remove - but keep children..
20516 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20517 while (node.childNodes.length) {
20518 var cn = node.childNodes[0];
20519 node.removeChild(cn);
20520 node.parentNode.insertBefore(cn, node);
20522 node.parentNode.removeChild(node);
20523 this.iterateChildren(node, this.cleanWord);
20527 if (node.className.length) {
20529 var cn = node.className.split(/\W+/);
20531 Roo.each(cn, function(cls) {
20532 if (cls.match(/Mso[a-zA-Z]+/)) {
20537 node.className = cna.length ? cna.join(' ') : '';
20539 node.removeAttribute("class");
20543 if (node.hasAttribute("lang")) {
20544 node.removeAttribute("lang");
20547 if (node.hasAttribute("style")) {
20549 var styles = node.getAttribute("style").split(";");
20551 Roo.each(styles, function(s) {
20552 if (!s.match(/:/)) {
20555 var kv = s.split(":");
20556 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20559 // what ever is left... we allow.
20562 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20563 if (!nstyle.length) {
20564 node.removeAttribute('style');
20567 this.iterateChildren(node, this.cleanWord);
20573 * iterateChildren of a Node, calling fn each time, using this as the scole..
20574 * @param {DomNode} node node to iterate children of.
20575 * @param {Function} fn method of this class to call on each item.
20577 iterateChildren : function(node, fn)
20579 if (!node.childNodes.length) {
20582 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20583 fn.call(this, node.childNodes[i])
20589 * cleanTableWidths.
20591 * Quite often pasting from word etc.. results in tables with column and widths.
20592 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20595 cleanTableWidths : function(node)
20600 this.cleanTableWidths(this.doc.body);
20605 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20608 Roo.log(node.tagName);
20609 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20610 this.iterateChildren(node, this.cleanTableWidths);
20613 if (node.hasAttribute('width')) {
20614 node.removeAttribute('width');
20618 if (node.hasAttribute("style")) {
20621 var styles = node.getAttribute("style").split(";");
20623 Roo.each(styles, function(s) {
20624 if (!s.match(/:/)) {
20627 var kv = s.split(":");
20628 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20631 // what ever is left... we allow.
20634 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20635 if (!nstyle.length) {
20636 node.removeAttribute('style');
20640 this.iterateChildren(node, this.cleanTableWidths);
20648 domToHTML : function(currentElement, depth, nopadtext) {
20650 depth = depth || 0;
20651 nopadtext = nopadtext || false;
20653 if (!currentElement) {
20654 return this.domToHTML(this.doc.body);
20657 //Roo.log(currentElement);
20659 var allText = false;
20660 var nodeName = currentElement.nodeName;
20661 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20663 if (nodeName == '#text') {
20665 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20670 if (nodeName != 'BODY') {
20673 // Prints the node tagName, such as <A>, <IMG>, etc
20676 for(i = 0; i < currentElement.attributes.length;i++) {
20678 var aname = currentElement.attributes.item(i).name;
20679 if (!currentElement.attributes.item(i).value.length) {
20682 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20685 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20694 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20697 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20702 // Traverse the tree
20704 var currentElementChild = currentElement.childNodes.item(i);
20705 var allText = true;
20706 var innerHTML = '';
20708 while (currentElementChild) {
20709 // Formatting code (indent the tree so it looks nice on the screen)
20710 var nopad = nopadtext;
20711 if (lastnode == 'SPAN') {
20715 if (currentElementChild.nodeName == '#text') {
20716 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20717 toadd = nopadtext ? toadd : toadd.trim();
20718 if (!nopad && toadd.length > 80) {
20719 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20721 innerHTML += toadd;
20724 currentElementChild = currentElement.childNodes.item(i);
20730 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20732 // Recursively traverse the tree structure of the child node
20733 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20734 lastnode = currentElementChild.nodeName;
20736 currentElementChild=currentElement.childNodes.item(i);
20742 // The remaining code is mostly for formatting the tree
20743 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20748 ret+= "</"+tagName+">";
20754 applyBlacklists : function()
20756 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20757 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20761 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20762 if (b.indexOf(tag) > -1) {
20765 this.white.push(tag);
20769 Roo.each(w, function(tag) {
20770 if (b.indexOf(tag) > -1) {
20773 if (this.white.indexOf(tag) > -1) {
20776 this.white.push(tag);
20781 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20782 if (w.indexOf(tag) > -1) {
20785 this.black.push(tag);
20789 Roo.each(b, function(tag) {
20790 if (w.indexOf(tag) > -1) {
20793 if (this.black.indexOf(tag) > -1) {
20796 this.black.push(tag);
20801 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20802 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20806 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20807 if (b.indexOf(tag) > -1) {
20810 this.cwhite.push(tag);
20814 Roo.each(w, function(tag) {
20815 if (b.indexOf(tag) > -1) {
20818 if (this.cwhite.indexOf(tag) > -1) {
20821 this.cwhite.push(tag);
20826 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20827 if (w.indexOf(tag) > -1) {
20830 this.cblack.push(tag);
20834 Roo.each(b, function(tag) {
20835 if (w.indexOf(tag) > -1) {
20838 if (this.cblack.indexOf(tag) > -1) {
20841 this.cblack.push(tag);
20846 setStylesheets : function(stylesheets)
20848 if(typeof(stylesheets) == 'string'){
20849 Roo.get(this.iframe.contentDocument.head).createChild({
20851 rel : 'stylesheet',
20860 Roo.each(stylesheets, function(s) {
20865 Roo.get(_this.iframe.contentDocument.head).createChild({
20867 rel : 'stylesheet',
20876 removeStylesheets : function()
20880 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20885 // hide stuff that is not compatible
20899 * @event specialkey
20903 * @cfg {String} fieldClass @hide
20906 * @cfg {String} focusClass @hide
20909 * @cfg {String} autoCreate @hide
20912 * @cfg {String} inputType @hide
20915 * @cfg {String} invalidClass @hide
20918 * @cfg {String} invalidText @hide
20921 * @cfg {String} msgFx @hide
20924 * @cfg {String} validateOnBlur @hide
20928 Roo.HtmlEditorCore.white = [
20929 'area', 'br', 'img', 'input', 'hr', 'wbr',
20931 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
20932 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
20933 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
20934 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
20935 'table', 'ul', 'xmp',
20937 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
20940 'dir', 'menu', 'ol', 'ul', 'dl',
20946 Roo.HtmlEditorCore.black = [
20947 // 'embed', 'object', // enable - backend responsiblity to clean thiese
20949 'base', 'basefont', 'bgsound', 'blink', 'body',
20950 'frame', 'frameset', 'head', 'html', 'ilayer',
20951 'iframe', 'layer', 'link', 'meta', 'object',
20952 'script', 'style' ,'title', 'xml' // clean later..
20954 Roo.HtmlEditorCore.clean = [
20955 'script', 'style', 'title', 'xml'
20957 Roo.HtmlEditorCore.remove = [
20962 Roo.HtmlEditorCore.ablack = [
20966 Roo.HtmlEditorCore.aclean = [
20967 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
20971 Roo.HtmlEditorCore.pwhite= [
20972 'http', 'https', 'mailto'
20975 // white listed style attributes.
20976 Roo.HtmlEditorCore.cwhite= [
20977 // 'text-align', /// default is to allow most things..
20983 // black listed style attributes.
20984 Roo.HtmlEditorCore.cblack= [
20985 // 'font-size' -- this can be set by the project
20989 Roo.HtmlEditorCore.swapCodes =[
21008 * @class Roo.bootstrap.HtmlEditor
21009 * @extends Roo.bootstrap.TextArea
21010 * Bootstrap HtmlEditor class
21013 * Create a new HtmlEditor
21014 * @param {Object} config The config object
21017 Roo.bootstrap.HtmlEditor = function(config){
21018 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21019 if (!this.toolbars) {
21020 this.toolbars = [];
21022 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21025 * @event initialize
21026 * Fires when the editor is fully initialized (including the iframe)
21027 * @param {HtmlEditor} this
21032 * Fires when the editor is first receives the focus. Any insertion must wait
21033 * until after this event.
21034 * @param {HtmlEditor} this
21038 * @event beforesync
21039 * Fires before the textarea is updated with content from the editor iframe. Return false
21040 * to cancel the sync.
21041 * @param {HtmlEditor} this
21042 * @param {String} html
21046 * @event beforepush
21047 * Fires before the iframe editor is updated with content from the textarea. Return false
21048 * to cancel the push.
21049 * @param {HtmlEditor} this
21050 * @param {String} html
21055 * Fires when the textarea is updated with content from the editor iframe.
21056 * @param {HtmlEditor} this
21057 * @param {String} html
21062 * Fires when the iframe editor is updated with content from the textarea.
21063 * @param {HtmlEditor} this
21064 * @param {String} html
21068 * @event editmodechange
21069 * Fires when the editor switches edit modes
21070 * @param {HtmlEditor} this
21071 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21073 editmodechange: true,
21075 * @event editorevent
21076 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21077 * @param {HtmlEditor} this
21081 * @event firstfocus
21082 * Fires when on first focus - needed by toolbars..
21083 * @param {HtmlEditor} this
21088 * Auto save the htmlEditor value as a file into Events
21089 * @param {HtmlEditor} this
21093 * @event savedpreview
21094 * preview the saved version of htmlEditor
21095 * @param {HtmlEditor} this
21102 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
21106 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21111 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21116 * @cfg {Number} height (in pixels)
21120 * @cfg {Number} width (in pixels)
21125 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21128 stylesheets: false,
21133 // private properties
21134 validationEvent : false,
21136 initialized : false,
21139 onFocus : Roo.emptyFn,
21141 hideMode:'offsets',
21144 tbContainer : false,
21146 toolbarContainer :function() {
21147 return this.wrap.select('.x-html-editor-tb',true).first();
21151 * Protected method that will not generally be called directly. It
21152 * is called when the editor creates its toolbar. Override this method if you need to
21153 * add custom toolbar buttons.
21154 * @param {HtmlEditor} editor
21156 createToolbar : function(){
21158 Roo.log("create toolbars");
21160 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21161 this.toolbars[0].render(this.toolbarContainer());
21165 // if (!editor.toolbars || !editor.toolbars.length) {
21166 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21169 // for (var i =0 ; i < editor.toolbars.length;i++) {
21170 // editor.toolbars[i] = Roo.factory(
21171 // typeof(editor.toolbars[i]) == 'string' ?
21172 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
21173 // Roo.bootstrap.HtmlEditor);
21174 // editor.toolbars[i].init(editor);
21180 onRender : function(ct, position)
21182 // Roo.log("Call onRender: " + this.xtype);
21184 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21186 this.wrap = this.inputEl().wrap({
21187 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21190 this.editorcore.onRender(ct, position);
21192 if (this.resizable) {
21193 this.resizeEl = new Roo.Resizable(this.wrap, {
21197 minHeight : this.height,
21198 height: this.height,
21199 handles : this.resizable,
21202 resize : function(r, w, h) {
21203 _t.onResize(w,h); // -something
21209 this.createToolbar(this);
21212 if(!this.width && this.resizable){
21213 this.setSize(this.wrap.getSize());
21215 if (this.resizeEl) {
21216 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21217 // should trigger onReize..
21223 onResize : function(w, h)
21225 Roo.log('resize: ' +w + ',' + h );
21226 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21230 if(this.inputEl() ){
21231 if(typeof w == 'number'){
21232 var aw = w - this.wrap.getFrameWidth('lr');
21233 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21236 if(typeof h == 'number'){
21237 var tbh = -11; // fixme it needs to tool bar size!
21238 for (var i =0; i < this.toolbars.length;i++) {
21239 // fixme - ask toolbars for heights?
21240 tbh += this.toolbars[i].el.getHeight();
21241 //if (this.toolbars[i].footer) {
21242 // tbh += this.toolbars[i].footer.el.getHeight();
21250 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21251 ah -= 5; // knock a few pixes off for look..
21252 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21256 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21257 this.editorcore.onResize(ew,eh);
21262 * Toggles the editor between standard and source edit mode.
21263 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21265 toggleSourceEdit : function(sourceEditMode)
21267 this.editorcore.toggleSourceEdit(sourceEditMode);
21269 if(this.editorcore.sourceEditMode){
21270 Roo.log('editor - showing textarea');
21273 // Roo.log(this.syncValue());
21275 this.inputEl().removeClass(['hide', 'x-hidden']);
21276 this.inputEl().dom.removeAttribute('tabIndex');
21277 this.inputEl().focus();
21279 Roo.log('editor - hiding textarea');
21281 // Roo.log(this.pushValue());
21284 this.inputEl().addClass(['hide', 'x-hidden']);
21285 this.inputEl().dom.setAttribute('tabIndex', -1);
21286 //this.deferFocus();
21289 if(this.resizable){
21290 this.setSize(this.wrap.getSize());
21293 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21296 // private (for BoxComponent)
21297 adjustSize : Roo.BoxComponent.prototype.adjustSize,
21299 // private (for BoxComponent)
21300 getResizeEl : function(){
21304 // private (for BoxComponent)
21305 getPositionEl : function(){
21310 initEvents : function(){
21311 this.originalValue = this.getValue();
21315 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21318 // markInvalid : Roo.emptyFn,
21320 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21323 // clearInvalid : Roo.emptyFn,
21325 setValue : function(v){
21326 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21327 this.editorcore.pushValue();
21332 deferFocus : function(){
21333 this.focus.defer(10, this);
21337 focus : function(){
21338 this.editorcore.focus();
21344 onDestroy : function(){
21350 for (var i =0; i < this.toolbars.length;i++) {
21351 // fixme - ask toolbars for heights?
21352 this.toolbars[i].onDestroy();
21355 this.wrap.dom.innerHTML = '';
21356 this.wrap.remove();
21361 onFirstFocus : function(){
21362 //Roo.log("onFirstFocus");
21363 this.editorcore.onFirstFocus();
21364 for (var i =0; i < this.toolbars.length;i++) {
21365 this.toolbars[i].onFirstFocus();
21371 syncValue : function()
21373 this.editorcore.syncValue();
21376 pushValue : function()
21378 this.editorcore.pushValue();
21382 // hide stuff that is not compatible
21396 * @event specialkey
21400 * @cfg {String} fieldClass @hide
21403 * @cfg {String} focusClass @hide
21406 * @cfg {String} autoCreate @hide
21409 * @cfg {String} inputType @hide
21412 * @cfg {String} invalidClass @hide
21415 * @cfg {String} invalidText @hide
21418 * @cfg {String} msgFx @hide
21421 * @cfg {String} validateOnBlur @hide
21430 Roo.namespace('Roo.bootstrap.htmleditor');
21432 * @class Roo.bootstrap.HtmlEditorToolbar1
21437 new Roo.bootstrap.HtmlEditor({
21440 new Roo.bootstrap.HtmlEditorToolbar1({
21441 disable : { fonts: 1 , format: 1, ..., ... , ...],
21447 * @cfg {Object} disable List of elements to disable..
21448 * @cfg {Array} btns List of additional buttons.
21452 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21455 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21458 Roo.apply(this, config);
21460 // default disabled, based on 'good practice'..
21461 this.disable = this.disable || {};
21462 Roo.applyIf(this.disable, {
21465 specialElements : true
21467 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21469 this.editor = config.editor;
21470 this.editorcore = config.editor.editorcore;
21472 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21474 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21475 // dont call parent... till later.
21477 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
21482 editorcore : false,
21487 "h1","h2","h3","h4","h5","h6",
21489 "abbr", "acronym", "address", "cite", "samp", "var",
21493 onRender : function(ct, position)
21495 // Roo.log("Call onRender: " + this.xtype);
21497 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21499 this.el.dom.style.marginBottom = '0';
21501 var editorcore = this.editorcore;
21502 var editor= this.editor;
21505 var btn = function(id,cmd , toggle, handler){
21507 var event = toggle ? 'toggle' : 'click';
21512 xns: Roo.bootstrap,
21515 enableToggle:toggle !== false,
21517 pressed : toggle ? false : null,
21520 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21521 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21530 xns: Roo.bootstrap,
21531 glyphicon : 'font',
21535 xns: Roo.bootstrap,
21539 Roo.each(this.formats, function(f) {
21540 style.menu.items.push({
21542 xns: Roo.bootstrap,
21543 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21548 editorcore.insertTag(this.tagname);
21555 children.push(style);
21558 btn('bold',false,true);
21559 btn('italic',false,true);
21560 btn('align-left', 'justifyleft',true);
21561 btn('align-center', 'justifycenter',true);
21562 btn('align-right' , 'justifyright',true);
21563 btn('link', false, false, function(btn) {
21564 //Roo.log("create link?");
21565 var url = prompt(this.createLinkText, this.defaultLinkValue);
21566 if(url && url != 'http:/'+'/'){
21567 this.editorcore.relayCmd('createlink', url);
21570 btn('list','insertunorderedlist',true);
21571 btn('pencil', false,true, function(btn){
21574 this.toggleSourceEdit(btn.pressed);
21580 xns: Roo.bootstrap,
21585 xns: Roo.bootstrap,
21590 cog.menu.items.push({
21592 xns: Roo.bootstrap,
21593 html : Clean styles,
21598 editorcore.insertTag(this.tagname);
21607 this.xtype = 'NavSimplebar';
21609 for(var i=0;i< children.length;i++) {
21611 this.buttons.add(this.addxtypeChild(children[i]));
21615 editor.on('editorevent', this.updateToolbar, this);
21617 onBtnClick : function(id)
21619 this.editorcore.relayCmd(id);
21620 this.editorcore.focus();
21624 * Protected method that will not generally be called directly. It triggers
21625 * a toolbar update by reading the markup state of the current selection in the editor.
21627 updateToolbar: function(){
21629 if(!this.editorcore.activated){
21630 this.editor.onFirstFocus(); // is this neeed?
21634 var btns = this.buttons;
21635 var doc = this.editorcore.doc;
21636 btns.get('bold').setActive(doc.queryCommandState('bold'));
21637 btns.get('italic').setActive(doc.queryCommandState('italic'));
21638 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21640 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21641 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21642 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21644 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21645 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21648 var ans = this.editorcore.getAllAncestors();
21649 if (this.formatCombo) {
21652 var store = this.formatCombo.store;
21653 this.formatCombo.setValue("");
21654 for (var i =0; i < ans.length;i++) {
21655 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21657 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21665 // hides menus... - so this cant be on a menu...
21666 Roo.bootstrap.MenuMgr.hideAll();
21668 Roo.bootstrap.MenuMgr.hideAll();
21669 //this.editorsyncValue();
21671 onFirstFocus: function() {
21672 this.buttons.each(function(item){
21676 toggleSourceEdit : function(sourceEditMode){
21679 if(sourceEditMode){
21680 Roo.log("disabling buttons");
21681 this.buttons.each( function(item){
21682 if(item.cmd != 'pencil'){
21688 Roo.log("enabling buttons");
21689 if(this.editorcore.initialized){
21690 this.buttons.each( function(item){
21696 Roo.log("calling toggole on editor");
21697 // tell the editor that it's been pressed..
21698 this.editor.toggleSourceEdit(sourceEditMode);
21708 * @class Roo.bootstrap.Table.AbstractSelectionModel
21709 * @extends Roo.util.Observable
21710 * Abstract base class for grid SelectionModels. It provides the interface that should be
21711 * implemented by descendant classes. This class should not be directly instantiated.
21714 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21715 this.locked = false;
21716 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21720 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21721 /** @ignore Called by the grid automatically. Do not call directly. */
21722 init : function(grid){
21728 * Locks the selections.
21731 this.locked = true;
21735 * Unlocks the selections.
21737 unlock : function(){
21738 this.locked = false;
21742 * Returns true if the selections are locked.
21743 * @return {Boolean}
21745 isLocked : function(){
21746 return this.locked;
21750 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21751 * @class Roo.bootstrap.Table.RowSelectionModel
21752 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21753 * It supports multiple selections and keyboard selection/navigation.
21755 * @param {Object} config
21758 Roo.bootstrap.Table.RowSelectionModel = function(config){
21759 Roo.apply(this, config);
21760 this.selections = new Roo.util.MixedCollection(false, function(o){
21765 this.lastActive = false;
21769 * @event selectionchange
21770 * Fires when the selection changes
21771 * @param {SelectionModel} this
21773 "selectionchange" : true,
21775 * @event afterselectionchange
21776 * Fires after the selection changes (eg. by key press or clicking)
21777 * @param {SelectionModel} this
21779 "afterselectionchange" : true,
21781 * @event beforerowselect
21782 * Fires when a row is selected being selected, return false to cancel.
21783 * @param {SelectionModel} this
21784 * @param {Number} rowIndex The selected index
21785 * @param {Boolean} keepExisting False if other selections will be cleared
21787 "beforerowselect" : true,
21790 * Fires when a row is selected.
21791 * @param {SelectionModel} this
21792 * @param {Number} rowIndex The selected index
21793 * @param {Roo.data.Record} r The record
21795 "rowselect" : true,
21797 * @event rowdeselect
21798 * Fires when a row is deselected.
21799 * @param {SelectionModel} this
21800 * @param {Number} rowIndex The selected index
21802 "rowdeselect" : true
21804 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21805 this.locked = false;
21808 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21810 * @cfg {Boolean} singleSelect
21811 * True to allow selection of only one row at a time (defaults to false)
21813 singleSelect : false,
21816 initEvents : function(){
21818 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21819 this.grid.on("mousedown", this.handleMouseDown, this);
21820 }else{ // allow click to work like normal
21821 this.grid.on("rowclick", this.handleDragableRowClick, this);
21824 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21825 "up" : function(e){
21827 this.selectPrevious(e.shiftKey);
21828 }else if(this.last !== false && this.lastActive !== false){
21829 var last = this.last;
21830 this.selectRange(this.last, this.lastActive-1);
21831 this.grid.getView().focusRow(this.lastActive);
21832 if(last !== false){
21836 this.selectFirstRow();
21838 this.fireEvent("afterselectionchange", this);
21840 "down" : function(e){
21842 this.selectNext(e.shiftKey);
21843 }else if(this.last !== false && this.lastActive !== false){
21844 var last = this.last;
21845 this.selectRange(this.last, this.lastActive+1);
21846 this.grid.getView().focusRow(this.lastActive);
21847 if(last !== false){
21851 this.selectFirstRow();
21853 this.fireEvent("afterselectionchange", this);
21858 var view = this.grid.view;
21859 view.on("refresh", this.onRefresh, this);
21860 view.on("rowupdated", this.onRowUpdated, this);
21861 view.on("rowremoved", this.onRemove, this);
21865 onRefresh : function(){
21866 var ds = this.grid.dataSource, i, v = this.grid.view;
21867 var s = this.selections;
21868 s.each(function(r){
21869 if((i = ds.indexOfId(r.id)) != -1){
21878 onRemove : function(v, index, r){
21879 this.selections.remove(r);
21883 onRowUpdated : function(v, index, r){
21884 if(this.isSelected(r)){
21885 v.onRowSelect(index);
21891 * @param {Array} records The records to select
21892 * @param {Boolean} keepExisting (optional) True to keep existing selections
21894 selectRecords : function(records, keepExisting){
21896 this.clearSelections();
21898 var ds = this.grid.dataSource;
21899 for(var i = 0, len = records.length; i < len; i++){
21900 this.selectRow(ds.indexOf(records[i]), true);
21905 * Gets the number of selected rows.
21908 getCount : function(){
21909 return this.selections.length;
21913 * Selects the first row in the grid.
21915 selectFirstRow : function(){
21920 * Select the last row.
21921 * @param {Boolean} keepExisting (optional) True to keep existing selections
21923 selectLastRow : function(keepExisting){
21924 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21928 * Selects the row immediately following the last selected row.
21929 * @param {Boolean} keepExisting (optional) True to keep existing selections
21931 selectNext : function(keepExisting){
21932 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21933 this.selectRow(this.last+1, keepExisting);
21934 this.grid.getView().focusRow(this.last);
21939 * Selects the row that precedes the last selected row.
21940 * @param {Boolean} keepExisting (optional) True to keep existing selections
21942 selectPrevious : function(keepExisting){
21944 this.selectRow(this.last-1, keepExisting);
21945 this.grid.getView().focusRow(this.last);
21950 * Returns the selected records
21951 * @return {Array} Array of selected records
21953 getSelections : function(){
21954 return [].concat(this.selections.items);
21958 * Returns the first selected record.
21961 getSelected : function(){
21962 return this.selections.itemAt(0);
21967 * Clears all selections.
21969 clearSelections : function(fast){
21974 var ds = this.grid.dataSource;
21975 var s = this.selections;
21976 s.each(function(r){
21977 this.deselectRow(ds.indexOfId(r.id));
21981 this.selections.clear();
21988 * Selects all rows.
21990 selectAll : function(){
21994 this.selections.clear();
21995 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21996 this.selectRow(i, true);
22001 * Returns True if there is a selection.
22002 * @return {Boolean}
22004 hasSelection : function(){
22005 return this.selections.length > 0;
22009 * Returns True if the specified row is selected.
22010 * @param {Number/Record} record The record or index of the record to check
22011 * @return {Boolean}
22013 isSelected : function(index){
22014 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
22015 return (r && this.selections.key(r.id) ? true : false);
22019 * Returns True if the specified record id is selected.
22020 * @param {String} id The id of record to check
22021 * @return {Boolean}
22023 isIdSelected : function(id){
22024 return (this.selections.key(id) ? true : false);
22028 handleMouseDown : function(e, t){
22029 var view = this.grid.getView(), rowIndex;
22030 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
22033 if(e.shiftKey && this.last !== false){
22034 var last = this.last;
22035 this.selectRange(last, rowIndex, e.ctrlKey);
22036 this.last = last; // reset the last
22037 view.focusRow(rowIndex);
22039 var isSelected = this.isSelected(rowIndex);
22040 if(e.button !== 0 && isSelected){
22041 view.focusRow(rowIndex);
22042 }else if(e.ctrlKey && isSelected){
22043 this.deselectRow(rowIndex);
22044 }else if(!isSelected){
22045 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22046 view.focusRow(rowIndex);
22049 this.fireEvent("afterselectionchange", this);
22052 handleDragableRowClick : function(grid, rowIndex, e)
22054 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22055 this.selectRow(rowIndex, false);
22056 grid.view.focusRow(rowIndex);
22057 this.fireEvent("afterselectionchange", this);
22062 * Selects multiple rows.
22063 * @param {Array} rows Array of the indexes of the row to select
22064 * @param {Boolean} keepExisting (optional) True to keep existing selections
22066 selectRows : function(rows, keepExisting){
22068 this.clearSelections();
22070 for(var i = 0, len = rows.length; i < len; i++){
22071 this.selectRow(rows[i], true);
22076 * Selects a range of rows. All rows in between startRow and endRow are also selected.
22077 * @param {Number} startRow The index of the first row in the range
22078 * @param {Number} endRow The index of the last row in the range
22079 * @param {Boolean} keepExisting (optional) True to retain existing selections
22081 selectRange : function(startRow, endRow, keepExisting){
22086 this.clearSelections();
22088 if(startRow <= endRow){
22089 for(var i = startRow; i <= endRow; i++){
22090 this.selectRow(i, true);
22093 for(var i = startRow; i >= endRow; i--){
22094 this.selectRow(i, true);
22100 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22101 * @param {Number} startRow The index of the first row in the range
22102 * @param {Number} endRow The index of the last row in the range
22104 deselectRange : function(startRow, endRow, preventViewNotify){
22108 for(var i = startRow; i <= endRow; i++){
22109 this.deselectRow(i, preventViewNotify);
22115 * @param {Number} row The index of the row to select
22116 * @param {Boolean} keepExisting (optional) True to keep existing selections
22118 selectRow : function(index, keepExisting, preventViewNotify){
22119 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22122 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22123 if(!keepExisting || this.singleSelect){
22124 this.clearSelections();
22126 var r = this.grid.dataSource.getAt(index);
22127 this.selections.add(r);
22128 this.last = this.lastActive = index;
22129 if(!preventViewNotify){
22130 this.grid.getView().onRowSelect(index);
22132 this.fireEvent("rowselect", this, index, r);
22133 this.fireEvent("selectionchange", this);
22139 * @param {Number} row The index of the row to deselect
22141 deselectRow : function(index, preventViewNotify){
22145 if(this.last == index){
22148 if(this.lastActive == index){
22149 this.lastActive = false;
22151 var r = this.grid.dataSource.getAt(index);
22152 this.selections.remove(r);
22153 if(!preventViewNotify){
22154 this.grid.getView().onRowDeselect(index);
22156 this.fireEvent("rowdeselect", this, index);
22157 this.fireEvent("selectionchange", this);
22161 restoreLast : function(){
22163 this.last = this._last;
22168 acceptsNav : function(row, col, cm){
22169 return !cm.isHidden(col) && cm.isCellEditable(col, row);
22173 onEditorKey : function(field, e){
22174 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22179 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22181 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22183 }else if(k == e.ENTER && !e.ctrlKey){
22187 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22189 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22191 }else if(k == e.ESC){
22195 g.startEditing(newCell[0], newCell[1]);
22200 * Ext JS Library 1.1.1
22201 * Copyright(c) 2006-2007, Ext JS, LLC.
22203 * Originally Released Under LGPL - original licence link has changed is not relivant.
22206 * <script type="text/javascript">
22210 * @class Roo.bootstrap.PagingToolbar
22211 * @extends Roo.bootstrap.NavSimplebar
22212 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22214 * Create a new PagingToolbar
22215 * @param {Object} config The config object
22216 * @param {Roo.data.Store} store
22218 Roo.bootstrap.PagingToolbar = function(config)
22220 // old args format still supported... - xtype is prefered..
22221 // created from xtype...
22223 this.ds = config.dataSource;
22225 if (config.store && !this.ds) {
22226 this.store= Roo.factory(config.store, Roo.data);
22227 this.ds = this.store;
22228 this.ds.xmodule = this.xmodule || false;
22231 this.toolbarItems = [];
22232 if (config.items) {
22233 this.toolbarItems = config.items;
22236 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22241 this.bind(this.ds);
22244 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22248 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22250 * @cfg {Roo.data.Store} dataSource
22251 * The underlying data store providing the paged data
22254 * @cfg {String/HTMLElement/Element} container
22255 * container The id or element that will contain the toolbar
22258 * @cfg {Boolean} displayInfo
22259 * True to display the displayMsg (defaults to false)
22262 * @cfg {Number} pageSize
22263 * The number of records to display per page (defaults to 20)
22267 * @cfg {String} displayMsg
22268 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22270 displayMsg : 'Displaying {0} - {1} of {2}',
22272 * @cfg {String} emptyMsg
22273 * The message to display when no records are found (defaults to "No data to display")
22275 emptyMsg : 'No data to display',
22277 * Customizable piece of the default paging text (defaults to "Page")
22280 beforePageText : "Page",
22282 * Customizable piece of the default paging text (defaults to "of %0")
22285 afterPageText : "of {0}",
22287 * Customizable piece of the default paging text (defaults to "First Page")
22290 firstText : "First Page",
22292 * Customizable piece of the default paging text (defaults to "Previous Page")
22295 prevText : "Previous Page",
22297 * Customizable piece of the default paging text (defaults to "Next Page")
22300 nextText : "Next Page",
22302 * Customizable piece of the default paging text (defaults to "Last Page")
22305 lastText : "Last Page",
22307 * Customizable piece of the default paging text (defaults to "Refresh")
22310 refreshText : "Refresh",
22314 onRender : function(ct, position)
22316 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22317 this.navgroup.parentId = this.id;
22318 this.navgroup.onRender(this.el, null);
22319 // add the buttons to the navgroup
22321 if(this.displayInfo){
22322 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22323 this.displayEl = this.el.select('.x-paging-info', true).first();
22324 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22325 // this.displayEl = navel.el.select('span',true).first();
22331 Roo.each(_this.buttons, function(e){ // this might need to use render????
22332 Roo.factory(e).onRender(_this.el, null);
22336 Roo.each(_this.toolbarItems, function(e) {
22337 _this.navgroup.addItem(e);
22341 this.first = this.navgroup.addItem({
22342 tooltip: this.firstText,
22344 icon : 'fa fa-backward',
22346 preventDefault: true,
22347 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22350 this.prev = this.navgroup.addItem({
22351 tooltip: this.prevText,
22353 icon : 'fa fa-step-backward',
22355 preventDefault: true,
22356 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
22358 //this.addSeparator();
22361 var field = this.navgroup.addItem( {
22363 cls : 'x-paging-position',
22365 html : this.beforePageText +
22366 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22367 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
22370 this.field = field.el.select('input', true).first();
22371 this.field.on("keydown", this.onPagingKeydown, this);
22372 this.field.on("focus", function(){this.dom.select();});
22375 this.afterTextEl = field.el.select('.x-paging-after',true).first();
22376 //this.field.setHeight(18);
22377 //this.addSeparator();
22378 this.next = this.navgroup.addItem({
22379 tooltip: this.nextText,
22381 html : ' <i class="fa fa-step-forward">',
22383 preventDefault: true,
22384 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
22386 this.last = this.navgroup.addItem({
22387 tooltip: this.lastText,
22388 icon : 'fa fa-forward',
22391 preventDefault: true,
22392 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
22394 //this.addSeparator();
22395 this.loading = this.navgroup.addItem({
22396 tooltip: this.refreshText,
22397 icon: 'fa fa-refresh',
22398 preventDefault: true,
22399 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22405 updateInfo : function(){
22406 if(this.displayEl){
22407 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22408 var msg = count == 0 ?
22412 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
22414 this.displayEl.update(msg);
22419 onLoad : function(ds, r, o){
22420 this.cursor = o.params ? o.params.start : 0;
22421 var d = this.getPageData(),
22425 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22426 this.field.dom.value = ap;
22427 this.first.setDisabled(ap == 1);
22428 this.prev.setDisabled(ap == 1);
22429 this.next.setDisabled(ap == ps);
22430 this.last.setDisabled(ap == ps);
22431 this.loading.enable();
22436 getPageData : function(){
22437 var total = this.ds.getTotalCount();
22440 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22441 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22446 onLoadError : function(){
22447 this.loading.enable();
22451 onPagingKeydown : function(e){
22452 var k = e.getKey();
22453 var d = this.getPageData();
22455 var v = this.field.dom.value, pageNum;
22456 if(!v || isNaN(pageNum = parseInt(v, 10))){
22457 this.field.dom.value = d.activePage;
22460 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22461 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22464 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))
22466 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22467 this.field.dom.value = pageNum;
22468 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22471 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22473 var v = this.field.dom.value, pageNum;
22474 var increment = (e.shiftKey) ? 10 : 1;
22475 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22478 if(!v || isNaN(pageNum = parseInt(v, 10))) {
22479 this.field.dom.value = d.activePage;
22482 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22484 this.field.dom.value = parseInt(v, 10) + increment;
22485 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22486 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22493 beforeLoad : function(){
22495 this.loading.disable();
22500 onClick : function(which){
22509 ds.load({params:{start: 0, limit: this.pageSize}});
22512 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22515 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22518 var total = ds.getTotalCount();
22519 var extra = total % this.pageSize;
22520 var lastStart = extra ? (total - extra) : total-this.pageSize;
22521 ds.load({params:{start: lastStart, limit: this.pageSize}});
22524 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22530 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22531 * @param {Roo.data.Store} store The data store to unbind
22533 unbind : function(ds){
22534 ds.un("beforeload", this.beforeLoad, this);
22535 ds.un("load", this.onLoad, this);
22536 ds.un("loadexception", this.onLoadError, this);
22537 ds.un("remove", this.updateInfo, this);
22538 ds.un("add", this.updateInfo, this);
22539 this.ds = undefined;
22543 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22544 * @param {Roo.data.Store} store The data store to bind
22546 bind : function(ds){
22547 ds.on("beforeload", this.beforeLoad, this);
22548 ds.on("load", this.onLoad, this);
22549 ds.on("loadexception", this.onLoadError, this);
22550 ds.on("remove", this.updateInfo, this);
22551 ds.on("add", this.updateInfo, this);
22562 * @class Roo.bootstrap.MessageBar
22563 * @extends Roo.bootstrap.Component
22564 * Bootstrap MessageBar class
22565 * @cfg {String} html contents of the MessageBar
22566 * @cfg {String} weight (info | success | warning | danger) default info
22567 * @cfg {String} beforeClass insert the bar before the given class
22568 * @cfg {Boolean} closable (true | false) default false
22569 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22572 * Create a new Element
22573 * @param {Object} config The config object
22576 Roo.bootstrap.MessageBar = function(config){
22577 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22580 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22586 beforeClass: 'bootstrap-sticky-wrap',
22588 getAutoCreate : function(){
22592 cls: 'alert alert-dismissable alert-' + this.weight,
22597 html: this.html || ''
22603 cfg.cls += ' alert-messages-fixed';
22617 onRender : function(ct, position)
22619 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22622 var cfg = Roo.apply({}, this.getAutoCreate());
22626 cfg.cls += ' ' + this.cls;
22629 cfg.style = this.style;
22631 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22633 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22636 this.el.select('>button.close').on('click', this.hide, this);
22642 if (!this.rendered) {
22648 this.fireEvent('show', this);
22654 if (!this.rendered) {
22660 this.fireEvent('hide', this);
22663 update : function()
22665 // var e = this.el.dom.firstChild;
22667 // if(this.closable){
22668 // e = e.nextSibling;
22671 // e.data = this.html || '';
22673 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22689 * @class Roo.bootstrap.Graph
22690 * @extends Roo.bootstrap.Component
22691 * Bootstrap Graph class
22695 @cfg {String} graphtype bar | vbar | pie
22696 @cfg {number} g_x coodinator | centre x (pie)
22697 @cfg {number} g_y coodinator | centre y (pie)
22698 @cfg {number} g_r radius (pie)
22699 @cfg {number} g_height height of the chart (respected by all elements in the set)
22700 @cfg {number} g_width width of the chart (respected by all elements in the set)
22701 @cfg {Object} title The title of the chart
22704 -opts (object) options for the chart
22706 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22707 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22709 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.
22710 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22712 o stretch (boolean)
22714 -opts (object) options for the pie
22717 o startAngle (number)
22718 o endAngle (number)
22722 * Create a new Input
22723 * @param {Object} config The config object
22726 Roo.bootstrap.Graph = function(config){
22727 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22733 * The img click event for the img.
22734 * @param {Roo.EventObject} e
22740 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22751 //g_colors: this.colors,
22758 getAutoCreate : function(){
22769 onRender : function(ct,position){
22770 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22771 this.raphael = Raphael(this.el.dom);
22773 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22774 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22775 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22776 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22778 r.text(160, 10, "Single Series Chart").attr(txtattr);
22779 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22780 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22781 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22783 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22784 r.barchart(330, 10, 300, 220, data1);
22785 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22786 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22789 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22790 // r.barchart(30, 30, 560, 250, xdata, {
22791 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22792 // axis : "0 0 1 1",
22793 // axisxlabels : xdata
22794 // //yvalues : cols,
22797 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22799 // this.load(null,xdata,{
22800 // axis : "0 0 1 1",
22801 // axisxlabels : xdata
22806 load : function(graphtype,xdata,opts){
22807 this.raphael.clear();
22809 graphtype = this.graphtype;
22814 var r = this.raphael,
22815 fin = function () {
22816 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22818 fout = function () {
22819 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22821 pfin = function() {
22822 this.sector.stop();
22823 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22826 this.label[0].stop();
22827 this.label[0].attr({ r: 7.5 });
22828 this.label[1].attr({ "font-weight": 800 });
22831 pfout = function() {
22832 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22835 this.label[0].animate({ r: 5 }, 500, "bounce");
22836 this.label[1].attr({ "font-weight": 400 });
22842 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22845 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22848 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
22849 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22851 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22858 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22863 setTitle: function(o)
22868 initEvents: function() {
22871 this.el.on('click', this.onClick, this);
22875 onClick : function(e)
22877 Roo.log('img onclick');
22878 this.fireEvent('click', this, e);
22890 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22893 * @class Roo.bootstrap.dash.NumberBox
22894 * @extends Roo.bootstrap.Component
22895 * Bootstrap NumberBox class
22896 * @cfg {String} headline Box headline
22897 * @cfg {String} content Box content
22898 * @cfg {String} icon Box icon
22899 * @cfg {String} footer Footer text
22900 * @cfg {String} fhref Footer href
22903 * Create a new NumberBox
22904 * @param {Object} config The config object
22908 Roo.bootstrap.dash.NumberBox = function(config){
22909 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22913 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
22922 getAutoCreate : function(){
22926 cls : 'small-box ',
22934 cls : 'roo-headline',
22935 html : this.headline
22939 cls : 'roo-content',
22940 html : this.content
22954 cls : 'ion ' + this.icon
22963 cls : 'small-box-footer',
22964 href : this.fhref || '#',
22968 cfg.cn.push(footer);
22975 onRender : function(ct,position){
22976 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22983 setHeadline: function (value)
22985 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22988 setFooter: function (value, href)
22990 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22993 this.el.select('a.small-box-footer',true).first().attr('href', href);
22998 setContent: function (value)
23000 this.el.select('.roo-content',true).first().dom.innerHTML = value;
23003 initEvents: function()
23017 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23020 * @class Roo.bootstrap.dash.TabBox
23021 * @extends Roo.bootstrap.Component
23022 * Bootstrap TabBox class
23023 * @cfg {String} title Title of the TabBox
23024 * @cfg {String} icon Icon of the TabBox
23025 * @cfg {Boolean} showtabs (true|false) show the tabs default true
23026 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23029 * Create a new TabBox
23030 * @param {Object} config The config object
23034 Roo.bootstrap.dash.TabBox = function(config){
23035 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23040 * When a pane is added
23041 * @param {Roo.bootstrap.dash.TabPane} pane
23045 * @event activatepane
23046 * When a pane is activated
23047 * @param {Roo.bootstrap.dash.TabPane} pane
23049 "activatepane" : true
23057 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
23062 tabScrollable : false,
23064 getChildContainer : function()
23066 return this.el.select('.tab-content', true).first();
23069 getAutoCreate : function(){
23073 cls: 'pull-left header',
23081 cls: 'fa ' + this.icon
23087 cls: 'nav nav-tabs pull-right',
23093 if(this.tabScrollable){
23100 cls: 'nav nav-tabs pull-right',
23111 cls: 'nav-tabs-custom',
23116 cls: 'tab-content no-padding',
23124 initEvents : function()
23126 //Roo.log('add add pane handler');
23127 this.on('addpane', this.onAddPane, this);
23130 * Updates the box title
23131 * @param {String} html to set the title to.
23133 setTitle : function(value)
23135 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23137 onAddPane : function(pane)
23139 this.panes.push(pane);
23140 //Roo.log('addpane');
23142 // tabs are rendere left to right..
23143 if(!this.showtabs){
23147 var ctr = this.el.select('.nav-tabs', true).first();
23150 var existing = ctr.select('.nav-tab',true);
23151 var qty = existing.getCount();;
23154 var tab = ctr.createChild({
23156 cls : 'nav-tab' + (qty ? '' : ' active'),
23164 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23167 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23169 pane.el.addClass('active');
23174 onTabClick : function(ev,un,ob,pane)
23176 //Roo.log('tab - prev default');
23177 ev.preventDefault();
23180 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23181 pane.tab.addClass('active');
23182 //Roo.log(pane.title);
23183 this.getChildContainer().select('.tab-pane',true).removeClass('active');
23184 // technically we should have a deactivate event.. but maybe add later.
23185 // and it should not de-activate the selected tab...
23186 this.fireEvent('activatepane', pane);
23187 pane.el.addClass('active');
23188 pane.fireEvent('activate');
23193 getActivePane : function()
23196 Roo.each(this.panes, function(p) {
23197 if(p.el.hasClass('active')){
23218 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23220 * @class Roo.bootstrap.TabPane
23221 * @extends Roo.bootstrap.Component
23222 * Bootstrap TabPane class
23223 * @cfg {Boolean} active (false | true) Default false
23224 * @cfg {String} title title of panel
23228 * Create a new TabPane
23229 * @param {Object} config The config object
23232 Roo.bootstrap.dash.TabPane = function(config){
23233 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23239 * When a pane is activated
23240 * @param {Roo.bootstrap.dash.TabPane} pane
23247 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
23252 // the tabBox that this is attached to.
23255 getAutoCreate : function()
23263 cfg.cls += ' active';
23268 initEvents : function()
23270 //Roo.log('trigger add pane handler');
23271 this.parent().fireEvent('addpane', this)
23275 * Updates the tab title
23276 * @param {String} html to set the title to.
23278 setTitle: function(str)
23284 this.tab.select('a', true).first().dom.innerHTML = str;
23301 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23304 * @class Roo.bootstrap.menu.Menu
23305 * @extends Roo.bootstrap.Component
23306 * Bootstrap Menu class - container for Menu
23307 * @cfg {String} html Text of the menu
23308 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23309 * @cfg {String} icon Font awesome icon
23310 * @cfg {String} pos Menu align to (top | bottom) default bottom
23314 * Create a new Menu
23315 * @param {Object} config The config object
23319 Roo.bootstrap.menu.Menu = function(config){
23320 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23324 * @event beforeshow
23325 * Fires before this menu is displayed
23326 * @param {Roo.bootstrap.menu.Menu} this
23330 * @event beforehide
23331 * Fires before this menu is hidden
23332 * @param {Roo.bootstrap.menu.Menu} this
23337 * Fires after this menu is displayed
23338 * @param {Roo.bootstrap.menu.Menu} this
23343 * Fires after this menu is hidden
23344 * @param {Roo.bootstrap.menu.Menu} this
23349 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23350 * @param {Roo.bootstrap.menu.Menu} this
23351 * @param {Roo.EventObject} e
23358 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
23362 weight : 'default',
23367 getChildContainer : function() {
23368 if(this.isSubMenu){
23372 return this.el.select('ul.dropdown-menu', true).first();
23375 getAutoCreate : function()
23380 cls : 'roo-menu-text',
23388 cls : 'fa ' + this.icon
23399 cls : 'dropdown-button btn btn-' + this.weight,
23404 cls : 'dropdown-toggle btn btn-' + this.weight,
23414 cls : 'dropdown-menu'
23420 if(this.pos == 'top'){
23421 cfg.cls += ' dropup';
23424 if(this.isSubMenu){
23427 cls : 'dropdown-menu'
23434 onRender : function(ct, position)
23436 this.isSubMenu = ct.hasClass('dropdown-submenu');
23438 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23441 initEvents : function()
23443 if(this.isSubMenu){
23447 this.hidden = true;
23449 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23450 this.triggerEl.on('click', this.onTriggerPress, this);
23452 this.buttonEl = this.el.select('button.dropdown-button', true).first();
23453 this.buttonEl.on('click', this.onClick, this);
23459 if(this.isSubMenu){
23463 return this.el.select('ul.dropdown-menu', true).first();
23466 onClick : function(e)
23468 this.fireEvent("click", this, e);
23471 onTriggerPress : function(e)
23473 if (this.isVisible()) {
23480 isVisible : function(){
23481 return !this.hidden;
23486 this.fireEvent("beforeshow", this);
23488 this.hidden = false;
23489 this.el.addClass('open');
23491 Roo.get(document).on("mouseup", this.onMouseUp, this);
23493 this.fireEvent("show", this);
23500 this.fireEvent("beforehide", this);
23502 this.hidden = true;
23503 this.el.removeClass('open');
23505 Roo.get(document).un("mouseup", this.onMouseUp);
23507 this.fireEvent("hide", this);
23510 onMouseUp : function()
23524 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23527 * @class Roo.bootstrap.menu.Item
23528 * @extends Roo.bootstrap.Component
23529 * Bootstrap MenuItem class
23530 * @cfg {Boolean} submenu (true | false) default false
23531 * @cfg {String} html text of the item
23532 * @cfg {String} href the link
23533 * @cfg {Boolean} disable (true | false) default false
23534 * @cfg {Boolean} preventDefault (true | false) default true
23535 * @cfg {String} icon Font awesome icon
23536 * @cfg {String} pos Submenu align to (left | right) default right
23540 * Create a new Item
23541 * @param {Object} config The config object
23545 Roo.bootstrap.menu.Item = function(config){
23546 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23550 * Fires when the mouse is hovering over this menu
23551 * @param {Roo.bootstrap.menu.Item} this
23552 * @param {Roo.EventObject} e
23557 * Fires when the mouse exits this menu
23558 * @param {Roo.bootstrap.menu.Item} this
23559 * @param {Roo.EventObject} e
23565 * The raw click event for the entire grid.
23566 * @param {Roo.EventObject} e
23572 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23577 preventDefault: true,
23582 getAutoCreate : function()
23587 cls : 'roo-menu-item-text',
23595 cls : 'fa ' + this.icon
23604 href : this.href || '#',
23611 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23615 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23617 if(this.pos == 'left'){
23618 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23625 initEvents : function()
23627 this.el.on('mouseover', this.onMouseOver, this);
23628 this.el.on('mouseout', this.onMouseOut, this);
23630 this.el.select('a', true).first().on('click', this.onClick, this);
23634 onClick : function(e)
23636 if(this.preventDefault){
23637 e.preventDefault();
23640 this.fireEvent("click", this, e);
23643 onMouseOver : function(e)
23645 if(this.submenu && this.pos == 'left'){
23646 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23649 this.fireEvent("mouseover", this, e);
23652 onMouseOut : function(e)
23654 this.fireEvent("mouseout", this, e);
23666 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23669 * @class Roo.bootstrap.menu.Separator
23670 * @extends Roo.bootstrap.Component
23671 * Bootstrap Separator class
23674 * Create a new Separator
23675 * @param {Object} config The config object
23679 Roo.bootstrap.menu.Separator = function(config){
23680 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23683 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23685 getAutoCreate : function(){
23706 * @class Roo.bootstrap.Tooltip
23707 * Bootstrap Tooltip class
23708 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23709 * to determine which dom element triggers the tooltip.
23711 * It needs to add support for additional attributes like tooltip-position
23714 * Create a new Toolti
23715 * @param {Object} config The config object
23718 Roo.bootstrap.Tooltip = function(config){
23719 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23722 Roo.apply(Roo.bootstrap.Tooltip, {
23724 * @function init initialize tooltip monitoring.
23728 currentTip : false,
23729 currentRegion : false,
23735 Roo.get(document).on('mouseover', this.enter ,this);
23736 Roo.get(document).on('mouseout', this.leave, this);
23739 this.currentTip = new Roo.bootstrap.Tooltip();
23742 enter : function(ev)
23744 var dom = ev.getTarget();
23746 //Roo.log(['enter',dom]);
23747 var el = Roo.fly(dom);
23748 if (this.currentEl) {
23750 //Roo.log(this.currentEl);
23751 //Roo.log(this.currentEl.contains(dom));
23752 if (this.currentEl == el) {
23755 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23761 if (this.currentTip.el) {
23762 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23767 // you can not look for children, as if el is the body.. then everythign is the child..
23768 if (!el.attr('tooltip')) { //
23769 if (!el.select("[tooltip]").elements.length) {
23772 // is the mouse over this child...?
23773 bindEl = el.select("[tooltip]").first();
23774 var xy = ev.getXY();
23775 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23776 //Roo.log("not in region.");
23779 //Roo.log("child element over..");
23782 this.currentEl = bindEl;
23783 this.currentTip.bind(bindEl);
23784 this.currentRegion = Roo.lib.Region.getRegion(dom);
23785 this.currentTip.enter();
23788 leave : function(ev)
23790 var dom = ev.getTarget();
23791 //Roo.log(['leave',dom]);
23792 if (!this.currentEl) {
23797 if (dom != this.currentEl.dom) {
23800 var xy = ev.getXY();
23801 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23804 // only activate leave if mouse cursor is outside... bounding box..
23809 if (this.currentTip) {
23810 this.currentTip.leave();
23812 //Roo.log('clear currentEl');
23813 this.currentEl = false;
23818 'left' : ['r-l', [-2,0], 'right'],
23819 'right' : ['l-r', [2,0], 'left'],
23820 'bottom' : ['t-b', [0,2], 'top'],
23821 'top' : [ 'b-t', [0,-2], 'bottom']
23827 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
23832 delay : null, // can be { show : 300 , hide: 500}
23836 hoverState : null, //???
23838 placement : 'bottom',
23840 getAutoCreate : function(){
23847 cls : 'tooltip-arrow'
23850 cls : 'tooltip-inner'
23857 bind : function(el)
23863 enter : function () {
23865 if (this.timeout != null) {
23866 clearTimeout(this.timeout);
23869 this.hoverState = 'in';
23870 //Roo.log("enter - show");
23871 if (!this.delay || !this.delay.show) {
23876 this.timeout = setTimeout(function () {
23877 if (_t.hoverState == 'in') {
23880 }, this.delay.show);
23884 clearTimeout(this.timeout);
23886 this.hoverState = 'out';
23887 if (!this.delay || !this.delay.hide) {
23893 this.timeout = setTimeout(function () {
23894 //Roo.log("leave - timeout");
23896 if (_t.hoverState == 'out') {
23898 Roo.bootstrap.Tooltip.currentEl = false;
23906 this.render(document.body);
23909 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23911 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23913 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23915 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23917 var placement = typeof this.placement == 'function' ?
23918 this.placement.call(this, this.el, on_el) :
23921 var autoToken = /\s?auto?\s?/i;
23922 var autoPlace = autoToken.test(placement);
23924 placement = placement.replace(autoToken, '') || 'top';
23928 //this.el.setXY([0,0]);
23930 //this.el.dom.style.display='block';
23932 //this.el.appendTo(on_el);
23934 var p = this.getPosition();
23935 var box = this.el.getBox();
23941 var align = Roo.bootstrap.Tooltip.alignment[placement];
23943 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23945 if(placement == 'top' || placement == 'bottom'){
23947 placement = 'right';
23950 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23951 placement = 'left';
23955 align = Roo.bootstrap.Tooltip.alignment[placement];
23957 this.el.alignTo(this.bindEl, align[0],align[1]);
23958 //var arrow = this.el.select('.arrow',true).first();
23959 //arrow.set(align[2],
23961 this.el.addClass(placement);
23963 this.el.addClass('in fade');
23965 this.hoverState = null;
23967 if (this.el.hasClass('fade')) {
23978 //this.el.setXY([0,0]);
23979 this.el.removeClass('in');
23995 * @class Roo.bootstrap.LocationPicker
23996 * @extends Roo.bootstrap.Component
23997 * Bootstrap LocationPicker class
23998 * @cfg {Number} latitude Position when init default 0
23999 * @cfg {Number} longitude Position when init default 0
24000 * @cfg {Number} zoom default 15
24001 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24002 * @cfg {Boolean} mapTypeControl default false
24003 * @cfg {Boolean} disableDoubleClickZoom default false
24004 * @cfg {Boolean} scrollwheel default true
24005 * @cfg {Boolean} streetViewControl default false
24006 * @cfg {Number} radius default 0
24007 * @cfg {String} locationName
24008 * @cfg {Boolean} draggable default true
24009 * @cfg {Boolean} enableAutocomplete default false
24010 * @cfg {Boolean} enableReverseGeocode default true
24011 * @cfg {String} markerTitle
24014 * Create a new LocationPicker
24015 * @param {Object} config The config object
24019 Roo.bootstrap.LocationPicker = function(config){
24021 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24026 * Fires when the picker initialized.
24027 * @param {Roo.bootstrap.LocationPicker} this
24028 * @param {Google Location} location
24032 * @event positionchanged
24033 * Fires when the picker position changed.
24034 * @param {Roo.bootstrap.LocationPicker} this
24035 * @param {Google Location} location
24037 positionchanged : true,
24040 * Fires when the map resize.
24041 * @param {Roo.bootstrap.LocationPicker} this
24046 * Fires when the map show.
24047 * @param {Roo.bootstrap.LocationPicker} this
24052 * Fires when the map hide.
24053 * @param {Roo.bootstrap.LocationPicker} this
24058 * Fires when click the map.
24059 * @param {Roo.bootstrap.LocationPicker} this
24060 * @param {Map event} e
24064 * @event mapRightClick
24065 * Fires when right click the map.
24066 * @param {Roo.bootstrap.LocationPicker} this
24067 * @param {Map event} e
24069 mapRightClick : true,
24071 * @event markerClick
24072 * Fires when click the marker.
24073 * @param {Roo.bootstrap.LocationPicker} this
24074 * @param {Map event} e
24076 markerClick : true,
24078 * @event markerRightClick
24079 * Fires when right click the marker.
24080 * @param {Roo.bootstrap.LocationPicker} this
24081 * @param {Map event} e
24083 markerRightClick : true,
24085 * @event OverlayViewDraw
24086 * Fires when OverlayView Draw
24087 * @param {Roo.bootstrap.LocationPicker} this
24089 OverlayViewDraw : true,
24091 * @event OverlayViewOnAdd
24092 * Fires when OverlayView Draw
24093 * @param {Roo.bootstrap.LocationPicker} this
24095 OverlayViewOnAdd : true,
24097 * @event OverlayViewOnRemove
24098 * Fires when OverlayView Draw
24099 * @param {Roo.bootstrap.LocationPicker} this
24101 OverlayViewOnRemove : true,
24103 * @event OverlayViewShow
24104 * Fires when OverlayView Draw
24105 * @param {Roo.bootstrap.LocationPicker} this
24106 * @param {Pixel} cpx
24108 OverlayViewShow : true,
24110 * @event OverlayViewHide
24111 * Fires when OverlayView Draw
24112 * @param {Roo.bootstrap.LocationPicker} this
24114 OverlayViewHide : true,
24116 * @event loadexception
24117 * Fires when load google lib failed.
24118 * @param {Roo.bootstrap.LocationPicker} this
24120 loadexception : true
24125 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
24127 gMapContext: false,
24133 mapTypeControl: false,
24134 disableDoubleClickZoom: false,
24136 streetViewControl: false,
24140 enableAutocomplete: false,
24141 enableReverseGeocode: true,
24144 getAutoCreate: function()
24149 cls: 'roo-location-picker'
24155 initEvents: function(ct, position)
24157 if(!this.el.getWidth() || this.isApplied()){
24161 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24166 initial: function()
24168 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24169 this.fireEvent('loadexception', this);
24173 if(!this.mapTypeId){
24174 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24177 this.gMapContext = this.GMapContext();
24179 this.initOverlayView();
24181 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24185 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24186 _this.setPosition(_this.gMapContext.marker.position);
24189 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24190 _this.fireEvent('mapClick', this, event);
24194 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24195 _this.fireEvent('mapRightClick', this, event);
24199 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24200 _this.fireEvent('markerClick', this, event);
24204 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24205 _this.fireEvent('markerRightClick', this, event);
24209 this.setPosition(this.gMapContext.location);
24211 this.fireEvent('initial', this, this.gMapContext.location);
24214 initOverlayView: function()
24218 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24222 _this.fireEvent('OverlayViewDraw', _this);
24227 _this.fireEvent('OverlayViewOnAdd', _this);
24230 onRemove: function()
24232 _this.fireEvent('OverlayViewOnRemove', _this);
24235 show: function(cpx)
24237 _this.fireEvent('OverlayViewShow', _this, cpx);
24242 _this.fireEvent('OverlayViewHide', _this);
24248 fromLatLngToContainerPixel: function(event)
24250 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24253 isApplied: function()
24255 return this.getGmapContext() == false ? false : true;
24258 getGmapContext: function()
24260 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24263 GMapContext: function()
24265 var position = new google.maps.LatLng(this.latitude, this.longitude);
24267 var _map = new google.maps.Map(this.el.dom, {
24270 mapTypeId: this.mapTypeId,
24271 mapTypeControl: this.mapTypeControl,
24272 disableDoubleClickZoom: this.disableDoubleClickZoom,
24273 scrollwheel: this.scrollwheel,
24274 streetViewControl: this.streetViewControl,
24275 locationName: this.locationName,
24276 draggable: this.draggable,
24277 enableAutocomplete: this.enableAutocomplete,
24278 enableReverseGeocode: this.enableReverseGeocode
24281 var _marker = new google.maps.Marker({
24282 position: position,
24284 title: this.markerTitle,
24285 draggable: this.draggable
24292 location: position,
24293 radius: this.radius,
24294 locationName: this.locationName,
24295 addressComponents: {
24296 formatted_address: null,
24297 addressLine1: null,
24298 addressLine2: null,
24300 streetNumber: null,
24304 stateOrProvince: null
24307 domContainer: this.el.dom,
24308 geodecoder: new google.maps.Geocoder()
24312 drawCircle: function(center, radius, options)
24314 if (this.gMapContext.circle != null) {
24315 this.gMapContext.circle.setMap(null);
24319 options = Roo.apply({}, options, {
24320 strokeColor: "#0000FF",
24321 strokeOpacity: .35,
24323 fillColor: "#0000FF",
24327 options.map = this.gMapContext.map;
24328 options.radius = radius;
24329 options.center = center;
24330 this.gMapContext.circle = new google.maps.Circle(options);
24331 return this.gMapContext.circle;
24337 setPosition: function(location)
24339 this.gMapContext.location = location;
24340 this.gMapContext.marker.setPosition(location);
24341 this.gMapContext.map.panTo(location);
24342 this.drawCircle(location, this.gMapContext.radius, {});
24346 if (this.gMapContext.settings.enableReverseGeocode) {
24347 this.gMapContext.geodecoder.geocode({
24348 latLng: this.gMapContext.location
24349 }, function(results, status) {
24351 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24352 _this.gMapContext.locationName = results[0].formatted_address;
24353 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24355 _this.fireEvent('positionchanged', this, location);
24362 this.fireEvent('positionchanged', this, location);
24367 google.maps.event.trigger(this.gMapContext.map, "resize");
24369 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24371 this.fireEvent('resize', this);
24374 setPositionByLatLng: function(latitude, longitude)
24376 this.setPosition(new google.maps.LatLng(latitude, longitude));
24379 getCurrentPosition: function()
24382 latitude: this.gMapContext.location.lat(),
24383 longitude: this.gMapContext.location.lng()
24387 getAddressName: function()
24389 return this.gMapContext.locationName;
24392 getAddressComponents: function()
24394 return this.gMapContext.addressComponents;
24397 address_component_from_google_geocode: function(address_components)
24401 for (var i = 0; i < address_components.length; i++) {
24402 var component = address_components[i];
24403 if (component.types.indexOf("postal_code") >= 0) {
24404 result.postalCode = component.short_name;
24405 } else if (component.types.indexOf("street_number") >= 0) {
24406 result.streetNumber = component.short_name;
24407 } else if (component.types.indexOf("route") >= 0) {
24408 result.streetName = component.short_name;
24409 } else if (component.types.indexOf("neighborhood") >= 0) {
24410 result.city = component.short_name;
24411 } else if (component.types.indexOf("locality") >= 0) {
24412 result.city = component.short_name;
24413 } else if (component.types.indexOf("sublocality") >= 0) {
24414 result.district = component.short_name;
24415 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24416 result.stateOrProvince = component.short_name;
24417 } else if (component.types.indexOf("country") >= 0) {
24418 result.country = component.short_name;
24422 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24423 result.addressLine2 = "";
24427 setZoomLevel: function(zoom)
24429 this.gMapContext.map.setZoom(zoom);
24442 this.fireEvent('show', this);
24453 this.fireEvent('hide', this);
24458 Roo.apply(Roo.bootstrap.LocationPicker, {
24460 OverlayView : function(map, options)
24462 options = options || {};
24476 * @class Roo.bootstrap.Alert
24477 * @extends Roo.bootstrap.Component
24478 * Bootstrap Alert class
24479 * @cfg {String} title The title of alert
24480 * @cfg {String} html The content of alert
24481 * @cfg {String} weight ( success | info | warning | danger )
24482 * @cfg {String} faicon font-awesomeicon
24485 * Create a new alert
24486 * @param {Object} config The config object
24490 Roo.bootstrap.Alert = function(config){
24491 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24495 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
24502 getAutoCreate : function()
24511 cls : 'roo-alert-icon'
24516 cls : 'roo-alert-title',
24521 cls : 'roo-alert-text',
24528 cfg.cn[0].cls += ' fa ' + this.faicon;
24532 cfg.cls += ' alert-' + this.weight;
24538 initEvents: function()
24540 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24543 setTitle : function(str)
24545 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24548 setText : function(str)
24550 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24553 setWeight : function(weight)
24556 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24559 this.weight = weight;
24561 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24564 setIcon : function(icon)
24567 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24570 this.faicon = icon;
24572 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24593 * @class Roo.bootstrap.UploadCropbox
24594 * @extends Roo.bootstrap.Component
24595 * Bootstrap UploadCropbox class
24596 * @cfg {String} emptyText show when image has been loaded
24597 * @cfg {String} rotateNotify show when image too small to rotate
24598 * @cfg {Number} errorTimeout default 3000
24599 * @cfg {Number} minWidth default 300
24600 * @cfg {Number} minHeight default 300
24601 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24602 * @cfg {Boolean} isDocument (true|false) default false
24603 * @cfg {String} url action url
24604 * @cfg {String} paramName default 'imageUpload'
24605 * @cfg {String} method default POST
24606 * @cfg {Boolean} loadMask (true|false) default true
24607 * @cfg {Boolean} loadingText default 'Loading...'
24610 * Create a new UploadCropbox
24611 * @param {Object} config The config object
24614 Roo.bootstrap.UploadCropbox = function(config){
24615 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24619 * @event beforeselectfile
24620 * Fire before select file
24621 * @param {Roo.bootstrap.UploadCropbox} this
24623 "beforeselectfile" : true,
24626 * Fire after initEvent
24627 * @param {Roo.bootstrap.UploadCropbox} this
24632 * Fire after initEvent
24633 * @param {Roo.bootstrap.UploadCropbox} this
24634 * @param {String} data
24639 * Fire when preparing the file data
24640 * @param {Roo.bootstrap.UploadCropbox} this
24641 * @param {Object} file
24646 * Fire when get exception
24647 * @param {Roo.bootstrap.UploadCropbox} this
24648 * @param {XMLHttpRequest} xhr
24650 "exception" : true,
24652 * @event beforeloadcanvas
24653 * Fire before load the canvas
24654 * @param {Roo.bootstrap.UploadCropbox} this
24655 * @param {String} src
24657 "beforeloadcanvas" : true,
24660 * Fire when trash image
24661 * @param {Roo.bootstrap.UploadCropbox} this
24666 * Fire when download the image
24667 * @param {Roo.bootstrap.UploadCropbox} this
24671 * @event footerbuttonclick
24672 * Fire when footerbuttonclick
24673 * @param {Roo.bootstrap.UploadCropbox} this
24674 * @param {String} type
24676 "footerbuttonclick" : true,
24680 * @param {Roo.bootstrap.UploadCropbox} this
24685 * Fire when rotate the image
24686 * @param {Roo.bootstrap.UploadCropbox} this
24687 * @param {String} pos
24692 * Fire when inspect the file
24693 * @param {Roo.bootstrap.UploadCropbox} this
24694 * @param {Object} file
24699 * Fire when xhr upload the file
24700 * @param {Roo.bootstrap.UploadCropbox} this
24701 * @param {Object} data
24706 * Fire when arrange the file data
24707 * @param {Roo.bootstrap.UploadCropbox} this
24708 * @param {Object} formData
24713 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24716 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24718 emptyText : 'Click to upload image',
24719 rotateNotify : 'Image is too small to rotate',
24720 errorTimeout : 3000,
24734 cropType : 'image/jpeg',
24736 canvasLoaded : false,
24737 isDocument : false,
24739 paramName : 'imageUpload',
24741 loadingText : 'Loading...',
24744 getAutoCreate : function()
24748 cls : 'roo-upload-cropbox',
24752 cls : 'roo-upload-cropbox-selector',
24757 cls : 'roo-upload-cropbox-body',
24758 style : 'cursor:pointer',
24762 cls : 'roo-upload-cropbox-preview'
24766 cls : 'roo-upload-cropbox-thumb'
24770 cls : 'roo-upload-cropbox-empty-notify',
24771 html : this.emptyText
24775 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24776 html : this.rotateNotify
24782 cls : 'roo-upload-cropbox-footer',
24785 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24795 onRender : function(ct, position)
24797 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24799 if (this.buttons.length) {
24801 Roo.each(this.buttons, function(bb) {
24803 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24805 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24811 this.maskEl = this.el;
24815 initEvents : function()
24817 this.urlAPI = (window.createObjectURL && window) ||
24818 (window.URL && URL.revokeObjectURL && URL) ||
24819 (window.webkitURL && webkitURL);
24821 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24822 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24824 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24825 this.selectorEl.hide();
24827 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24828 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24830 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24831 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24832 this.thumbEl.hide();
24834 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24835 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24837 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24838 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24839 this.errorEl.hide();
24841 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24842 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24843 this.footerEl.hide();
24845 this.setThumbBoxSize();
24851 this.fireEvent('initial', this);
24858 window.addEventListener("resize", function() { _this.resize(); } );
24860 this.bodyEl.on('click', this.beforeSelectFile, this);
24863 this.bodyEl.on('touchstart', this.onTouchStart, this);
24864 this.bodyEl.on('touchmove', this.onTouchMove, this);
24865 this.bodyEl.on('touchend', this.onTouchEnd, this);
24869 this.bodyEl.on('mousedown', this.onMouseDown, this);
24870 this.bodyEl.on('mousemove', this.onMouseMove, this);
24871 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24872 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24873 Roo.get(document).on('mouseup', this.onMouseUp, this);
24876 this.selectorEl.on('change', this.onFileSelected, this);
24882 this.baseScale = 1;
24884 this.baseRotate = 1;
24885 this.dragable = false;
24886 this.pinching = false;
24889 this.cropData = false;
24890 this.notifyEl.dom.innerHTML = this.emptyText;
24892 this.selectorEl.dom.value = '';
24896 resize : function()
24898 if(this.fireEvent('resize', this) != false){
24899 this.setThumbBoxPosition();
24900 this.setCanvasPosition();
24904 onFooterButtonClick : function(e, el, o, type)
24907 case 'rotate-left' :
24908 this.onRotateLeft(e);
24910 case 'rotate-right' :
24911 this.onRotateRight(e);
24914 this.beforeSelectFile(e);
24929 this.fireEvent('footerbuttonclick', this, type);
24932 beforeSelectFile : function(e)
24934 e.preventDefault();
24936 if(this.fireEvent('beforeselectfile', this) != false){
24937 this.selectorEl.dom.click();
24941 onFileSelected : function(e)
24943 e.preventDefault();
24945 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24949 var file = this.selectorEl.dom.files[0];
24951 if(this.fireEvent('inspect', this, file) != false){
24952 this.prepare(file);
24957 trash : function(e)
24959 this.fireEvent('trash', this);
24962 download : function(e)
24964 this.fireEvent('download', this);
24967 loadCanvas : function(src)
24969 if(this.fireEvent('beforeloadcanvas', this, src) != false){
24973 this.imageEl = document.createElement('img');
24977 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24979 this.imageEl.src = src;
24983 onLoadCanvas : function()
24985 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24986 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24988 this.bodyEl.un('click', this.beforeSelectFile, this);
24990 this.notifyEl.hide();
24991 this.thumbEl.show();
24992 this.footerEl.show();
24994 this.baseRotateLevel();
24996 if(this.isDocument){
24997 this.setThumbBoxSize();
25000 this.setThumbBoxPosition();
25002 this.baseScaleLevel();
25008 this.canvasLoaded = true;
25011 this.maskEl.unmask();
25016 setCanvasPosition : function()
25018 if(!this.canvasEl){
25022 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25023 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25025 this.previewEl.setLeft(pw);
25026 this.previewEl.setTop(ph);
25030 onMouseDown : function(e)
25034 this.dragable = true;
25035 this.pinching = false;
25037 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25038 this.dragable = false;
25042 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25043 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25047 onMouseMove : function(e)
25051 if(!this.canvasLoaded){
25055 if (!this.dragable){
25059 var minX = Math.ceil(this.thumbEl.getLeft(true));
25060 var minY = Math.ceil(this.thumbEl.getTop(true));
25062 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25063 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25065 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25066 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25068 x = x - this.mouseX;
25069 y = y - this.mouseY;
25071 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25072 var bgY = Math.ceil(y + this.previewEl.getTop(true));
25074 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25075 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25077 this.previewEl.setLeft(bgX);
25078 this.previewEl.setTop(bgY);
25080 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25081 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25084 onMouseUp : function(e)
25088 this.dragable = false;
25091 onMouseWheel : function(e)
25095 this.startScale = this.scale;
25097 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25099 if(!this.zoomable()){
25100 this.scale = this.startScale;
25109 zoomable : function()
25111 var minScale = this.thumbEl.getWidth() / this.minWidth;
25113 if(this.minWidth < this.minHeight){
25114 minScale = this.thumbEl.getHeight() / this.minHeight;
25117 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25118 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25122 (this.rotate == 0 || this.rotate == 180) &&
25124 width > this.imageEl.OriginWidth ||
25125 height > this.imageEl.OriginHeight ||
25126 (width < this.minWidth && height < this.minHeight)
25134 (this.rotate == 90 || this.rotate == 270) &&
25136 width > this.imageEl.OriginWidth ||
25137 height > this.imageEl.OriginHeight ||
25138 (width < this.minHeight && height < this.minWidth)
25145 !this.isDocument &&
25146 (this.rotate == 0 || this.rotate == 180) &&
25148 width < this.minWidth ||
25149 width > this.imageEl.OriginWidth ||
25150 height < this.minHeight ||
25151 height > this.imageEl.OriginHeight
25158 !this.isDocument &&
25159 (this.rotate == 90 || this.rotate == 270) &&
25161 width < this.minHeight ||
25162 width > this.imageEl.OriginWidth ||
25163 height < this.minWidth ||
25164 height > this.imageEl.OriginHeight
25174 onRotateLeft : function(e)
25176 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25178 var minScale = this.thumbEl.getWidth() / this.minWidth;
25180 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25181 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25183 this.startScale = this.scale;
25185 while (this.getScaleLevel() < minScale){
25187 this.scale = this.scale + 1;
25189 if(!this.zoomable()){
25194 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25195 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25200 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25207 this.scale = this.startScale;
25209 this.onRotateFail();
25214 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25216 if(this.isDocument){
25217 this.setThumbBoxSize();
25218 this.setThumbBoxPosition();
25219 this.setCanvasPosition();
25224 this.fireEvent('rotate', this, 'left');
25228 onRotateRight : function(e)
25230 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25232 var minScale = this.thumbEl.getWidth() / this.minWidth;
25234 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25235 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25237 this.startScale = this.scale;
25239 while (this.getScaleLevel() < minScale){
25241 this.scale = this.scale + 1;
25243 if(!this.zoomable()){
25248 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25249 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25254 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25261 this.scale = this.startScale;
25263 this.onRotateFail();
25268 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25270 if(this.isDocument){
25271 this.setThumbBoxSize();
25272 this.setThumbBoxPosition();
25273 this.setCanvasPosition();
25278 this.fireEvent('rotate', this, 'right');
25281 onRotateFail : function()
25283 this.errorEl.show(true);
25287 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25292 this.previewEl.dom.innerHTML = '';
25294 var canvasEl = document.createElement("canvas");
25296 var contextEl = canvasEl.getContext("2d");
25298 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25299 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25300 var center = this.imageEl.OriginWidth / 2;
25302 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25303 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25304 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25305 center = this.imageEl.OriginHeight / 2;
25308 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25310 contextEl.translate(center, center);
25311 contextEl.rotate(this.rotate * Math.PI / 180);
25313 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25315 this.canvasEl = document.createElement("canvas");
25317 this.contextEl = this.canvasEl.getContext("2d");
25319 switch (this.rotate) {
25322 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25323 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25325 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25330 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25331 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25333 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25334 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);
25338 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25343 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25344 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25346 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25347 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);
25351 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);
25356 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25357 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25359 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25360 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25364 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);
25371 this.previewEl.appendChild(this.canvasEl);
25373 this.setCanvasPosition();
25378 if(!this.canvasLoaded){
25382 var imageCanvas = document.createElement("canvas");
25384 var imageContext = imageCanvas.getContext("2d");
25386 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25387 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25389 var center = imageCanvas.width / 2;
25391 imageContext.translate(center, center);
25393 imageContext.rotate(this.rotate * Math.PI / 180);
25395 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25397 var canvas = document.createElement("canvas");
25399 var context = canvas.getContext("2d");
25401 canvas.width = this.minWidth;
25402 canvas.height = this.minHeight;
25404 switch (this.rotate) {
25407 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25408 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25410 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25411 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25413 var targetWidth = this.minWidth - 2 * x;
25414 var targetHeight = this.minHeight - 2 * y;
25418 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25419 scale = targetWidth / width;
25422 if(x > 0 && y == 0){
25423 scale = targetHeight / height;
25426 if(x > 0 && y > 0){
25427 scale = targetWidth / width;
25429 if(width < height){
25430 scale = targetHeight / height;
25434 context.scale(scale, scale);
25436 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25437 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25439 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25440 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25442 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25447 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25448 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25450 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25451 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25453 var targetWidth = this.minWidth - 2 * x;
25454 var targetHeight = this.minHeight - 2 * y;
25458 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25459 scale = targetWidth / width;
25462 if(x > 0 && y == 0){
25463 scale = targetHeight / height;
25466 if(x > 0 && y > 0){
25467 scale = targetWidth / width;
25469 if(width < height){
25470 scale = targetHeight / height;
25474 context.scale(scale, scale);
25476 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25477 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25479 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25480 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25482 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25484 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25489 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25490 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25492 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25493 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25495 var targetWidth = this.minWidth - 2 * x;
25496 var targetHeight = this.minHeight - 2 * y;
25500 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25501 scale = targetWidth / width;
25504 if(x > 0 && y == 0){
25505 scale = targetHeight / height;
25508 if(x > 0 && y > 0){
25509 scale = targetWidth / width;
25511 if(width < height){
25512 scale = targetHeight / height;
25516 context.scale(scale, scale);
25518 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25519 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25521 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25522 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25524 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25525 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25527 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25532 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25533 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25535 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25536 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25538 var targetWidth = this.minWidth - 2 * x;
25539 var targetHeight = this.minHeight - 2 * y;
25543 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25544 scale = targetWidth / width;
25547 if(x > 0 && y == 0){
25548 scale = targetHeight / height;
25551 if(x > 0 && y > 0){
25552 scale = targetWidth / width;
25554 if(width < height){
25555 scale = targetHeight / height;
25559 context.scale(scale, scale);
25561 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25562 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25564 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25565 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25567 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25569 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25576 this.cropData = canvas.toDataURL(this.cropType);
25578 if(this.fireEvent('crop', this, this.cropData) !== false){
25579 this.process(this.file, this.cropData);
25586 setThumbBoxSize : function()
25590 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25591 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25592 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25594 this.minWidth = width;
25595 this.minHeight = height;
25597 if(this.rotate == 90 || this.rotate == 270){
25598 this.minWidth = height;
25599 this.minHeight = width;
25604 width = Math.ceil(this.minWidth * height / this.minHeight);
25606 if(this.minWidth > this.minHeight){
25608 height = Math.ceil(this.minHeight * width / this.minWidth);
25611 this.thumbEl.setStyle({
25612 width : width + 'px',
25613 height : height + 'px'
25620 setThumbBoxPosition : function()
25622 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25623 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25625 this.thumbEl.setLeft(x);
25626 this.thumbEl.setTop(y);
25630 baseRotateLevel : function()
25632 this.baseRotate = 1;
25635 typeof(this.exif) != 'undefined' &&
25636 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25637 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25639 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25642 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25646 baseScaleLevel : function()
25650 if(this.isDocument){
25652 if(this.baseRotate == 6 || this.baseRotate == 8){
25654 height = this.thumbEl.getHeight();
25655 this.baseScale = height / this.imageEl.OriginWidth;
25657 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25658 width = this.thumbEl.getWidth();
25659 this.baseScale = width / this.imageEl.OriginHeight;
25665 height = this.thumbEl.getHeight();
25666 this.baseScale = height / this.imageEl.OriginHeight;
25668 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25669 width = this.thumbEl.getWidth();
25670 this.baseScale = width / this.imageEl.OriginWidth;
25676 if(this.baseRotate == 6 || this.baseRotate == 8){
25678 width = this.thumbEl.getHeight();
25679 this.baseScale = width / this.imageEl.OriginHeight;
25681 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25682 height = this.thumbEl.getWidth();
25683 this.baseScale = height / this.imageEl.OriginHeight;
25686 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25687 height = this.thumbEl.getWidth();
25688 this.baseScale = height / this.imageEl.OriginHeight;
25690 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25691 width = this.thumbEl.getHeight();
25692 this.baseScale = width / this.imageEl.OriginWidth;
25699 width = this.thumbEl.getWidth();
25700 this.baseScale = width / this.imageEl.OriginWidth;
25702 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25703 height = this.thumbEl.getHeight();
25704 this.baseScale = height / this.imageEl.OriginHeight;
25707 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25709 height = this.thumbEl.getHeight();
25710 this.baseScale = height / this.imageEl.OriginHeight;
25712 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25713 width = this.thumbEl.getWidth();
25714 this.baseScale = width / this.imageEl.OriginWidth;
25722 getScaleLevel : function()
25724 return this.baseScale * Math.pow(1.1, this.scale);
25727 onTouchStart : function(e)
25729 if(!this.canvasLoaded){
25730 this.beforeSelectFile(e);
25734 var touches = e.browserEvent.touches;
25740 if(touches.length == 1){
25741 this.onMouseDown(e);
25745 if(touches.length != 2){
25751 for(var i = 0, finger; finger = touches[i]; i++){
25752 coords.push(finger.pageX, finger.pageY);
25755 var x = Math.pow(coords[0] - coords[2], 2);
25756 var y = Math.pow(coords[1] - coords[3], 2);
25758 this.startDistance = Math.sqrt(x + y);
25760 this.startScale = this.scale;
25762 this.pinching = true;
25763 this.dragable = false;
25767 onTouchMove : function(e)
25769 if(!this.pinching && !this.dragable){
25773 var touches = e.browserEvent.touches;
25780 this.onMouseMove(e);
25786 for(var i = 0, finger; finger = touches[i]; i++){
25787 coords.push(finger.pageX, finger.pageY);
25790 var x = Math.pow(coords[0] - coords[2], 2);
25791 var y = Math.pow(coords[1] - coords[3], 2);
25793 this.endDistance = Math.sqrt(x + y);
25795 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25797 if(!this.zoomable()){
25798 this.scale = this.startScale;
25806 onTouchEnd : function(e)
25808 this.pinching = false;
25809 this.dragable = false;
25813 process : function(file, crop)
25816 this.maskEl.mask(this.loadingText);
25819 this.xhr = new XMLHttpRequest();
25821 file.xhr = this.xhr;
25823 this.xhr.open(this.method, this.url, true);
25826 "Accept": "application/json",
25827 "Cache-Control": "no-cache",
25828 "X-Requested-With": "XMLHttpRequest"
25831 for (var headerName in headers) {
25832 var headerValue = headers[headerName];
25834 this.xhr.setRequestHeader(headerName, headerValue);
25840 this.xhr.onload = function()
25842 _this.xhrOnLoad(_this.xhr);
25845 this.xhr.onerror = function()
25847 _this.xhrOnError(_this.xhr);
25850 var formData = new FormData();
25852 formData.append('returnHTML', 'NO');
25855 formData.append('crop', crop);
25858 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25859 formData.append(this.paramName, file, file.name);
25862 if(typeof(file.filename) != 'undefined'){
25863 formData.append('filename', file.filename);
25866 if(typeof(file.mimetype) != 'undefined'){
25867 formData.append('mimetype', file.mimetype);
25870 if(this.fireEvent('arrange', this, formData) != false){
25871 this.xhr.send(formData);
25875 xhrOnLoad : function(xhr)
25878 this.maskEl.unmask();
25881 if (xhr.readyState !== 4) {
25882 this.fireEvent('exception', this, xhr);
25886 var response = Roo.decode(xhr.responseText);
25888 if(!response.success){
25889 this.fireEvent('exception', this, xhr);
25893 var response = Roo.decode(xhr.responseText);
25895 this.fireEvent('upload', this, response);
25899 xhrOnError : function()
25902 this.maskEl.unmask();
25905 Roo.log('xhr on error');
25907 var response = Roo.decode(xhr.responseText);
25913 prepare : function(file)
25916 this.maskEl.mask(this.loadingText);
25922 if(typeof(file) === 'string'){
25923 this.loadCanvas(file);
25927 if(!file || !this.urlAPI){
25932 this.cropType = file.type;
25936 if(this.fireEvent('prepare', this, this.file) != false){
25938 var reader = new FileReader();
25940 reader.onload = function (e) {
25941 if (e.target.error) {
25942 Roo.log(e.target.error);
25946 var buffer = e.target.result,
25947 dataView = new DataView(buffer),
25949 maxOffset = dataView.byteLength - 4,
25953 if (dataView.getUint16(0) === 0xffd8) {
25954 while (offset < maxOffset) {
25955 markerBytes = dataView.getUint16(offset);
25957 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25958 markerLength = dataView.getUint16(offset + 2) + 2;
25959 if (offset + markerLength > dataView.byteLength) {
25960 Roo.log('Invalid meta data: Invalid segment size.');
25964 if(markerBytes == 0xffe1){
25965 _this.parseExifData(
25972 offset += markerLength;
25982 var url = _this.urlAPI.createObjectURL(_this.file);
25984 _this.loadCanvas(url);
25989 reader.readAsArrayBuffer(this.file);
25995 parseExifData : function(dataView, offset, length)
25997 var tiffOffset = offset + 10,
26001 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26002 // No Exif data, might be XMP data instead
26006 // Check for the ASCII code for "Exif" (0x45786966):
26007 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26008 // No Exif data, might be XMP data instead
26011 if (tiffOffset + 8 > dataView.byteLength) {
26012 Roo.log('Invalid Exif data: Invalid segment size.');
26015 // Check for the two null bytes:
26016 if (dataView.getUint16(offset + 8) !== 0x0000) {
26017 Roo.log('Invalid Exif data: Missing byte alignment offset.');
26020 // Check the byte alignment:
26021 switch (dataView.getUint16(tiffOffset)) {
26023 littleEndian = true;
26026 littleEndian = false;
26029 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26032 // Check for the TIFF tag marker (0x002A):
26033 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26034 Roo.log('Invalid Exif data: Missing TIFF marker.');
26037 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26038 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26040 this.parseExifTags(
26043 tiffOffset + dirOffset,
26048 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26053 if (dirOffset + 6 > dataView.byteLength) {
26054 Roo.log('Invalid Exif data: Invalid directory offset.');
26057 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26058 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26059 if (dirEndOffset + 4 > dataView.byteLength) {
26060 Roo.log('Invalid Exif data: Invalid directory size.');
26063 for (i = 0; i < tagsNumber; i += 1) {
26067 dirOffset + 2 + 12 * i, // tag offset
26071 // Return the offset to the next directory:
26072 return dataView.getUint32(dirEndOffset, littleEndian);
26075 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
26077 var tag = dataView.getUint16(offset, littleEndian);
26079 this.exif[tag] = this.getExifValue(
26083 dataView.getUint16(offset + 2, littleEndian), // tag type
26084 dataView.getUint32(offset + 4, littleEndian), // tag length
26089 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26091 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26100 Roo.log('Invalid Exif data: Invalid tag type.');
26104 tagSize = tagType.size * length;
26105 // Determine if the value is contained in the dataOffset bytes,
26106 // or if the value at the dataOffset is a pointer to the actual data:
26107 dataOffset = tagSize > 4 ?
26108 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26109 if (dataOffset + tagSize > dataView.byteLength) {
26110 Roo.log('Invalid Exif data: Invalid data offset.');
26113 if (length === 1) {
26114 return tagType.getValue(dataView, dataOffset, littleEndian);
26117 for (i = 0; i < length; i += 1) {
26118 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26121 if (tagType.ascii) {
26123 // Concatenate the chars:
26124 for (i = 0; i < values.length; i += 1) {
26126 // Ignore the terminating NULL byte(s):
26127 if (c === '\u0000') {
26139 Roo.apply(Roo.bootstrap.UploadCropbox, {
26141 'Orientation': 0x0112
26145 1: 0, //'top-left',
26147 3: 180, //'bottom-right',
26148 // 4: 'bottom-left',
26150 6: 90, //'right-top',
26151 // 7: 'right-bottom',
26152 8: 270 //'left-bottom'
26156 // byte, 8-bit unsigned int:
26158 getValue: function (dataView, dataOffset) {
26159 return dataView.getUint8(dataOffset);
26163 // ascii, 8-bit byte:
26165 getValue: function (dataView, dataOffset) {
26166 return String.fromCharCode(dataView.getUint8(dataOffset));
26171 // short, 16 bit int:
26173 getValue: function (dataView, dataOffset, littleEndian) {
26174 return dataView.getUint16(dataOffset, littleEndian);
26178 // long, 32 bit int:
26180 getValue: function (dataView, dataOffset, littleEndian) {
26181 return dataView.getUint32(dataOffset, littleEndian);
26185 // rational = two long values, first is numerator, second is denominator:
26187 getValue: function (dataView, dataOffset, littleEndian) {
26188 return dataView.getUint32(dataOffset, littleEndian) /
26189 dataView.getUint32(dataOffset + 4, littleEndian);
26193 // slong, 32 bit signed int:
26195 getValue: function (dataView, dataOffset, littleEndian) {
26196 return dataView.getInt32(dataOffset, littleEndian);
26200 // srational, two slongs, first is numerator, second is denominator:
26202 getValue: function (dataView, dataOffset, littleEndian) {
26203 return dataView.getInt32(dataOffset, littleEndian) /
26204 dataView.getInt32(dataOffset + 4, littleEndian);
26214 cls : 'btn-group roo-upload-cropbox-rotate-left',
26215 action : 'rotate-left',
26219 cls : 'btn btn-default',
26220 html : '<i class="fa fa-undo"></i>'
26226 cls : 'btn-group roo-upload-cropbox-picture',
26227 action : 'picture',
26231 cls : 'btn btn-default',
26232 html : '<i class="fa fa-picture-o"></i>'
26238 cls : 'btn-group roo-upload-cropbox-rotate-right',
26239 action : 'rotate-right',
26243 cls : 'btn btn-default',
26244 html : '<i class="fa fa-repeat"></i>'
26252 cls : 'btn-group roo-upload-cropbox-rotate-left',
26253 action : 'rotate-left',
26257 cls : 'btn btn-default',
26258 html : '<i class="fa fa-undo"></i>'
26264 cls : 'btn-group roo-upload-cropbox-download',
26265 action : 'download',
26269 cls : 'btn btn-default',
26270 html : '<i class="fa fa-download"></i>'
26276 cls : 'btn-group roo-upload-cropbox-crop',
26281 cls : 'btn btn-default',
26282 html : '<i class="fa fa-crop"></i>'
26288 cls : 'btn-group roo-upload-cropbox-trash',
26293 cls : 'btn btn-default',
26294 html : '<i class="fa fa-trash"></i>'
26300 cls : 'btn-group roo-upload-cropbox-rotate-right',
26301 action : 'rotate-right',
26305 cls : 'btn btn-default',
26306 html : '<i class="fa fa-repeat"></i>'
26314 cls : 'btn-group roo-upload-cropbox-rotate-left',
26315 action : 'rotate-left',
26319 cls : 'btn btn-default',
26320 html : '<i class="fa fa-undo"></i>'
26326 cls : 'btn-group roo-upload-cropbox-rotate-right',
26327 action : 'rotate-right',
26331 cls : 'btn btn-default',
26332 html : '<i class="fa fa-repeat"></i>'
26345 * @class Roo.bootstrap.DocumentManager
26346 * @extends Roo.bootstrap.Component
26347 * Bootstrap DocumentManager class
26348 * @cfg {String} paramName default 'imageUpload'
26349 * @cfg {String} method default POST
26350 * @cfg {String} url action url
26351 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26352 * @cfg {Boolean} multiple multiple upload default true
26353 * @cfg {Number} thumbSize default 300
26354 * @cfg {String} fieldLabel
26355 * @cfg {Number} labelWidth default 4
26356 * @cfg {String} labelAlign (left|top) default left
26357 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26360 * Create a new DocumentManager
26361 * @param {Object} config The config object
26364 Roo.bootstrap.DocumentManager = function(config){
26365 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26370 * Fire when initial the DocumentManager
26371 * @param {Roo.bootstrap.DocumentManager} this
26376 * inspect selected file
26377 * @param {Roo.bootstrap.DocumentManager} this
26378 * @param {File} file
26383 * Fire when xhr load exception
26384 * @param {Roo.bootstrap.DocumentManager} this
26385 * @param {XMLHttpRequest} xhr
26387 "exception" : true,
26390 * prepare the form data
26391 * @param {Roo.bootstrap.DocumentManager} this
26392 * @param {Object} formData
26397 * Fire when remove the file
26398 * @param {Roo.bootstrap.DocumentManager} this
26399 * @param {Object} file
26404 * Fire after refresh the file
26405 * @param {Roo.bootstrap.DocumentManager} this
26410 * Fire after click the image
26411 * @param {Roo.bootstrap.DocumentManager} this
26412 * @param {Object} file
26417 * Fire when upload a image and editable set to true
26418 * @param {Roo.bootstrap.DocumentManager} this
26419 * @param {Object} file
26423 * @event beforeselectfile
26424 * Fire before select file
26425 * @param {Roo.bootstrap.DocumentManager} this
26427 "beforeselectfile" : true,
26430 * Fire before process file
26431 * @param {Roo.bootstrap.DocumentManager} this
26432 * @param {Object} file
26439 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
26448 paramName : 'imageUpload',
26451 labelAlign : 'left',
26458 getAutoCreate : function()
26460 var managerWidget = {
26462 cls : 'roo-document-manager',
26466 cls : 'roo-document-manager-selector',
26471 cls : 'roo-document-manager-uploader',
26475 cls : 'roo-document-manager-upload-btn',
26476 html : '<i class="fa fa-plus"></i>'
26487 cls : 'column col-md-12',
26492 if(this.fieldLabel.length){
26497 cls : 'column col-md-12',
26498 html : this.fieldLabel
26502 cls : 'column col-md-12',
26507 if(this.labelAlign == 'left'){
26511 cls : 'column col-md-' + this.labelWidth,
26512 html : this.fieldLabel
26516 cls : 'column col-md-' + (12 - this.labelWidth),
26526 cls : 'row clearfix',
26534 initEvents : function()
26536 this.managerEl = this.el.select('.roo-document-manager', true).first();
26537 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26539 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26540 this.selectorEl.hide();
26543 this.selectorEl.attr('multiple', 'multiple');
26546 this.selectorEl.on('change', this.onFileSelected, this);
26548 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26549 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26551 this.uploader.on('click', this.onUploaderClick, this);
26553 this.renderProgressDialog();
26557 window.addEventListener("resize", function() { _this.refresh(); } );
26559 this.fireEvent('initial', this);
26562 renderProgressDialog : function()
26566 this.progressDialog = new Roo.bootstrap.Modal({
26567 cls : 'roo-document-manager-progress-dialog',
26568 allow_close : false,
26578 btnclick : function() {
26579 _this.uploadCancel();
26585 this.progressDialog.render(Roo.get(document.body));
26587 this.progress = new Roo.bootstrap.Progress({
26588 cls : 'roo-document-manager-progress',
26593 this.progress.render(this.progressDialog.getChildContainer());
26595 this.progressBar = new Roo.bootstrap.ProgressBar({
26596 cls : 'roo-document-manager-progress-bar',
26599 aria_valuemax : 12,
26603 this.progressBar.render(this.progress.getChildContainer());
26606 onUploaderClick : function(e)
26608 e.preventDefault();
26610 if(this.fireEvent('beforeselectfile', this) != false){
26611 this.selectorEl.dom.click();
26616 onFileSelected : function(e)
26618 e.preventDefault();
26620 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26624 Roo.each(this.selectorEl.dom.files, function(file){
26625 if(this.fireEvent('inspect', this, file) != false){
26626 this.files.push(file);
26636 this.selectorEl.dom.value = '';
26638 if(!this.files.length){
26642 if(this.boxes > 0 && this.files.length > this.boxes){
26643 this.files = this.files.slice(0, this.boxes);
26646 this.uploader.show();
26648 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26649 this.uploader.hide();
26658 Roo.each(this.files, function(file){
26660 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26661 var f = this.renderPreview(file);
26666 if(file.type.indexOf('image') != -1){
26667 this.delegates.push(
26669 _this.process(file);
26670 }).createDelegate(this)
26678 _this.process(file);
26679 }).createDelegate(this)
26684 this.files = files;
26686 this.delegates = this.delegates.concat(docs);
26688 if(!this.delegates.length){
26693 this.progressBar.aria_valuemax = this.delegates.length;
26700 arrange : function()
26702 if(!this.delegates.length){
26703 this.progressDialog.hide();
26708 var delegate = this.delegates.shift();
26710 this.progressDialog.show();
26712 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26714 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26719 refresh : function()
26721 this.uploader.show();
26723 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26724 this.uploader.hide();
26727 Roo.isTouch ? this.closable(false) : this.closable(true);
26729 this.fireEvent('refresh', this);
26732 onRemove : function(e, el, o)
26734 e.preventDefault();
26736 this.fireEvent('remove', this, o);
26740 remove : function(o)
26744 Roo.each(this.files, function(file){
26745 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26754 this.files = files;
26761 Roo.each(this.files, function(file){
26766 file.target.remove();
26775 onClick : function(e, el, o)
26777 e.preventDefault();
26779 this.fireEvent('click', this, o);
26783 closable : function(closable)
26785 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26787 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26799 xhrOnLoad : function(xhr)
26801 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26805 if (xhr.readyState !== 4) {
26807 this.fireEvent('exception', this, xhr);
26811 var response = Roo.decode(xhr.responseText);
26813 if(!response.success){
26815 this.fireEvent('exception', this, xhr);
26819 var file = this.renderPreview(response.data);
26821 this.files.push(file);
26827 xhrOnError : function(xhr)
26829 Roo.log('xhr on error');
26831 var response = Roo.decode(xhr.responseText);
26838 process : function(file)
26840 if(this.fireEvent('process', this, file) !== false){
26841 if(this.editable && file.type.indexOf('image') != -1){
26842 this.fireEvent('edit', this, file);
26846 this.uploadStart(file, false);
26853 uploadStart : function(file, crop)
26855 this.xhr = new XMLHttpRequest();
26857 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26862 file.xhr = this.xhr;
26864 this.managerEl.createChild({
26866 cls : 'roo-document-manager-loading',
26870 tooltip : file.name,
26871 cls : 'roo-document-manager-thumb',
26872 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26878 this.xhr.open(this.method, this.url, true);
26881 "Accept": "application/json",
26882 "Cache-Control": "no-cache",
26883 "X-Requested-With": "XMLHttpRequest"
26886 for (var headerName in headers) {
26887 var headerValue = headers[headerName];
26889 this.xhr.setRequestHeader(headerName, headerValue);
26895 this.xhr.onload = function()
26897 _this.xhrOnLoad(_this.xhr);
26900 this.xhr.onerror = function()
26902 _this.xhrOnError(_this.xhr);
26905 var formData = new FormData();
26907 formData.append('returnHTML', 'NO');
26910 formData.append('crop', crop);
26913 formData.append(this.paramName, file, file.name);
26915 if(this.fireEvent('prepare', this, formData) != false){
26916 this.xhr.send(formData);
26920 uploadCancel : function()
26927 this.delegates = [];
26929 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26936 renderPreview : function(file)
26938 if(typeof(file.target) != 'undefined' && file.target){
26942 var previewEl = this.managerEl.createChild({
26944 cls : 'roo-document-manager-preview',
26948 tooltip : file.filename,
26949 cls : 'roo-document-manager-thumb',
26950 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26955 html : '<i class="fa fa-times-circle"></i>'
26960 var close = previewEl.select('button.close', true).first();
26962 close.on('click', this.onRemove, this, file);
26964 file.target = previewEl;
26966 var image = previewEl.select('img', true).first();
26970 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26972 image.on('click', this.onClick, this, file);
26978 onPreviewLoad : function(file, image)
26980 if(typeof(file.target) == 'undefined' || !file.target){
26984 var width = image.dom.naturalWidth || image.dom.width;
26985 var height = image.dom.naturalHeight || image.dom.height;
26987 if(width > height){
26988 file.target.addClass('wide');
26992 file.target.addClass('tall');
26997 uploadFromSource : function(file, crop)
26999 this.xhr = new XMLHttpRequest();
27001 this.managerEl.createChild({
27003 cls : 'roo-document-manager-loading',
27007 tooltip : file.name,
27008 cls : 'roo-document-manager-thumb',
27009 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27015 this.xhr.open(this.method, this.url, true);
27018 "Accept": "application/json",
27019 "Cache-Control": "no-cache",
27020 "X-Requested-With": "XMLHttpRequest"
27023 for (var headerName in headers) {
27024 var headerValue = headers[headerName];
27026 this.xhr.setRequestHeader(headerName, headerValue);
27032 this.xhr.onload = function()
27034 _this.xhrOnLoad(_this.xhr);
27037 this.xhr.onerror = function()
27039 _this.xhrOnError(_this.xhr);
27042 var formData = new FormData();
27044 formData.append('returnHTML', 'NO');
27046 formData.append('crop', crop);
27048 if(typeof(file.filename) != 'undefined'){
27049 formData.append('filename', file.filename);
27052 if(typeof(file.mimetype) != 'undefined'){
27053 formData.append('mimetype', file.mimetype);
27056 if(this.fireEvent('prepare', this, formData) != false){
27057 this.xhr.send(formData);
27067 * @class Roo.bootstrap.DocumentViewer
27068 * @extends Roo.bootstrap.Component
27069 * Bootstrap DocumentViewer class
27072 * Create a new DocumentViewer
27073 * @param {Object} config The config object
27076 Roo.bootstrap.DocumentViewer = function(config){
27077 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27082 * Fire after initEvent
27083 * @param {Roo.bootstrap.DocumentViewer} this
27089 * @param {Roo.bootstrap.DocumentViewer} this
27094 * Fire after trash button
27095 * @param {Roo.bootstrap.DocumentViewer} this
27102 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
27104 getAutoCreate : function()
27108 cls : 'roo-document-viewer',
27112 cls : 'roo-document-viewer-body',
27116 cls : 'roo-document-viewer-thumb',
27120 cls : 'roo-document-viewer-image'
27128 cls : 'roo-document-viewer-footer',
27131 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27139 cls : 'btn btn-default roo-document-viewer-trash',
27140 html : '<i class="fa fa-trash"></i>'
27153 initEvents : function()
27156 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27157 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27159 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27160 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27162 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27163 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27165 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27166 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27168 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27169 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27171 this.bodyEl.on('click', this.onClick, this);
27173 this.trashBtn.on('click', this.onTrash, this);
27177 initial : function()
27179 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27182 this.fireEvent('initial', this);
27186 onClick : function(e)
27188 e.preventDefault();
27190 this.fireEvent('click', this);
27193 onTrash : function(e)
27195 e.preventDefault();
27197 this.fireEvent('trash', this);
27209 * @class Roo.bootstrap.NavProgressBar
27210 * @extends Roo.bootstrap.Component
27211 * Bootstrap NavProgressBar class
27214 * Create a new nav progress bar
27215 * @param {Object} config The config object
27218 Roo.bootstrap.NavProgressBar = function(config){
27219 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27221 this.bullets = this.bullets || [];
27223 // Roo.bootstrap.NavProgressBar.register(this);
27227 * Fires when the active item changes
27228 * @param {Roo.bootstrap.NavProgressBar} this
27229 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27230 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
27237 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
27242 getAutoCreate : function()
27244 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27248 cls : 'roo-navigation-bar-group',
27252 cls : 'roo-navigation-top-bar'
27256 cls : 'roo-navigation-bullets-bar',
27260 cls : 'roo-navigation-bar'
27267 cls : 'roo-navigation-bottom-bar'
27277 initEvents: function()
27282 onRender : function(ct, position)
27284 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27286 if(this.bullets.length){
27287 Roo.each(this.bullets, function(b){
27296 addItem : function(cfg)
27298 var item = new Roo.bootstrap.NavProgressItem(cfg);
27300 item.parentId = this.id;
27301 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27304 var top = new Roo.bootstrap.Element({
27306 cls : 'roo-navigation-bar-text'
27309 var bottom = new Roo.bootstrap.Element({
27311 cls : 'roo-navigation-bar-text'
27314 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27315 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27317 var topText = new Roo.bootstrap.Element({
27319 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27322 var bottomText = new Roo.bootstrap.Element({
27324 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27327 topText.onRender(top.el, null);
27328 bottomText.onRender(bottom.el, null);
27331 item.bottomEl = bottom;
27334 this.barItems.push(item);
27339 getActive : function()
27341 var active = false;
27343 Roo.each(this.barItems, function(v){
27345 if (!v.isActive()) {
27357 setActiveItem : function(item)
27361 Roo.each(this.barItems, function(v){
27362 if (v.rid == item.rid) {
27366 if (v.isActive()) {
27367 v.setActive(false);
27372 item.setActive(true);
27374 this.fireEvent('changed', this, item, prev);
27377 getBarItem: function(rid)
27381 Roo.each(this.barItems, function(e) {
27382 if (e.rid != rid) {
27393 indexOfItem : function(item)
27397 Roo.each(this.barItems, function(v, i){
27399 if (v.rid != item.rid) {
27410 setActiveNext : function()
27412 var i = this.indexOfItem(this.getActive());
27414 if (i > this.barItems.length) {
27418 this.setActiveItem(this.barItems[i+1]);
27421 setActivePrev : function()
27423 var i = this.indexOfItem(this.getActive());
27429 this.setActiveItem(this.barItems[i-1]);
27432 format : function()
27434 if(!this.barItems.length){
27438 var width = 100 / this.barItems.length;
27440 Roo.each(this.barItems, function(i){
27441 i.el.setStyle('width', width + '%');
27442 i.topEl.el.setStyle('width', width + '%');
27443 i.bottomEl.el.setStyle('width', width + '%');
27452 * Nav Progress Item
27457 * @class Roo.bootstrap.NavProgressItem
27458 * @extends Roo.bootstrap.Component
27459 * Bootstrap NavProgressItem class
27460 * @cfg {String} rid the reference id
27461 * @cfg {Boolean} active (true|false) Is item active default false
27462 * @cfg {Boolean} disabled (true|false) Is item active default false
27463 * @cfg {String} html
27464 * @cfg {String} position (top|bottom) text position default bottom
27465 * @cfg {String} icon show icon instead of number
27468 * Create a new NavProgressItem
27469 * @param {Object} config The config object
27471 Roo.bootstrap.NavProgressItem = function(config){
27472 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27477 * The raw click event for the entire grid.
27478 * @param {Roo.bootstrap.NavProgressItem} this
27479 * @param {Roo.EventObject} e
27486 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
27492 position : 'bottom',
27495 getAutoCreate : function()
27497 var iconCls = 'roo-navigation-bar-item-icon';
27499 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27503 cls: 'roo-navigation-bar-item',
27513 cfg.cls += ' active';
27516 cfg.cls += ' disabled';
27522 disable : function()
27524 this.setDisabled(true);
27527 enable : function()
27529 this.setDisabled(false);
27532 initEvents: function()
27534 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27536 this.iconEl.on('click', this.onClick, this);
27539 onClick : function(e)
27541 e.preventDefault();
27547 if(this.fireEvent('click', this, e) === false){
27551 this.parent().setActiveItem(this);
27554 isActive: function ()
27556 return this.active;
27559 setActive : function(state)
27561 if(this.active == state){
27565 this.active = state;
27568 this.el.addClass('active');
27572 this.el.removeClass('active');
27577 setDisabled : function(state)
27579 if(this.disabled == state){
27583 this.disabled = state;
27586 this.el.addClass('disabled');
27590 this.el.removeClass('disabled');
27593 tooltipEl : function()
27595 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27608 * @class Roo.bootstrap.FieldLabel
27609 * @extends Roo.bootstrap.Component
27610 * Bootstrap FieldLabel class
27611 * @cfg {String} html contents of the element
27612 * @cfg {String} tag tag of the element default label
27613 * @cfg {String} cls class of the element
27614 * @cfg {String} target label target
27615 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27616 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27617 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27618 * @cfg {String} iconTooltip default "This field is required"
27621 * Create a new FieldLabel
27622 * @param {Object} config The config object
27625 Roo.bootstrap.FieldLabel = function(config){
27626 Roo.bootstrap.Element.superclass.constructor.call(this, config);
27631 * Fires after the field has been marked as invalid.
27632 * @param {Roo.form.FieldLabel} this
27633 * @param {String} msg The validation message
27638 * Fires after the field has been validated with no errors.
27639 * @param {Roo.form.FieldLabel} this
27645 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
27652 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27653 validClass : 'text-success fa fa-lg fa-check',
27654 iconTooltip : 'This field is required',
27656 getAutoCreate : function(){
27660 cls : 'roo-bootstrap-field-label ' + this.cls,
27666 tooltip : this.iconTooltip
27678 initEvents: function()
27680 Roo.bootstrap.Element.superclass.initEvents.call(this);
27682 this.iconEl = this.el.select('i', true).first();
27684 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27686 Roo.bootstrap.FieldLabel.register(this);
27690 * Mark this field as valid
27692 markValid : function()
27694 this.iconEl.show();
27696 this.iconEl.removeClass(this.invalidClass);
27698 this.iconEl.addClass(this.validClass);
27700 this.fireEvent('valid', this);
27704 * Mark this field as invalid
27705 * @param {String} msg The validation message
27707 markInvalid : function(msg)
27709 this.iconEl.show();
27711 this.iconEl.removeClass(this.validClass);
27713 this.iconEl.addClass(this.invalidClass);
27715 this.fireEvent('invalid', this, msg);
27721 Roo.apply(Roo.bootstrap.FieldLabel, {
27726 * register a FieldLabel Group
27727 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27729 register : function(label)
27731 if(this.groups.hasOwnProperty(label.target)){
27735 this.groups[label.target] = label;
27739 * fetch a FieldLabel Group based on the target
27740 * @param {string} target
27741 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27743 get: function(target) {
27744 if (typeof(this.groups[target]) == 'undefined') {
27748 return this.groups[target] ;
27757 * page DateSplitField.
27763 * @class Roo.bootstrap.DateSplitField
27764 * @extends Roo.bootstrap.Component
27765 * Bootstrap DateSplitField class
27766 * @cfg {string} fieldLabel - the label associated
27767 * @cfg {Number} labelWidth set the width of label (0-12)
27768 * @cfg {String} labelAlign (top|left)
27769 * @cfg {Boolean} dayAllowBlank (true|false) default false
27770 * @cfg {Boolean} monthAllowBlank (true|false) default false
27771 * @cfg {Boolean} yearAllowBlank (true|false) default false
27772 * @cfg {string} dayPlaceholder
27773 * @cfg {string} monthPlaceholder
27774 * @cfg {string} yearPlaceholder
27775 * @cfg {string} dayFormat default 'd'
27776 * @cfg {string} monthFormat default 'm'
27777 * @cfg {string} yearFormat default 'Y'
27781 * Create a new DateSplitField
27782 * @param {Object} config The config object
27785 Roo.bootstrap.DateSplitField = function(config){
27786 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27792 * getting the data of years
27793 * @param {Roo.bootstrap.DateSplitField} this
27794 * @param {Object} years
27799 * getting the data of days
27800 * @param {Roo.bootstrap.DateSplitField} this
27801 * @param {Object} days
27806 * Fires after the field has been marked as invalid.
27807 * @param {Roo.form.Field} this
27808 * @param {String} msg The validation message
27813 * Fires after the field has been validated with no errors.
27814 * @param {Roo.form.Field} this
27820 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
27823 labelAlign : 'top',
27825 dayAllowBlank : false,
27826 monthAllowBlank : false,
27827 yearAllowBlank : false,
27828 dayPlaceholder : '',
27829 monthPlaceholder : '',
27830 yearPlaceholder : '',
27834 isFormField : true,
27836 getAutoCreate : function()
27840 cls : 'row roo-date-split-field-group',
27845 cls : 'form-hidden-field roo-date-split-field-group-value',
27851 if(this.fieldLabel){
27854 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27858 html : this.fieldLabel
27864 Roo.each(['day', 'month', 'year'], function(t){
27867 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27874 inputEl: function ()
27876 return this.el.select('.roo-date-split-field-group-value', true).first();
27879 onRender : function(ct, position)
27883 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27885 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27887 this.dayField = new Roo.bootstrap.ComboBox({
27888 allowBlank : this.dayAllowBlank,
27889 alwaysQuery : true,
27890 displayField : 'value',
27893 forceSelection : true,
27895 placeholder : this.dayPlaceholder,
27896 selectOnFocus : true,
27897 tpl : '<div class="select2-result"><b>{value}</b></div>',
27898 triggerAction : 'all',
27900 valueField : 'value',
27901 store : new Roo.data.SimpleStore({
27902 data : (function() {
27904 _this.fireEvent('days', _this, days);
27907 fields : [ 'value' ]
27910 select : function (_self, record, index)
27912 _this.setValue(_this.getValue());
27917 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27919 this.monthField = new Roo.bootstrap.MonthField({
27920 after : '<i class=\"fa fa-calendar\"></i>',
27921 allowBlank : this.monthAllowBlank,
27922 placeholder : this.monthPlaceholder,
27925 render : function (_self)
27927 this.el.select('span.input-group-addon', true).first().on('click', function(e){
27928 e.preventDefault();
27932 select : function (_self, oldvalue, newvalue)
27934 _this.setValue(_this.getValue());
27939 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27941 this.yearField = new Roo.bootstrap.ComboBox({
27942 allowBlank : this.yearAllowBlank,
27943 alwaysQuery : true,
27944 displayField : 'value',
27947 forceSelection : true,
27949 placeholder : this.yearPlaceholder,
27950 selectOnFocus : true,
27951 tpl : '<div class="select2-result"><b>{value}</b></div>',
27952 triggerAction : 'all',
27954 valueField : 'value',
27955 store : new Roo.data.SimpleStore({
27956 data : (function() {
27958 _this.fireEvent('years', _this, years);
27961 fields : [ 'value' ]
27964 select : function (_self, record, index)
27966 _this.setValue(_this.getValue());
27971 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
27974 setValue : function(v, format)
27976 this.inputEl.dom.value = v;
27978 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
27980 var d = Date.parseDate(v, f);
27987 this.setDay(d.format(this.dayFormat));
27988 this.setMonth(d.format(this.monthFormat));
27989 this.setYear(d.format(this.yearFormat));
27996 setDay : function(v)
27998 this.dayField.setValue(v);
27999 this.inputEl.dom.value = this.getValue();
28004 setMonth : function(v)
28006 this.monthField.setValue(v, true);
28007 this.inputEl.dom.value = this.getValue();
28012 setYear : function(v)
28014 this.yearField.setValue(v);
28015 this.inputEl.dom.value = this.getValue();
28020 getDay : function()
28022 return this.dayField.getValue();
28025 getMonth : function()
28027 return this.monthField.getValue();
28030 getYear : function()
28032 return this.yearField.getValue();
28035 getValue : function()
28037 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28039 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28049 this.inputEl.dom.value = '';
28054 validate : function()
28056 var d = this.dayField.validate();
28057 var m = this.monthField.validate();
28058 var y = this.yearField.validate();
28063 (!this.dayAllowBlank && !d) ||
28064 (!this.monthAllowBlank && !m) ||
28065 (!this.yearAllowBlank && !y)
28070 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28079 this.markInvalid();
28084 markValid : function()
28087 var label = this.el.select('label', true).first();
28088 var icon = this.el.select('i.fa-star', true).first();
28094 this.fireEvent('valid', this);
28098 * Mark this field as invalid
28099 * @param {String} msg The validation message
28101 markInvalid : function(msg)
28104 var label = this.el.select('label', true).first();
28105 var icon = this.el.select('i.fa-star', true).first();
28107 if(label && !icon){
28108 this.el.select('.roo-date-split-field-label', true).createChild({
28110 cls : 'text-danger fa fa-lg fa-star',
28111 tooltip : 'This field is required',
28112 style : 'margin-right:5px;'
28116 this.fireEvent('invalid', this, msg);
28119 clearInvalid : function()
28121 var label = this.el.select('label', true).first();
28122 var icon = this.el.select('i.fa-star', true).first();
28128 this.fireEvent('valid', this);
28131 getName: function()