4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
23 * Do not use directly - it does not do anything..
24 * @param {Object} config The config object
29 Roo.bootstrap.Component = function(config){
30 Roo.bootstrap.Component.superclass.constructor.call(this, config);
34 * @event childrenrendered
35 * Fires when the children have been rendered..
36 * @param {Roo.bootstrap.Component} this
38 "childrenrendered" : true
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
50 allowDomMove : false, // to stop relocations in parent onRender...
60 * Initialize Events for the element
62 initEvents : function() { },
68 can_build_overlaid : true,
70 container_method : false,
77 // returns the parent component..
78 return Roo.ComponentMgr.get(this.parentId)
84 onRender : function(ct, position)
86 // Roo.log("Call onRender: " + this.xtype);
88 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
91 if (this.el.attr('xtype')) {
92 this.el.attr('xtypex', this.el.attr('xtype'));
93 this.el.dom.removeAttribute('xtype');
103 var cfg = Roo.apply({}, this.getAutoCreate());
105 cfg.id = this.id || Roo.id();
107 // fill in the extra attributes
108 if (this.xattr && typeof(this.xattr) =='object') {
109 for (var i in this.xattr) {
110 cfg[i] = this.xattr[i];
115 cfg.dataId = this.dataId;
119 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
122 if (this.style) { // fixme needs to support more complex style data.
123 cfg.style = this.style;
127 cfg.name = this.name;
130 this.el = ct.createChild(cfg, position);
133 this.tooltipEl().attr('tooltip', this.tooltip);
136 if(this.tabIndex !== undefined){
137 this.el.dom.setAttribute('tabIndex', this.tabIndex);
144 * Fetch the element to add children to
145 * @return {Roo.Element} defaults to this.el
147 getChildContainer : function()
152 * Fetch the element to display the tooltip on.
153 * @return {Roo.Element} defaults to this.el
155 tooltipEl : function()
160 addxtype : function(tree,cntr)
164 cn = Roo.factory(tree);
166 cn.parentType = this.xtype; //??
167 cn.parentId = this.id;
169 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
170 if (typeof(cn.container_method) == 'string') {
171 cntr = cn.container_method;
175 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
177 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
179 var build_from_html = Roo.XComponent.build_from_html;
181 var is_body = (tree.xtype == 'Body') ;
183 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185 var self_cntr_el = Roo.get(this[cntr](false));
187 // do not try and build conditional elements
188 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
192 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
193 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
194 return this.addxtypeChild(tree,cntr);
197 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
200 return this.addxtypeChild(Roo.apply({}, tree),cntr);
203 Roo.log('skipping render');
209 if (!build_from_html) {
213 // this i think handles overlaying multiple children of the same type
214 // with the sam eelement.. - which might be buggy..
216 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
222 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
226 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
231 addxtypeChild : function (tree, cntr, is_body)
233 Roo.debug && Roo.log('addxtypeChild:' + cntr);
235 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
238 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
239 (typeof(tree['flexy:foreach']) != 'undefined');
243 skip_children = false;
244 // render the element if it's not BODY.
247 cn = Roo.factory(tree);
249 cn.parentType = this.xtype; //??
250 cn.parentId = this.id;
252 var build_from_html = Roo.XComponent.build_from_html;
255 // does the container contain child eleemnts with 'xtype' attributes.
256 // that match this xtype..
257 // note - when we render we create these as well..
258 // so we should check to see if body has xtype set.
259 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
261 var self_cntr_el = Roo.get(this[cntr](false));
262 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
264 //Roo.log(Roo.XComponent.build_from_html);
265 //Roo.log("got echild:");
268 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
269 // and are not displayed -this causes this to use up the wrong element when matching.
270 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
273 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
274 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
280 //echild.dom.removeAttribute('xtype');
282 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
283 Roo.debug && Roo.log(self_cntr_el);
284 Roo.debug && Roo.log(echild);
285 Roo.debug && Roo.log(cn);
291 // if object has flexy:if - then it may or may not be rendered.
292 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
293 // skip a flexy if element.
294 Roo.debug && Roo.log('skipping render');
295 Roo.debug && Roo.log(tree);
297 Roo.debug && Roo.log('skipping all children');
298 skip_children = true;
303 // actually if flexy:foreach is found, we really want to create
304 // multiple copies here...
306 //Roo.log(this[cntr]());
307 cn.render(this[cntr](true));
309 // then add the element..
317 if (typeof (tree.menu) != 'undefined') {
318 tree.menu.parentType = cn.xtype;
319 tree.menu.triggerEl = cn.el;
320 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
324 if (!tree.items || !tree.items.length) {
328 var items = tree.items;
331 //Roo.log(items.length);
333 if (!skip_children) {
334 for(var i =0;i < items.length;i++) {
335 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
341 this.fireEvent('childrenrendered', this);
346 * Show a component - removes 'hidden' class
351 this.el.removeClass('hidden');
355 * Hide a component - adds 'hidden' class
359 if (this.el && !this.el.hasClass('hidden')) {
360 this.el.addClass('hidden');
374 * @class Roo.bootstrap.Body
375 * @extends Roo.bootstrap.Component
376 * Bootstrap Body class
380 * @param {Object} config The config object
383 Roo.bootstrap.Body = function(config){
384 Roo.bootstrap.Body.superclass.constructor.call(this, config);
385 this.el = Roo.get(document.body);
386 if (this.cls && this.cls.length) {
387 Roo.get(document.body).addClass(this.cls);
391 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
393 is_body : true,// just to make sure it's constructed?
398 onRender : function(ct, position)
400 /* Roo.log("Roo.bootstrap.Body - onRender");
401 if (this.cls && this.cls.length) {
402 Roo.get(document.body).addClass(this.cls);
422 * @class Roo.bootstrap.ButtonGroup
423 * @extends Roo.bootstrap.Component
424 * Bootstrap ButtonGroup class
425 * @cfg {String} size lg | sm | xs (default empty normal)
426 * @cfg {String} align vertical | justified (default none)
427 * @cfg {String} direction up | down (default down)
428 * @cfg {Boolean} toolbar false | true
429 * @cfg {Boolean} btn true | false
434 * @param {Object} config The config object
437 Roo.bootstrap.ButtonGroup = function(config){
438 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
441 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
449 getAutoCreate : function(){
455 cfg.html = this.html || cfg.html;
466 if (['vertical','justified'].indexOf(this.align)!==-1) {
467 cfg.cls = 'btn-group-' + this.align;
469 if (this.align == 'justified') {
470 console.log(this.items);
474 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
475 cfg.cls += ' btn-group-' + this.size;
478 if (this.direction == 'up') {
479 cfg.cls += ' dropup' ;
495 * @class Roo.bootstrap.Button
496 * @extends Roo.bootstrap.Component
497 * Bootstrap Button class
498 * @cfg {String} html The button content
499 * @cfg {String} weight ( primary | success | info | warning | danger | link ) default
500 * @cfg {String} size ( lg | sm | xs)
501 * @cfg {String} tag ( a | input | submit)
502 * @cfg {String} href empty or href
503 * @cfg {Boolean} disabled default false;
504 * @cfg {Boolean} isClose default false;
505 * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
506 * @cfg {String} badge text for badge
507 * @cfg {String} theme default
508 * @cfg {Boolean} inverse
509 * @cfg {Boolean} toggle
510 * @cfg {String} ontext text for on toggle state
511 * @cfg {String} offtext text for off toggle state
512 * @cfg {Boolean} defaulton
513 * @cfg {Boolean} preventDefault default true
514 * @cfg {Boolean} removeClass remove the standard class..
515 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
518 * Create a new button
519 * @param {Object} config The config object
523 Roo.bootstrap.Button = function(config){
524 Roo.bootstrap.Button.superclass.constructor.call(this, config);
529 * When a butotn is pressed
530 * @param {Roo.bootstrap.Button} this
531 * @param {Roo.EventObject} e
536 * After the button has been toggles
537 * @param {Roo.EventObject} e
538 * @param {boolean} pressed (also available as button.pressed)
544 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
562 preventDefault: true,
571 getAutoCreate : function(){
579 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
580 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
585 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
587 if (this.toggle == true) {
590 cls: 'slider-frame roo-button',
595 'data-off-text':'OFF',
596 cls: 'slider-button',
602 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
603 cfg.cls += ' '+this.weight;
612 cfg["aria-hidden"] = true;
614 cfg.html = "×";
620 if (this.theme==='default') {
621 cfg.cls = 'btn roo-button';
623 //if (this.parentType != 'Navbar') {
624 this.weight = this.weight.length ? this.weight : 'default';
626 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
628 cfg.cls += ' btn-' + this.weight;
630 } else if (this.theme==='glow') {
633 cfg.cls = 'btn-glow roo-button';
635 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
637 cfg.cls += ' ' + this.weight;
643 this.cls += ' inverse';
648 cfg.cls += ' active';
652 cfg.disabled = 'disabled';
656 Roo.log('changing to ul' );
658 this.glyphicon = 'caret';
661 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
663 //gsRoo.log(this.parentType);
664 if (this.parentType === 'Navbar' && !this.parent().bar) {
665 Roo.log('changing to li?');
674 href : this.href || '#'
677 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
678 cfg.cls += ' dropdown';
685 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
687 if (this.glyphicon) {
688 cfg.html = ' ' + cfg.html;
693 cls: 'glyphicon glyphicon-' + this.glyphicon
703 // cfg.cls='btn roo-button';
707 var value = cfg.html;
712 cls: 'glyphicon glyphicon-' + this.glyphicon,
731 cfg.cls += ' dropdown';
732 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
735 if (cfg.tag !== 'a' && this.href !== '') {
736 throw "Tag must be a to set href.";
737 } else if (this.href.length > 0) {
738 cfg.href = this.href;
741 if(this.removeClass){
746 cfg.target = this.target;
751 initEvents: function() {
752 // Roo.log('init events?');
753 // Roo.log(this.el.dom);
756 if (typeof (this.menu) != 'undefined') {
757 this.menu.parentType = this.xtype;
758 this.menu.triggerEl = this.el;
759 this.addxtype(Roo.apply({}, this.menu));
763 if (this.el.hasClass('roo-button')) {
764 this.el.on('click', this.onClick, this);
766 this.el.select('.roo-button').on('click', this.onClick, this);
769 if(this.removeClass){
770 this.el.on('click', this.onClick, this);
773 this.el.enableDisplayMode();
776 onClick : function(e)
783 Roo.log('button on click ');
784 if(this.preventDefault){
787 if (this.pressed === true || this.pressed === false) {
788 this.pressed = !this.pressed;
789 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
790 this.fireEvent('toggle', this, e, this.pressed);
794 this.fireEvent('click', this, e);
798 * Enables this button
802 this.disabled = false;
803 this.el.removeClass('disabled');
807 * Disable this button
811 this.disabled = true;
812 this.el.addClass('disabled');
815 * sets the active state on/off,
816 * @param {Boolean} state (optional) Force a particular state
818 setActive : function(v) {
820 this.el[v ? 'addClass' : 'removeClass']('active');
823 * toggles the current active state
825 toggleActive : function()
827 var active = this.el.hasClass('active');
828 this.setActive(!active);
832 setText : function(str)
834 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
838 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
861 * @class Roo.bootstrap.Column
862 * @extends Roo.bootstrap.Component
863 * Bootstrap Column class
864 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
865 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
866 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
867 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
868 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
869 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
870 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
871 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
874 * @cfg {Boolean} hidden (true|false) hide the element
875 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
876 * @cfg {String} fa (ban|check|...) font awesome icon
877 * @cfg {Number} fasize (1|2|....) font awsome size
879 * @cfg {String} icon (info-sign|check|...) glyphicon name
881 * @cfg {String} html content of column.
884 * Create a new Column
885 * @param {Object} config The config object
888 Roo.bootstrap.Column = function(config){
889 Roo.bootstrap.Column.superclass.constructor.call(this, config);
892 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
910 getAutoCreate : function(){
911 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
919 ['xs','sm','md','lg'].map(function(size){
920 //Roo.log( size + ':' + settings[size]);
922 if (settings[size+'off'] !== false) {
923 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
926 if (settings[size] === false) {
930 if (!settings[size]) { // 0 = hidden
931 cfg.cls += ' hidden-' + size;
934 cfg.cls += ' col-' + size + '-' + settings[size];
939 cfg.cls += ' hidden';
942 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
943 cfg.cls +=' alert alert-' + this.alert;
947 if (this.html.length) {
948 cfg.html = this.html;
952 if (this.fasize > 1) {
953 fasize = ' fa-' + this.fasize + 'x';
955 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
960 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
979 * @class Roo.bootstrap.Container
980 * @extends Roo.bootstrap.Component
981 * Bootstrap Container class
982 * @cfg {Boolean} jumbotron is it a jumbotron element
983 * @cfg {String} html content of element
984 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
985 * @cfg {String} panel (primary|success|info|warning|danger) render as panel - type - primary/success.....
986 * @cfg {String} header content of header (for panel)
987 * @cfg {String} footer content of footer (for panel)
988 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
989 * @cfg {String} tag (header|aside|section) type of HTML tag.
990 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
991 * @cfg {String} fa font awesome icon
992 * @cfg {String} icon (info-sign|check|...) glyphicon name
993 * @cfg {Boolean} hidden (true|false) hide the element
994 * @cfg {Boolean} expandable (true|false) default false
995 * @cfg {Boolean} expanded (true|false) default true
996 * @cfg {String} rheader contet on the right of header
997 * @cfg {Boolean} clickable (true|false) default false
1001 * Create a new Container
1002 * @param {Object} config The config object
1005 Roo.bootstrap.Container = function(config){
1006 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1012 * After the panel has been expand
1014 * @param {Roo.bootstrap.Container} this
1019 * After the panel has been collapsed
1021 * @param {Roo.bootstrap.Container} this
1026 * When a element is chick
1027 * @param {Roo.bootstrap.Container} this
1028 * @param {Roo.EventObject} e
1034 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1052 getChildContainer : function() {
1058 if (this.panel.length) {
1059 return this.el.select('.panel-body',true).first();
1066 getAutoCreate : function(){
1069 tag : this.tag || 'div',
1073 if (this.jumbotron) {
1074 cfg.cls = 'jumbotron';
1079 // - this is applied by the parent..
1081 // cfg.cls = this.cls + '';
1084 if (this.sticky.length) {
1086 var bd = Roo.get(document.body);
1087 if (!bd.hasClass('bootstrap-sticky')) {
1088 bd.addClass('bootstrap-sticky');
1089 Roo.select('html',true).setStyle('height', '100%');
1092 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1096 if (this.well.length) {
1097 switch (this.well) {
1100 cfg.cls +=' well well-' +this.well;
1109 cfg.cls += ' hidden';
1113 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1114 cfg.cls +=' alert alert-' + this.alert;
1119 if (this.panel.length) {
1120 cfg.cls += ' panel panel-' + this.panel;
1122 if (this.header.length) {
1126 if(this.expandable){
1128 cfg.cls = cfg.cls + ' expandable';
1132 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1140 cls : 'panel-title',
1141 html : (this.expandable ? ' ' : '') + this.header
1145 cls: 'panel-header-right',
1151 cls : 'panel-heading',
1152 style : this.expandable ? 'cursor: pointer' : '',
1160 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1165 if (this.footer.length) {
1167 cls : 'panel-footer',
1176 body.html = this.html || cfg.html;
1177 // prefix with the icons..
1179 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1182 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1187 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1188 cfg.cls = 'container';
1194 initEvents: function()
1196 if(this.expandable){
1197 var headerEl = this.headerEl();
1200 headerEl.on('click', this.onToggleClick, this);
1205 this.el.on('click', this.onClick, this);
1210 onToggleClick : function()
1212 var headerEl = this.headerEl();
1228 if(this.fireEvent('expand', this)) {
1230 this.expanded = true;
1232 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1234 this.el.select('.panel-body',true).first().removeClass('hide');
1236 var toggleEl = this.toggleEl();
1242 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1247 collapse : function()
1249 if(this.fireEvent('collapse', this)) {
1251 this.expanded = false;
1253 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1254 this.el.select('.panel-body',true).first().addClass('hide');
1256 var toggleEl = this.toggleEl();
1262 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1266 toggleEl : function()
1268 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1272 return this.el.select('.panel-heading .fa',true).first();
1275 headerEl : function()
1277 if(!this.el || !this.panel.length || !this.header.length){
1281 return this.el.select('.panel-heading',true).first()
1284 titleEl : function()
1286 if(!this.el || !this.panel.length || !this.header.length){
1290 return this.el.select('.panel-title',true).first();
1293 setTitle : function(v)
1295 var titleEl = this.titleEl();
1301 titleEl.dom.innerHTML = v;
1304 getTitle : function()
1307 var titleEl = this.titleEl();
1313 return titleEl.dom.innerHTML;
1316 setRightTitle : function(v)
1318 var t = this.el.select('.panel-header-right',true).first();
1324 t.dom.innerHTML = v;
1327 onClick : function(e)
1331 this.fireEvent('click', this, e);
1345 * @class Roo.bootstrap.Img
1346 * @extends Roo.bootstrap.Component
1347 * Bootstrap Img class
1348 * @cfg {Boolean} imgResponsive false | true
1349 * @cfg {String} border rounded | circle | thumbnail
1350 * @cfg {String} src image source
1351 * @cfg {String} alt image alternative text
1352 * @cfg {String} href a tag href
1353 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1354 * @cfg {String} xsUrl xs image source
1355 * @cfg {String} smUrl sm image source
1356 * @cfg {String} mdUrl md image source
1357 * @cfg {String} lgUrl lg image source
1360 * Create a new Input
1361 * @param {Object} config The config object
1364 Roo.bootstrap.Img = function(config){
1365 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1371 * The img click event for the img.
1372 * @param {Roo.EventObject} e
1378 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1380 imgResponsive: true,
1390 getAutoCreate : function()
1392 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1393 return this.createSingleImg();
1398 cls: 'roo-image-responsive-group',
1403 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1405 if(!_this[size + 'Url']){
1411 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1412 html: _this.html || cfg.html,
1413 src: _this[size + 'Url']
1416 img.cls += ' roo-image-responsive-' + size;
1418 var s = ['xs', 'sm', 'md', 'lg'];
1420 s.splice(s.indexOf(size), 1);
1422 Roo.each(s, function(ss){
1423 img.cls += ' hidden-' + ss;
1426 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1427 cfg.cls += ' img-' + _this.border;
1431 cfg.alt = _this.alt;
1444 a.target = _this.target;
1448 cfg.cn.push((_this.href) ? a : img);
1455 createSingleImg : function()
1459 cls: (this.imgResponsive) ? 'img-responsive' : '',
1461 src : 'about:blank' // just incase src get's set to undefined?!?
1464 cfg.html = this.html || cfg.html;
1466 cfg.src = this.src || cfg.src;
1468 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1469 cfg.cls += ' img-' + this.border;
1486 a.target = this.target;
1491 return (this.href) ? a : cfg;
1494 initEvents: function()
1497 this.el.on('click', this.onClick, this);
1502 onClick : function(e)
1504 Roo.log('img onclick');
1505 this.fireEvent('click', this, e);
1519 * @class Roo.bootstrap.Link
1520 * @extends Roo.bootstrap.Component
1521 * Bootstrap Link Class
1522 * @cfg {String} alt image alternative text
1523 * @cfg {String} href a tag href
1524 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1525 * @cfg {String} html the content of the link.
1526 * @cfg {String} anchor name for the anchor link
1528 * @cfg {Boolean} preventDefault (true | false) default false
1532 * Create a new Input
1533 * @param {Object} config The config object
1536 Roo.bootstrap.Link = function(config){
1537 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1543 * The img click event for the img.
1544 * @param {Roo.EventObject} e
1550 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1554 preventDefault: false,
1558 getAutoCreate : function()
1564 // anchor's do not require html/href...
1565 if (this.anchor === false) {
1566 cfg.html = this.html || '';
1567 cfg.href = this.href || '#';
1569 cfg.name = this.anchor;
1570 if (this.html !== false) {
1571 cfg.html = this.html;
1573 if (this.href !== false) {
1574 cfg.href = this.href;
1578 if(this.alt !== false){
1583 if(this.target !== false) {
1584 cfg.target = this.target;
1590 initEvents: function() {
1592 if(!this.href || this.preventDefault){
1593 this.el.on('click', this.onClick, this);
1597 onClick : function(e)
1599 if(this.preventDefault){
1602 //Roo.log('img onclick');
1603 this.fireEvent('click', this, e);
1616 * @class Roo.bootstrap.Header
1617 * @extends Roo.bootstrap.Component
1618 * Bootstrap Header class
1619 * @cfg {String} html content of header
1620 * @cfg {Number} level (1|2|3|4|5|6) default 1
1623 * Create a new Header
1624 * @param {Object} config The config object
1628 Roo.bootstrap.Header = function(config){
1629 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1632 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1640 getAutoCreate : function(){
1645 tag: 'h' + (1 *this.level),
1646 html: this.html || ''
1658 * Ext JS Library 1.1.1
1659 * Copyright(c) 2006-2007, Ext JS, LLC.
1661 * Originally Released Under LGPL - original licence link has changed is not relivant.
1664 * <script type="text/javascript">
1668 * @class Roo.bootstrap.MenuMgr
1669 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1672 Roo.bootstrap.MenuMgr = function(){
1673 var menus, active, groups = {}, attached = false, lastShow = new Date();
1675 // private - called when first menu is created
1678 active = new Roo.util.MixedCollection();
1679 Roo.get(document).addKeyListener(27, function(){
1680 if(active.length > 0){
1688 if(active && active.length > 0){
1689 var c = active.clone();
1699 if(active.length < 1){
1700 Roo.get(document).un("mouseup", onMouseDown);
1708 var last = active.last();
1709 lastShow = new Date();
1712 Roo.get(document).on("mouseup", onMouseDown);
1717 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1718 m.parentMenu.activeChild = m;
1719 }else if(last && last.isVisible()){
1720 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1725 function onBeforeHide(m){
1727 m.activeChild.hide();
1729 if(m.autoHideTimer){
1730 clearTimeout(m.autoHideTimer);
1731 delete m.autoHideTimer;
1736 function onBeforeShow(m){
1737 var pm = m.parentMenu;
1738 if(!pm && !m.allowOtherMenus){
1740 }else if(pm && pm.activeChild && active != m){
1741 pm.activeChild.hide();
1745 // private this should really trigger on mouseup..
1746 function onMouseDown(e){
1747 Roo.log("on Mouse Up");
1748 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1758 function onBeforeCheck(mi, state){
1760 var g = groups[mi.group];
1761 for(var i = 0, l = g.length; i < l; i++){
1763 g[i].setChecked(false);
1772 * Hides all menus that are currently visible
1774 hideAll : function(){
1779 register : function(menu){
1783 menus[menu.id] = menu;
1784 menu.on("beforehide", onBeforeHide);
1785 menu.on("hide", onHide);
1786 menu.on("beforeshow", onBeforeShow);
1787 menu.on("show", onShow);
1789 if(g && menu.events["checkchange"]){
1793 groups[g].push(menu);
1794 menu.on("checkchange", onCheck);
1799 * Returns a {@link Roo.menu.Menu} object
1800 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1801 * be used to generate and return a new Menu instance.
1803 get : function(menu){
1804 if(typeof menu == "string"){ // menu id
1806 }else if(menu.events){ // menu instance
1809 /*else if(typeof menu.length == 'number'){ // array of menu items?
1810 return new Roo.bootstrap.Menu({items:menu});
1811 }else{ // otherwise, must be a config
1812 return new Roo.bootstrap.Menu(menu);
1819 unregister : function(menu){
1820 delete menus[menu.id];
1821 menu.un("beforehide", onBeforeHide);
1822 menu.un("hide", onHide);
1823 menu.un("beforeshow", onBeforeShow);
1824 menu.un("show", onShow);
1826 if(g && menu.events["checkchange"]){
1827 groups[g].remove(menu);
1828 menu.un("checkchange", onCheck);
1833 registerCheckable : function(menuItem){
1834 var g = menuItem.group;
1839 groups[g].push(menuItem);
1840 menuItem.on("beforecheckchange", onBeforeCheck);
1845 unregisterCheckable : function(menuItem){
1846 var g = menuItem.group;
1848 groups[g].remove(menuItem);
1849 menuItem.un("beforecheckchange", onBeforeCheck);
1861 * @class Roo.bootstrap.Menu
1862 * @extends Roo.bootstrap.Component
1863 * Bootstrap Menu class - container for MenuItems
1864 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1868 * @param {Object} config The config object
1872 Roo.bootstrap.Menu = function(config){
1873 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1874 if (this.registerMenu) {
1875 Roo.bootstrap.MenuMgr.register(this);
1880 * Fires before this menu is displayed
1881 * @param {Roo.menu.Menu} this
1886 * Fires before this menu is hidden
1887 * @param {Roo.menu.Menu} this
1892 * Fires after this menu is displayed
1893 * @param {Roo.menu.Menu} this
1898 * Fires after this menu is hidden
1899 * @param {Roo.menu.Menu} this
1904 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1905 * @param {Roo.menu.Menu} this
1906 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1907 * @param {Roo.EventObject} e
1912 * Fires when the mouse is hovering over this menu
1913 * @param {Roo.menu.Menu} this
1914 * @param {Roo.EventObject} e
1915 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1920 * Fires when the mouse exits this menu
1921 * @param {Roo.menu.Menu} this
1922 * @param {Roo.EventObject} e
1923 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1928 * Fires when a menu item contained in this menu is clicked
1929 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1930 * @param {Roo.EventObject} e
1934 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1937 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1941 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1944 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1946 registerMenu : true,
1948 menuItems :false, // stores the menu items..
1954 getChildContainer : function() {
1958 getAutoCreate : function(){
1960 //if (['right'].indexOf(this.align)!==-1) {
1961 // cfg.cn[1].cls += ' pull-right'
1967 cls : 'dropdown-menu' ,
1968 style : 'z-index:1000'
1972 if (this.type === 'submenu') {
1973 cfg.cls = 'submenu active';
1975 if (this.type === 'treeview') {
1976 cfg.cls = 'treeview-menu';
1981 initEvents : function() {
1983 // Roo.log("ADD event");
1984 // Roo.log(this.triggerEl.dom);
1985 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1987 this.triggerEl.addClass('dropdown-toggle');
1990 this.el.on('touchstart' , this.onTouch, this);
1992 this.el.on('click' , this.onClick, this);
1994 this.el.on("mouseover", this.onMouseOver, this);
1995 this.el.on("mouseout", this.onMouseOut, this);
1999 findTargetItem : function(e)
2001 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2005 //Roo.log(t); Roo.log(t.id);
2007 //Roo.log(this.menuitems);
2008 return this.menuitems.get(t.id);
2010 //return this.items.get(t.menuItemId);
2016 onTouch : function(e)
2018 //e.stopEvent(); this make the user popdown broken
2022 onClick : function(e)
2024 Roo.log("menu.onClick");
2025 var t = this.findTargetItem(e);
2026 if(!t || t.isContainer){
2031 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2032 if(t == this.activeItem && t.shouldDeactivate(e)){
2033 this.activeItem.deactivate();
2034 delete this.activeItem;
2038 this.setActiveItem(t, true);
2046 Roo.log('pass click event');
2050 this.fireEvent("click", this, t, e);
2054 onMouseOver : function(e){
2055 var t = this.findTargetItem(e);
2058 // if(t.canActivate && !t.disabled){
2059 // this.setActiveItem(t, true);
2063 this.fireEvent("mouseover", this, e, t);
2065 isVisible : function(){
2066 return !this.hidden;
2068 onMouseOut : function(e){
2069 var t = this.findTargetItem(e);
2072 // if(t == this.activeItem && t.shouldDeactivate(e)){
2073 // this.activeItem.deactivate();
2074 // delete this.activeItem;
2077 this.fireEvent("mouseout", this, e, t);
2082 * Displays this menu relative to another element
2083 * @param {String/HTMLElement/Roo.Element} element The element to align to
2084 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2085 * the element (defaults to this.defaultAlign)
2086 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2088 show : function(el, pos, parentMenu){
2089 this.parentMenu = parentMenu;
2093 this.fireEvent("beforeshow", this);
2094 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2097 * Displays this menu at a specific xy position
2098 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2099 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2101 showAt : function(xy, parentMenu, /* private: */_e){
2102 this.parentMenu = parentMenu;
2107 this.fireEvent("beforeshow", this);
2108 //xy = this.el.adjustForConstraints(xy);
2112 this.hideMenuItems();
2113 this.hidden = false;
2114 this.triggerEl.addClass('open');
2116 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2117 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2122 this.fireEvent("show", this);
2128 this.doFocus.defer(50, this);
2132 doFocus : function(){
2134 this.focusEl.focus();
2139 * Hides this menu and optionally all parent menus
2140 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2142 hide : function(deep){
2144 this.hideMenuItems();
2145 if(this.el && this.isVisible()){
2146 this.fireEvent("beforehide", this);
2147 if(this.activeItem){
2148 this.activeItem.deactivate();
2149 this.activeItem = null;
2151 this.triggerEl.removeClass('open');;
2153 this.fireEvent("hide", this);
2155 if(deep === true && this.parentMenu){
2156 this.parentMenu.hide(true);
2160 onTriggerPress : function(e)
2163 Roo.log('trigger press');
2164 //Roo.log(e.getTarget());
2165 // Roo.log(this.triggerEl.dom);
2166 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2170 if (this.isVisible()) {
2175 this.show(this.triggerEl, false, false);
2184 hideMenuItems : function()
2186 //$(backdrop).remove()
2187 Roo.select('.open',true).each(function(aa) {
2189 aa.removeClass('open');
2190 //var parent = getParent($(this))
2191 //var relatedTarget = { relatedTarget: this }
2193 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2194 //if (e.isDefaultPrevented()) return
2195 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2198 addxtypeChild : function (tree, cntr) {
2199 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2201 this.menuitems.add(comp);
2222 * @class Roo.bootstrap.MenuItem
2223 * @extends Roo.bootstrap.Component
2224 * Bootstrap MenuItem class
2225 * @cfg {String} html the menu label
2226 * @cfg {String} href the link
2227 * @cfg {Boolean} preventDefault (true | false) default true
2228 * @cfg {Boolean} isContainer (true | false) default false
2232 * Create a new MenuItem
2233 * @param {Object} config The config object
2237 Roo.bootstrap.MenuItem = function(config){
2238 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2243 * The raw click event for the entire grid.
2244 * @param {Roo.bootstrap.MenuItem} this
2245 * @param {Roo.EventObject} e
2251 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2255 preventDefault: true,
2256 isContainer : false,
2258 getAutoCreate : function(){
2260 if(this.isContainer){
2263 cls: 'dropdown-menu-item'
2269 cls: 'dropdown-menu-item',
2278 if (this.parent().type == 'treeview') {
2279 cfg.cls = 'treeview-menu';
2282 cfg.cn[0].href = this.href || cfg.cn[0].href ;
2283 cfg.cn[0].html = this.html || cfg.cn[0].html ;
2287 initEvents: function() {
2289 //this.el.select('a').on('click', this.onClick, this);
2292 onClick : function(e)
2294 Roo.log('item on click ');
2295 //if(this.preventDefault){
2296 // e.preventDefault();
2298 //this.parent().hideMenuItems();
2300 this.fireEvent('click', this, e);
2319 * @class Roo.bootstrap.MenuSeparator
2320 * @extends Roo.bootstrap.Component
2321 * Bootstrap MenuSeparator class
2324 * Create a new MenuItem
2325 * @param {Object} config The config object
2329 Roo.bootstrap.MenuSeparator = function(config){
2330 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2333 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2335 getAutoCreate : function(){
2354 * @class Roo.bootstrap.Modal
2355 * @extends Roo.bootstrap.Component
2356 * Bootstrap Modal class
2357 * @cfg {String} title Title of dialog
2358 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2359 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2360 * @cfg {Boolean} specificTitle default false
2361 * @cfg {Array} buttons Array of buttons or standard button set..
2362 * @cfg {String} buttonPosition (left|right|center) default right
2363 * @cfg {Boolean} animate default true
2364 * @cfg {Boolean} allow_close default true
2367 * Create a new Modal Dialog
2368 * @param {Object} config The config object
2371 Roo.bootstrap.Modal = function(config){
2372 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2377 * The raw btnclick event for the button
2378 * @param {Roo.EventObject} e
2382 this.buttons = this.buttons || [];
2385 this.tmpl = Roo.factory(this.tmpl);
2390 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2392 title : 'test dialog',
2402 specificTitle: false,
2404 buttonPosition: 'right',
2418 onRender : function(ct, position)
2420 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2423 var cfg = Roo.apply({}, this.getAutoCreate());
2426 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2428 //if (!cfg.name.length) {
2432 cfg.cls += ' ' + this.cls;
2435 cfg.style = this.style;
2437 this.el = Roo.get(document.body).createChild(cfg, position);
2439 //var type = this.el.dom.type;
2442 if(this.tabIndex !== undefined){
2443 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2447 this.bodyEl = this.el.select('.modal-body',true).first();
2448 this.closeEl = this.el.select('.modal-header .close', true).first();
2449 this.footerEl = this.el.select('.modal-footer',true).first();
2450 this.titleEl = this.el.select('.modal-title',true).first();
2454 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2455 this.maskEl.enableDisplayMode("block");
2457 //this.el.addClass("x-dlg-modal");
2459 if (this.buttons.length) {
2460 Roo.each(this.buttons, function(bb) {
2461 var b = Roo.apply({}, bb);
2462 b.xns = b.xns || Roo.bootstrap;
2463 b.xtype = b.xtype || 'Button';
2464 if (typeof(b.listeners) == 'undefined') {
2465 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2468 var btn = Roo.factory(b);
2470 btn.onRender(this.el.select('.modal-footer div').first());
2474 // render the children.
2477 if(typeof(this.items) != 'undefined'){
2478 var items = this.items;
2481 for(var i =0;i < items.length;i++) {
2482 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2486 this.items = nitems;
2488 // where are these used - they used to be body/close/footer
2492 //this.el.addClass([this.fieldClass, this.cls]);
2496 getAutoCreate : function(){
2501 html : this.html || ''
2506 cls : 'modal-title',
2510 if(this.specificTitle){
2516 if (this.allow_close) {
2527 style : 'display: none',
2530 cls: "modal-dialog",
2533 cls : "modal-content",
2536 cls : 'modal-header',
2541 cls : 'modal-footer',
2545 cls: 'btn-' + this.buttonPosition
2562 modal.cls += ' fade';
2568 getChildContainer : function() {
2573 getButtonContainer : function() {
2574 return this.el.select('.modal-footer div',true).first();
2577 initEvents : function()
2579 if (this.allow_close) {
2580 this.closeEl.on('click', this.hide, this);
2585 window.addEventListener("resize", function() { _this.resize(); } );
2591 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2596 if (!this.rendered) {
2600 this.el.setStyle('display', 'block');
2602 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2605 this.el.addClass('in');
2608 this.el.addClass('in');
2612 // not sure how we can show data in here..
2614 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2617 Roo.get(document.body).addClass("x-body-masked");
2618 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2620 this.el.setStyle('zIndex', '10001');
2622 this.fireEvent('show', this);
2630 Roo.get(document.body).removeClass("x-body-masked");
2631 this.el.removeClass('in');
2632 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2634 if(this.animate){ // why
2636 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2638 this.el.setStyle('display', 'none');
2641 this.fireEvent('hide', this);
2644 addButton : function(str, cb)
2648 var b = Roo.apply({}, { html : str } );
2649 b.xns = b.xns || Roo.bootstrap;
2650 b.xtype = b.xtype || 'Button';
2651 if (typeof(b.listeners) == 'undefined') {
2652 b.listeners = { click : cb.createDelegate(this) };
2655 var btn = Roo.factory(b);
2657 btn.onRender(this.el.select('.modal-footer div').first());
2663 setDefaultButton : function(btn)
2665 //this.el.select('.modal-footer').()
2667 resizeTo: function(w,h)
2671 setContentSize : function(w, h)
2675 onButtonClick: function(btn,e)
2678 this.fireEvent('btnclick', btn.name, e);
2681 * Set the title of the Dialog
2682 * @param {String} str new Title
2684 setTitle: function(str) {
2685 this.titleEl.dom.innerHTML = str;
2688 * Set the body of the Dialog
2689 * @param {String} str new Title
2691 setBody: function(str) {
2692 this.bodyEl.dom.innerHTML = str;
2695 * Set the body of the Dialog using the template
2696 * @param {Obj} data - apply this data to the template and replace the body contents.
2698 applyBody: function(obj)
2701 Roo.log("Error - using apply Body without a template");
2704 this.tmpl.overwrite(this.bodyEl, obj);
2710 Roo.apply(Roo.bootstrap.Modal, {
2712 * Button config that displays a single OK button
2721 * Button config that displays Yes and No buttons
2737 * Button config that displays OK and Cancel buttons
2752 * Button config that displays Yes, No and Cancel buttons
2775 * messagebox - can be used as a replace
2779 * @class Roo.MessageBox
2780 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2784 Roo.Msg.alert('Status', 'Changes saved successfully.');
2786 // Prompt for user data:
2787 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2789 // process text value...
2793 // Show a dialog using config options:
2795 title:'Save Changes?',
2796 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2797 buttons: Roo.Msg.YESNOCANCEL,
2804 Roo.bootstrap.MessageBox = function(){
2805 var dlg, opt, mask, waitTimer;
2806 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2807 var buttons, activeTextEl, bwidth;
2811 var handleButton = function(button){
2813 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2817 var handleHide = function(){
2819 dlg.el.removeClass(opt.cls);
2822 // Roo.TaskMgr.stop(waitTimer);
2823 // waitTimer = null;
2828 var updateButtons = function(b){
2831 buttons["ok"].hide();
2832 buttons["cancel"].hide();
2833 buttons["yes"].hide();
2834 buttons["no"].hide();
2835 //dlg.footer.dom.style.display = 'none';
2838 dlg.footerEl.dom.style.display = '';
2839 for(var k in buttons){
2840 if(typeof buttons[k] != "function"){
2843 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2844 width += buttons[k].el.getWidth()+15;
2854 var handleEsc = function(d, k, e){
2855 if(opt && opt.closable !== false){
2865 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2866 * @return {Roo.BasicDialog} The BasicDialog element
2868 getDialog : function(){
2870 dlg = new Roo.bootstrap.Modal( {
2873 //constraintoviewport:false,
2875 //collapsible : false,
2880 //buttonAlign:"center",
2881 closeClick : function(){
2882 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2885 handleButton("cancel");
2890 dlg.on("hide", handleHide);
2892 //dlg.addKeyListener(27, handleEsc);
2894 this.buttons = buttons;
2895 var bt = this.buttonText;
2896 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2897 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2898 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2899 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2901 bodyEl = dlg.bodyEl.createChild({
2903 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2904 '<textarea class="roo-mb-textarea"></textarea>' +
2905 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2907 msgEl = bodyEl.dom.firstChild;
2908 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2909 textboxEl.enableDisplayMode();
2910 textboxEl.addKeyListener([10,13], function(){
2911 if(dlg.isVisible() && opt && opt.buttons){
2914 }else if(opt.buttons.yes){
2915 handleButton("yes");
2919 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2920 textareaEl.enableDisplayMode();
2921 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2922 progressEl.enableDisplayMode();
2923 var pf = progressEl.dom.firstChild;
2925 pp = Roo.get(pf.firstChild);
2926 pp.setHeight(pf.offsetHeight);
2934 * Updates the message box body text
2935 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2936 * the XHTML-compliant non-breaking space character '&#160;')
2937 * @return {Roo.MessageBox} This message box
2939 updateText : function(text){
2940 if(!dlg.isVisible() && !opt.width){
2941 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2943 msgEl.innerHTML = text || ' ';
2945 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2946 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2948 Math.min(opt.width || cw , this.maxWidth),
2949 Math.max(opt.minWidth || this.minWidth, bwidth)
2952 activeTextEl.setWidth(w);
2954 if(dlg.isVisible()){
2955 dlg.fixedcenter = false;
2957 // to big, make it scroll. = But as usual stupid IE does not support
2960 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2961 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2962 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2964 bodyEl.dom.style.height = '';
2965 bodyEl.dom.style.overflowY = '';
2968 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2970 bodyEl.dom.style.overflowX = '';
2973 dlg.setContentSize(w, bodyEl.getHeight());
2974 if(dlg.isVisible()){
2975 dlg.fixedcenter = true;
2981 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
2982 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2983 * @param {Number} value Any number between 0 and 1 (e.g., .5)
2984 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2985 * @return {Roo.MessageBox} This message box
2987 updateProgress : function(value, text){
2989 this.updateText(text);
2991 if (pp) { // weird bug on my firefox - for some reason this is not defined
2992 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2998 * Returns true if the message box is currently displayed
2999 * @return {Boolean} True if the message box is visible, else false
3001 isVisible : function(){
3002 return dlg && dlg.isVisible();
3006 * Hides the message box if it is displayed
3009 if(this.isVisible()){
3015 * Displays a new message box, or reinitializes an existing message box, based on the config options
3016 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3017 * The following config object properties are supported:
3019 Property Type Description
3020 ---------- --------------- ------------------------------------------------------------------------------------
3021 animEl String/Element An id or Element from which the message box should animate as it opens and
3022 closes (defaults to undefined)
3023 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3024 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3025 closable Boolean False to hide the top-right close button (defaults to true). Note that
3026 progress and wait dialogs will ignore this property and always hide the
3027 close button as they can only be closed programmatically.
3028 cls String A custom CSS class to apply to the message box element
3029 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3030 displayed (defaults to 75)
3031 fn Function A callback function to execute after closing the dialog. The arguments to the
3032 function will be btn (the name of the button that was clicked, if applicable,
3033 e.g. "ok"), and text (the value of the active text field, if applicable).
3034 Progress and wait dialogs will ignore this option since they do not respond to
3035 user actions and can only be closed programmatically, so any required function
3036 should be called by the same code after it closes the dialog.
3037 icon String A CSS class that provides a background image to be used as an icon for
3038 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3039 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3040 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3041 modal Boolean False to allow user interaction with the page while the message box is
3042 displayed (defaults to true)
3043 msg String A string that will replace the existing message box body text (defaults
3044 to the XHTML-compliant non-breaking space character ' ')
3045 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3046 progress Boolean True to display a progress bar (defaults to false)
3047 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3048 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3049 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3050 title String The title text
3051 value String The string value to set into the active textbox element if displayed
3052 wait Boolean True to display a progress bar (defaults to false)
3053 width Number The width of the dialog in pixels
3060 msg: 'Please enter your address:',
3062 buttons: Roo.MessageBox.OKCANCEL,
3065 animEl: 'addAddressBtn'
3068 * @param {Object} config Configuration options
3069 * @return {Roo.MessageBox} This message box
3071 show : function(options)
3074 // this causes nightmares if you show one dialog after another
3075 // especially on callbacks..
3077 if(this.isVisible()){
3080 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3081 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3082 Roo.log("New Dialog Message:" + options.msg )
3083 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3084 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3087 var d = this.getDialog();
3089 d.setTitle(opt.title || " ");
3090 d.closeEl.setDisplayed(opt.closable !== false);
3091 activeTextEl = textboxEl;
3092 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3097 textareaEl.setHeight(typeof opt.multiline == "number" ?
3098 opt.multiline : this.defaultTextHeight);
3099 activeTextEl = textareaEl;
3108 progressEl.setDisplayed(opt.progress === true);
3109 this.updateProgress(0);
3110 activeTextEl.dom.value = opt.value || "";
3112 dlg.setDefaultButton(activeTextEl);
3114 var bs = opt.buttons;
3118 }else if(bs && bs.yes){
3119 db = buttons["yes"];
3121 dlg.setDefaultButton(db);
3123 bwidth = updateButtons(opt.buttons);
3124 this.updateText(opt.msg);
3126 d.el.addClass(opt.cls);
3128 d.proxyDrag = opt.proxyDrag === true;
3129 d.modal = opt.modal !== false;
3130 d.mask = opt.modal !== false ? mask : false;
3132 // force it to the end of the z-index stack so it gets a cursor in FF
3133 document.body.appendChild(dlg.el.dom);
3134 d.animateTarget = null;
3135 d.show(options.animEl);
3141 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3142 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3143 * and closing the message box when the process is complete.
3144 * @param {String} title The title bar text
3145 * @param {String} msg The message box body text
3146 * @return {Roo.MessageBox} This message box
3148 progress : function(title, msg){
3155 minWidth: this.minProgressWidth,
3162 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3163 * If a callback function is passed it will be called after the user clicks the button, and the
3164 * id of the button that was clicked will be passed as the only parameter to the callback
3165 * (could also be the top-right close button).
3166 * @param {String} title The title bar text
3167 * @param {String} msg The message box body text
3168 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3169 * @param {Object} scope (optional) The scope of the callback function
3170 * @return {Roo.MessageBox} This message box
3172 alert : function(title, msg, fn, scope){
3185 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3186 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3187 * You are responsible for closing the message box when the process is complete.
3188 * @param {String} msg The message box body text
3189 * @param {String} title (optional) The title bar text
3190 * @return {Roo.MessageBox} This message box
3192 wait : function(msg, title){
3203 waitTimer = Roo.TaskMgr.start({
3205 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3213 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3214 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3215 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3216 * @param {String} title The title bar text
3217 * @param {String} msg The message box body text
3218 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3219 * @param {Object} scope (optional) The scope of the callback function
3220 * @return {Roo.MessageBox} This message box
3222 confirm : function(title, msg, fn, scope){
3226 buttons: this.YESNO,
3235 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3236 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3237 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3238 * (could also be the top-right close button) and the text that was entered will be passed as the two
3239 * parameters to the callback.
3240 * @param {String} title The title bar text
3241 * @param {String} msg The message box body text
3242 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3243 * @param {Object} scope (optional) The scope of the callback function
3244 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3245 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3246 * @return {Roo.MessageBox} This message box
3248 prompt : function(title, msg, fn, scope, multiline){
3252 buttons: this.OKCANCEL,
3257 multiline: multiline,
3264 * Button config that displays a single OK button
3269 * Button config that displays Yes and No buttons
3272 YESNO : {yes:true, no:true},
3274 * Button config that displays OK and Cancel buttons
3277 OKCANCEL : {ok:true, cancel:true},
3279 * Button config that displays Yes, No and Cancel buttons
3282 YESNOCANCEL : {yes:true, no:true, cancel:true},
3285 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3288 defaultTextHeight : 75,
3290 * The maximum width in pixels of the message box (defaults to 600)
3295 * The minimum width in pixels of the message box (defaults to 100)
3300 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3301 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3304 minProgressWidth : 250,
3306 * An object containing the default button text strings that can be overriden for localized language support.
3307 * Supported properties are: ok, cancel, yes and no.
3308 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3321 * Shorthand for {@link Roo.MessageBox}
3323 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3324 Roo.Msg = Roo.Msg || Roo.MessageBox;
3333 * @class Roo.bootstrap.Navbar
3334 * @extends Roo.bootstrap.Component
3335 * Bootstrap Navbar class
3338 * Create a new Navbar
3339 * @param {Object} config The config object
3343 Roo.bootstrap.Navbar = function(config){
3344 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3348 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3357 getAutoCreate : function(){
3360 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3364 initEvents :function ()
3366 //Roo.log(this.el.select('.navbar-toggle',true));
3367 this.el.select('.navbar-toggle',true).on('click', function() {
3368 // Roo.log('click');
3369 this.el.select('.navbar-collapse',true).toggleClass('in');
3377 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3379 var size = this.el.getSize();
3380 this.maskEl.setSize(size.width, size.height);
3381 this.maskEl.enableDisplayMode("block");
3390 getChildContainer : function()
3392 if (this.el.select('.collapse').getCount()) {
3393 return this.el.select('.collapse',true).first();
3426 * @class Roo.bootstrap.NavSimplebar
3427 * @extends Roo.bootstrap.Navbar
3428 * Bootstrap Sidebar class
3430 * @cfg {Boolean} inverse is inverted color
3432 * @cfg {String} type (nav | pills | tabs)
3433 * @cfg {Boolean} arrangement stacked | justified
3434 * @cfg {String} align (left | right) alignment
3436 * @cfg {Boolean} main (true|false) main nav bar? default false
3437 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3439 * @cfg {String} tag (header|footer|nav|div) default is nav
3445 * Create a new Sidebar
3446 * @param {Object} config The config object
3450 Roo.bootstrap.NavSimplebar = function(config){
3451 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3454 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3470 getAutoCreate : function(){
3474 tag : this.tag || 'div',
3487 this.type = this.type || 'nav';
3488 if (['tabs','pills'].indexOf(this.type)!==-1) {
3489 cfg.cn[0].cls += ' nav-' + this.type
3493 if (this.type!=='nav') {
3494 Roo.log('nav type must be nav/tabs/pills')
3496 cfg.cn[0].cls += ' navbar-nav'
3502 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3503 cfg.cn[0].cls += ' nav-' + this.arrangement;
3507 if (this.align === 'right') {
3508 cfg.cn[0].cls += ' navbar-right';
3512 cfg.cls += ' navbar-inverse';
3539 * @class Roo.bootstrap.NavHeaderbar
3540 * @extends Roo.bootstrap.NavSimplebar
3541 * Bootstrap Sidebar class
3543 * @cfg {String} brand what is brand
3544 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3545 * @cfg {String} brand_href href of the brand
3546 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3547 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3548 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3549 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3552 * Create a new Sidebar
3553 * @param {Object} config The config object
3557 Roo.bootstrap.NavHeaderbar = function(config){
3558 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3562 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3569 desktopCenter : false,
3572 getAutoCreate : function(){
3575 tag: this.nav || 'nav',
3582 if (this.desktopCenter) {
3583 cn.push({cls : 'container', cn : []});
3590 cls: 'navbar-header',
3595 cls: 'navbar-toggle',
3596 'data-toggle': 'collapse',
3601 html: 'Toggle navigation'
3623 cls: 'collapse navbar-collapse',
3627 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3629 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3630 cfg.cls += ' navbar-' + this.position;
3632 // tag can override this..
3634 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3637 if (this.brand !== '') {
3640 href: this.brand_href ? this.brand_href : '#',
3641 cls: 'navbar-brand',
3649 cfg.cls += ' main-nav';
3657 getHeaderChildContainer : function()
3659 if (this.el.select('.navbar-header').getCount()) {
3660 return this.el.select('.navbar-header',true).first();
3663 return this.getChildContainer();
3667 initEvents : function()
3669 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3671 if (this.autohide) {
3676 Roo.get(document).on('scroll',function(e) {
3677 var ns = Roo.get(document).getScroll().top;
3678 var os = prevScroll;
3682 ft.removeClass('slideDown');
3683 ft.addClass('slideUp');
3686 ft.removeClass('slideUp');
3687 ft.addClass('slideDown');
3708 * @class Roo.bootstrap.NavSidebar
3709 * @extends Roo.bootstrap.Navbar
3710 * Bootstrap Sidebar class
3713 * Create a new Sidebar
3714 * @param {Object} config The config object
3718 Roo.bootstrap.NavSidebar = function(config){
3719 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3722 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3724 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3726 getAutoCreate : function(){
3731 cls: 'sidebar sidebar-nav'
3753 * @class Roo.bootstrap.NavGroup
3754 * @extends Roo.bootstrap.Component
3755 * Bootstrap NavGroup class
3756 * @cfg {String} align (left|right)
3757 * @cfg {Boolean} inverse
3758 * @cfg {String} type (nav|pills|tab) default nav
3759 * @cfg {String} navId - reference Id for navbar.
3763 * Create a new nav group
3764 * @param {Object} config The config object
3767 Roo.bootstrap.NavGroup = function(config){
3768 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3771 Roo.bootstrap.NavGroup.register(this);
3775 * Fires when the active item changes
3776 * @param {Roo.bootstrap.NavGroup} this
3777 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3778 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3785 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3796 getAutoCreate : function()
3798 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3805 if (['tabs','pills'].indexOf(this.type)!==-1) {
3806 cfg.cls += ' nav-' + this.type
3808 if (this.type!=='nav') {
3809 Roo.log('nav type must be nav/tabs/pills')
3811 cfg.cls += ' navbar-nav'
3814 if (this.parent().sidebar) {
3817 cls: 'dashboard-menu sidebar-menu'
3823 if (this.form === true) {
3829 if (this.align === 'right') {
3830 cfg.cls += ' navbar-right';
3832 cfg.cls += ' navbar-left';
3836 if (this.align === 'right') {
3837 cfg.cls += ' navbar-right';
3841 cfg.cls += ' navbar-inverse';
3849 * sets the active Navigation item
3850 * @param {Roo.bootstrap.NavItem} the new current navitem
3852 setActiveItem : function(item)
3855 Roo.each(this.navItems, function(v){
3860 v.setActive(false, true);
3867 item.setActive(true, true);
3868 this.fireEvent('changed', this, item, prev);
3873 * gets the active Navigation item
3874 * @return {Roo.bootstrap.NavItem} the current navitem
3876 getActive : function()
3880 Roo.each(this.navItems, function(v){
3891 indexOfNav : function()
3895 Roo.each(this.navItems, function(v,i){
3906 * adds a Navigation item
3907 * @param {Roo.bootstrap.NavItem} the navitem to add
3909 addItem : function(cfg)
3911 var cn = new Roo.bootstrap.NavItem(cfg);
3913 cn.parentId = this.id;
3914 cn.onRender(this.el, null);
3918 * register a Navigation item
3919 * @param {Roo.bootstrap.NavItem} the navitem to add
3921 register : function(item)
3923 this.navItems.push( item);
3924 item.navId = this.navId;
3929 * clear all the Navigation item
3932 clearAll : function()
3935 this.el.dom.innerHTML = '';
3938 getNavItem: function(tabId)
3941 Roo.each(this.navItems, function(e) {
3942 if (e.tabId == tabId) {
3952 setActiveNext : function()
3954 var i = this.indexOfNav(this.getActive());
3955 if (i > this.navItems.length) {
3958 this.setActiveItem(this.navItems[i+1]);
3960 setActivePrev : function()
3962 var i = this.indexOfNav(this.getActive());
3966 this.setActiveItem(this.navItems[i-1]);
3968 clearWasActive : function(except) {
3969 Roo.each(this.navItems, function(e) {
3970 if (e.tabId != except.tabId && e.was_active) {
3971 e.was_active = false;
3978 getWasActive : function ()
3981 Roo.each(this.navItems, function(e) {
3996 Roo.apply(Roo.bootstrap.NavGroup, {
4000 * register a Navigation Group
4001 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4003 register : function(navgrp)
4005 this.groups[navgrp.navId] = navgrp;
4009 * fetch a Navigation Group based on the navigation ID
4010 * @param {string} the navgroup to add
4011 * @returns {Roo.bootstrap.NavGroup} the navgroup
4013 get: function(navId) {
4014 if (typeof(this.groups[navId]) == 'undefined') {
4016 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4018 return this.groups[navId] ;
4033 * @class Roo.bootstrap.NavItem
4034 * @extends Roo.bootstrap.Component
4035 * Bootstrap Navbar.NavItem class
4036 * @cfg {String} href link to
4037 * @cfg {String} html content of button
4038 * @cfg {String} badge text inside badge
4039 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4040 * @cfg {String} glyphicon name of glyphicon
4041 * @cfg {String} icon name of font awesome icon
4042 * @cfg {Boolean} active Is item active
4043 * @cfg {Boolean} disabled Is item disabled
4045 * @cfg {Boolean} preventDefault (true | false) default false
4046 * @cfg {String} tabId the tab that this item activates.
4047 * @cfg {String} tagtype (a|span) render as a href or span?
4048 * @cfg {Boolean} animateRef (true|false) link to element default false
4051 * Create a new Navbar Item
4052 * @param {Object} config The config object
4054 Roo.bootstrap.NavItem = function(config){
4055 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4060 * The raw click event for the entire grid.
4061 * @param {Roo.EventObject} e
4066 * Fires when the active item active state changes
4067 * @param {Roo.bootstrap.NavItem} this
4068 * @param {boolean} state the new state
4074 * Fires when scroll to element
4075 * @param {Roo.bootstrap.NavItem} this
4076 * @param {Object} options
4077 * @param {Roo.EventObject} e
4085 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4093 preventDefault : false,
4100 getAutoCreate : function(){
4109 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4111 if (this.disabled) {
4112 cfg.cls += ' disabled';
4115 if (this.href || this.html || this.glyphicon || this.icon) {
4119 href : this.href || "#",
4120 html: this.html || ''
4125 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4128 if(this.glyphicon) {
4129 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4134 cfg.cn[0].html += " <span class='caret'></span>";
4138 if (this.badge !== '') {
4140 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4148 initEvents: function()
4150 if (typeof (this.menu) != 'undefined') {
4151 this.menu.parentType = this.xtype;
4152 this.menu.triggerEl = this.el;
4153 this.menu = this.addxtype(Roo.apply({}, this.menu));
4156 this.el.select('a',true).on('click', this.onClick, this);
4158 if(this.tagtype == 'span'){
4159 this.el.select('span',true).on('click', this.onClick, this);
4162 // at this point parent should be available..
4163 this.parent().register(this);
4166 onClick : function(e)
4169 this.preventDefault ||
4176 if (this.disabled) {
4180 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4181 if (tg && tg.transition) {
4182 Roo.log("waiting for the transitionend");
4188 //Roo.log("fire event clicked");
4189 if(this.fireEvent('click', this, e) === false){
4193 if(this.tagtype == 'span'){
4197 //Roo.log(this.href);
4198 var ael = this.el.select('a',true).first();
4201 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4202 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4203 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4204 return; // ignore... - it's a 'hash' to another page.
4208 this.scrollToElement(e);
4212 var p = this.parent();
4214 if (['tabs','pills'].indexOf(p.type)!==-1) {
4215 if (typeof(p.setActiveItem) !== 'undefined') {
4216 p.setActiveItem(this);
4220 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4221 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4222 // remove the collapsed menu expand...
4223 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4227 isActive: function () {
4230 setActive : function(state, fire, is_was_active)
4232 if (this.active && !state && this.navId) {
4233 this.was_active = true;
4234 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4236 nv.clearWasActive(this);
4240 this.active = state;
4243 this.el.removeClass('active');
4244 } else if (!this.el.hasClass('active')) {
4245 this.el.addClass('active');
4248 this.fireEvent('changed', this, state);
4251 // show a panel if it's registered and related..
4253 if (!this.navId || !this.tabId || !state || is_was_active) {
4257 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4261 var pan = tg.getPanelByName(this.tabId);
4265 // if we can not flip to new panel - go back to old nav highlight..
4266 if (false == tg.showPanel(pan)) {
4267 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4269 var onav = nv.getWasActive();
4271 onav.setActive(true, false, true);
4280 // this should not be here...
4281 setDisabled : function(state)
4283 this.disabled = state;
4285 this.el.removeClass('disabled');
4286 } else if (!this.el.hasClass('disabled')) {
4287 this.el.addClass('disabled');
4293 * Fetch the element to display the tooltip on.
4294 * @return {Roo.Element} defaults to this.el
4296 tooltipEl : function()
4298 return this.el.select('' + this.tagtype + '', true).first();
4301 scrollToElement : function(e)
4303 var c = document.body;
4306 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4308 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4309 c = document.documentElement;
4312 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4318 var o = target.calcOffsetsTo(c);
4325 this.fireEvent('scrollto', this, options, e);
4327 Roo.get(c).scrollTo('top', options.value, true);
4340 * <span> icon </span>
4341 * <span> text </span>
4342 * <span>badge </span>
4346 * @class Roo.bootstrap.NavSidebarItem
4347 * @extends Roo.bootstrap.NavItem
4348 * Bootstrap Navbar.NavSidebarItem class
4349 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4351 * Create a new Navbar Button
4352 * @param {Object} config The config object
4354 Roo.bootstrap.NavSidebarItem = function(config){
4355 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4360 * The raw click event for the entire grid.
4361 * @param {Roo.EventObject} e
4366 * Fires when the active item active state changes
4367 * @param {Roo.bootstrap.NavSidebarItem} this
4368 * @param {boolean} state the new state
4376 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4378 badgeWeight : 'default',
4380 getAutoCreate : function(){
4385 href : this.href || '#',
4397 html : this.html || ''
4402 cfg.cls += ' active';
4405 if (this.disabled) {
4406 cfg.cls += ' disabled';
4410 if (this.glyphicon || this.icon) {
4411 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4412 a.cn.push({ tag : 'i', cls : c }) ;
4417 if (this.badge !== '') {
4419 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4423 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4424 a.cls += 'dropdown-toggle treeview' ;
4435 initEvents : function()
4437 this.el.on('click', this.onClick, this);
4440 if(this.badge !== ''){
4442 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4447 onClick : function(e)
4454 if(this.preventDefault){
4458 this.fireEvent('click', this);
4461 disable : function()
4463 this.setDisabled(true);
4468 this.setDisabled(false);
4471 setDisabled : function(state)
4473 if(this.disabled == state){
4477 this.disabled = state;
4480 this.el.addClass('disabled');
4484 this.el.removeClass('disabled');
4489 setActive : function(state)
4491 if(this.active == state){
4495 this.active = state;
4498 this.el.addClass('active');
4502 this.el.removeClass('active');
4507 isActive: function ()
4512 setBadge : function(str)
4518 this.badgeEl.dom.innerHTML = str;
4535 * @class Roo.bootstrap.Row
4536 * @extends Roo.bootstrap.Component
4537 * Bootstrap Row class (contains columns...)
4541 * @param {Object} config The config object
4544 Roo.bootstrap.Row = function(config){
4545 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4548 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4550 getAutoCreate : function(){
4569 * @class Roo.bootstrap.Element
4570 * @extends Roo.bootstrap.Component
4571 * Bootstrap Element class
4572 * @cfg {String} html contents of the element
4573 * @cfg {String} tag tag of the element
4574 * @cfg {String} cls class of the element
4575 * @cfg {Boolean} preventDefault (true|false) default false
4576 * @cfg {Boolean} clickable (true|false) default false
4579 * Create a new Element
4580 * @param {Object} config The config object
4583 Roo.bootstrap.Element = function(config){
4584 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4590 * When a element is chick
4591 * @param {Roo.bootstrap.Element} this
4592 * @param {Roo.EventObject} e
4598 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4603 preventDefault: false,
4606 getAutoCreate : function(){
4617 initEvents: function()
4619 Roo.bootstrap.Element.superclass.initEvents.call(this);
4622 this.el.on('click', this.onClick, this);
4627 onClick : function(e)
4629 if(this.preventDefault){
4633 this.fireEvent('click', this, e);
4636 getValue : function()
4638 return this.el.dom.innerHTML;
4641 setValue : function(value)
4643 this.el.dom.innerHTML = value;
4658 * @class Roo.bootstrap.Pagination
4659 * @extends Roo.bootstrap.Component
4660 * Bootstrap Pagination class
4661 * @cfg {String} size xs | sm | md | lg
4662 * @cfg {Boolean} inverse false | true
4665 * Create a new Pagination
4666 * @param {Object} config The config object
4669 Roo.bootstrap.Pagination = function(config){
4670 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4673 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4679 getAutoCreate : function(){
4685 cfg.cls += ' inverse';
4691 cfg.cls += " " + this.cls;
4709 * @class Roo.bootstrap.PaginationItem
4710 * @extends Roo.bootstrap.Component
4711 * Bootstrap PaginationItem class
4712 * @cfg {String} html text
4713 * @cfg {String} href the link
4714 * @cfg {Boolean} preventDefault (true | false) default true
4715 * @cfg {Boolean} active (true | false) default false
4716 * @cfg {Boolean} disabled default false
4720 * Create a new PaginationItem
4721 * @param {Object} config The config object
4725 Roo.bootstrap.PaginationItem = function(config){
4726 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4731 * The raw click event for the entire grid.
4732 * @param {Roo.EventObject} e
4738 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4742 preventDefault: true,
4747 getAutoCreate : function(){
4753 href : this.href ? this.href : '#',
4754 html : this.html ? this.html : ''
4764 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4768 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4774 initEvents: function() {
4776 this.el.on('click', this.onClick, this);
4779 onClick : function(e)
4781 Roo.log('PaginationItem on click ');
4782 if(this.preventDefault){
4790 this.fireEvent('click', this, e);
4806 * @class Roo.bootstrap.Slider
4807 * @extends Roo.bootstrap.Component
4808 * Bootstrap Slider class
4811 * Create a new Slider
4812 * @param {Object} config The config object
4815 Roo.bootstrap.Slider = function(config){
4816 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4819 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4821 getAutoCreate : function(){
4825 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4829 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4841 * Ext JS Library 1.1.1
4842 * Copyright(c) 2006-2007, Ext JS, LLC.
4844 * Originally Released Under LGPL - original licence link has changed is not relivant.
4847 * <script type="text/javascript">
4852 * @class Roo.grid.ColumnModel
4853 * @extends Roo.util.Observable
4854 * This is the default implementation of a ColumnModel used by the Grid. It defines
4855 * the columns in the grid.
4858 var colModel = new Roo.grid.ColumnModel([
4859 {header: "Ticker", width: 60, sortable: true, locked: true},
4860 {header: "Company Name", width: 150, sortable: true},
4861 {header: "Market Cap.", width: 100, sortable: true},
4862 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4863 {header: "Employees", width: 100, sortable: true, resizable: false}
4868 * The config options listed for this class are options which may appear in each
4869 * individual column definition.
4870 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4872 * @param {Object} config An Array of column config objects. See this class's
4873 * config objects for details.
4875 Roo.grid.ColumnModel = function(config){
4877 * The config passed into the constructor
4879 this.config = config;
4882 // if no id, create one
4883 // if the column does not have a dataIndex mapping,
4884 // map it to the order it is in the config
4885 for(var i = 0, len = config.length; i < len; i++){
4887 if(typeof c.dataIndex == "undefined"){
4890 if(typeof c.renderer == "string"){
4891 c.renderer = Roo.util.Format[c.renderer];
4893 if(typeof c.id == "undefined"){
4896 if(c.editor && c.editor.xtype){
4897 c.editor = Roo.factory(c.editor, Roo.grid);
4899 if(c.editor && c.editor.isFormField){
4900 c.editor = new Roo.grid.GridEditor(c.editor);
4902 this.lookup[c.id] = c;
4906 * The width of columns which have no width specified (defaults to 100)
4909 this.defaultWidth = 100;
4912 * Default sortable of columns which have no sortable specified (defaults to false)
4915 this.defaultSortable = false;
4919 * @event widthchange
4920 * Fires when the width of a column changes.
4921 * @param {ColumnModel} this
4922 * @param {Number} columnIndex The column index
4923 * @param {Number} newWidth The new width
4925 "widthchange": true,
4927 * @event headerchange
4928 * Fires when the text of a header changes.
4929 * @param {ColumnModel} this
4930 * @param {Number} columnIndex The column index
4931 * @param {Number} newText The new header text
4933 "headerchange": true,
4935 * @event hiddenchange
4936 * Fires when a column is hidden or "unhidden".
4937 * @param {ColumnModel} this
4938 * @param {Number} columnIndex The column index
4939 * @param {Boolean} hidden true if hidden, false otherwise
4941 "hiddenchange": true,
4943 * @event columnmoved
4944 * Fires when a column is moved.
4945 * @param {ColumnModel} this
4946 * @param {Number} oldIndex
4947 * @param {Number} newIndex
4949 "columnmoved" : true,
4951 * @event columlockchange
4952 * Fires when a column's locked state is changed
4953 * @param {ColumnModel} this
4954 * @param {Number} colIndex
4955 * @param {Boolean} locked true if locked
4957 "columnlockchange" : true
4959 Roo.grid.ColumnModel.superclass.constructor.call(this);
4961 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4963 * @cfg {String} header The header text to display in the Grid view.
4966 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4967 * {@link Roo.data.Record} definition from which to draw the column's value. If not
4968 * specified, the column's index is used as an index into the Record's data Array.
4971 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4972 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4975 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4976 * Defaults to the value of the {@link #defaultSortable} property.
4977 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4980 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
4983 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
4986 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4989 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4992 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4993 * given the cell's data value. See {@link #setRenderer}. If not specified, the
4994 * default renderer uses the raw data value. If an object is returned (bootstrap only)
4995 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4998 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5001 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5004 * @cfg {String} cursor (Optional)
5007 * @cfg {String} tooltip (Optional)
5010 * @cfg {Number} xs (Optional)
5013 * @cfg {Number} sm (Optional)
5016 * @cfg {Number} md (Optional)
5019 * @cfg {Number} lg (Optional)
5022 * Returns the id of the column at the specified index.
5023 * @param {Number} index The column index
5024 * @return {String} the id
5026 getColumnId : function(index){
5027 return this.config[index].id;
5031 * Returns the column for a specified id.
5032 * @param {String} id The column id
5033 * @return {Object} the column
5035 getColumnById : function(id){
5036 return this.lookup[id];
5041 * Returns the column for a specified dataIndex.
5042 * @param {String} dataIndex The column dataIndex
5043 * @return {Object|Boolean} the column or false if not found
5045 getColumnByDataIndex: function(dataIndex){
5046 var index = this.findColumnIndex(dataIndex);
5047 return index > -1 ? this.config[index] : false;
5051 * Returns the index for a specified column id.
5052 * @param {String} id The column id
5053 * @return {Number} the index, or -1 if not found
5055 getIndexById : function(id){
5056 for(var i = 0, len = this.config.length; i < len; i++){
5057 if(this.config[i].id == id){
5065 * Returns the index for a specified column dataIndex.
5066 * @param {String} dataIndex The column dataIndex
5067 * @return {Number} the index, or -1 if not found
5070 findColumnIndex : function(dataIndex){
5071 for(var i = 0, len = this.config.length; i < len; i++){
5072 if(this.config[i].dataIndex == dataIndex){
5080 moveColumn : function(oldIndex, newIndex){
5081 var c = this.config[oldIndex];
5082 this.config.splice(oldIndex, 1);
5083 this.config.splice(newIndex, 0, c);
5084 this.dataMap = null;
5085 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5088 isLocked : function(colIndex){
5089 return this.config[colIndex].locked === true;
5092 setLocked : function(colIndex, value, suppressEvent){
5093 if(this.isLocked(colIndex) == value){
5096 this.config[colIndex].locked = value;
5098 this.fireEvent("columnlockchange", this, colIndex, value);
5102 getTotalLockedWidth : function(){
5104 for(var i = 0; i < this.config.length; i++){
5105 if(this.isLocked(i) && !this.isHidden(i)){
5106 this.totalWidth += this.getColumnWidth(i);
5112 getLockedCount : function(){
5113 for(var i = 0, len = this.config.length; i < len; i++){
5114 if(!this.isLocked(i)){
5119 return this.config.length;
5123 * Returns the number of columns.
5126 getColumnCount : function(visibleOnly){
5127 if(visibleOnly === true){
5129 for(var i = 0, len = this.config.length; i < len; i++){
5130 if(!this.isHidden(i)){
5136 return this.config.length;
5140 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5141 * @param {Function} fn
5142 * @param {Object} scope (optional)
5143 * @return {Array} result
5145 getColumnsBy : function(fn, scope){
5147 for(var i = 0, len = this.config.length; i < len; i++){
5148 var c = this.config[i];
5149 if(fn.call(scope||this, c, i) === true){
5157 * Returns true if the specified column is sortable.
5158 * @param {Number} col The column index
5161 isSortable : function(col){
5162 if(typeof this.config[col].sortable == "undefined"){
5163 return this.defaultSortable;
5165 return this.config[col].sortable;
5169 * Returns the rendering (formatting) function defined for the column.
5170 * @param {Number} col The column index.
5171 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5173 getRenderer : function(col){
5174 if(!this.config[col].renderer){
5175 return Roo.grid.ColumnModel.defaultRenderer;
5177 return this.config[col].renderer;
5181 * Sets the rendering (formatting) function for a column.
5182 * @param {Number} col The column index
5183 * @param {Function} fn The function to use to process the cell's raw data
5184 * to return HTML markup for the grid view. The render function is called with
5185 * the following parameters:<ul>
5186 * <li>Data value.</li>
5187 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5188 * <li>css A CSS style string to apply to the table cell.</li>
5189 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5190 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5191 * <li>Row index</li>
5192 * <li>Column index</li>
5193 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5195 setRenderer : function(col, fn){
5196 this.config[col].renderer = fn;
5200 * Returns the width for the specified column.
5201 * @param {Number} col The column index
5204 getColumnWidth : function(col){
5205 return this.config[col].width * 1 || this.defaultWidth;
5209 * Sets the width for a column.
5210 * @param {Number} col The column index
5211 * @param {Number} width The new width
5213 setColumnWidth : function(col, width, suppressEvent){
5214 this.config[col].width = width;
5215 this.totalWidth = null;
5217 this.fireEvent("widthchange", this, col, width);
5222 * Returns the total width of all columns.
5223 * @param {Boolean} includeHidden True to include hidden column widths
5226 getTotalWidth : function(includeHidden){
5227 if(!this.totalWidth){
5228 this.totalWidth = 0;
5229 for(var i = 0, len = this.config.length; i < len; i++){
5230 if(includeHidden || !this.isHidden(i)){
5231 this.totalWidth += this.getColumnWidth(i);
5235 return this.totalWidth;
5239 * Returns the header for the specified column.
5240 * @param {Number} col The column index
5243 getColumnHeader : function(col){
5244 return this.config[col].header;
5248 * Sets the header for a column.
5249 * @param {Number} col The column index
5250 * @param {String} header The new header
5252 setColumnHeader : function(col, header){
5253 this.config[col].header = header;
5254 this.fireEvent("headerchange", this, col, header);
5258 * Returns the tooltip for the specified column.
5259 * @param {Number} col The column index
5262 getColumnTooltip : function(col){
5263 return this.config[col].tooltip;
5266 * Sets the tooltip for a column.
5267 * @param {Number} col The column index
5268 * @param {String} tooltip The new tooltip
5270 setColumnTooltip : function(col, tooltip){
5271 this.config[col].tooltip = tooltip;
5275 * Returns the dataIndex for the specified column.
5276 * @param {Number} col The column index
5279 getDataIndex : function(col){
5280 return this.config[col].dataIndex;
5284 * Sets the dataIndex for a column.
5285 * @param {Number} col The column index
5286 * @param {Number} dataIndex The new dataIndex
5288 setDataIndex : function(col, dataIndex){
5289 this.config[col].dataIndex = dataIndex;
5295 * Returns true if the cell is editable.
5296 * @param {Number} colIndex The column index
5297 * @param {Number} rowIndex The row index - this is nto actually used..?
5300 isCellEditable : function(colIndex, rowIndex){
5301 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5305 * Returns the editor defined for the cell/column.
5306 * return false or null to disable editing.
5307 * @param {Number} colIndex The column index
5308 * @param {Number} rowIndex The row index
5311 getCellEditor : function(colIndex, rowIndex){
5312 return this.config[colIndex].editor;
5316 * Sets if a column is editable.
5317 * @param {Number} col The column index
5318 * @param {Boolean} editable True if the column is editable
5320 setEditable : function(col, editable){
5321 this.config[col].editable = editable;
5326 * Returns true if the column is hidden.
5327 * @param {Number} colIndex The column index
5330 isHidden : function(colIndex){
5331 return this.config[colIndex].hidden;
5336 * Returns true if the column width cannot be changed
5338 isFixed : function(colIndex){
5339 return this.config[colIndex].fixed;
5343 * Returns true if the column can be resized
5346 isResizable : function(colIndex){
5347 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5350 * Sets if a column is hidden.
5351 * @param {Number} colIndex The column index
5352 * @param {Boolean} hidden True if the column is hidden
5354 setHidden : function(colIndex, hidden){
5355 this.config[colIndex].hidden = hidden;
5356 this.totalWidth = null;
5357 this.fireEvent("hiddenchange", this, colIndex, hidden);
5361 * Sets the editor for a column.
5362 * @param {Number} col The column index
5363 * @param {Object} editor The editor object
5365 setEditor : function(col, editor){
5366 this.config[col].editor = editor;
5370 Roo.grid.ColumnModel.defaultRenderer = function(value){
5371 if(typeof value == "string" && value.length < 1){
5377 // Alias for backwards compatibility
5378 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5381 * Ext JS Library 1.1.1
5382 * Copyright(c) 2006-2007, Ext JS, LLC.
5384 * Originally Released Under LGPL - original licence link has changed is not relivant.
5387 * <script type="text/javascript">
5391 * @class Roo.LoadMask
5392 * A simple utility class for generically masking elements while loading data. If the element being masked has
5393 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5394 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5395 * element's UpdateManager load indicator and will be destroyed after the initial load.
5397 * Create a new LoadMask
5398 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5399 * @param {Object} config The config object
5401 Roo.LoadMask = function(el, config){
5402 this.el = Roo.get(el);
5403 Roo.apply(this, config);
5405 this.store.on('beforeload', this.onBeforeLoad, this);
5406 this.store.on('load', this.onLoad, this);
5407 this.store.on('loadexception', this.onLoadException, this);
5408 this.removeMask = false;
5410 var um = this.el.getUpdateManager();
5411 um.showLoadIndicator = false; // disable the default indicator
5412 um.on('beforeupdate', this.onBeforeLoad, this);
5413 um.on('update', this.onLoad, this);
5414 um.on('failure', this.onLoad, this);
5415 this.removeMask = true;
5419 Roo.LoadMask.prototype = {
5421 * @cfg {Boolean} removeMask
5422 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5423 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5427 * The text to display in a centered loading message box (defaults to 'Loading...')
5431 * @cfg {String} msgCls
5432 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5434 msgCls : 'x-mask-loading',
5437 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5443 * Disables the mask to prevent it from being displayed
5445 disable : function(){
5446 this.disabled = true;
5450 * Enables the mask so that it can be displayed
5452 enable : function(){
5453 this.disabled = false;
5456 onLoadException : function()
5460 if (typeof(arguments[3]) != 'undefined') {
5461 Roo.MessageBox.alert("Error loading",arguments[3]);
5465 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5466 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5475 this.el.unmask(this.removeMask);
5480 this.el.unmask(this.removeMask);
5484 onBeforeLoad : function(){
5486 this.el.mask(this.msg, this.msgCls);
5491 destroy : function(){
5493 this.store.un('beforeload', this.onBeforeLoad, this);
5494 this.store.un('load', this.onLoad, this);
5495 this.store.un('loadexception', this.onLoadException, this);
5497 var um = this.el.getUpdateManager();
5498 um.un('beforeupdate', this.onBeforeLoad, this);
5499 um.un('update', this.onLoad, this);
5500 um.un('failure', this.onLoad, this);
5511 * @class Roo.bootstrap.Table
5512 * @extends Roo.bootstrap.Component
5513 * Bootstrap Table class
5514 * @cfg {String} cls table class
5515 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5516 * @cfg {String} bgcolor Specifies the background color for a table
5517 * @cfg {Number} border Specifies whether the table cells should have borders or not
5518 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5519 * @cfg {Number} cellspacing Specifies the space between cells
5520 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5521 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5522 * @cfg {String} sortable Specifies that the table should be sortable
5523 * @cfg {String} summary Specifies a summary of the content of a table
5524 * @cfg {Number} width Specifies the width of a table
5525 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5527 * @cfg {boolean} striped Should the rows be alternative striped
5528 * @cfg {boolean} bordered Add borders to the table
5529 * @cfg {boolean} hover Add hover highlighting
5530 * @cfg {boolean} condensed Format condensed
5531 * @cfg {boolean} responsive Format condensed
5532 * @cfg {Boolean} loadMask (true|false) default false
5533 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5534 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5535 * @cfg {Boolean} rowSelection (true|false) default false
5536 * @cfg {Boolean} cellSelection (true|false) default false
5537 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5541 * Create a new Table
5542 * @param {Object} config The config object
5545 Roo.bootstrap.Table = function(config){
5546 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5549 this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5550 this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5551 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5552 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5556 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5557 this.sm = this.selModel;
5558 this.sm.xmodule = this.xmodule || false;
5560 if (this.cm && typeof(this.cm.config) == 'undefined') {
5561 this.colModel = new Roo.grid.ColumnModel(this.cm);
5562 this.cm = this.colModel;
5563 this.cm.xmodule = this.xmodule || false;
5566 this.store= Roo.factory(this.store, Roo.data);
5567 this.ds = this.store;
5568 this.ds.xmodule = this.xmodule || false;
5571 if (this.footer && this.store) {
5572 this.footer.dataSource = this.ds;
5573 this.footer = Roo.factory(this.footer);
5580 * Fires when a cell is clicked
5581 * @param {Roo.bootstrap.Table} this
5582 * @param {Roo.Element} el
5583 * @param {Number} rowIndex
5584 * @param {Number} columnIndex
5585 * @param {Roo.EventObject} e
5589 * @event celldblclick
5590 * Fires when a cell is double clicked
5591 * @param {Roo.bootstrap.Table} this
5592 * @param {Roo.Element} el
5593 * @param {Number} rowIndex
5594 * @param {Number} columnIndex
5595 * @param {Roo.EventObject} e
5597 "celldblclick" : true,
5600 * Fires when a row is clicked
5601 * @param {Roo.bootstrap.Table} this
5602 * @param {Roo.Element} el
5603 * @param {Number} rowIndex
5604 * @param {Roo.EventObject} e
5608 * @event rowdblclick
5609 * Fires when a row is double clicked
5610 * @param {Roo.bootstrap.Table} this
5611 * @param {Roo.Element} el
5612 * @param {Number} rowIndex
5613 * @param {Roo.EventObject} e
5615 "rowdblclick" : true,
5618 * Fires when a mouseover occur
5619 * @param {Roo.bootstrap.Table} this
5620 * @param {Roo.Element} el
5621 * @param {Number} rowIndex
5622 * @param {Number} columnIndex
5623 * @param {Roo.EventObject} e
5628 * Fires when a mouseout occur
5629 * @param {Roo.bootstrap.Table} this
5630 * @param {Roo.Element} el
5631 * @param {Number} rowIndex
5632 * @param {Number} columnIndex
5633 * @param {Roo.EventObject} e
5638 * Fires when a row is rendered, so you can change add a style to it.
5639 * @param {Roo.bootstrap.Table} this
5640 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5644 * @event rowsrendered
5645 * Fires when all the rows have been rendered
5646 * @param {Roo.bootstrap.Table} this
5648 'rowsrendered' : true
5653 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5678 rowSelection : false,
5679 cellSelection : false,
5682 // Roo.Element - the tbody
5685 getAutoCreate : function(){
5686 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5695 cfg.cls += ' table-striped';
5699 cfg.cls += ' table-hover';
5701 if (this.bordered) {
5702 cfg.cls += ' table-bordered';
5704 if (this.condensed) {
5705 cfg.cls += ' table-condensed';
5707 if (this.responsive) {
5708 cfg.cls += ' table-responsive';
5712 cfg.cls+= ' ' +this.cls;
5715 // this lot should be simplifed...
5718 cfg.align=this.align;
5721 cfg.bgcolor=this.bgcolor;
5724 cfg.border=this.border;
5726 if (this.cellpadding) {
5727 cfg.cellpadding=this.cellpadding;
5729 if (this.cellspacing) {
5730 cfg.cellspacing=this.cellspacing;
5733 cfg.frame=this.frame;
5736 cfg.rules=this.rules;
5738 if (this.sortable) {
5739 cfg.sortable=this.sortable;
5742 cfg.summary=this.summary;
5745 cfg.width=this.width;
5748 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5751 if(this.store || this.cm){
5752 if(this.headerShow){
5753 cfg.cn.push(this.renderHeader());
5756 cfg.cn.push(this.renderBody());
5758 if(this.footerShow){
5759 cfg.cn.push(this.renderFooter());
5762 cfg.cls+= ' TableGrid';
5765 return { cn : [ cfg ] };
5768 initEvents : function()
5770 if(!this.store || !this.cm){
5774 //Roo.log('initEvents with ds!!!!');
5776 this.mainBody = this.el.select('tbody', true).first();
5781 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5782 e.on('click', _this.sort, _this);
5785 this.el.on("click", this.onClick, this);
5786 this.el.on("dblclick", this.onDblClick, this);
5788 // why is this done????? = it breaks dialogs??
5789 //this.parent().el.setStyle('position', 'relative');
5793 this.footer.parentId = this.id;
5794 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5797 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5799 this.store.on('load', this.onLoad, this);
5800 this.store.on('beforeload', this.onBeforeLoad, this);
5801 this.store.on('update', this.onUpdate, this);
5802 this.store.on('add', this.onAdd, this);
5806 onMouseover : function(e, el)
5808 var cell = Roo.get(el);
5814 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5815 cell = cell.findParent('td', false, true);
5818 var row = cell.findParent('tr', false, true);
5819 var cellIndex = cell.dom.cellIndex;
5820 var rowIndex = row.dom.rowIndex - 1; // start from 0
5822 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5826 onMouseout : function(e, el)
5828 var cell = Roo.get(el);
5834 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5835 cell = cell.findParent('td', false, true);
5838 var row = cell.findParent('tr', false, true);
5839 var cellIndex = cell.dom.cellIndex;
5840 var rowIndex = row.dom.rowIndex - 1; // start from 0
5842 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5846 onClick : function(e, el)
5848 var cell = Roo.get(el);
5850 if(!cell || (!this.cellSelection && !this.rowSelection)){
5854 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5855 cell = cell.findParent('td', false, true);
5858 if(!cell || typeof(cell) == 'undefined'){
5862 var row = cell.findParent('tr', false, true);
5864 if(!row || typeof(row) == 'undefined'){
5868 var cellIndex = cell.dom.cellIndex;
5869 var rowIndex = this.getRowIndex(row);
5871 // why??? - should these not be based on SelectionModel?
5872 if(this.cellSelection){
5873 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5876 if(this.rowSelection){
5877 this.fireEvent('rowclick', this, row, rowIndex, e);
5883 onDblClick : function(e,el)
5885 var cell = Roo.get(el);
5887 if(!cell || (!this.CellSelection && !this.RowSelection)){
5891 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5892 cell = cell.findParent('td', false, true);
5895 if(!cell || typeof(cell) == 'undefined'){
5899 var row = cell.findParent('tr', false, true);
5901 if(!row || typeof(row) == 'undefined'){
5905 var cellIndex = cell.dom.cellIndex;
5906 var rowIndex = this.getRowIndex(row);
5908 if(this.CellSelection){
5909 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5912 if(this.RowSelection){
5913 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5917 sort : function(e,el)
5919 var col = Roo.get(el);
5921 if(!col.hasClass('sortable')){
5925 var sort = col.attr('sort');
5928 if(col.hasClass('glyphicon-arrow-up')){
5932 this.store.sortInfo = {field : sort, direction : dir};
5935 Roo.log("calling footer first");
5936 this.footer.onClick('first');
5939 this.store.load({ params : { start : 0 } });
5943 renderHeader : function()
5952 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5954 var config = cm.config[i];
5959 html: cm.getColumnHeader(i)
5964 if(typeof(config.lgHeader) != 'undefined'){
5965 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5968 if(typeof(config.mdHeader) != 'undefined'){
5969 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5972 if(typeof(config.smHeader) != 'undefined'){
5973 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5976 if(typeof(config.xsHeader) != 'undefined'){
5977 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5984 if(typeof(config.tooltip) != 'undefined'){
5985 c.tooltip = config.tooltip;
5988 if(typeof(config.colspan) != 'undefined'){
5989 c.colspan = config.colspan;
5992 if(typeof(config.hidden) != 'undefined' && config.hidden){
5993 c.style += ' display:none;';
5996 if(typeof(config.dataIndex) != 'undefined'){
5997 c.sort = config.dataIndex;
6000 if(typeof(config.sortable) != 'undefined' && config.sortable){
6004 if(typeof(config.align) != 'undefined' && config.align.length){
6005 c.style += ' text-align:' + config.align + ';';
6008 if(typeof(config.width) != 'undefined'){
6009 c.style += ' width:' + config.width + 'px;';
6012 if(typeof(config.cls) != 'undefined'){
6013 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6016 ['xs','sm','md','lg'].map(function(size){
6018 if(typeof(config[size]) == 'undefined'){
6022 if (!config[size]) { // 0 = hidden
6023 c.cls += ' hidden-' + size;
6027 c.cls += ' col-' + size + '-' + config[size];
6037 renderBody : function()
6047 colspan : this.cm.getColumnCount()
6057 renderFooter : function()
6067 colspan : this.cm.getColumnCount()
6081 // Roo.log('ds onload');
6086 var ds = this.store;
6088 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6089 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6091 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6092 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6095 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6096 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6100 var tbody = this.mainBody;
6102 if(ds.getCount() > 0){
6103 ds.data.each(function(d,rowIndex){
6104 var row = this.renderRow(cm, ds, rowIndex);
6106 tbody.createChild(row);
6110 if(row.cellObjects.length){
6111 Roo.each(row.cellObjects, function(r){
6112 _this.renderCellObject(r);
6119 Roo.each(this.el.select('tbody td', true).elements, function(e){
6120 e.on('mouseover', _this.onMouseover, _this);
6123 Roo.each(this.el.select('tbody td', true).elements, function(e){
6124 e.on('mouseout', _this.onMouseout, _this);
6126 this.fireEvent('rowsrendered', this);
6127 //if(this.loadMask){
6128 // this.maskEl.hide();
6133 onUpdate : function(ds,record)
6135 this.refreshRow(record);
6138 onRemove : function(ds, record, index, isUpdate){
6139 if(isUpdate !== true){
6140 this.fireEvent("beforerowremoved", this, index, record);
6142 var bt = this.mainBody.dom;
6144 var rows = this.el.select('tbody > tr', true).elements;
6146 if(typeof(rows[index]) != 'undefined'){
6147 bt.removeChild(rows[index].dom);
6150 // if(bt.rows[index]){
6151 // bt.removeChild(bt.rows[index]);
6154 if(isUpdate !== true){
6155 //this.stripeRows(index);
6156 //this.syncRowHeights(index, index);
6158 this.fireEvent("rowremoved", this, index, record);
6162 onAdd : function(ds, records, rowIndex)
6164 //Roo.log('on Add called');
6165 // - note this does not handle multiple adding very well..
6166 var bt = this.mainBody.dom;
6167 for (var i =0 ; i < records.length;i++) {
6168 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6169 //Roo.log(records[i]);
6170 //Roo.log(this.store.getAt(rowIndex+i));
6171 this.insertRow(this.store, rowIndex + i, false);
6178 refreshRow : function(record){
6179 var ds = this.store, index;
6180 if(typeof record == 'number'){
6182 record = ds.getAt(index);
6184 index = ds.indexOf(record);
6186 this.insertRow(ds, index, true);
6187 this.onRemove(ds, record, index+1, true);
6188 //this.syncRowHeights(index, index);
6190 this.fireEvent("rowupdated", this, index, record);
6193 insertRow : function(dm, rowIndex, isUpdate){
6196 this.fireEvent("beforerowsinserted", this, rowIndex);
6198 //var s = this.getScrollState();
6199 var row = this.renderRow(this.cm, this.store, rowIndex);
6200 // insert before rowIndex..
6201 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6205 if(row.cellObjects.length){
6206 Roo.each(row.cellObjects, function(r){
6207 _this.renderCellObject(r);
6212 this.fireEvent("rowsinserted", this, rowIndex);
6213 //this.syncRowHeights(firstRow, lastRow);
6214 //this.stripeRows(firstRow);
6221 getRowDom : function(rowIndex)
6223 var rows = this.el.select('tbody > tr', true).elements;
6225 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6228 // returns the object tree for a tr..
6231 renderRow : function(cm, ds, rowIndex)
6234 var d = ds.getAt(rowIndex);
6241 var cellObjects = [];
6243 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6244 var config = cm.config[i];
6246 var renderer = cm.getRenderer(i);
6250 if(typeof(renderer) !== 'undefined'){
6251 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6253 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6254 // and are rendered into the cells after the row is rendered - using the id for the element.
6256 if(typeof(value) === 'object'){
6266 rowIndex : rowIndex,
6271 this.fireEvent('rowclass', this, rowcfg);
6275 cls : rowcfg.rowClass,
6277 html: (typeof(value) === 'object') ? '' : value
6284 if(typeof(config.colspan) != 'undefined'){
6285 td.colspan = config.colspan;
6288 if(typeof(config.hidden) != 'undefined' && config.hidden){
6289 td.style += ' display:none;';
6292 if(typeof(config.align) != 'undefined' && config.align.length){
6293 td.style += ' text-align:' + config.align + ';';
6296 if(typeof(config.width) != 'undefined'){
6297 td.style += ' width:' + config.width + 'px;';
6300 if(typeof(config.cursor) != 'undefined'){
6301 td.style += ' cursor:' + config.cursor + ';';
6304 if(typeof(config.cls) != 'undefined'){
6305 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6308 ['xs','sm','md','lg'].map(function(size){
6310 if(typeof(config[size]) == 'undefined'){
6314 if (!config[size]) { // 0 = hidden
6315 td.cls += ' hidden-' + size;
6319 td.cls += ' col-' + size + '-' + config[size];
6327 row.cellObjects = cellObjects;
6335 onBeforeLoad : function()
6337 //Roo.log('ds onBeforeLoad');
6341 //if(this.loadMask){
6342 // this.maskEl.show();
6350 this.el.select('tbody', true).first().dom.innerHTML = '';
6353 * Show or hide a row.
6354 * @param {Number} rowIndex to show or hide
6355 * @param {Boolean} state hide
6357 setRowVisibility : function(rowIndex, state)
6359 var bt = this.mainBody.dom;
6361 var rows = this.el.select('tbody > tr', true).elements;
6363 if(typeof(rows[rowIndex]) == 'undefined'){
6366 rows[rowIndex].dom.style.display = state ? '' : 'none';
6370 getSelectionModel : function(){
6372 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6374 return this.selModel;
6377 * Render the Roo.bootstrap object from renderder
6379 renderCellObject : function(r)
6383 var t = r.cfg.render(r.container);
6386 Roo.each(r.cfg.cn, function(c){
6388 container: t.getChildContainer(),
6391 _this.renderCellObject(child);
6396 getRowIndex : function(row)
6400 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6423 * @class Roo.bootstrap.TableCell
6424 * @extends Roo.bootstrap.Component
6425 * Bootstrap TableCell class
6426 * @cfg {String} html cell contain text
6427 * @cfg {String} cls cell class
6428 * @cfg {String} tag cell tag (td|th) default td
6429 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6430 * @cfg {String} align Aligns the content in a cell
6431 * @cfg {String} axis Categorizes cells
6432 * @cfg {String} bgcolor Specifies the background color of a cell
6433 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6434 * @cfg {Number} colspan Specifies the number of columns a cell should span
6435 * @cfg {String} headers Specifies one or more header cells a cell is related to
6436 * @cfg {Number} height Sets the height of a cell
6437 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6438 * @cfg {Number} rowspan Sets the number of rows a cell should span
6439 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6440 * @cfg {String} valign Vertical aligns the content in a cell
6441 * @cfg {Number} width Specifies the width of a cell
6444 * Create a new TableCell
6445 * @param {Object} config The config object
6448 Roo.bootstrap.TableCell = function(config){
6449 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6452 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6472 getAutoCreate : function(){
6473 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6493 cfg.align=this.align
6499 cfg.bgcolor=this.bgcolor
6502 cfg.charoff=this.charoff
6505 cfg.colspan=this.colspan
6508 cfg.headers=this.headers
6511 cfg.height=this.height
6514 cfg.nowrap=this.nowrap
6517 cfg.rowspan=this.rowspan
6520 cfg.scope=this.scope
6523 cfg.valign=this.valign
6526 cfg.width=this.width
6545 * @class Roo.bootstrap.TableRow
6546 * @extends Roo.bootstrap.Component
6547 * Bootstrap TableRow class
6548 * @cfg {String} cls row class
6549 * @cfg {String} align Aligns the content in a table row
6550 * @cfg {String} bgcolor Specifies a background color for a table row
6551 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6552 * @cfg {String} valign Vertical aligns the content in a table row
6555 * Create a new TableRow
6556 * @param {Object} config The config object
6559 Roo.bootstrap.TableRow = function(config){
6560 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6563 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6571 getAutoCreate : function(){
6572 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6582 cfg.align = this.align;
6585 cfg.bgcolor = this.bgcolor;
6588 cfg.charoff = this.charoff;
6591 cfg.valign = this.valign;
6609 * @class Roo.bootstrap.TableBody
6610 * @extends Roo.bootstrap.Component
6611 * Bootstrap TableBody class
6612 * @cfg {String} cls element class
6613 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6614 * @cfg {String} align Aligns the content inside the element
6615 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6616 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6619 * Create a new TableBody
6620 * @param {Object} config The config object
6623 Roo.bootstrap.TableBody = function(config){
6624 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6627 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6635 getAutoCreate : function(){
6636 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6650 cfg.align = this.align;
6653 cfg.charoff = this.charoff;
6656 cfg.valign = this.valign;
6663 // initEvents : function()
6670 // this.store = Roo.factory(this.store, Roo.data);
6671 // this.store.on('load', this.onLoad, this);
6673 // this.store.load();
6677 // onLoad: function ()
6679 // this.fireEvent('load', this);
6689 * Ext JS Library 1.1.1
6690 * Copyright(c) 2006-2007, Ext JS, LLC.
6692 * Originally Released Under LGPL - original licence link has changed is not relivant.
6695 * <script type="text/javascript">
6698 // as we use this in bootstrap.
6699 Roo.namespace('Roo.form');
6701 * @class Roo.form.Action
6702 * Internal Class used to handle form actions
6704 * @param {Roo.form.BasicForm} el The form element or its id
6705 * @param {Object} config Configuration options
6710 // define the action interface
6711 Roo.form.Action = function(form, options){
6713 this.options = options || {};
6716 * Client Validation Failed
6719 Roo.form.Action.CLIENT_INVALID = 'client';
6721 * Server Validation Failed
6724 Roo.form.Action.SERVER_INVALID = 'server';
6726 * Connect to Server Failed
6729 Roo.form.Action.CONNECT_FAILURE = 'connect';
6731 * Reading Data from Server Failed
6734 Roo.form.Action.LOAD_FAILURE = 'load';
6736 Roo.form.Action.prototype = {
6738 failureType : undefined,
6739 response : undefined,
6743 run : function(options){
6748 success : function(response){
6753 handleResponse : function(response){
6757 // default connection failure
6758 failure : function(response){
6760 this.response = response;
6761 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6762 this.form.afterAction(this, false);
6765 processResponse : function(response){
6766 this.response = response;
6767 if(!response.responseText){
6770 this.result = this.handleResponse(response);
6774 // utility functions used internally
6775 getUrl : function(appendParams){
6776 var url = this.options.url || this.form.url || this.form.el.dom.action;
6778 var p = this.getParams();
6780 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6786 getMethod : function(){
6787 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6790 getParams : function(){
6791 var bp = this.form.baseParams;
6792 var p = this.options.params;
6794 if(typeof p == "object"){
6795 p = Roo.urlEncode(Roo.applyIf(p, bp));
6796 }else if(typeof p == 'string' && bp){
6797 p += '&' + Roo.urlEncode(bp);
6800 p = Roo.urlEncode(bp);
6805 createCallback : function(){
6807 success: this.success,
6808 failure: this.failure,
6810 timeout: (this.form.timeout*1000),
6811 upload: this.form.fileUpload ? this.success : undefined
6816 Roo.form.Action.Submit = function(form, options){
6817 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6820 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6823 haveProgress : false,
6824 uploadComplete : false,
6826 // uploadProgress indicator.
6827 uploadProgress : function()
6829 if (!this.form.progressUrl) {
6833 if (!this.haveProgress) {
6834 Roo.MessageBox.progress("Uploading", "Uploading");
6836 if (this.uploadComplete) {
6837 Roo.MessageBox.hide();
6841 this.haveProgress = true;
6843 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6845 var c = new Roo.data.Connection();
6847 url : this.form.progressUrl,
6852 success : function(req){
6853 //console.log(data);
6857 rdata = Roo.decode(req.responseText)
6859 Roo.log("Invalid data from server..");
6863 if (!rdata || !rdata.success) {
6865 Roo.MessageBox.alert(Roo.encode(rdata));
6868 var data = rdata.data;
6870 if (this.uploadComplete) {
6871 Roo.MessageBox.hide();
6876 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6877 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6880 this.uploadProgress.defer(2000,this);
6883 failure: function(data) {
6884 Roo.log('progress url failed ');
6895 // run get Values on the form, so it syncs any secondary forms.
6896 this.form.getValues();
6898 var o = this.options;
6899 var method = this.getMethod();
6900 var isPost = method == 'POST';
6901 if(o.clientValidation === false || this.form.isValid()){
6903 if (this.form.progressUrl) {
6904 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6905 (new Date() * 1) + '' + Math.random());
6910 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6911 form:this.form.el.dom,
6912 url:this.getUrl(!isPost),
6914 params:isPost ? this.getParams() : null,
6915 isUpload: this.form.fileUpload
6918 this.uploadProgress();
6920 }else if (o.clientValidation !== false){ // client validation failed
6921 this.failureType = Roo.form.Action.CLIENT_INVALID;
6922 this.form.afterAction(this, false);
6926 success : function(response)
6928 this.uploadComplete= true;
6929 if (this.haveProgress) {
6930 Roo.MessageBox.hide();
6934 var result = this.processResponse(response);
6935 if(result === true || result.success){
6936 this.form.afterAction(this, true);
6940 this.form.markInvalid(result.errors);
6941 this.failureType = Roo.form.Action.SERVER_INVALID;
6943 this.form.afterAction(this, false);
6945 failure : function(response)
6947 this.uploadComplete= true;
6948 if (this.haveProgress) {
6949 Roo.MessageBox.hide();
6952 this.response = response;
6953 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6954 this.form.afterAction(this, false);
6957 handleResponse : function(response){
6958 if(this.form.errorReader){
6959 var rs = this.form.errorReader.read(response);
6962 for(var i = 0, len = rs.records.length; i < len; i++) {
6963 var r = rs.records[i];
6967 if(errors.length < 1){
6971 success : rs.success,
6977 ret = Roo.decode(response.responseText);
6981 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6991 Roo.form.Action.Load = function(form, options){
6992 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6993 this.reader = this.form.reader;
6996 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7001 Roo.Ajax.request(Roo.apply(
7002 this.createCallback(), {
7003 method:this.getMethod(),
7004 url:this.getUrl(false),
7005 params:this.getParams()
7009 success : function(response){
7011 var result = this.processResponse(response);
7012 if(result === true || !result.success || !result.data){
7013 this.failureType = Roo.form.Action.LOAD_FAILURE;
7014 this.form.afterAction(this, false);
7017 this.form.clearInvalid();
7018 this.form.setValues(result.data);
7019 this.form.afterAction(this, true);
7022 handleResponse : function(response){
7023 if(this.form.reader){
7024 var rs = this.form.reader.read(response);
7025 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7027 success : rs.success,
7031 return Roo.decode(response.responseText);
7035 Roo.form.Action.ACTION_TYPES = {
7036 'load' : Roo.form.Action.Load,
7037 'submit' : Roo.form.Action.Submit
7046 * @class Roo.bootstrap.Form
7047 * @extends Roo.bootstrap.Component
7048 * Bootstrap Form class
7049 * @cfg {String} method GET | POST (default POST)
7050 * @cfg {String} labelAlign top | left (default top)
7051 * @cfg {String} align left | right - for navbars
7052 * @cfg {Boolean} loadMask load mask when submit (default true)
7057 * @param {Object} config The config object
7061 Roo.bootstrap.Form = function(config){
7062 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7065 * @event clientvalidation
7066 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7067 * @param {Form} this
7068 * @param {Boolean} valid true if the form has passed client-side validation
7070 clientvalidation: true,
7072 * @event beforeaction
7073 * Fires before any action is performed. Return false to cancel the action.
7074 * @param {Form} this
7075 * @param {Action} action The action to be performed
7079 * @event actionfailed
7080 * Fires when an action fails.
7081 * @param {Form} this
7082 * @param {Action} action The action that failed
7084 actionfailed : true,
7086 * @event actioncomplete
7087 * Fires when an action is completed.
7088 * @param {Form} this
7089 * @param {Action} action The action that completed
7091 actioncomplete : true
7096 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7099 * @cfg {String} method
7100 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7105 * The URL to use for form actions if one isn't supplied in the action options.
7108 * @cfg {Boolean} fileUpload
7109 * Set to true if this form is a file upload.
7113 * @cfg {Object} baseParams
7114 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7118 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7122 * @cfg {Sting} align (left|right) for navbar forms
7127 activeAction : null,
7130 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7131 * element by passing it or its id or mask the form itself by passing in true.
7134 waitMsgTarget : false,
7138 getAutoCreate : function(){
7142 method : this.method || 'POST',
7143 id : this.id || Roo.id(),
7146 if (this.parent().xtype.match(/^Nav/)) {
7147 cfg.cls = 'navbar-form navbar-' + this.align;
7151 if (this.labelAlign == 'left' ) {
7152 cfg.cls += ' form-horizontal';
7158 initEvents : function()
7160 this.el.on('submit', this.onSubmit, this);
7161 // this was added as random key presses on the form where triggering form submit.
7162 this.el.on('keypress', function(e) {
7163 if (e.getCharCode() != 13) {
7166 // we might need to allow it for textareas.. and some other items.
7167 // check e.getTarget().
7169 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7173 Roo.log("keypress blocked");
7181 onSubmit : function(e){
7186 * Returns true if client-side validation on the form is successful.
7189 isValid : function(){
7190 var items = this.getItems();
7192 items.each(function(f){
7201 * Returns true if any fields in this form have changed since their original load.
7204 isDirty : function(){
7206 var items = this.getItems();
7207 items.each(function(f){
7217 * Performs a predefined action (submit or load) or custom actions you define on this form.
7218 * @param {String} actionName The name of the action type
7219 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7220 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7221 * accept other config options):
7223 Property Type Description
7224 ---------------- --------------- ----------------------------------------------------------------------------------
7225 url String The url for the action (defaults to the form's url)
7226 method String The form method to use (defaults to the form's method, or POST if not defined)
7227 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7228 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7229 validate the form on the client (defaults to false)
7231 * @return {BasicForm} this
7233 doAction : function(action, options){
7234 if(typeof action == 'string'){
7235 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7237 if(this.fireEvent('beforeaction', this, action) !== false){
7238 this.beforeAction(action);
7239 action.run.defer(100, action);
7245 beforeAction : function(action){
7246 var o = action.options;
7249 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7251 // not really supported yet.. ??
7253 //if(this.waitMsgTarget === true){
7254 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7255 //}else if(this.waitMsgTarget){
7256 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7257 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7259 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7265 afterAction : function(action, success){
7266 this.activeAction = null;
7267 var o = action.options;
7269 //if(this.waitMsgTarget === true){
7271 //}else if(this.waitMsgTarget){
7272 // this.waitMsgTarget.unmask();
7274 // Roo.MessageBox.updateProgress(1);
7275 // Roo.MessageBox.hide();
7282 Roo.callback(o.success, o.scope, [this, action]);
7283 this.fireEvent('actioncomplete', this, action);
7287 // failure condition..
7288 // we have a scenario where updates need confirming.
7289 // eg. if a locking scenario exists..
7290 // we look for { errors : { needs_confirm : true }} in the response.
7292 (typeof(action.result) != 'undefined') &&
7293 (typeof(action.result.errors) != 'undefined') &&
7294 (typeof(action.result.errors.needs_confirm) != 'undefined')
7297 Roo.log("not supported yet");
7300 Roo.MessageBox.confirm(
7301 "Change requires confirmation",
7302 action.result.errorMsg,
7307 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7317 Roo.callback(o.failure, o.scope, [this, action]);
7318 // show an error message if no failed handler is set..
7319 if (!this.hasListener('actionfailed')) {
7320 Roo.log("need to add dialog support");
7322 Roo.MessageBox.alert("Error",
7323 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7324 action.result.errorMsg :
7325 "Saving Failed, please check your entries or try again"
7330 this.fireEvent('actionfailed', this, action);
7335 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7336 * @param {String} id The value to search for
7339 findField : function(id){
7340 var items = this.getItems();
7341 var field = items.get(id);
7343 items.each(function(f){
7344 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7351 return field || null;
7354 * Mark fields in this form invalid in bulk.
7355 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7356 * @return {BasicForm} this
7358 markInvalid : function(errors){
7359 if(errors instanceof Array){
7360 for(var i = 0, len = errors.length; i < len; i++){
7361 var fieldError = errors[i];
7362 var f = this.findField(fieldError.id);
7364 f.markInvalid(fieldError.msg);
7370 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7371 field.markInvalid(errors[id]);
7375 //Roo.each(this.childForms || [], function (f) {
7376 // f.markInvalid(errors);
7383 * Set values for fields in this form in bulk.
7384 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7385 * @return {BasicForm} this
7387 setValues : function(values){
7388 if(values instanceof Array){ // array of objects
7389 for(var i = 0, len = values.length; i < len; i++){
7391 var f = this.findField(v.id);
7393 f.setValue(v.value);
7394 if(this.trackResetOnLoad){
7395 f.originalValue = f.getValue();
7399 }else{ // object hash
7402 if(typeof values[id] != 'function' && (field = this.findField(id))){
7404 if (field.setFromData &&
7406 field.displayField &&
7407 // combos' with local stores can
7408 // be queried via setValue()
7409 // to set their value..
7410 (field.store && !field.store.isLocal)
7414 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7415 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7416 field.setFromData(sd);
7419 field.setValue(values[id]);
7423 if(this.trackResetOnLoad){
7424 field.originalValue = field.getValue();
7430 //Roo.each(this.childForms || [], function (f) {
7431 // f.setValues(values);
7438 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7439 * they are returned as an array.
7440 * @param {Boolean} asString
7443 getValues : function(asString){
7444 //if (this.childForms) {
7445 // copy values from the child forms
7446 // Roo.each(this.childForms, function (f) {
7447 // this.setValues(f.getValues());
7453 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7454 if(asString === true){
7457 return Roo.urlDecode(fs);
7461 * Returns the fields in this form as an object with key/value pairs.
7462 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7465 getFieldValues : function(with_hidden)
7467 var items = this.getItems();
7469 items.each(function(f){
7473 var v = f.getValue();
7474 if (f.inputType =='radio') {
7475 if (typeof(ret[f.getName()]) == 'undefined') {
7476 ret[f.getName()] = ''; // empty..
7479 if (!f.el.dom.checked) {
7487 // not sure if this supported any more..
7488 if ((typeof(v) == 'object') && f.getRawValue) {
7489 v = f.getRawValue() ; // dates..
7491 // combo boxes where name != hiddenName...
7492 if (f.name != f.getName()) {
7493 ret[f.name] = f.getRawValue();
7495 ret[f.getName()] = v;
7502 * Clears all invalid messages in this form.
7503 * @return {BasicForm} this
7505 clearInvalid : function(){
7506 var items = this.getItems();
7508 items.each(function(f){
7519 * @return {BasicForm} this
7522 var items = this.getItems();
7523 items.each(function(f){
7527 Roo.each(this.childForms || [], function (f) {
7534 getItems : function()
7536 var r=new Roo.util.MixedCollection(false, function(o){
7537 return o.id || (o.id = Roo.id());
7539 var iter = function(el) {
7546 Roo.each(el.items,function(e) {
7566 * Ext JS Library 1.1.1
7567 * Copyright(c) 2006-2007, Ext JS, LLC.
7569 * Originally Released Under LGPL - original licence link has changed is not relivant.
7572 * <script type="text/javascript">
7575 * @class Roo.form.VTypes
7576 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7579 Roo.form.VTypes = function(){
7580 // closure these in so they are only created once.
7581 var alpha = /^[a-zA-Z_]+$/;
7582 var alphanum = /^[a-zA-Z0-9_]+$/;
7583 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7584 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7586 // All these messages and functions are configurable
7589 * The function used to validate email addresses
7590 * @param {String} value The email address
7592 'email' : function(v){
7593 return email.test(v);
7596 * The error text to display when the email validation function returns false
7599 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7601 * The keystroke filter mask to be applied on email input
7604 'emailMask' : /[a-z0-9_\.\-@]/i,
7607 * The function used to validate URLs
7608 * @param {String} value The URL
7610 'url' : function(v){
7614 * The error text to display when the url validation function returns false
7617 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7620 * The function used to validate alpha values
7621 * @param {String} value The value
7623 'alpha' : function(v){
7624 return alpha.test(v);
7627 * The error text to display when the alpha validation function returns false
7630 'alphaText' : 'This field should only contain letters and _',
7632 * The keystroke filter mask to be applied on alpha input
7635 'alphaMask' : /[a-z_]/i,
7638 * The function used to validate alphanumeric values
7639 * @param {String} value The value
7641 'alphanum' : function(v){
7642 return alphanum.test(v);
7645 * The error text to display when the alphanumeric validation function returns false
7648 'alphanumText' : 'This field should only contain letters, numbers and _',
7650 * The keystroke filter mask to be applied on alphanumeric input
7653 'alphanumMask' : /[a-z0-9_]/i
7663 * @class Roo.bootstrap.Input
7664 * @extends Roo.bootstrap.Component
7665 * Bootstrap Input class
7666 * @cfg {Boolean} disabled is it disabled
7667 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7668 * @cfg {String} name name of the input
7669 * @cfg {string} fieldLabel - the label associated
7670 * @cfg {string} placeholder - placeholder to put in text.
7671 * @cfg {string} before - input group add on before
7672 * @cfg {string} after - input group add on after
7673 * @cfg {string} size - (lg|sm) or leave empty..
7674 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7675 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7676 * @cfg {Number} md colspan out of 12 for computer-sized screens
7677 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7678 * @cfg {string} value default value of the input
7679 * @cfg {Number} labelWidth set the width of label (0-12)
7680 * @cfg {String} labelAlign (top|left)
7681 * @cfg {Boolean} readOnly Specifies that the field should be read-only
7682 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7684 * @cfg {String} align (left|center|right) Default left
7685 * @cfg {Boolean} forceFeedback (true|false) Default false
7691 * Create a new Input
7692 * @param {Object} config The config object
7695 Roo.bootstrap.Input = function(config){
7696 Roo.bootstrap.Input.superclass.constructor.call(this, config);
7701 * Fires when this field receives input focus.
7702 * @param {Roo.form.Field} this
7707 * Fires when this field loses input focus.
7708 * @param {Roo.form.Field} this
7713 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
7714 * {@link Roo.EventObject#getKey} to determine which key was pressed.
7715 * @param {Roo.form.Field} this
7716 * @param {Roo.EventObject} e The event object
7721 * Fires just before the field blurs if the field value has changed.
7722 * @param {Roo.form.Field} this
7723 * @param {Mixed} newValue The new value
7724 * @param {Mixed} oldValue The original value
7729 * Fires after the field has been marked as invalid.
7730 * @param {Roo.form.Field} this
7731 * @param {String} msg The validation message
7736 * Fires after the field has been validated with no errors.
7737 * @param {Roo.form.Field} this
7742 * Fires after the key up
7743 * @param {Roo.form.Field} this
7744 * @param {Roo.EventObject} e The event Object
7750 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
7752 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7753 automatic validation (defaults to "keyup").
7755 validationEvent : "keyup",
7757 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7759 validateOnBlur : true,
7761 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7763 validationDelay : 250,
7765 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7767 focusClass : "x-form-focus", // not needed???
7771 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7773 invalidClass : "has-warning",
7776 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7778 validClass : "has-success",
7781 * @cfg {Boolean} hasFeedback (true|false) default true
7786 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7788 invalidFeedbackClass : "glyphicon-warning-sign",
7791 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7793 validFeedbackClass : "glyphicon-ok",
7796 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7798 selectOnFocus : false,
7801 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7805 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7810 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7812 disableKeyFilter : false,
7815 * @cfg {Boolean} disabled True to disable the field (defaults to false).
7819 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7823 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7825 blankText : "This field is required",
7828 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7832 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7834 maxLength : Number.MAX_VALUE,
7836 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7838 minLengthText : "The minimum length for this field is {0}",
7840 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7842 maxLengthText : "The maximum length for this field is {0}",
7846 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7847 * If available, this function will be called only after the basic validators all return true, and will be passed the
7848 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7852 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7853 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7854 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
7858 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7862 autocomplete: false,
7881 formatedValue : false,
7882 forceFeedback : false,
7884 parentLabelAlign : function()
7887 while (parent.parent()) {
7888 parent = parent.parent();
7889 if (typeof(parent.labelAlign) !='undefined') {
7890 return parent.labelAlign;
7897 getAutoCreate : function(){
7899 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7905 if(this.inputType != 'hidden'){
7906 cfg.cls = 'form-group' //input-group
7912 type : this.inputType,
7914 cls : 'form-control',
7915 placeholder : this.placeholder || '',
7916 autocomplete : this.autocomplete || 'new-password'
7921 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7924 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7925 input.maxLength = this.maxLength;
7928 if (this.disabled) {
7929 input.disabled=true;
7932 if (this.readOnly) {
7933 input.readonly=true;
7937 input.name = this.name;
7940 input.cls += ' input-' + this.size;
7943 ['xs','sm','md','lg'].map(function(size){
7944 if (settings[size]) {
7945 cfg.cls += ' col-' + size + '-' + settings[size];
7949 var inputblock = input;
7953 cls: 'glyphicon form-control-feedback'
7956 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7959 cls : 'has-feedback',
7967 if (this.before || this.after) {
7970 cls : 'input-group',
7974 if (this.before && typeof(this.before) == 'string') {
7976 inputblock.cn.push({
7978 cls : 'roo-input-before input-group-addon',
7982 if (this.before && typeof(this.before) == 'object') {
7983 this.before = Roo.factory(this.before);
7985 inputblock.cn.push({
7987 cls : 'roo-input-before input-group-' +
7988 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7992 inputblock.cn.push(input);
7994 if (this.after && typeof(this.after) == 'string') {
7995 inputblock.cn.push({
7997 cls : 'roo-input-after input-group-addon',
8001 if (this.after && typeof(this.after) == 'object') {
8002 this.after = Roo.factory(this.after);
8004 inputblock.cn.push({
8006 cls : 'roo-input-after input-group-' +
8007 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8011 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8012 inputblock.cls += ' has-feedback';
8013 inputblock.cn.push(feedback);
8017 if (align ==='left' && this.fieldLabel.length) {
8024 cls : 'control-label col-sm-' + this.labelWidth,
8025 html : this.fieldLabel
8029 cls : "col-sm-" + (12 - this.labelWidth),
8036 } else if ( this.fieldLabel.length) {
8042 //cls : 'input-group-addon',
8043 html : this.fieldLabel
8062 if (this.parentType === 'Navbar' && this.parent().bar) {
8063 cfg.cls += ' navbar-form';
8070 * return the real input element.
8072 inputEl: function ()
8074 return this.el.select('input.form-control',true).first();
8077 tooltipEl : function()
8079 return this.inputEl();
8082 setDisabled : function(v)
8084 var i = this.inputEl().dom;
8086 i.removeAttribute('disabled');
8090 i.setAttribute('disabled','true');
8092 initEvents : function()
8095 this.inputEl().on("keydown" , this.fireKey, this);
8096 this.inputEl().on("focus", this.onFocus, this);
8097 this.inputEl().on("blur", this.onBlur, this);
8099 this.inputEl().relayEvent('keyup', this);
8101 // reference to original value for reset
8102 this.originalValue = this.getValue();
8103 //Roo.form.TextField.superclass.initEvents.call(this);
8104 if(this.validationEvent == 'keyup'){
8105 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8106 this.inputEl().on('keyup', this.filterValidation, this);
8108 else if(this.validationEvent !== false){
8109 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8112 if(this.selectOnFocus){
8113 this.on("focus", this.preFocus, this);
8116 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8117 this.inputEl().on("keypress", this.filterKeys, this);
8120 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8121 this.el.on("click", this.autoSize, this);
8124 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8125 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8128 if (typeof(this.before) == 'object') {
8129 this.before.render(this.el.select('.roo-input-before',true).first());
8131 if (typeof(this.after) == 'object') {
8132 this.after.render(this.el.select('.roo-input-after',true).first());
8137 filterValidation : function(e){
8138 if(!e.isNavKeyPress()){
8139 this.validationTask.delay(this.validationDelay);
8143 * Validates the field value
8144 * @return {Boolean} True if the value is valid, else false
8146 validate : function(){
8147 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8148 if(this.disabled || this.validateValue(this.getRawValue())){
8159 * Validates a value according to the field's validation rules and marks the field as invalid
8160 * if the validation fails
8161 * @param {Mixed} value The value to validate
8162 * @return {Boolean} True if the value is valid, else false
8164 validateValue : function(value){
8165 if(value.length < 1) { // if it's blank
8166 if(this.allowBlank){
8172 if(value.length < this.minLength){
8175 if(value.length > this.maxLength){
8179 var vt = Roo.form.VTypes;
8180 if(!vt[this.vtype](value, this)){
8184 if(typeof this.validator == "function"){
8185 var msg = this.validator(value);
8191 if(this.regex && !this.regex.test(value)){
8201 fireKey : function(e){
8202 //Roo.log('field ' + e.getKey());
8203 if(e.isNavKeyPress()){
8204 this.fireEvent("specialkey", this, e);
8207 focus : function (selectText){
8209 this.inputEl().focus();
8210 if(selectText === true){
8211 this.inputEl().dom.select();
8217 onFocus : function(){
8218 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8219 // this.el.addClass(this.focusClass);
8222 this.hasFocus = true;
8223 this.startValue = this.getValue();
8224 this.fireEvent("focus", this);
8228 beforeBlur : Roo.emptyFn,
8232 onBlur : function(){
8234 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8235 //this.el.removeClass(this.focusClass);
8237 this.hasFocus = false;
8238 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8241 var v = this.getValue();
8242 if(String(v) !== String(this.startValue)){
8243 this.fireEvent('change', this, v, this.startValue);
8245 this.fireEvent("blur", this);
8249 * Resets the current field value to the originally loaded value and clears any validation messages
8252 this.setValue(this.originalValue);
8256 * Returns the name of the field
8257 * @return {Mixed} name The name field
8259 getName: function(){
8263 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8264 * @return {Mixed} value The field value
8266 getValue : function(){
8268 var v = this.inputEl().getValue();
8273 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8274 * @return {Mixed} value The field value
8276 getRawValue : function(){
8277 var v = this.inputEl().getValue();
8283 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8284 * @param {Mixed} value The value to set
8286 setRawValue : function(v){
8287 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8290 selectText : function(start, end){
8291 var v = this.getRawValue();
8293 start = start === undefined ? 0 : start;
8294 end = end === undefined ? v.length : end;
8295 var d = this.inputEl().dom;
8296 if(d.setSelectionRange){
8297 d.setSelectionRange(start, end);
8298 }else if(d.createTextRange){
8299 var range = d.createTextRange();
8300 range.moveStart("character", start);
8301 range.moveEnd("character", v.length-end);
8308 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8309 * @param {Mixed} value The value to set
8311 setValue : function(v){
8314 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8320 processValue : function(value){
8321 if(this.stripCharsRe){
8322 var newValue = value.replace(this.stripCharsRe, '');
8323 if(newValue !== value){
8324 this.setRawValue(newValue);
8331 preFocus : function(){
8333 if(this.selectOnFocus){
8334 this.inputEl().dom.select();
8337 filterKeys : function(e){
8339 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8342 var c = e.getCharCode(), cc = String.fromCharCode(c);
8343 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8346 if(!this.maskRe.test(cc)){
8351 * Clear any invalid styles/messages for this field
8353 clearInvalid : function(){
8355 if(!this.el || this.preventMark){ // not rendered
8358 this.el.removeClass(this.invalidClass);
8360 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8362 var feedback = this.el.select('.form-control-feedback', true).first();
8365 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8370 this.fireEvent('valid', this);
8374 * Mark this field as valid
8376 markValid : function()
8378 if(!this.el || this.preventMark){ // not rendered
8382 this.el.removeClass([this.invalidClass, this.validClass]);
8384 var feedback = this.el.select('.form-control-feedback', true).first();
8387 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8390 if(this.disabled || this.allowBlank){
8394 var formGroup = this.el.findParent('.form-group', false, true);
8398 var label = formGroup.select('label', true).first();
8399 var icon = formGroup.select('i.fa-star', true).first();
8406 this.el.addClass(this.validClass);
8408 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8410 var feedback = this.el.select('.form-control-feedback', true).first();
8413 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8414 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8419 this.fireEvent('valid', this);
8423 * Mark this field as invalid
8424 * @param {String} msg The validation message
8426 markInvalid : function(msg)
8428 if(!this.el || this.preventMark){ // not rendered
8432 this.el.removeClass([this.invalidClass, this.validClass]);
8434 var feedback = this.el.select('.form-control-feedback', true).first();
8437 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8440 if(this.disabled || this.allowBlank){
8444 var formGroup = this.el.findParent('.form-group', false, true);
8447 var label = formGroup.select('label', true).first();
8448 var icon = formGroup.select('i.fa-star', true).first();
8450 if(!this.getValue().length && label && !icon){
8451 this.el.findParent('.form-group', false, true).createChild({
8453 cls : 'text-danger fa fa-lg fa-star',
8454 tooltip : 'This field is required',
8455 style : 'margin-right:5px;'
8461 this.el.addClass(this.invalidClass);
8463 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8465 var feedback = this.el.select('.form-control-feedback', true).first();
8468 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8470 if(this.getValue().length || this.forceFeedback){
8471 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8478 this.fireEvent('invalid', this, msg);
8481 SafariOnKeyDown : function(event)
8483 // this is a workaround for a password hang bug on chrome/ webkit.
8485 var isSelectAll = false;
8487 if(this.inputEl().dom.selectionEnd > 0){
8488 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8490 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8491 event.preventDefault();
8496 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8498 event.preventDefault();
8499 // this is very hacky as keydown always get's upper case.
8501 var cc = String.fromCharCode(event.getCharCode());
8502 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8506 adjustWidth : function(tag, w){
8507 tag = tag.toLowerCase();
8508 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8509 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8513 if(tag == 'textarea'){
8516 }else if(Roo.isOpera){
8520 if(tag == 'textarea'){
8539 * @class Roo.bootstrap.TextArea
8540 * @extends Roo.bootstrap.Input
8541 * Bootstrap TextArea class
8542 * @cfg {Number} cols Specifies the visible width of a text area
8543 * @cfg {Number} rows Specifies the visible number of lines in a text area
8544 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8545 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8546 * @cfg {string} html text
8549 * Create a new TextArea
8550 * @param {Object} config The config object
8553 Roo.bootstrap.TextArea = function(config){
8554 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8558 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8568 getAutoCreate : function(){
8570 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8581 value : this.value || '',
8582 html: this.html || '',
8583 cls : 'form-control',
8584 placeholder : this.placeholder || ''
8588 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8589 input.maxLength = this.maxLength;
8593 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8597 input.cols = this.cols;
8600 if (this.readOnly) {
8601 input.readonly = true;
8605 input.name = this.name;
8609 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8613 ['xs','sm','md','lg'].map(function(size){
8614 if (settings[size]) {
8615 cfg.cls += ' col-' + size + '-' + settings[size];
8619 var inputblock = input;
8621 if(this.hasFeedback && !this.allowBlank){
8625 cls: 'glyphicon form-control-feedback'
8629 cls : 'has-feedback',
8638 if (this.before || this.after) {
8641 cls : 'input-group',
8645 inputblock.cn.push({
8647 cls : 'input-group-addon',
8652 inputblock.cn.push(input);
8654 if(this.hasFeedback && !this.allowBlank){
8655 inputblock.cls += ' has-feedback';
8656 inputblock.cn.push(feedback);
8660 inputblock.cn.push({
8662 cls : 'input-group-addon',
8669 if (align ==='left' && this.fieldLabel.length) {
8670 // Roo.log("left and has label");
8676 cls : 'control-label col-sm-' + this.labelWidth,
8677 html : this.fieldLabel
8681 cls : "col-sm-" + (12 - this.labelWidth),
8688 } else if ( this.fieldLabel.length) {
8689 // Roo.log(" label");
8694 //cls : 'input-group-addon',
8695 html : this.fieldLabel
8705 // Roo.log(" no label && no align");
8715 if (this.disabled) {
8716 input.disabled=true;
8723 * return the real textarea element.
8725 inputEl: function ()
8727 return this.el.select('textarea.form-control',true).first();
8731 * Clear any invalid styles/messages for this field
8733 clearInvalid : function()
8736 if(!this.el || this.preventMark){ // not rendered
8740 var label = this.el.select('label', true).first();
8741 var icon = this.el.select('i.fa-star', true).first();
8747 this.el.removeClass(this.invalidClass);
8749 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8751 var feedback = this.el.select('.form-control-feedback', true).first();
8754 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8759 this.fireEvent('valid', this);
8763 * Mark this field as valid
8765 markValid : function()
8767 if(!this.el || this.preventMark){ // not rendered
8771 this.el.removeClass([this.invalidClass, this.validClass]);
8773 var feedback = this.el.select('.form-control-feedback', true).first();
8776 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8779 if(this.disabled || this.allowBlank){
8783 var label = this.el.select('label', true).first();
8784 var icon = this.el.select('i.fa-star', true).first();
8790 this.el.addClass(this.validClass);
8792 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8794 var feedback = this.el.select('.form-control-feedback', true).first();
8797 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8798 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8803 this.fireEvent('valid', this);
8807 * Mark this field as invalid
8808 * @param {String} msg The validation message
8810 markInvalid : function(msg)
8812 if(!this.el || this.preventMark){ // not rendered
8816 this.el.removeClass([this.invalidClass, this.validClass]);
8818 var feedback = this.el.select('.form-control-feedback', true).first();
8821 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8824 if(this.disabled || this.allowBlank){
8828 var label = this.el.select('label', true).first();
8829 var icon = this.el.select('i.fa-star', true).first();
8831 if(!this.getValue().length && label && !icon){
8832 this.el.createChild({
8834 cls : 'text-danger fa fa-lg fa-star',
8835 tooltip : 'This field is required',
8836 style : 'margin-right:5px;'
8840 this.el.addClass(this.invalidClass);
8842 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8844 var feedback = this.el.select('.form-control-feedback', true).first();
8847 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8849 if(this.getValue().length || this.forceFeedback){
8850 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8857 this.fireEvent('invalid', this, msg);
8865 * trigger field - base class for combo..
8870 * @class Roo.bootstrap.TriggerField
8871 * @extends Roo.bootstrap.Input
8872 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8873 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8874 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8875 * for which you can provide a custom implementation. For example:
8877 var trigger = new Roo.bootstrap.TriggerField();
8878 trigger.onTriggerClick = myTriggerFn;
8879 trigger.applyTo('my-field');
8882 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8883 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8884 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
8885 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8886 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8889 * Create a new TriggerField.
8890 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8891 * to the base TextField)
8893 Roo.bootstrap.TriggerField = function(config){
8894 this.mimicing = false;
8895 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8898 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
8900 * @cfg {String} triggerClass A CSS class to apply to the trigger
8903 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8908 * @cfg {Boolean} removable (true|false) special filter default false
8912 /** @cfg {Boolean} grow @hide */
8913 /** @cfg {Number} growMin @hide */
8914 /** @cfg {Number} growMax @hide */
8920 autoSize: Roo.emptyFn,
8927 actionMode : 'wrap',
8932 getAutoCreate : function(){
8934 var align = this.labelAlign || this.parentLabelAlign();
8939 cls: 'form-group' //input-group
8946 type : this.inputType,
8947 cls : 'form-control',
8948 autocomplete: 'new-password',
8949 placeholder : this.placeholder || ''
8953 input.name = this.name;
8956 input.cls += ' input-' + this.size;
8959 if (this.disabled) {
8960 input.disabled=true;
8963 var inputblock = input;
8965 if(this.hasFeedback && !this.allowBlank){
8969 cls: 'glyphicon form-control-feedback'
8972 if(this.removable && !this.editable && !this.tickable){
8974 cls : 'has-feedback',
8980 cls : 'roo-combo-removable-btn close'
8987 cls : 'has-feedback',
8996 if(this.removable && !this.editable && !this.tickable){
8998 cls : 'roo-removable',
9004 cls : 'roo-combo-removable-btn close'
9011 if (this.before || this.after) {
9014 cls : 'input-group',
9018 inputblock.cn.push({
9020 cls : 'input-group-addon',
9025 inputblock.cn.push(input);
9027 if(this.hasFeedback && !this.allowBlank){
9028 inputblock.cls += ' has-feedback';
9029 inputblock.cn.push(feedback);
9033 inputblock.cn.push({
9035 cls : 'input-group-addon',
9048 cls: 'form-hidden-field'
9062 cls: 'form-hidden-field'
9066 cls: 'select2-choices',
9070 cls: 'select2-search-field',
9083 cls: 'select2-container input-group',
9088 // cls: 'typeahead typeahead-long dropdown-menu',
9089 // style: 'display:none'
9094 if(!this.multiple && this.showToggleBtn){
9100 if (this.caret != false) {
9103 cls: 'fa fa-' + this.caret
9110 cls : 'input-group-addon btn dropdown-toggle',
9115 cls: 'combobox-clear',
9129 combobox.cls += ' select2-container-multi';
9132 if (align ==='left' && this.fieldLabel.length) {
9134 // Roo.log("left and has label");
9140 cls : 'control-label col-sm-' + this.labelWidth,
9141 html : this.fieldLabel
9145 cls : "col-sm-" + (12 - this.labelWidth),
9152 } else if ( this.fieldLabel.length) {
9153 // Roo.log(" label");
9158 //cls : 'input-group-addon',
9159 html : this.fieldLabel
9169 // Roo.log(" no label && no align");
9176 ['xs','sm','md','lg'].map(function(size){
9177 if (settings[size]) {
9178 cfg.cls += ' col-' + size + '-' + settings[size];
9189 onResize : function(w, h){
9190 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9191 // if(typeof w == 'number'){
9192 // var x = w - this.trigger.getWidth();
9193 // this.inputEl().setWidth(this.adjustWidth('input', x));
9194 // this.trigger.setStyle('left', x+'px');
9199 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9202 getResizeEl : function(){
9203 return this.inputEl();
9207 getPositionEl : function(){
9208 return this.inputEl();
9212 alignErrorIcon : function(){
9213 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9217 initEvents : function(){
9221 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9222 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9223 if(!this.multiple && this.showToggleBtn){
9224 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9225 if(this.hideTrigger){
9226 this.trigger.setDisplayed(false);
9228 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9232 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9235 if(this.removable && !this.editable && !this.tickable){
9236 var close = this.closeTriggerEl();
9239 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9240 close.on('click', this.removeBtnClick, this, close);
9244 //this.trigger.addClassOnOver('x-form-trigger-over');
9245 //this.trigger.addClassOnClick('x-form-trigger-click');
9248 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9252 closeTriggerEl : function()
9254 var close = this.el.select('.roo-combo-removable-btn', true).first();
9255 return close ? close : false;
9258 removeBtnClick : function(e, h, el)
9262 if(this.fireEvent("remove", this) !== false){
9267 createList : function()
9269 this.list = Roo.get(document.body).createChild({
9271 cls: 'typeahead typeahead-long dropdown-menu',
9272 style: 'display:none'
9275 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9280 initTrigger : function(){
9285 onDestroy : function(){
9287 this.trigger.removeAllListeners();
9288 // this.trigger.remove();
9291 // this.wrap.remove();
9293 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9297 onFocus : function(){
9298 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9301 this.wrap.addClass('x-trigger-wrap-focus');
9302 this.mimicing = true;
9303 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9304 if(this.monitorTab){
9305 this.el.on("keydown", this.checkTab, this);
9312 checkTab : function(e){
9313 if(e.getKey() == e.TAB){
9319 onBlur : function(){
9324 mimicBlur : function(e, t){
9326 if(!this.wrap.contains(t) && this.validateBlur()){
9333 triggerBlur : function(){
9334 this.mimicing = false;
9335 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9336 if(this.monitorTab){
9337 this.el.un("keydown", this.checkTab, this);
9339 //this.wrap.removeClass('x-trigger-wrap-focus');
9340 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9344 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9345 validateBlur : function(e, t){
9350 onDisable : function(){
9351 this.inputEl().dom.disabled = true;
9352 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9354 // this.wrap.addClass('x-item-disabled');
9359 onEnable : function(){
9360 this.inputEl().dom.disabled = false;
9361 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9363 // this.el.removeClass('x-item-disabled');
9368 onShow : function(){
9369 var ae = this.getActionEl();
9372 ae.dom.style.display = '';
9373 ae.dom.style.visibility = 'visible';
9379 onHide : function(){
9380 var ae = this.getActionEl();
9381 ae.dom.style.display = 'none';
9385 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9386 * by an implementing function.
9388 * @param {EventObject} e
9390 onTriggerClick : Roo.emptyFn
9394 * Ext JS Library 1.1.1
9395 * Copyright(c) 2006-2007, Ext JS, LLC.
9397 * Originally Released Under LGPL - original licence link has changed is not relivant.
9400 * <script type="text/javascript">
9405 * @class Roo.data.SortTypes
9407 * Defines the default sorting (casting?) comparison functions used when sorting data.
9409 Roo.data.SortTypes = {
9411 * Default sort that does nothing
9412 * @param {Mixed} s The value being converted
9413 * @return {Mixed} The comparison value
9420 * The regular expression used to strip tags
9424 stripTagsRE : /<\/?[^>]+>/gi,
9427 * Strips all HTML tags to sort on text only
9428 * @param {Mixed} s The value being converted
9429 * @return {String} The comparison value
9431 asText : function(s){
9432 return String(s).replace(this.stripTagsRE, "");
9436 * Strips all HTML tags to sort on text only - Case insensitive
9437 * @param {Mixed} s The value being converted
9438 * @return {String} The comparison value
9440 asUCText : function(s){
9441 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9445 * Case insensitive string
9446 * @param {Mixed} s The value being converted
9447 * @return {String} The comparison value
9449 asUCString : function(s) {
9450 return String(s).toUpperCase();
9455 * @param {Mixed} s The value being converted
9456 * @return {Number} The comparison value
9458 asDate : function(s) {
9462 if(s instanceof Date){
9465 return Date.parse(String(s));
9470 * @param {Mixed} s The value being converted
9471 * @return {Float} The comparison value
9473 asFloat : function(s) {
9474 var val = parseFloat(String(s).replace(/,/g, ""));
9483 * @param {Mixed} s The value being converted
9484 * @return {Number} The comparison value
9486 asInt : function(s) {
9487 var val = parseInt(String(s).replace(/,/g, ""));
9495 * Ext JS Library 1.1.1
9496 * Copyright(c) 2006-2007, Ext JS, LLC.
9498 * Originally Released Under LGPL - original licence link has changed is not relivant.
9501 * <script type="text/javascript">
9505 * @class Roo.data.Record
9506 * Instances of this class encapsulate both record <em>definition</em> information, and record
9507 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9508 * to access Records cached in an {@link Roo.data.Store} object.<br>
9510 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9511 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9514 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9516 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9517 * {@link #create}. The parameters are the same.
9518 * @param {Array} data An associative Array of data values keyed by the field name.
9519 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9520 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9521 * not specified an integer id is generated.
9523 Roo.data.Record = function(data, id){
9524 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9529 * Generate a constructor for a specific record layout.
9530 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9531 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9532 * Each field definition object may contain the following properties: <ul>
9533 * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
9534 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9535 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9536 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9537 * is being used, then this is a string containing the javascript expression to reference the data relative to
9538 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9539 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9540 * this may be omitted.</p></li>
9541 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9542 * <ul><li>auto (Default, implies no conversion)</li>
9547 * <li>date</li></ul></p></li>
9548 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9549 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9550 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9551 * by the Reader into an object that will be stored in the Record. It is passed the
9552 * following parameters:<ul>
9553 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9555 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9557 * <br>usage:<br><pre><code>
9558 var TopicRecord = Roo.data.Record.create(
9559 {name: 'title', mapping: 'topic_title'},
9560 {name: 'author', mapping: 'username'},
9561 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9562 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9563 {name: 'lastPoster', mapping: 'user2'},
9564 {name: 'excerpt', mapping: 'post_text'}
9567 var myNewRecord = new TopicRecord({
9568 title: 'Do my job please',
9571 lastPost: new Date(),
9572 lastPoster: 'Animal',
9573 excerpt: 'No way dude!'
9575 myStore.add(myNewRecord);
9580 Roo.data.Record.create = function(o){
9582 f.superclass.constructor.apply(this, arguments);
9584 Roo.extend(f, Roo.data.Record);
9585 var p = f.prototype;
9586 p.fields = new Roo.util.MixedCollection(false, function(field){
9589 for(var i = 0, len = o.length; i < len; i++){
9590 p.fields.add(new Roo.data.Field(o[i]));
9592 f.getField = function(name){
9593 return p.fields.get(name);
9598 Roo.data.Record.AUTO_ID = 1000;
9599 Roo.data.Record.EDIT = 'edit';
9600 Roo.data.Record.REJECT = 'reject';
9601 Roo.data.Record.COMMIT = 'commit';
9603 Roo.data.Record.prototype = {
9605 * Readonly flag - true if this record has been modified.
9614 join : function(store){
9619 * Set the named field to the specified value.
9620 * @param {String} name The name of the field to set.
9621 * @param {Object} value The value to set the field to.
9623 set : function(name, value){
9624 if(this.data[name] == value){
9631 if(typeof this.modified[name] == 'undefined'){
9632 this.modified[name] = this.data[name];
9634 this.data[name] = value;
9635 if(!this.editing && this.store){
9636 this.store.afterEdit(this);
9641 * Get the value of the named field.
9642 * @param {String} name The name of the field to get the value of.
9643 * @return {Object} The value of the field.
9645 get : function(name){
9646 return this.data[name];
9650 beginEdit : function(){
9651 this.editing = true;
9656 cancelEdit : function(){
9657 this.editing = false;
9658 delete this.modified;
9662 endEdit : function(){
9663 this.editing = false;
9664 if(this.dirty && this.store){
9665 this.store.afterEdit(this);
9670 * Usually called by the {@link Roo.data.Store} which owns the Record.
9671 * Rejects all changes made to the Record since either creation, or the last commit operation.
9672 * Modified fields are reverted to their original values.
9674 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9675 * of reject operations.
9677 reject : function(){
9678 var m = this.modified;
9680 if(typeof m[n] != "function"){
9681 this.data[n] = m[n];
9685 delete this.modified;
9686 this.editing = false;
9688 this.store.afterReject(this);
9693 * Usually called by the {@link Roo.data.Store} which owns the Record.
9694 * Commits all changes made to the Record since either creation, or the last commit operation.
9696 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9697 * of commit operations.
9699 commit : function(){
9701 delete this.modified;
9702 this.editing = false;
9704 this.store.afterCommit(this);
9709 hasError : function(){
9710 return this.error != null;
9714 clearError : function(){
9719 * Creates a copy of this record.
9720 * @param {String} id (optional) A new record id if you don't want to use this record's id
9723 copy : function(newId) {
9724 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9728 * Ext JS Library 1.1.1
9729 * Copyright(c) 2006-2007, Ext JS, LLC.
9731 * Originally Released Under LGPL - original licence link has changed is not relivant.
9734 * <script type="text/javascript">
9740 * @class Roo.data.Store
9741 * @extends Roo.util.Observable
9742 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9743 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9745 * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
9746 * has no knowledge of the format of the data returned by the Proxy.<br>
9748 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9749 * instances from the data object. These records are cached and made available through accessor functions.
9751 * Creates a new Store.
9752 * @param {Object} config A config object containing the objects needed for the Store to access data,
9753 * and read the data into Records.
9755 Roo.data.Store = function(config){
9756 this.data = new Roo.util.MixedCollection(false);
9757 this.data.getKey = function(o){
9760 this.baseParams = {};
9767 "multisort" : "_multisort"
9770 if(config && config.data){
9771 this.inlineData = config.data;
9775 Roo.apply(this, config);
9777 if(this.reader){ // reader passed
9778 this.reader = Roo.factory(this.reader, Roo.data);
9779 this.reader.xmodule = this.xmodule || false;
9780 if(!this.recordType){
9781 this.recordType = this.reader.recordType;
9783 if(this.reader.onMetaChange){
9784 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9788 if(this.recordType){
9789 this.fields = this.recordType.prototype.fields;
9795 * @event datachanged
9796 * Fires when the data cache has changed, and a widget which is using this Store
9797 * as a Record cache should refresh its view.
9798 * @param {Store} this
9803 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9804 * @param {Store} this
9805 * @param {Object} meta The JSON metadata
9810 * Fires when Records have been added to the Store
9811 * @param {Store} this
9812 * @param {Roo.data.Record[]} records The array of Records added
9813 * @param {Number} index The index at which the record(s) were added
9818 * Fires when a Record has been removed from the Store
9819 * @param {Store} this
9820 * @param {Roo.data.Record} record The Record that was removed
9821 * @param {Number} index The index at which the record was removed
9826 * Fires when a Record has been updated
9827 * @param {Store} this
9828 * @param {Roo.data.Record} record The Record that was updated
9829 * @param {String} operation The update operation being performed. Value may be one of:
9831 Roo.data.Record.EDIT
9832 Roo.data.Record.REJECT
9833 Roo.data.Record.COMMIT
9839 * Fires when the data cache has been cleared.
9840 * @param {Store} this
9845 * Fires before a request is made for a new data object. If the beforeload handler returns false
9846 * the load action will be canceled.
9847 * @param {Store} this
9848 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9852 * @event beforeloadadd
9853 * Fires after a new set of Records has been loaded.
9854 * @param {Store} this
9855 * @param {Roo.data.Record[]} records The Records that were loaded
9856 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9858 beforeloadadd : true,
9861 * Fires after a new set of Records has been loaded, before they are added to the store.
9862 * @param {Store} this
9863 * @param {Roo.data.Record[]} records The Records that were loaded
9864 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9865 * @params {Object} return from reader
9869 * @event loadexception
9870 * Fires if an exception occurs in the Proxy during loading.
9871 * Called with the signature of the Proxy's "loadexception" event.
9872 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9875 * @param {Object} return from JsonData.reader() - success, totalRecords, records
9876 * @param {Object} load options
9877 * @param {Object} jsonData from your request (normally this contains the Exception)
9879 loadexception : true
9883 this.proxy = Roo.factory(this.proxy, Roo.data);
9884 this.proxy.xmodule = this.xmodule || false;
9885 this.relayEvents(this.proxy, ["loadexception"]);
9887 this.sortToggle = {};
9888 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9890 Roo.data.Store.superclass.constructor.call(this);
9892 if(this.inlineData){
9893 this.loadData(this.inlineData);
9894 delete this.inlineData;
9898 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9900 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
9901 * without a remote query - used by combo/forms at present.
9905 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9908 * @cfg {Array} data Inline data to be loaded when the store is initialized.
9911 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9912 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9915 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9916 * on any HTTP request
9919 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9922 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9926 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9927 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9932 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9933 * loaded or when a record is removed. (defaults to false).
9935 pruneModifiedRecords : false,
9941 * Add Records to the Store and fires the add event.
9942 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9944 add : function(records){
9945 records = [].concat(records);
9946 for(var i = 0, len = records.length; i < len; i++){
9947 records[i].join(this);
9949 var index = this.data.length;
9950 this.data.addAll(records);
9951 this.fireEvent("add", this, records, index);
9955 * Remove a Record from the Store and fires the remove event.
9956 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9958 remove : function(record){
9959 var index = this.data.indexOf(record);
9960 this.data.removeAt(index);
9961 if(this.pruneModifiedRecords){
9962 this.modified.remove(record);
9964 this.fireEvent("remove", this, record, index);
9968 * Remove all Records from the Store and fires the clear event.
9970 removeAll : function(){
9972 if(this.pruneModifiedRecords){
9975 this.fireEvent("clear", this);
9979 * Inserts Records to the Store at the given index and fires the add event.
9980 * @param {Number} index The start index at which to insert the passed Records.
9981 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9983 insert : function(index, records){
9984 records = [].concat(records);
9985 for(var i = 0, len = records.length; i < len; i++){
9986 this.data.insert(index, records[i]);
9987 records[i].join(this);
9989 this.fireEvent("add", this, records, index);
9993 * Get the index within the cache of the passed Record.
9994 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9995 * @return {Number} The index of the passed Record. Returns -1 if not found.
9997 indexOf : function(record){
9998 return this.data.indexOf(record);
10002 * Get the index within the cache of the Record with the passed id.
10003 * @param {String} id The id of the Record to find.
10004 * @return {Number} The index of the Record. Returns -1 if not found.
10006 indexOfId : function(id){
10007 return this.data.indexOfKey(id);
10011 * Get the Record with the specified id.
10012 * @param {String} id The id of the Record to find.
10013 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10015 getById : function(id){
10016 return this.data.key(id);
10020 * Get the Record at the specified index.
10021 * @param {Number} index The index of the Record to find.
10022 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10024 getAt : function(index){
10025 return this.data.itemAt(index);
10029 * Returns a range of Records between specified indices.
10030 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10031 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10032 * @return {Roo.data.Record[]} An array of Records
10034 getRange : function(start, end){
10035 return this.data.getRange(start, end);
10039 storeOptions : function(o){
10040 o = Roo.apply({}, o);
10043 this.lastOptions = o;
10047 * Loads the Record cache from the configured Proxy using the configured Reader.
10049 * If using remote paging, then the first load call must specify the <em>start</em>
10050 * and <em>limit</em> properties in the options.params property to establish the initial
10051 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10053 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10054 * and this call will return before the new data has been loaded. Perform any post-processing
10055 * in a callback function, or in a "load" event handler.</strong>
10057 * @param {Object} options An object containing properties which control loading options:<ul>
10058 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10059 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10060 * passed the following arguments:<ul>
10061 * <li>r : Roo.data.Record[]</li>
10062 * <li>options: Options object from the load call</li>
10063 * <li>success: Boolean success indicator</li></ul></li>
10064 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10065 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10068 load : function(options){
10069 options = options || {};
10070 if(this.fireEvent("beforeload", this, options) !== false){
10071 this.storeOptions(options);
10072 var p = Roo.apply(options.params || {}, this.baseParams);
10073 // if meta was not loaded from remote source.. try requesting it.
10074 if (!this.reader.metaFromRemote) {
10075 p._requestMeta = 1;
10077 if(this.sortInfo && this.remoteSort){
10078 var pn = this.paramNames;
10079 p[pn["sort"]] = this.sortInfo.field;
10080 p[pn["dir"]] = this.sortInfo.direction;
10082 if (this.multiSort) {
10083 var pn = this.paramNames;
10084 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10087 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10092 * Reloads the Record cache from the configured Proxy using the configured Reader and
10093 * the options from the last load operation performed.
10094 * @param {Object} options (optional) An object containing properties which may override the options
10095 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10096 * the most recently used options are reused).
10098 reload : function(options){
10099 this.load(Roo.applyIf(options||{}, this.lastOptions));
10103 // Called as a callback by the Reader during a load operation.
10104 loadRecords : function(o, options, success){
10105 if(!o || success === false){
10106 if(success !== false){
10107 this.fireEvent("load", this, [], options, o);
10109 if(options.callback){
10110 options.callback.call(options.scope || this, [], options, false);
10114 // if data returned failure - throw an exception.
10115 if (o.success === false) {
10116 // show a message if no listener is registered.
10117 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10118 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10120 // loadmask wil be hooked into this..
10121 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10124 var r = o.records, t = o.totalRecords || r.length;
10126 this.fireEvent("beforeloadadd", this, r, options, o);
10128 if(!options || options.add !== true){
10129 if(this.pruneModifiedRecords){
10130 this.modified = [];
10132 for(var i = 0, len = r.length; i < len; i++){
10136 this.data = this.snapshot;
10137 delete this.snapshot;
10140 this.data.addAll(r);
10141 this.totalLength = t;
10143 this.fireEvent("datachanged", this);
10145 this.totalLength = Math.max(t, this.data.length+r.length);
10148 this.fireEvent("load", this, r, options, o);
10149 if(options.callback){
10150 options.callback.call(options.scope || this, r, options, true);
10156 * Loads data from a passed data block. A Reader which understands the format of the data
10157 * must have been configured in the constructor.
10158 * @param {Object} data The data block from which to read the Records. The format of the data expected
10159 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10160 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10162 loadData : function(o, append){
10163 var r = this.reader.readRecords(o);
10164 this.loadRecords(r, {add: append}, true);
10168 * Gets the number of cached records.
10170 * <em>If using paging, this may not be the total size of the dataset. If the data object
10171 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10172 * the data set size</em>
10174 getCount : function(){
10175 return this.data.length || 0;
10179 * Gets the total number of records in the dataset as returned by the server.
10181 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10182 * the dataset size</em>
10184 getTotalCount : function(){
10185 return this.totalLength || 0;
10189 * Returns the sort state of the Store as an object with two properties:
10191 field {String} The name of the field by which the Records are sorted
10192 direction {String} The sort order, "ASC" or "DESC"
10195 getSortState : function(){
10196 return this.sortInfo;
10200 applySort : function(){
10201 if(this.sortInfo && !this.remoteSort){
10202 var s = this.sortInfo, f = s.field;
10203 var st = this.fields.get(f).sortType;
10204 var fn = function(r1, r2){
10205 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10206 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10208 this.data.sort(s.direction, fn);
10209 if(this.snapshot && this.snapshot != this.data){
10210 this.snapshot.sort(s.direction, fn);
10216 * Sets the default sort column and order to be used by the next load operation.
10217 * @param {String} fieldName The name of the field to sort by.
10218 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10220 setDefaultSort : function(field, dir){
10221 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10225 * Sort the Records.
10226 * If remote sorting is used, the sort is performed on the server, and the cache is
10227 * reloaded. If local sorting is used, the cache is sorted internally.
10228 * @param {String} fieldName The name of the field to sort by.
10229 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10231 sort : function(fieldName, dir){
10232 var f = this.fields.get(fieldName);
10234 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10236 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10237 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10242 this.sortToggle[f.name] = dir;
10243 this.sortInfo = {field: f.name, direction: dir};
10244 if(!this.remoteSort){
10246 this.fireEvent("datachanged", this);
10248 this.load(this.lastOptions);
10253 * Calls the specified function for each of the Records in the cache.
10254 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10255 * Returning <em>false</em> aborts and exits the iteration.
10256 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10258 each : function(fn, scope){
10259 this.data.each(fn, scope);
10263 * Gets all records modified since the last commit. Modified records are persisted across load operations
10264 * (e.g., during paging).
10265 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10267 getModifiedRecords : function(){
10268 return this.modified;
10272 createFilterFn : function(property, value, anyMatch){
10273 if(!value.exec){ // not a regex
10274 value = String(value);
10275 if(value.length == 0){
10278 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10280 return function(r){
10281 return value.test(r.data[property]);
10286 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10287 * @param {String} property A field on your records
10288 * @param {Number} start The record index to start at (defaults to 0)
10289 * @param {Number} end The last record index to include (defaults to length - 1)
10290 * @return {Number} The sum
10292 sum : function(property, start, end){
10293 var rs = this.data.items, v = 0;
10294 start = start || 0;
10295 end = (end || end === 0) ? end : rs.length-1;
10297 for(var i = start; i <= end; i++){
10298 v += (rs[i].data[property] || 0);
10304 * Filter the records by a specified property.
10305 * @param {String} field A field on your records
10306 * @param {String/RegExp} value Either a string that the field
10307 * should start with or a RegExp to test against the field
10308 * @param {Boolean} anyMatch True to match any part not just the beginning
10310 filter : function(property, value, anyMatch){
10311 var fn = this.createFilterFn(property, value, anyMatch);
10312 return fn ? this.filterBy(fn) : this.clearFilter();
10316 * Filter by a function. The specified function will be called with each
10317 * record in this data source. If the function returns true the record is included,
10318 * otherwise it is filtered.
10319 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10320 * @param {Object} scope (optional) The scope of the function (defaults to this)
10322 filterBy : function(fn, scope){
10323 this.snapshot = this.snapshot || this.data;
10324 this.data = this.queryBy(fn, scope||this);
10325 this.fireEvent("datachanged", this);
10329 * Query the records by a specified property.
10330 * @param {String} field A field on your records
10331 * @param {String/RegExp} value Either a string that the field
10332 * should start with or a RegExp to test against the field
10333 * @param {Boolean} anyMatch True to match any part not just the beginning
10334 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10336 query : function(property, value, anyMatch){
10337 var fn = this.createFilterFn(property, value, anyMatch);
10338 return fn ? this.queryBy(fn) : this.data.clone();
10342 * Query by a function. The specified function will be called with each
10343 * record in this data source. If the function returns true the record is included
10345 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10346 * @param {Object} scope (optional) The scope of the function (defaults to this)
10347 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10349 queryBy : function(fn, scope){
10350 var data = this.snapshot || this.data;
10351 return data.filterBy(fn, scope||this);
10355 * Collects unique values for a particular dataIndex from this store.
10356 * @param {String} dataIndex The property to collect
10357 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10358 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10359 * @return {Array} An array of the unique values
10361 collect : function(dataIndex, allowNull, bypassFilter){
10362 var d = (bypassFilter === true && this.snapshot) ?
10363 this.snapshot.items : this.data.items;
10364 var v, sv, r = [], l = {};
10365 for(var i = 0, len = d.length; i < len; i++){
10366 v = d[i].data[dataIndex];
10368 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10377 * Revert to a view of the Record cache with no filtering applied.
10378 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10380 clearFilter : function(suppressEvent){
10381 if(this.snapshot && this.snapshot != this.data){
10382 this.data = this.snapshot;
10383 delete this.snapshot;
10384 if(suppressEvent !== true){
10385 this.fireEvent("datachanged", this);
10391 afterEdit : function(record){
10392 if(this.modified.indexOf(record) == -1){
10393 this.modified.push(record);
10395 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10399 afterReject : function(record){
10400 this.modified.remove(record);
10401 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10405 afterCommit : function(record){
10406 this.modified.remove(record);
10407 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10411 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10412 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10414 commitChanges : function(){
10415 var m = this.modified.slice(0);
10416 this.modified = [];
10417 for(var i = 0, len = m.length; i < len; i++){
10423 * Cancel outstanding changes on all changed records.
10425 rejectChanges : function(){
10426 var m = this.modified.slice(0);
10427 this.modified = [];
10428 for(var i = 0, len = m.length; i < len; i++){
10433 onMetaChange : function(meta, rtype, o){
10434 this.recordType = rtype;
10435 this.fields = rtype.prototype.fields;
10436 delete this.snapshot;
10437 this.sortInfo = meta.sortInfo || this.sortInfo;
10438 this.modified = [];
10439 this.fireEvent('metachange', this, this.reader.meta);
10442 moveIndex : function(data, type)
10444 var index = this.indexOf(data);
10446 var newIndex = index + type;
10450 this.insert(newIndex, data);
10455 * Ext JS Library 1.1.1
10456 * Copyright(c) 2006-2007, Ext JS, LLC.
10458 * Originally Released Under LGPL - original licence link has changed is not relivant.
10461 * <script type="text/javascript">
10465 * @class Roo.data.SimpleStore
10466 * @extends Roo.data.Store
10467 * Small helper class to make creating Stores from Array data easier.
10468 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10469 * @cfg {Array} fields An array of field definition objects, or field name strings.
10470 * @cfg {Array} data The multi-dimensional array of data
10472 * @param {Object} config
10474 Roo.data.SimpleStore = function(config){
10475 Roo.data.SimpleStore.superclass.constructor.call(this, {
10477 reader: new Roo.data.ArrayReader({
10480 Roo.data.Record.create(config.fields)
10482 proxy : new Roo.data.MemoryProxy(config.data)
10486 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10488 * Ext JS Library 1.1.1
10489 * Copyright(c) 2006-2007, Ext JS, LLC.
10491 * Originally Released Under LGPL - original licence link has changed is not relivant.
10494 * <script type="text/javascript">
10499 * @extends Roo.data.Store
10500 * @class Roo.data.JsonStore
10501 * Small helper class to make creating Stores for JSON data easier. <br/>
10503 var store = new Roo.data.JsonStore({
10504 url: 'get-images.php',
10506 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10509 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10510 * JsonReader and HttpProxy (unless inline data is provided).</b>
10511 * @cfg {Array} fields An array of field definition objects, or field name strings.
10513 * @param {Object} config
10515 Roo.data.JsonStore = function(c){
10516 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10517 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10518 reader: new Roo.data.JsonReader(c, c.fields)
10521 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10523 * Ext JS Library 1.1.1
10524 * Copyright(c) 2006-2007, Ext JS, LLC.
10526 * Originally Released Under LGPL - original licence link has changed is not relivant.
10529 * <script type="text/javascript">
10533 Roo.data.Field = function(config){
10534 if(typeof config == "string"){
10535 config = {name: config};
10537 Roo.apply(this, config);
10540 this.type = "auto";
10543 var st = Roo.data.SortTypes;
10544 // named sortTypes are supported, here we look them up
10545 if(typeof this.sortType == "string"){
10546 this.sortType = st[this.sortType];
10549 // set default sortType for strings and dates
10550 if(!this.sortType){
10553 this.sortType = st.asUCString;
10556 this.sortType = st.asDate;
10559 this.sortType = st.none;
10564 var stripRe = /[\$,%]/g;
10566 // prebuilt conversion function for this field, instead of
10567 // switching every time we're reading a value
10569 var cv, dateFormat = this.dateFormat;
10574 cv = function(v){ return v; };
10577 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10581 return v !== undefined && v !== null && v !== '' ?
10582 parseInt(String(v).replace(stripRe, ""), 10) : '';
10587 return v !== undefined && v !== null && v !== '' ?
10588 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10593 cv = function(v){ return v === true || v === "true" || v == 1; };
10600 if(v instanceof Date){
10604 if(dateFormat == "timestamp"){
10605 return new Date(v*1000);
10607 return Date.parseDate(v, dateFormat);
10609 var parsed = Date.parse(v);
10610 return parsed ? new Date(parsed) : null;
10619 Roo.data.Field.prototype = {
10627 * Ext JS Library 1.1.1
10628 * Copyright(c) 2006-2007, Ext JS, LLC.
10630 * Originally Released Under LGPL - original licence link has changed is not relivant.
10633 * <script type="text/javascript">
10636 // Base class for reading structured data from a data source. This class is intended to be
10637 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10640 * @class Roo.data.DataReader
10641 * Base class for reading structured data from a data source. This class is intended to be
10642 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10645 Roo.data.DataReader = function(meta, recordType){
10649 this.recordType = recordType instanceof Array ?
10650 Roo.data.Record.create(recordType) : recordType;
10653 Roo.data.DataReader.prototype = {
10655 * Create an empty record
10656 * @param {Object} data (optional) - overlay some values
10657 * @return {Roo.data.Record} record created.
10659 newRow : function(d) {
10661 this.recordType.prototype.fields.each(function(c) {
10663 case 'int' : da[c.name] = 0; break;
10664 case 'date' : da[c.name] = new Date(); break;
10665 case 'float' : da[c.name] = 0.0; break;
10666 case 'boolean' : da[c.name] = false; break;
10667 default : da[c.name] = ""; break;
10671 return new this.recordType(Roo.apply(da, d));
10676 * Ext JS Library 1.1.1
10677 * Copyright(c) 2006-2007, Ext JS, LLC.
10679 * Originally Released Under LGPL - original licence link has changed is not relivant.
10682 * <script type="text/javascript">
10686 * @class Roo.data.DataProxy
10687 * @extends Roo.data.Observable
10688 * This class is an abstract base class for implementations which provide retrieval of
10689 * unformatted data objects.<br>
10691 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10692 * (of the appropriate type which knows how to parse the data object) to provide a block of
10693 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10695 * Custom implementations must implement the load method as described in
10696 * {@link Roo.data.HttpProxy#load}.
10698 Roo.data.DataProxy = function(){
10701 * @event beforeload
10702 * Fires before a network request is made to retrieve a data object.
10703 * @param {Object} This DataProxy object.
10704 * @param {Object} params The params parameter to the load function.
10709 * Fires before the load method's callback is called.
10710 * @param {Object} This DataProxy object.
10711 * @param {Object} o The data object.
10712 * @param {Object} arg The callback argument object passed to the load function.
10716 * @event loadexception
10717 * Fires if an Exception occurs during data retrieval.
10718 * @param {Object} This DataProxy object.
10719 * @param {Object} o The data object.
10720 * @param {Object} arg The callback argument object passed to the load function.
10721 * @param {Object} e The Exception.
10723 loadexception : true
10725 Roo.data.DataProxy.superclass.constructor.call(this);
10728 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10731 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10735 * Ext JS Library 1.1.1
10736 * Copyright(c) 2006-2007, Ext JS, LLC.
10738 * Originally Released Under LGPL - original licence link has changed is not relivant.
10741 * <script type="text/javascript">
10744 * @class Roo.data.MemoryProxy
10745 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10746 * to the Reader when its load method is called.
10748 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10750 Roo.data.MemoryProxy = function(data){
10754 Roo.data.MemoryProxy.superclass.constructor.call(this);
10758 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10760 * Load data from the requested source (in this case an in-memory
10761 * data object passed to the constructor), read the data object into
10762 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10763 * process that block using the passed callback.
10764 * @param {Object} params This parameter is not used by the MemoryProxy class.
10765 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10766 * object into a block of Roo.data.Records.
10767 * @param {Function} callback The function into which to pass the block of Roo.data.records.
10768 * The function must be passed <ul>
10769 * <li>The Record block object</li>
10770 * <li>The "arg" argument from the load function</li>
10771 * <li>A boolean success indicator</li>
10773 * @param {Object} scope The scope in which to call the callback
10774 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10776 load : function(params, reader, callback, scope, arg){
10777 params = params || {};
10780 result = reader.readRecords(this.data);
10782 this.fireEvent("loadexception", this, arg, null, e);
10783 callback.call(scope, null, arg, false);
10786 callback.call(scope, result, arg, true);
10790 update : function(params, records){
10795 * Ext JS Library 1.1.1
10796 * Copyright(c) 2006-2007, Ext JS, LLC.
10798 * Originally Released Under LGPL - original licence link has changed is not relivant.
10801 * <script type="text/javascript">
10804 * @class Roo.data.HttpProxy
10805 * @extends Roo.data.DataProxy
10806 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10807 * configured to reference a certain URL.<br><br>
10809 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10810 * from which the running page was served.<br><br>
10812 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10814 * Be aware that to enable the browser to parse an XML document, the server must set
10815 * the Content-Type header in the HTTP response to "text/xml".
10817 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10818 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
10819 * will be used to make the request.
10821 Roo.data.HttpProxy = function(conn){
10822 Roo.data.HttpProxy.superclass.constructor.call(this);
10823 // is conn a conn config or a real conn?
10825 this.useAjax = !conn || !conn.events;
10829 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10830 // thse are take from connection...
10833 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10836 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10837 * extra parameters to each request made by this object. (defaults to undefined)
10840 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10841 * to each request made by this object. (defaults to undefined)
10844 * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
10847 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10850 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10856 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10860 * Return the {@link Roo.data.Connection} object being used by this Proxy.
10861 * @return {Connection} The Connection object. This object may be used to subscribe to events on
10862 * a finer-grained basis than the DataProxy events.
10864 getConnection : function(){
10865 return this.useAjax ? Roo.Ajax : this.conn;
10869 * Load data from the configured {@link Roo.data.Connection}, read the data object into
10870 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10871 * process that block using the passed callback.
10872 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10873 * for the request to the remote server.
10874 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10875 * object into a block of Roo.data.Records.
10876 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10877 * The function must be passed <ul>
10878 * <li>The Record block object</li>
10879 * <li>The "arg" argument from the load function</li>
10880 * <li>A boolean success indicator</li>
10882 * @param {Object} scope The scope in which to call the callback
10883 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10885 load : function(params, reader, callback, scope, arg){
10886 if(this.fireEvent("beforeload", this, params) !== false){
10888 params : params || {},
10890 callback : callback,
10895 callback : this.loadResponse,
10899 Roo.applyIf(o, this.conn);
10900 if(this.activeRequest){
10901 Roo.Ajax.abort(this.activeRequest);
10903 this.activeRequest = Roo.Ajax.request(o);
10905 this.conn.request(o);
10908 callback.call(scope||this, null, arg, false);
10913 loadResponse : function(o, success, response){
10914 delete this.activeRequest;
10916 this.fireEvent("loadexception", this, o, response);
10917 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10922 result = o.reader.read(response);
10924 this.fireEvent("loadexception", this, o, response, e);
10925 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10929 this.fireEvent("load", this, o, o.request.arg);
10930 o.request.callback.call(o.request.scope, result, o.request.arg, true);
10934 update : function(dataSet){
10939 updateResponse : function(dataSet){
10944 * Ext JS Library 1.1.1
10945 * Copyright(c) 2006-2007, Ext JS, LLC.
10947 * Originally Released Under LGPL - original licence link has changed is not relivant.
10950 * <script type="text/javascript">
10954 * @class Roo.data.ScriptTagProxy
10955 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10956 * other than the originating domain of the running page.<br><br>
10958 * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
10959 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10961 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10962 * source code that is used as the source inside a <script> tag.<br><br>
10964 * In order for the browser to process the returned data, the server must wrap the data object
10965 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10966 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10967 * depending on whether the callback name was passed:
10970 boolean scriptTag = false;
10971 String cb = request.getParameter("callback");
10974 response.setContentType("text/javascript");
10976 response.setContentType("application/x-json");
10978 Writer out = response.getWriter();
10980 out.write(cb + "(");
10982 out.print(dataBlock.toJsonString());
10989 * @param {Object} config A configuration object.
10991 Roo.data.ScriptTagProxy = function(config){
10992 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10993 Roo.apply(this, config);
10994 this.head = document.getElementsByTagName("head")[0];
10997 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10999 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11001 * @cfg {String} url The URL from which to request the data object.
11004 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11008 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11009 * the server the name of the callback function set up by the load call to process the returned data object.
11010 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11011 * javascript output which calls this named function passing the data object as its only parameter.
11013 callbackParam : "callback",
11015 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11016 * name to the request.
11021 * Load data from the configured URL, read the data object into
11022 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11023 * process that block using the passed callback.
11024 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11025 * for the request to the remote server.
11026 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11027 * object into a block of Roo.data.Records.
11028 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11029 * The function must be passed <ul>
11030 * <li>The Record block object</li>
11031 * <li>The "arg" argument from the load function</li>
11032 * <li>A boolean success indicator</li>
11034 * @param {Object} scope The scope in which to call the callback
11035 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11037 load : function(params, reader, callback, scope, arg){
11038 if(this.fireEvent("beforeload", this, params) !== false){
11040 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11042 var url = this.url;
11043 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11045 url += "&_dc=" + (new Date().getTime());
11047 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11050 cb : "stcCallback"+transId,
11051 scriptId : "stcScript"+transId,
11055 callback : callback,
11061 window[trans.cb] = function(o){
11062 conn.handleResponse(o, trans);
11065 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11067 if(this.autoAbort !== false){
11071 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11073 var script = document.createElement("script");
11074 script.setAttribute("src", url);
11075 script.setAttribute("type", "text/javascript");
11076 script.setAttribute("id", trans.scriptId);
11077 this.head.appendChild(script);
11079 this.trans = trans;
11081 callback.call(scope||this, null, arg, false);
11086 isLoading : function(){
11087 return this.trans ? true : false;
11091 * Abort the current server request.
11093 abort : function(){
11094 if(this.isLoading()){
11095 this.destroyTrans(this.trans);
11100 destroyTrans : function(trans, isLoaded){
11101 this.head.removeChild(document.getElementById(trans.scriptId));
11102 clearTimeout(trans.timeoutId);
11104 window[trans.cb] = undefined;
11106 delete window[trans.cb];
11109 // if hasn't been loaded, wait for load to remove it to prevent script error
11110 window[trans.cb] = function(){
11111 window[trans.cb] = undefined;
11113 delete window[trans.cb];
11120 handleResponse : function(o, trans){
11121 this.trans = false;
11122 this.destroyTrans(trans, true);
11125 result = trans.reader.readRecords(o);
11127 this.fireEvent("loadexception", this, o, trans.arg, e);
11128 trans.callback.call(trans.scope||window, null, trans.arg, false);
11131 this.fireEvent("load", this, o, trans.arg);
11132 trans.callback.call(trans.scope||window, result, trans.arg, true);
11136 handleFailure : function(trans){
11137 this.trans = false;
11138 this.destroyTrans(trans, false);
11139 this.fireEvent("loadexception", this, null, trans.arg);
11140 trans.callback.call(trans.scope||window, null, trans.arg, false);
11144 * Ext JS Library 1.1.1
11145 * Copyright(c) 2006-2007, Ext JS, LLC.
11147 * Originally Released Under LGPL - original licence link has changed is not relivant.
11150 * <script type="text/javascript">
11154 * @class Roo.data.JsonReader
11155 * @extends Roo.data.DataReader
11156 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11157 * based on mappings in a provided Roo.data.Record constructor.
11159 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11160 * in the reply previously.
11165 var RecordDef = Roo.data.Record.create([
11166 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11167 {name: 'occupation'} // This field will use "occupation" as the mapping.
11169 var myReader = new Roo.data.JsonReader({
11170 totalProperty: "results", // The property which contains the total dataset size (optional)
11171 root: "rows", // The property which contains an Array of row objects
11172 id: "id" // The property within each row object that provides an ID for the record (optional)
11176 * This would consume a JSON file like this:
11178 { 'results': 2, 'rows': [
11179 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11180 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11183 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11184 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11185 * paged from the remote server.
11186 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11187 * @cfg {String} root name of the property which contains the Array of row objects.
11188 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11189 * @cfg {Array} fields Array of field definition objects
11191 * Create a new JsonReader
11192 * @param {Object} meta Metadata configuration options
11193 * @param {Object} recordType Either an Array of field definition objects,
11194 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11196 Roo.data.JsonReader = function(meta, recordType){
11199 // set some defaults:
11200 Roo.applyIf(meta, {
11201 totalProperty: 'total',
11202 successProperty : 'success',
11207 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11209 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11212 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11213 * Used by Store query builder to append _requestMeta to params.
11216 metaFromRemote : false,
11218 * This method is only used by a DataProxy which has retrieved data from a remote server.
11219 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11220 * @return {Object} data A data block which is used by an Roo.data.Store object as
11221 * a cache of Roo.data.Records.
11223 read : function(response){
11224 var json = response.responseText;
11226 var o = /* eval:var:o */ eval("("+json+")");
11228 throw {message: "JsonReader.read: Json object not found"};
11234 this.metaFromRemote = true;
11235 this.meta = o.metaData;
11236 this.recordType = Roo.data.Record.create(o.metaData.fields);
11237 this.onMetaChange(this.meta, this.recordType, o);
11239 return this.readRecords(o);
11242 // private function a store will implement
11243 onMetaChange : function(meta, recordType, o){
11250 simpleAccess: function(obj, subsc) {
11257 getJsonAccessor: function(){
11259 return function(expr) {
11261 return(re.test(expr))
11262 ? new Function("obj", "return obj." + expr)
11267 return Roo.emptyFn;
11272 * Create a data block containing Roo.data.Records from an XML document.
11273 * @param {Object} o An object which contains an Array of row objects in the property specified
11274 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11275 * which contains the total size of the dataset.
11276 * @return {Object} data A data block which is used by an Roo.data.Store object as
11277 * a cache of Roo.data.Records.
11279 readRecords : function(o){
11281 * After any data loads, the raw JSON data is available for further custom processing.
11285 var s = this.meta, Record = this.recordType,
11286 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11288 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11290 if(s.totalProperty) {
11291 this.getTotal = this.getJsonAccessor(s.totalProperty);
11293 if(s.successProperty) {
11294 this.getSuccess = this.getJsonAccessor(s.successProperty);
11296 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11298 var g = this.getJsonAccessor(s.id);
11299 this.getId = function(rec) {
11301 return (r === undefined || r === "") ? null : r;
11304 this.getId = function(){return null;};
11307 for(var jj = 0; jj < fl; jj++){
11309 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11310 this.ef[jj] = this.getJsonAccessor(map);
11314 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11315 if(s.totalProperty){
11316 var vt = parseInt(this.getTotal(o), 10);
11321 if(s.successProperty){
11322 var vs = this.getSuccess(o);
11323 if(vs === false || vs === 'false'){
11328 for(var i = 0; i < c; i++){
11331 var id = this.getId(n);
11332 for(var j = 0; j < fl; j++){
11334 var v = this.ef[j](n);
11336 Roo.log('missing convert for ' + f.name);
11340 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11342 var record = new Record(values, id);
11344 records[i] = record;
11350 totalRecords : totalRecords
11355 * Ext JS Library 1.1.1
11356 * Copyright(c) 2006-2007, Ext JS, LLC.
11358 * Originally Released Under LGPL - original licence link has changed is not relivant.
11361 * <script type="text/javascript">
11365 * @class Roo.data.ArrayReader
11366 * @extends Roo.data.DataReader
11367 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11368 * Each element of that Array represents a row of data fields. The
11369 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11370 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11374 var RecordDef = Roo.data.Record.create([
11375 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11376 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11378 var myReader = new Roo.data.ArrayReader({
11379 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11383 * This would consume an Array like this:
11385 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11387 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11389 * Create a new JsonReader
11390 * @param {Object} meta Metadata configuration options.
11391 * @param {Object} recordType Either an Array of field definition objects
11392 * as specified to {@link Roo.data.Record#create},
11393 * or an {@link Roo.data.Record} object
11394 * created using {@link Roo.data.Record#create}.
11396 Roo.data.ArrayReader = function(meta, recordType){
11397 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11400 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11402 * Create a data block containing Roo.data.Records from an XML document.
11403 * @param {Object} o An Array of row objects which represents the dataset.
11404 * @return {Object} data A data block which is used by an Roo.data.Store object as
11405 * a cache of Roo.data.Records.
11407 readRecords : function(o){
11408 var sid = this.meta ? this.meta.id : null;
11409 var recordType = this.recordType, fields = recordType.prototype.fields;
11412 for(var i = 0; i < root.length; i++){
11415 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11416 for(var j = 0, jlen = fields.length; j < jlen; j++){
11417 var f = fields.items[j];
11418 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11419 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11421 values[f.name] = v;
11423 var record = new recordType(values, id);
11425 records[records.length] = record;
11429 totalRecords : records.length
11438 * @class Roo.bootstrap.ComboBox
11439 * @extends Roo.bootstrap.TriggerField
11440 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11441 * @cfg {Boolean} append (true|false) default false
11442 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11443 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11444 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11445 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11446 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11447 * @cfg {Boolean} animate default true
11448 * @cfg {Boolean} emptyResultText only for touch device
11449 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11451 * Create a new ComboBox.
11452 * @param {Object} config Configuration options
11454 Roo.bootstrap.ComboBox = function(config){
11455 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11459 * Fires when the dropdown list is expanded
11460 * @param {Roo.bootstrap.ComboBox} combo This combo box
11465 * Fires when the dropdown list is collapsed
11466 * @param {Roo.bootstrap.ComboBox} combo This combo box
11470 * @event beforeselect
11471 * Fires before a list item is selected. Return false to cancel the selection.
11472 * @param {Roo.bootstrap.ComboBox} combo This combo box
11473 * @param {Roo.data.Record} record The data record returned from the underlying store
11474 * @param {Number} index The index of the selected item in the dropdown list
11476 'beforeselect' : true,
11479 * Fires when a list item is selected
11480 * @param {Roo.bootstrap.ComboBox} combo This combo box
11481 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11482 * @param {Number} index The index of the selected item in the dropdown list
11486 * @event beforequery
11487 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11488 * The event object passed has these properties:
11489 * @param {Roo.bootstrap.ComboBox} combo This combo box
11490 * @param {String} query The query
11491 * @param {Boolean} forceAll true to force "all" query
11492 * @param {Boolean} cancel true to cancel the query
11493 * @param {Object} e The query event object
11495 'beforequery': true,
11498 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11499 * @param {Roo.bootstrap.ComboBox} combo This combo box
11504 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11505 * @param {Roo.bootstrap.ComboBox} combo This combo box
11506 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11511 * Fires when the remove value from the combobox array
11512 * @param {Roo.bootstrap.ComboBox} combo This combo box
11516 * @event specialfilter
11517 * Fires when specialfilter
11518 * @param {Roo.bootstrap.ComboBox} combo This combo box
11520 'specialfilter' : true,
11523 * Fires when tick the element
11524 * @param {Roo.bootstrap.ComboBox} combo This combo box
11528 * @event touchviewdisplay
11529 * Fires when touch view require special display (default is using displayField)
11530 * @param {Roo.bootstrap.ComboBox} combo This combo box
11531 * @param {Object} cfg set html .
11533 'touchviewdisplay' : true
11538 this.tickItems = [];
11540 this.selectedIndex = -1;
11541 if(this.mode == 'local'){
11542 if(config.queryDelay === undefined){
11543 this.queryDelay = 10;
11545 if(config.minChars === undefined){
11551 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11554 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11555 * rendering into an Roo.Editor, defaults to false)
11558 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11559 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11562 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11565 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11566 * the dropdown list (defaults to undefined, with no header element)
11570 * @cfg {String/Roo.Template} tpl The template to use to render the output
11574 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11576 listWidth: undefined,
11578 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11579 * mode = 'remote' or 'text' if mode = 'local')
11581 displayField: undefined,
11584 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11585 * mode = 'remote' or 'value' if mode = 'local').
11586 * Note: use of a valueField requires the user make a selection
11587 * in order for a value to be mapped.
11589 valueField: undefined,
11593 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11594 * field's data value (defaults to the underlying DOM element's name)
11596 hiddenName: undefined,
11598 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11602 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11604 selectedClass: 'active',
11607 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11611 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11612 * anchor positions (defaults to 'tl-bl')
11614 listAlign: 'tl-bl?',
11616 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11620 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11621 * query specified by the allQuery config option (defaults to 'query')
11623 triggerAction: 'query',
11625 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11626 * (defaults to 4, does not apply if editable = false)
11630 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11631 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11635 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11636 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11640 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11641 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11645 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11646 * when editable = true (defaults to false)
11648 selectOnFocus:false,
11650 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11652 queryParam: 'query',
11654 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
11655 * when mode = 'remote' (defaults to 'Loading...')
11657 loadingText: 'Loading...',
11659 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11663 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11667 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11668 * traditional select (defaults to true)
11672 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11676 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11680 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11681 * listWidth has a higher value)
11685 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11686 * allow the user to set arbitrary text into the field (defaults to false)
11688 forceSelection:false,
11690 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11691 * if typeAhead = true (defaults to 250)
11693 typeAheadDelay : 250,
11695 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11696 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11698 valueNotFoundText : undefined,
11700 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11702 blockFocus : false,
11705 * @cfg {Boolean} disableClear Disable showing of clear button.
11707 disableClear : false,
11709 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
11711 alwaysQuery : false,
11714 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
11719 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11721 invalidClass : "has-warning",
11724 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11726 validClass : "has-success",
11729 * @cfg {Boolean} specialFilter (true|false) special filter default false
11731 specialFilter : false,
11734 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11736 mobileTouchView : true,
11748 btnPosition : 'right',
11749 triggerList : true,
11750 showToggleBtn : true,
11752 emptyResultText: 'Empty',
11753 triggerText : 'Select',
11755 // element that contains real text value.. (when hidden is used..)
11757 getAutoCreate : function()
11765 if(Roo.isTouch && this.mobileTouchView){
11766 cfg = this.getAutoCreateTouchView();
11773 if(!this.tickable){
11774 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11779 * ComboBox with tickable selections
11782 var align = this.labelAlign || this.parentLabelAlign();
11785 cls : 'form-group roo-combobox-tickable' //input-group
11790 cls : 'tickable-buttons',
11795 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11796 html : this.triggerText
11802 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11809 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11816 buttons.cn.unshift({
11818 cls: 'select2-search-field-input'
11824 Roo.each(buttons.cn, function(c){
11826 c.cls += ' btn-' + _this.size;
11829 if (_this.disabled) {
11840 cls: 'form-hidden-field'
11844 cls: 'select2-choices',
11848 cls: 'select2-search-field',
11860 cls: 'select2-container input-group select2-container-multi',
11865 // cls: 'typeahead typeahead-long dropdown-menu',
11866 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
11871 if(this.hasFeedback && !this.allowBlank){
11875 cls: 'glyphicon form-control-feedback'
11878 combobox.cn.push(feedback);
11881 if (align ==='left' && this.fieldLabel.length) {
11883 // Roo.log("left and has label");
11889 cls : 'control-label col-sm-' + this.labelWidth,
11890 html : this.fieldLabel
11894 cls : "col-sm-" + (12 - this.labelWidth),
11901 } else if ( this.fieldLabel.length) {
11902 // Roo.log(" label");
11907 //cls : 'input-group-addon',
11908 html : this.fieldLabel
11918 // Roo.log(" no label && no align");
11925 ['xs','sm','md','lg'].map(function(size){
11926 if (settings[size]) {
11927 cfg.cls += ' col-' + size + '-' + settings[size];
11935 _initEventsCalled : false,
11938 initEvents: function()
11941 if (this._initEventsCalled) { // as we call render... prevent looping...
11944 this._initEventsCalled = true;
11947 throw "can not find store for combo";
11950 this.store = Roo.factory(this.store, Roo.data);
11952 // if we are building from html. then this element is so complex, that we can not really
11953 // use the rendered HTML.
11954 // so we have to trash and replace the previous code.
11955 if (Roo.XComponent.build_from_html) {
11957 // remove this element....
11958 var e = this.el.dom, k=0;
11959 while (e ) { e = e.previousSibling; ++k;}
11964 this.rendered = false;
11966 this.render(this.parent().getChildContainer(true), k);
11977 if(Roo.isTouch && this.mobileTouchView){
11978 this.initTouchView();
11983 this.initTickableEvents();
11987 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11989 if(this.hiddenName){
11991 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11993 this.hiddenField.dom.value =
11994 this.hiddenValue !== undefined ? this.hiddenValue :
11995 this.value !== undefined ? this.value : '';
11997 // prevent input submission
11998 this.el.dom.removeAttribute('name');
11999 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12004 // this.el.dom.setAttribute('autocomplete', 'off');
12007 var cls = 'x-combo-list';
12009 //this.list = new Roo.Layer({
12010 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12016 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12017 _this.list.setWidth(lw);
12020 this.list.on('mouseover', this.onViewOver, this);
12021 this.list.on('mousemove', this.onViewMove, this);
12023 this.list.on('scroll', this.onViewScroll, this);
12026 this.list.swallowEvent('mousewheel');
12027 this.assetHeight = 0;
12030 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12031 this.assetHeight += this.header.getHeight();
12034 this.innerList = this.list.createChild({cls:cls+'-inner'});
12035 this.innerList.on('mouseover', this.onViewOver, this);
12036 this.innerList.on('mousemove', this.onViewMove, this);
12037 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12039 if(this.allowBlank && !this.pageSize && !this.disableClear){
12040 this.footer = this.list.createChild({cls:cls+'-ft'});
12041 this.pageTb = new Roo.Toolbar(this.footer);
12045 this.footer = this.list.createChild({cls:cls+'-ft'});
12046 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12047 {pageSize: this.pageSize});
12051 if (this.pageTb && this.allowBlank && !this.disableClear) {
12053 this.pageTb.add(new Roo.Toolbar.Fill(), {
12054 cls: 'x-btn-icon x-btn-clear',
12056 handler: function()
12059 _this.clearValue();
12060 _this.onSelect(false, -1);
12065 this.assetHeight += this.footer.getHeight();
12070 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12073 this.view = new Roo.View(this.list, this.tpl, {
12074 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12076 //this.view.wrapEl.setDisplayed(false);
12077 this.view.on('click', this.onViewClick, this);
12081 this.store.on('beforeload', this.onBeforeLoad, this);
12082 this.store.on('load', this.onLoad, this);
12083 this.store.on('loadexception', this.onLoadException, this);
12085 if(this.resizable){
12086 this.resizer = new Roo.Resizable(this.list, {
12087 pinned:true, handles:'se'
12089 this.resizer.on('resize', function(r, w, h){
12090 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12091 this.listWidth = w;
12092 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12093 this.restrictHeight();
12095 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12098 if(!this.editable){
12099 this.editable = true;
12100 this.setEditable(false);
12105 if (typeof(this.events.add.listeners) != 'undefined') {
12107 this.addicon = this.wrap.createChild(
12108 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12110 this.addicon.on('click', function(e) {
12111 this.fireEvent('add', this);
12114 if (typeof(this.events.edit.listeners) != 'undefined') {
12116 this.editicon = this.wrap.createChild(
12117 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12118 if (this.addicon) {
12119 this.editicon.setStyle('margin-left', '40px');
12121 this.editicon.on('click', function(e) {
12123 // we fire even if inothing is selected..
12124 this.fireEvent('edit', this, this.lastData );
12130 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12131 "up" : function(e){
12132 this.inKeyMode = true;
12136 "down" : function(e){
12137 if(!this.isExpanded()){
12138 this.onTriggerClick();
12140 this.inKeyMode = true;
12145 "enter" : function(e){
12146 // this.onViewClick();
12150 if(this.fireEvent("specialkey", this, e)){
12151 this.onViewClick(false);
12157 "esc" : function(e){
12161 "tab" : function(e){
12164 if(this.fireEvent("specialkey", this, e)){
12165 this.onViewClick(false);
12173 doRelay : function(foo, bar, hname){
12174 if(hname == 'down' || this.scope.isExpanded()){
12175 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12184 this.queryDelay = Math.max(this.queryDelay || 10,
12185 this.mode == 'local' ? 10 : 250);
12188 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12190 if(this.typeAhead){
12191 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12193 if(this.editable !== false){
12194 this.inputEl().on("keyup", this.onKeyUp, this);
12196 if(this.forceSelection){
12197 this.inputEl().on('blur', this.doForce, this);
12201 this.choices = this.el.select('ul.select2-choices', true).first();
12202 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12206 initTickableEvents: function()
12210 if(this.hiddenName){
12212 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12214 this.hiddenField.dom.value =
12215 this.hiddenValue !== undefined ? this.hiddenValue :
12216 this.value !== undefined ? this.value : '';
12218 // prevent input submission
12219 this.el.dom.removeAttribute('name');
12220 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12225 // this.list = this.el.select('ul.dropdown-menu',true).first();
12227 this.choices = this.el.select('ul.select2-choices', true).first();
12228 this.searchField = this.el.select('ul li.select2-search-field', true).first();
12229 if(this.triggerList){
12230 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12233 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12234 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12236 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12237 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12239 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12240 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12242 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12243 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12244 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12247 this.cancelBtn.hide();
12252 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12253 _this.list.setWidth(lw);
12256 this.list.on('mouseover', this.onViewOver, this);
12257 this.list.on('mousemove', this.onViewMove, this);
12259 this.list.on('scroll', this.onViewScroll, this);
12262 this.tpl = '<li class="select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
12265 this.view = new Roo.View(this.list, this.tpl, {
12266 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12269 //this.view.wrapEl.setDisplayed(false);
12270 this.view.on('click', this.onViewClick, this);
12274 this.store.on('beforeload', this.onBeforeLoad, this);
12275 this.store.on('load', this.onLoad, this);
12276 this.store.on('loadexception', this.onLoadException, this);
12279 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12280 "up" : function(e){
12281 this.inKeyMode = true;
12285 "down" : function(e){
12286 this.inKeyMode = true;
12290 "enter" : function(e){
12291 if(this.fireEvent("specialkey", this, e)){
12292 this.onViewClick(false);
12298 "esc" : function(e){
12299 this.onTickableFooterButtonClick(e, false, false);
12302 "tab" : function(e){
12303 this.fireEvent("specialkey", this, e);
12305 this.onTickableFooterButtonClick(e, false, false);
12312 doRelay : function(e, fn, key){
12313 if(this.scope.isExpanded()){
12314 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12323 this.queryDelay = Math.max(this.queryDelay || 10,
12324 this.mode == 'local' ? 10 : 250);
12327 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12329 if(this.typeAhead){
12330 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12333 if(this.editable !== false){
12334 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12339 onDestroy : function(){
12341 this.view.setStore(null);
12342 this.view.el.removeAllListeners();
12343 this.view.el.remove();
12344 this.view.purgeListeners();
12347 this.list.dom.innerHTML = '';
12351 this.store.un('beforeload', this.onBeforeLoad, this);
12352 this.store.un('load', this.onLoad, this);
12353 this.store.un('loadexception', this.onLoadException, this);
12355 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12359 fireKey : function(e){
12360 if(e.isNavKeyPress() && !this.list.isVisible()){
12361 this.fireEvent("specialkey", this, e);
12366 onResize: function(w, h){
12367 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12369 // if(typeof w != 'number'){
12370 // // we do not handle it!?!?
12373 // var tw = this.trigger.getWidth();
12374 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12375 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12377 // this.inputEl().setWidth( this.adjustWidth('input', x));
12379 // //this.trigger.setStyle('left', x+'px');
12381 // if(this.list && this.listWidth === undefined){
12382 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12383 // this.list.setWidth(lw);
12384 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12392 * Allow or prevent the user from directly editing the field text. If false is passed,
12393 * the user will only be able to select from the items defined in the dropdown list. This method
12394 * is the runtime equivalent of setting the 'editable' config option at config time.
12395 * @param {Boolean} value True to allow the user to directly edit the field text
12397 setEditable : function(value){
12398 if(value == this.editable){
12401 this.editable = value;
12403 this.inputEl().dom.setAttribute('readOnly', true);
12404 this.inputEl().on('mousedown', this.onTriggerClick, this);
12405 this.inputEl().addClass('x-combo-noedit');
12407 this.inputEl().dom.setAttribute('readOnly', false);
12408 this.inputEl().un('mousedown', this.onTriggerClick, this);
12409 this.inputEl().removeClass('x-combo-noedit');
12415 onBeforeLoad : function(combo,opts){
12416 if(!this.hasFocus){
12420 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12422 this.restrictHeight();
12423 this.selectedIndex = -1;
12427 onLoad : function(){
12429 this.hasQuery = false;
12431 if(!this.hasFocus){
12435 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12436 this.loading.hide();
12439 if(this.store.getCount() > 0){
12441 this.restrictHeight();
12442 if(this.lastQuery == this.allQuery){
12443 if(this.editable && !this.tickable){
12444 this.inputEl().dom.select();
12448 !this.selectByValue(this.value, true) &&
12451 !this.store.lastOptions ||
12452 typeof(this.store.lastOptions.add) == 'undefined' ||
12453 this.store.lastOptions.add != true
12456 this.select(0, true);
12459 if(this.autoFocus){
12462 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12463 this.taTask.delay(this.typeAheadDelay);
12467 this.onEmptyResults();
12473 onLoadException : function()
12475 this.hasQuery = false;
12477 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12478 this.loading.hide();
12481 if(this.tickable && this.editable){
12486 // only causes errors at present
12487 //Roo.log(this.store.reader.jsonData);
12488 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12490 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12496 onTypeAhead : function(){
12497 if(this.store.getCount() > 0){
12498 var r = this.store.getAt(0);
12499 var newValue = r.data[this.displayField];
12500 var len = newValue.length;
12501 var selStart = this.getRawValue().length;
12503 if(selStart != len){
12504 this.setRawValue(newValue);
12505 this.selectText(selStart, newValue.length);
12511 onSelect : function(record, index){
12513 if(this.fireEvent('beforeselect', this, record, index) !== false){
12515 this.setFromData(index > -1 ? record.data : false);
12518 this.fireEvent('select', this, record, index);
12523 * Returns the currently selected field value or empty string if no value is set.
12524 * @return {String} value The selected value
12526 getValue : function(){
12529 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12532 if(this.valueField){
12533 return typeof this.value != 'undefined' ? this.value : '';
12535 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12540 * Clears any text/value currently set in the field
12542 clearValue : function(){
12543 if(this.hiddenField){
12544 this.hiddenField.dom.value = '';
12547 this.setRawValue('');
12548 this.lastSelectionText = '';
12549 this.lastData = false;
12551 var close = this.closeTriggerEl();
12560 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12561 * will be displayed in the field. If the value does not match the data value of an existing item,
12562 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12563 * Otherwise the field will be blank (although the value will still be set).
12564 * @param {String} value The value to match
12566 setValue : function(v){
12573 if(this.valueField){
12574 var r = this.findRecord(this.valueField, v);
12576 text = r.data[this.displayField];
12577 }else if(this.valueNotFoundText !== undefined){
12578 text = this.valueNotFoundText;
12581 this.lastSelectionText = text;
12582 if(this.hiddenField){
12583 this.hiddenField.dom.value = v;
12585 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12588 var close = this.closeTriggerEl();
12591 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12595 * @property {Object} the last set data for the element
12600 * Sets the value of the field based on a object which is related to the record format for the store.
12601 * @param {Object} value the value to set as. or false on reset?
12603 setFromData : function(o){
12610 var dv = ''; // display value
12611 var vv = ''; // value value..
12613 if (this.displayField) {
12614 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12616 // this is an error condition!!!
12617 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12620 if(this.valueField){
12621 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12624 var close = this.closeTriggerEl();
12627 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12630 if(this.hiddenField){
12631 this.hiddenField.dom.value = vv;
12633 this.lastSelectionText = dv;
12634 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12638 // no hidden field.. - we store the value in 'value', but still display
12639 // display field!!!!
12640 this.lastSelectionText = dv;
12641 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12648 reset : function(){
12649 // overridden so that last data is reset..
12656 this.setValue(this.originalValue);
12657 this.clearInvalid();
12658 this.lastData = false;
12660 this.view.clearSelections();
12664 findRecord : function(prop, value){
12666 if(this.store.getCount() > 0){
12667 this.store.each(function(r){
12668 if(r.data[prop] == value){
12678 getName: function()
12680 // returns hidden if it's set..
12681 if (!this.rendered) {return ''};
12682 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
12686 onViewMove : function(e, t){
12687 this.inKeyMode = false;
12691 onViewOver : function(e, t){
12692 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12695 var item = this.view.findItemFromChild(t);
12698 var index = this.view.indexOf(item);
12699 this.select(index, false);
12704 onViewClick : function(view, doFocus, el, e)
12706 var index = this.view.getSelectedIndexes()[0];
12708 var r = this.store.getAt(index);
12712 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12719 Roo.each(this.tickItems, function(v,k){
12721 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12723 _this.tickItems.splice(k, 1);
12725 if(typeof(e) == 'undefined' && view == false){
12726 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12738 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
12739 this.tickItems.push(r.data);
12742 if(typeof(e) == 'undefined' && view == false){
12743 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12750 this.onSelect(r, index);
12752 if(doFocus !== false && !this.blockFocus){
12753 this.inputEl().focus();
12758 restrictHeight : function(){
12759 //this.innerList.dom.style.height = '';
12760 //var inner = this.innerList.dom;
12761 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12762 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12763 //this.list.beginUpdate();
12764 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12765 this.list.alignTo(this.inputEl(), this.listAlign);
12766 this.list.alignTo(this.inputEl(), this.listAlign);
12767 //this.list.endUpdate();
12771 onEmptyResults : function(){
12773 if(this.tickable && this.editable){
12774 this.restrictHeight();
12782 * Returns true if the dropdown list is expanded, else false.
12784 isExpanded : function(){
12785 return this.list.isVisible();
12789 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12790 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12791 * @param {String} value The data value of the item to select
12792 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12793 * selected item if it is not currently in view (defaults to true)
12794 * @return {Boolean} True if the value matched an item in the list, else false
12796 selectByValue : function(v, scrollIntoView){
12797 if(v !== undefined && v !== null){
12798 var r = this.findRecord(this.valueField || this.displayField, v);
12800 this.select(this.store.indexOf(r), scrollIntoView);
12808 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12809 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12810 * @param {Number} index The zero-based index of the list item to select
12811 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12812 * selected item if it is not currently in view (defaults to true)
12814 select : function(index, scrollIntoView){
12815 this.selectedIndex = index;
12816 this.view.select(index);
12817 if(scrollIntoView !== false){
12818 var el = this.view.getNode(index);
12820 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12823 this.list.scrollChildIntoView(el, false);
12829 selectNext : function(){
12830 var ct = this.store.getCount();
12832 if(this.selectedIndex == -1){
12834 }else if(this.selectedIndex < ct-1){
12835 this.select(this.selectedIndex+1);
12841 selectPrev : function(){
12842 var ct = this.store.getCount();
12844 if(this.selectedIndex == -1){
12846 }else if(this.selectedIndex != 0){
12847 this.select(this.selectedIndex-1);
12853 onKeyUp : function(e){
12854 if(this.editable !== false && !e.isSpecialKey()){
12855 this.lastKey = e.getKey();
12856 this.dqTask.delay(this.queryDelay);
12861 validateBlur : function(){
12862 return !this.list || !this.list.isVisible();
12866 initQuery : function(){
12868 var v = this.getRawValue();
12870 if(this.tickable && this.editable){
12871 v = this.tickableInputEl().getValue();
12878 doForce : function(){
12879 if(this.inputEl().dom.value.length > 0){
12880 this.inputEl().dom.value =
12881 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12887 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
12888 * query allowing the query action to be canceled if needed.
12889 * @param {String} query The SQL query to execute
12890 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12891 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
12892 * saved in the current store (defaults to false)
12894 doQuery : function(q, forceAll){
12896 if(q === undefined || q === null){
12901 forceAll: forceAll,
12905 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12910 forceAll = qe.forceAll;
12911 if(forceAll === true || (q.length >= this.minChars)){
12913 this.hasQuery = true;
12915 if(this.lastQuery != q || this.alwaysQuery){
12916 this.lastQuery = q;
12917 if(this.mode == 'local'){
12918 this.selectedIndex = -1;
12920 this.store.clearFilter();
12923 if(this.specialFilter){
12924 this.fireEvent('specialfilter', this);
12929 this.store.filter(this.displayField, q);
12932 this.store.fireEvent("datachanged", this.store);
12939 this.store.baseParams[this.queryParam] = q;
12941 var options = {params : this.getParams(q)};
12944 options.add = true;
12945 options.params.start = this.page * this.pageSize;
12948 this.store.load(options);
12951 * this code will make the page width larger, at the beginning, the list not align correctly,
12952 * we should expand the list on onLoad
12953 * so command out it
12958 this.selectedIndex = -1;
12963 this.loadNext = false;
12967 getParams : function(q){
12969 //p[this.queryParam] = q;
12973 p.limit = this.pageSize;
12979 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12981 collapse : function(){
12982 if(!this.isExpanded()){
12989 this.hasFocus = false;
12991 this.cancelBtn.hide();
12992 this.trigger.show();
12995 this.tickableInputEl().dom.value = '';
12996 this.tickableInputEl().blur();
13001 Roo.get(document).un('mousedown', this.collapseIf, this);
13002 Roo.get(document).un('mousewheel', this.collapseIf, this);
13003 if (!this.editable) {
13004 Roo.get(document).un('keydown', this.listKeyPress, this);
13006 this.fireEvent('collapse', this);
13010 collapseIf : function(e){
13011 var in_combo = e.within(this.el);
13012 var in_list = e.within(this.list);
13013 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13015 if (in_combo || in_list || is_list) {
13016 //e.stopPropagation();
13021 this.onTickableFooterButtonClick(e, false, false);
13029 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13031 expand : function(){
13033 if(this.isExpanded() || !this.hasFocus){
13037 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13038 this.list.setWidth(lw);
13045 this.restrictHeight();
13049 this.tickItems = Roo.apply([], this.item);
13052 this.cancelBtn.show();
13053 this.trigger.hide();
13056 this.tickableInputEl().focus();
13061 Roo.get(document).on('mousedown', this.collapseIf, this);
13062 Roo.get(document).on('mousewheel', this.collapseIf, this);
13063 if (!this.editable) {
13064 Roo.get(document).on('keydown', this.listKeyPress, this);
13067 this.fireEvent('expand', this);
13071 // Implements the default empty TriggerField.onTriggerClick function
13072 onTriggerClick : function(e)
13074 Roo.log('trigger click');
13076 if(this.disabled || !this.triggerList){
13081 this.loadNext = false;
13083 if(this.isExpanded()){
13085 if (!this.blockFocus) {
13086 this.inputEl().focus();
13090 this.hasFocus = true;
13091 if(this.triggerAction == 'all') {
13092 this.doQuery(this.allQuery, true);
13094 this.doQuery(this.getRawValue());
13096 if (!this.blockFocus) {
13097 this.inputEl().focus();
13102 onTickableTriggerClick : function(e)
13109 this.loadNext = false;
13110 this.hasFocus = true;
13112 if(this.triggerAction == 'all') {
13113 this.doQuery(this.allQuery, true);
13115 this.doQuery(this.getRawValue());
13119 onSearchFieldClick : function(e)
13121 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13122 this.onTickableFooterButtonClick(e, false, false);
13126 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13131 this.loadNext = false;
13132 this.hasFocus = true;
13134 if(this.triggerAction == 'all') {
13135 this.doQuery(this.allQuery, true);
13137 this.doQuery(this.getRawValue());
13141 listKeyPress : function(e)
13143 //Roo.log('listkeypress');
13144 // scroll to first matching element based on key pres..
13145 if (e.isSpecialKey()) {
13148 var k = String.fromCharCode(e.getKey()).toUpperCase();
13151 var csel = this.view.getSelectedNodes();
13152 var cselitem = false;
13154 var ix = this.view.indexOf(csel[0]);
13155 cselitem = this.store.getAt(ix);
13156 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13162 this.store.each(function(v) {
13164 // start at existing selection.
13165 if (cselitem.id == v.id) {
13171 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13172 match = this.store.indexOf(v);
13178 if (match === false) {
13179 return true; // no more action?
13182 this.view.select(match);
13183 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13184 sn.scrollIntoView(sn.dom.parentNode, false);
13187 onViewScroll : function(e, t){
13189 if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
13193 this.hasQuery = true;
13195 this.loading = this.list.select('.loading', true).first();
13197 if(this.loading === null){
13198 this.list.createChild({
13200 cls: 'loading select2-more-results select2-active',
13201 html: 'Loading more results...'
13204 this.loading = this.list.select('.loading', true).first();
13206 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13208 this.loading.hide();
13211 this.loading.show();
13216 this.loadNext = true;
13218 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13223 addItem : function(o)
13225 var dv = ''; // display value
13227 if (this.displayField) {
13228 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13230 // this is an error condition!!!
13231 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13238 var choice = this.choices.createChild({
13240 cls: 'select2-search-choice',
13249 cls: 'select2-search-choice-close',
13254 }, this.searchField);
13256 var close = choice.select('a.select2-search-choice-close', true).first();
13258 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13266 this.inputEl().dom.value = '';
13271 onRemoveItem : function(e, _self, o)
13273 e.preventDefault();
13275 this.lastItem = Roo.apply([], this.item);
13277 var index = this.item.indexOf(o.data) * 1;
13280 Roo.log('not this item?!');
13284 this.item.splice(index, 1);
13289 this.fireEvent('remove', this, e);
13295 syncValue : function()
13297 if(!this.item.length){
13304 Roo.each(this.item, function(i){
13305 if(_this.valueField){
13306 value.push(i[_this.valueField]);
13313 this.value = value.join(',');
13315 if(this.hiddenField){
13316 this.hiddenField.dom.value = this.value;
13319 this.store.fireEvent("datachanged", this.store);
13322 clearItem : function()
13324 if(!this.multiple){
13330 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13338 if(this.tickable && !Roo.isTouch){
13339 this.view.refresh();
13343 inputEl: function ()
13345 if(Roo.isTouch && this.mobileTouchView){
13346 return this.el.select('input.form-control',true).first();
13350 return this.searchField;
13353 return this.el.select('input.form-control',true).first();
13357 onTickableFooterButtonClick : function(e, btn, el)
13359 e.preventDefault();
13361 this.lastItem = Roo.apply([], this.item);
13363 if(btn && btn.name == 'cancel'){
13364 this.tickItems = Roo.apply([], this.item);
13373 Roo.each(this.tickItems, function(o){
13381 validate : function()
13383 var v = this.getRawValue();
13386 v = this.getValue();
13389 if(this.disabled || this.allowBlank || v.length){
13394 this.markInvalid();
13398 tickableInputEl : function()
13400 if(!this.tickable || !this.editable){
13401 return this.inputEl();
13404 return this.inputEl().select('.select2-search-field-input', true).first();
13408 getAutoCreateTouchView : function()
13413 cls: 'form-group' //input-group
13419 type : this.inputType,
13420 cls : 'form-control x-combo-noedit',
13421 autocomplete: 'new-password',
13422 placeholder : this.placeholder || '',
13427 input.name = this.name;
13431 input.cls += ' input-' + this.size;
13434 if (this.disabled) {
13435 input.disabled = true;
13446 inputblock.cls += ' input-group';
13448 inputblock.cn.unshift({
13450 cls : 'input-group-addon',
13455 if(this.removable && !this.multiple){
13456 inputblock.cls += ' roo-removable';
13458 inputblock.cn.push({
13461 cls : 'roo-combo-removable-btn close'
13465 if(this.hasFeedback && !this.allowBlank){
13467 inputblock.cls += ' has-feedback';
13469 inputblock.cn.push({
13471 cls: 'glyphicon form-control-feedback'
13478 inputblock.cls += (this.before) ? '' : ' input-group';
13480 inputblock.cn.push({
13482 cls : 'input-group-addon',
13493 cls: 'form-hidden-field'
13507 cls: 'form-hidden-field'
13511 cls: 'select2-choices',
13515 cls: 'select2-search-field',
13528 cls: 'select2-container input-group',
13535 combobox.cls += ' select2-container-multi';
13538 var align = this.labelAlign || this.parentLabelAlign();
13542 if(this.fieldLabel.length){
13544 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13545 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13550 cls : 'control-label ' + lw,
13551 html : this.fieldLabel
13563 var settings = this;
13565 ['xs','sm','md','lg'].map(function(size){
13566 if (settings[size]) {
13567 cfg.cls += ' col-' + size + '-' + settings[size];
13574 initTouchView : function()
13576 this.renderTouchView();
13578 this.touchViewEl.on('scroll', function(){
13579 this.el.dom.scrollTop = 0;
13582 this.originalValue = this.getValue();
13584 this.inputEl().on("click", this.showTouchView, this);
13586 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13587 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13589 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13591 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13592 this.store.on('load', this.onTouchViewLoad, this);
13593 this.store.on('loadexception', this.onTouchViewLoadException, this);
13595 if(this.hiddenName){
13597 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13599 this.hiddenField.dom.value =
13600 this.hiddenValue !== undefined ? this.hiddenValue :
13601 this.value !== undefined ? this.value : '';
13603 this.el.dom.removeAttribute('name');
13604 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13608 this.choices = this.el.select('ul.select2-choices', true).first();
13609 this.searchField = this.el.select('ul li.select2-search-field', true).first();
13612 if(this.removable && !this.multiple){
13613 var close = this.closeTriggerEl();
13615 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13616 close.on('click', this.removeBtnClick, this, close);
13620 * fix the bug in Safari iOS8
13622 this.inputEl().on("focus", function(e){
13623 document.activeElement.blur();
13631 renderTouchView : function()
13633 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13634 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13636 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13637 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13639 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13640 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13641 this.touchViewBodyEl.setStyle('overflow', 'auto');
13643 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13644 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13646 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13647 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13651 showTouchView : function()
13657 this.touchViewHeaderEl.hide();
13659 if(this.fieldLabel.length){
13660 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13661 this.touchViewHeaderEl.show();
13664 this.touchViewEl.show();
13666 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13667 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13669 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13671 if(this.fieldLabel.length){
13672 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13675 this.touchViewBodyEl.setHeight(bodyHeight);
13679 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13681 this.touchViewEl.addClass('in');
13684 this.doTouchViewQuery();
13688 hideTouchView : function()
13690 this.touchViewEl.removeClass('in');
13694 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13696 this.touchViewEl.setStyle('display', 'none');
13701 setTouchViewValue : function()
13708 Roo.each(this.tickItems, function(o){
13713 this.hideTouchView();
13716 doTouchViewQuery : function()
13725 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13729 if(!this.alwaysQuery || this.mode == 'local'){
13730 this.onTouchViewLoad();
13737 onTouchViewBeforeLoad : function(combo,opts)
13743 onTouchViewLoad : function()
13745 if(this.store.getCount() < 1){
13746 this.onTouchViewEmptyResults();
13750 this.clearTouchView();
13752 var rawValue = this.getRawValue();
13754 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13756 this.tickItems = [];
13758 this.store.data.each(function(d, rowIndex){
13759 var row = this.touchViewListGroup.createChild(template);
13761 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13764 html : d.data[this.displayField]
13767 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
13768 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
13772 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13773 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13776 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13777 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13778 this.tickItems.push(d.data);
13781 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13785 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13787 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13789 if(this.fieldLabel.length){
13790 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13793 var listHeight = this.touchViewListGroup.getHeight();
13797 if(firstChecked && listHeight > bodyHeight){
13798 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13803 onTouchViewLoadException : function()
13805 this.hideTouchView();
13808 onTouchViewEmptyResults : function()
13810 this.clearTouchView();
13812 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13814 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13818 clearTouchView : function()
13820 this.touchViewListGroup.dom.innerHTML = '';
13823 onTouchViewClick : function(e, el, o)
13825 e.preventDefault();
13828 var rowIndex = o.rowIndex;
13830 var r = this.store.getAt(rowIndex);
13832 if(!this.multiple){
13833 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13834 c.dom.removeAttribute('checked');
13837 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13839 this.setFromData(r.data);
13841 var close = this.closeTriggerEl();
13847 this.hideTouchView();
13849 this.fireEvent('select', this, r, rowIndex);
13854 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13855 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13856 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13860 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13861 this.addItem(r.data);
13862 this.tickItems.push(r.data);
13868 * @cfg {Boolean} grow
13872 * @cfg {Number} growMin
13876 * @cfg {Number} growMax
13885 Roo.apply(Roo.bootstrap.ComboBox, {
13889 cls: 'modal-header',
13911 cls: 'list-group-item',
13915 cls: 'roo-combobox-list-group-item-value'
13919 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13933 listItemCheckbox : {
13935 cls: 'list-group-item',
13939 cls: 'roo-combobox-list-group-item-value'
13943 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13959 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13964 cls: 'modal-footer',
13972 cls: 'col-xs-6 text-left',
13975 cls: 'btn btn-danger roo-touch-view-cancel',
13981 cls: 'col-xs-6 text-right',
13984 cls: 'btn btn-success roo-touch-view-ok',
13995 Roo.apply(Roo.bootstrap.ComboBox, {
13997 touchViewTemplate : {
13999 cls: 'modal fade roo-combobox-touch-view',
14003 cls: 'modal-dialog',
14004 style : 'position:fixed', // we have to fix position....
14008 cls: 'modal-content',
14010 Roo.bootstrap.ComboBox.header,
14011 Roo.bootstrap.ComboBox.body,
14012 Roo.bootstrap.ComboBox.footer
14021 * Ext JS Library 1.1.1
14022 * Copyright(c) 2006-2007, Ext JS, LLC.
14024 * Originally Released Under LGPL - original licence link has changed is not relivant.
14027 * <script type="text/javascript">
14032 * @extends Roo.util.Observable
14033 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14034 * This class also supports single and multi selection modes. <br>
14035 * Create a data model bound view:
14037 var store = new Roo.data.Store(...);
14039 var view = new Roo.View({
14041 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14043 singleSelect: true,
14044 selectedClass: "ydataview-selected",
14048 // listen for node click?
14049 view.on("click", function(vw, index, node, e){
14050 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14054 dataModel.load("foobar.xml");
14056 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14058 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14059 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14061 * Note: old style constructor is still suported (container, template, config)
14064 * Create a new View
14065 * @param {Object} config The config object
14068 Roo.View = function(config, depreciated_tpl, depreciated_config){
14070 this.parent = false;
14072 if (typeof(depreciated_tpl) == 'undefined') {
14073 // new way.. - universal constructor.
14074 Roo.apply(this, config);
14075 this.el = Roo.get(this.el);
14078 this.el = Roo.get(config);
14079 this.tpl = depreciated_tpl;
14080 Roo.apply(this, depreciated_config);
14082 this.wrapEl = this.el.wrap().wrap();
14083 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14086 if(typeof(this.tpl) == "string"){
14087 this.tpl = new Roo.Template(this.tpl);
14089 // support xtype ctors..
14090 this.tpl = new Roo.factory(this.tpl, Roo);
14094 this.tpl.compile();
14099 * @event beforeclick
14100 * Fires before a click is processed. Returns false to cancel the default action.
14101 * @param {Roo.View} this
14102 * @param {Number} index The index of the target node
14103 * @param {HTMLElement} node The target node
14104 * @param {Roo.EventObject} e The raw event object
14106 "beforeclick" : true,
14109 * Fires when a template node is clicked.
14110 * @param {Roo.View} this
14111 * @param {Number} index The index of the target node
14112 * @param {HTMLElement} node The target node
14113 * @param {Roo.EventObject} e The raw event object
14118 * Fires when a template node is double clicked.
14119 * @param {Roo.View} this
14120 * @param {Number} index The index of the target node
14121 * @param {HTMLElement} node The target node
14122 * @param {Roo.EventObject} e The raw event object
14126 * @event contextmenu
14127 * Fires when a template node is right clicked.
14128 * @param {Roo.View} this
14129 * @param {Number} index The index of the target node
14130 * @param {HTMLElement} node The target node
14131 * @param {Roo.EventObject} e The raw event object
14133 "contextmenu" : true,
14135 * @event selectionchange
14136 * Fires when the selected nodes change.
14137 * @param {Roo.View} this
14138 * @param {Array} selections Array of the selected nodes
14140 "selectionchange" : true,
14143 * @event beforeselect
14144 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14145 * @param {Roo.View} this
14146 * @param {HTMLElement} node The node to be selected
14147 * @param {Array} selections Array of currently selected nodes
14149 "beforeselect" : true,
14151 * @event preparedata
14152 * Fires on every row to render, to allow you to change the data.
14153 * @param {Roo.View} this
14154 * @param {Object} data to be rendered (change this)
14156 "preparedata" : true
14164 "click": this.onClick,
14165 "dblclick": this.onDblClick,
14166 "contextmenu": this.onContextMenu,
14170 this.selections = [];
14172 this.cmp = new Roo.CompositeElementLite([]);
14174 this.store = Roo.factory(this.store, Roo.data);
14175 this.setStore(this.store, true);
14178 if ( this.footer && this.footer.xtype) {
14180 var fctr = this.wrapEl.appendChild(document.createElement("div"));
14182 this.footer.dataSource = this.store;
14183 this.footer.container = fctr;
14184 this.footer = Roo.factory(this.footer, Roo);
14185 fctr.insertFirst(this.el);
14187 // this is a bit insane - as the paging toolbar seems to detach the el..
14188 // dom.parentNode.parentNode.parentNode
14189 // they get detached?
14193 Roo.View.superclass.constructor.call(this);
14198 Roo.extend(Roo.View, Roo.util.Observable, {
14201 * @cfg {Roo.data.Store} store Data store to load data from.
14206 * @cfg {String|Roo.Element} el The container element.
14211 * @cfg {String|Roo.Template} tpl The template used by this View
14215 * @cfg {String} dataName the named area of the template to use as the data area
14216 * Works with domtemplates roo-name="name"
14220 * @cfg {String} selectedClass The css class to add to selected nodes
14222 selectedClass : "x-view-selected",
14224 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14229 * @cfg {String} text to display on mask (default Loading)
14233 * @cfg {Boolean} multiSelect Allow multiple selection
14235 multiSelect : false,
14237 * @cfg {Boolean} singleSelect Allow single selection
14239 singleSelect: false,
14242 * @cfg {Boolean} toggleSelect - selecting
14244 toggleSelect : false,
14247 * @cfg {Boolean} tickable - selecting
14252 * Returns the element this view is bound to.
14253 * @return {Roo.Element}
14255 getEl : function(){
14256 return this.wrapEl;
14262 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14264 refresh : function(){
14265 //Roo.log('refresh');
14268 // if we are using something like 'domtemplate', then
14269 // the what gets used is:
14270 // t.applySubtemplate(NAME, data, wrapping data..)
14271 // the outer template then get' applied with
14272 // the store 'extra data'
14273 // and the body get's added to the
14274 // roo-name="data" node?
14275 // <span class='roo-tpl-{name}'></span> ?????
14279 this.clearSelections();
14280 this.el.update("");
14282 var records = this.store.getRange();
14283 if(records.length < 1) {
14285 // is this valid?? = should it render a template??
14287 this.el.update(this.emptyText);
14291 if (this.dataName) {
14292 this.el.update(t.apply(this.store.meta)); //????
14293 el = this.el.child('.roo-tpl-' + this.dataName);
14296 for(var i = 0, len = records.length; i < len; i++){
14297 var data = this.prepareData(records[i].data, i, records[i]);
14298 this.fireEvent("preparedata", this, data, i, records[i]);
14300 var d = Roo.apply({}, data);
14303 Roo.apply(d, {'roo-id' : Roo.id()});
14307 Roo.each(this.parent.item, function(item){
14308 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14311 Roo.apply(d, {'roo-data-checked' : 'checked'});
14315 html[html.length] = Roo.util.Format.trim(
14317 t.applySubtemplate(this.dataName, d, this.store.meta) :
14324 el.update(html.join(""));
14325 this.nodes = el.dom.childNodes;
14326 this.updateIndexes(0);
14331 * Function to override to reformat the data that is sent to
14332 * the template for each node.
14333 * DEPRICATED - use the preparedata event handler.
14334 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14335 * a JSON object for an UpdateManager bound view).
14337 prepareData : function(data, index, record)
14339 this.fireEvent("preparedata", this, data, index, record);
14343 onUpdate : function(ds, record){
14344 // Roo.log('on update');
14345 this.clearSelections();
14346 var index = this.store.indexOf(record);
14347 var n = this.nodes[index];
14348 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14349 n.parentNode.removeChild(n);
14350 this.updateIndexes(index, index);
14356 onAdd : function(ds, records, index)
14358 //Roo.log(['on Add', ds, records, index] );
14359 this.clearSelections();
14360 if(this.nodes.length == 0){
14364 var n = this.nodes[index];
14365 for(var i = 0, len = records.length; i < len; i++){
14366 var d = this.prepareData(records[i].data, i, records[i]);
14368 this.tpl.insertBefore(n, d);
14371 this.tpl.append(this.el, d);
14374 this.updateIndexes(index);
14377 onRemove : function(ds, record, index){
14378 // Roo.log('onRemove');
14379 this.clearSelections();
14380 var el = this.dataName ?
14381 this.el.child('.roo-tpl-' + this.dataName) :
14384 el.dom.removeChild(this.nodes[index]);
14385 this.updateIndexes(index);
14389 * Refresh an individual node.
14390 * @param {Number} index
14392 refreshNode : function(index){
14393 this.onUpdate(this.store, this.store.getAt(index));
14396 updateIndexes : function(startIndex, endIndex){
14397 var ns = this.nodes;
14398 startIndex = startIndex || 0;
14399 endIndex = endIndex || ns.length - 1;
14400 for(var i = startIndex; i <= endIndex; i++){
14401 ns[i].nodeIndex = i;
14406 * Changes the data store this view uses and refresh the view.
14407 * @param {Store} store
14409 setStore : function(store, initial){
14410 if(!initial && this.store){
14411 this.store.un("datachanged", this.refresh);
14412 this.store.un("add", this.onAdd);
14413 this.store.un("remove", this.onRemove);
14414 this.store.un("update", this.onUpdate);
14415 this.store.un("clear", this.refresh);
14416 this.store.un("beforeload", this.onBeforeLoad);
14417 this.store.un("load", this.onLoad);
14418 this.store.un("loadexception", this.onLoad);
14422 store.on("datachanged", this.refresh, this);
14423 store.on("add", this.onAdd, this);
14424 store.on("remove", this.onRemove, this);
14425 store.on("update", this.onUpdate, this);
14426 store.on("clear", this.refresh, this);
14427 store.on("beforeload", this.onBeforeLoad, this);
14428 store.on("load", this.onLoad, this);
14429 store.on("loadexception", this.onLoad, this);
14437 * onbeforeLoad - masks the loading area.
14440 onBeforeLoad : function(store,opts)
14442 //Roo.log('onBeforeLoad');
14444 this.el.update("");
14446 this.el.mask(this.mask ? this.mask : "Loading" );
14448 onLoad : function ()
14455 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14456 * @param {HTMLElement} node
14457 * @return {HTMLElement} The template node
14459 findItemFromChild : function(node){
14460 var el = this.dataName ?
14461 this.el.child('.roo-tpl-' + this.dataName,true) :
14464 if(!node || node.parentNode == el){
14467 var p = node.parentNode;
14468 while(p && p != el){
14469 if(p.parentNode == el){
14478 onClick : function(e){
14479 var item = this.findItemFromChild(e.getTarget());
14481 var index = this.indexOf(item);
14482 if(this.onItemClick(item, index, e) !== false){
14483 this.fireEvent("click", this, index, item, e);
14486 this.clearSelections();
14491 onContextMenu : function(e){
14492 var item = this.findItemFromChild(e.getTarget());
14494 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14499 onDblClick : function(e){
14500 var item = this.findItemFromChild(e.getTarget());
14502 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14506 onItemClick : function(item, index, e)
14508 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14511 if (this.toggleSelect) {
14512 var m = this.isSelected(item) ? 'unselect' : 'select';
14515 _t[m](item, true, false);
14518 if(this.multiSelect || this.singleSelect){
14519 if(this.multiSelect && e.shiftKey && this.lastSelection){
14520 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14522 this.select(item, this.multiSelect && e.ctrlKey);
14523 this.lastSelection = item;
14526 if(!this.tickable){
14527 e.preventDefault();
14535 * Get the number of selected nodes.
14538 getSelectionCount : function(){
14539 return this.selections.length;
14543 * Get the currently selected nodes.
14544 * @return {Array} An array of HTMLElements
14546 getSelectedNodes : function(){
14547 return this.selections;
14551 * Get the indexes of the selected nodes.
14554 getSelectedIndexes : function(){
14555 var indexes = [], s = this.selections;
14556 for(var i = 0, len = s.length; i < len; i++){
14557 indexes.push(s[i].nodeIndex);
14563 * Clear all selections
14564 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14566 clearSelections : function(suppressEvent){
14567 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14568 this.cmp.elements = this.selections;
14569 this.cmp.removeClass(this.selectedClass);
14570 this.selections = [];
14571 if(!suppressEvent){
14572 this.fireEvent("selectionchange", this, this.selections);
14578 * Returns true if the passed node is selected
14579 * @param {HTMLElement/Number} node The node or node index
14580 * @return {Boolean}
14582 isSelected : function(node){
14583 var s = this.selections;
14587 node = this.getNode(node);
14588 return s.indexOf(node) !== -1;
14593 * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
14594 * @param {Boolean} keepExisting (optional) true to keep existing selections
14595 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14597 select : function(nodeInfo, keepExisting, suppressEvent){
14598 if(nodeInfo instanceof Array){
14600 this.clearSelections(true);
14602 for(var i = 0, len = nodeInfo.length; i < len; i++){
14603 this.select(nodeInfo[i], true, true);
14607 var node = this.getNode(nodeInfo);
14608 if(!node || this.isSelected(node)){
14609 return; // already selected.
14612 this.clearSelections(true);
14615 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14616 Roo.fly(node).addClass(this.selectedClass);
14617 this.selections.push(node);
14618 if(!suppressEvent){
14619 this.fireEvent("selectionchange", this, this.selections);
14627 * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
14628 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14629 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14631 unselect : function(nodeInfo, keepExisting, suppressEvent)
14633 if(nodeInfo instanceof Array){
14634 Roo.each(this.selections, function(s) {
14635 this.unselect(s, nodeInfo);
14639 var node = this.getNode(nodeInfo);
14640 if(!node || !this.isSelected(node)){
14641 //Roo.log("not selected");
14642 return; // not selected.
14646 Roo.each(this.selections, function(s) {
14648 Roo.fly(node).removeClass(this.selectedClass);
14655 this.selections= ns;
14656 this.fireEvent("selectionchange", this, this.selections);
14660 * Gets a template node.
14661 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14662 * @return {HTMLElement} The node or null if it wasn't found
14664 getNode : function(nodeInfo){
14665 if(typeof nodeInfo == "string"){
14666 return document.getElementById(nodeInfo);
14667 }else if(typeof nodeInfo == "number"){
14668 return this.nodes[nodeInfo];
14674 * Gets a range template nodes.
14675 * @param {Number} startIndex
14676 * @param {Number} endIndex
14677 * @return {Array} An array of nodes
14679 getNodes : function(start, end){
14680 var ns = this.nodes;
14681 start = start || 0;
14682 end = typeof end == "undefined" ? ns.length - 1 : end;
14685 for(var i = start; i <= end; i++){
14689 for(var i = start; i >= end; i--){
14697 * Finds the index of the passed node
14698 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14699 * @return {Number} The index of the node or -1
14701 indexOf : function(node){
14702 node = this.getNode(node);
14703 if(typeof node.nodeIndex == "number"){
14704 return node.nodeIndex;
14706 var ns = this.nodes;
14707 for(var i = 0, len = ns.length; i < len; i++){
14718 * based on jquery fullcalendar
14722 Roo.bootstrap = Roo.bootstrap || {};
14724 * @class Roo.bootstrap.Calendar
14725 * @extends Roo.bootstrap.Component
14726 * Bootstrap Calendar class
14727 * @cfg {Boolean} loadMask (true|false) default false
14728 * @cfg {Object} header generate the user specific header of the calendar, default false
14731 * Create a new Container
14732 * @param {Object} config The config object
14737 Roo.bootstrap.Calendar = function(config){
14738 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14742 * Fires when a date is selected
14743 * @param {DatePicker} this
14744 * @param {Date} date The selected date
14748 * @event monthchange
14749 * Fires when the displayed month changes
14750 * @param {DatePicker} this
14751 * @param {Date} date The selected month
14753 'monthchange': true,
14755 * @event evententer
14756 * Fires when mouse over an event
14757 * @param {Calendar} this
14758 * @param {event} Event
14760 'evententer': true,
14762 * @event eventleave
14763 * Fires when the mouse leaves an
14764 * @param {Calendar} this
14767 'eventleave': true,
14769 * @event eventclick
14770 * Fires when the mouse click an
14771 * @param {Calendar} this
14780 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
14783 * @cfg {Number} startDay
14784 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14792 getAutoCreate : function(){
14795 var fc_button = function(name, corner, style, content ) {
14796 return Roo.apply({},{
14798 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
14800 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14803 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14814 style : 'width:100%',
14821 cls : 'fc-header-left',
14823 fc_button('prev', 'left', 'arrow', '‹' ),
14824 fc_button('next', 'right', 'arrow', '›' ),
14825 { tag: 'span', cls: 'fc-header-space' },
14826 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
14834 cls : 'fc-header-center',
14838 cls: 'fc-header-title',
14841 html : 'month / year'
14849 cls : 'fc-header-right',
14851 /* fc_button('month', 'left', '', 'month' ),
14852 fc_button('week', '', '', 'week' ),
14853 fc_button('day', 'right', '', 'day' )
14865 header = this.header;
14868 var cal_heads = function() {
14870 // fixme - handle this.
14872 for (var i =0; i < Date.dayNames.length; i++) {
14873 var d = Date.dayNames[i];
14876 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14877 html : d.substring(0,3)
14881 ret[0].cls += ' fc-first';
14882 ret[6].cls += ' fc-last';
14885 var cal_cell = function(n) {
14888 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14893 cls: 'fc-day-number',
14897 cls: 'fc-day-content',
14901 style: 'position: relative;' // height: 17px;
14913 var cal_rows = function() {
14916 for (var r = 0; r < 6; r++) {
14923 for (var i =0; i < Date.dayNames.length; i++) {
14924 var d = Date.dayNames[i];
14925 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14928 row.cn[0].cls+=' fc-first';
14929 row.cn[0].cn[0].style = 'min-height:90px';
14930 row.cn[6].cls+=' fc-last';
14934 ret[0].cls += ' fc-first';
14935 ret[4].cls += ' fc-prev-last';
14936 ret[5].cls += ' fc-last';
14943 cls: 'fc-border-separate',
14944 style : 'width:100%',
14952 cls : 'fc-first fc-last',
14970 cls : 'fc-content',
14971 style : "position: relative;",
14974 cls : 'fc-view fc-view-month fc-grid',
14975 style : 'position: relative',
14976 unselectable : 'on',
14979 cls : 'fc-event-container',
14980 style : 'position:absolute;z-index:8;top:0;left:0;'
14998 initEvents : function()
15001 throw "can not find store for calendar";
15007 style: "text-align:center",
15011 style: "background-color:white;width:50%;margin:250 auto",
15015 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15026 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15028 var size = this.el.select('.fc-content', true).first().getSize();
15029 this.maskEl.setSize(size.width, size.height);
15030 this.maskEl.enableDisplayMode("block");
15031 if(!this.loadMask){
15032 this.maskEl.hide();
15035 this.store = Roo.factory(this.store, Roo.data);
15036 this.store.on('load', this.onLoad, this);
15037 this.store.on('beforeload', this.onBeforeLoad, this);
15041 this.cells = this.el.select('.fc-day',true);
15042 //Roo.log(this.cells);
15043 this.textNodes = this.el.query('.fc-day-number');
15044 this.cells.addClassOnOver('fc-state-hover');
15046 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15047 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15048 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15049 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15051 this.on('monthchange', this.onMonthChange, this);
15053 this.update(new Date().clearTime());
15056 resize : function() {
15057 var sz = this.el.getSize();
15059 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15060 this.el.select('.fc-day-content div',true).setHeight(34);
15065 showPrevMonth : function(e){
15066 this.update(this.activeDate.add("mo", -1));
15068 showToday : function(e){
15069 this.update(new Date().clearTime());
15072 showNextMonth : function(e){
15073 this.update(this.activeDate.add("mo", 1));
15077 showPrevYear : function(){
15078 this.update(this.activeDate.add("y", -1));
15082 showNextYear : function(){
15083 this.update(this.activeDate.add("y", 1));
15088 update : function(date)
15090 var vd = this.activeDate;
15091 this.activeDate = date;
15092 // if(vd && this.el){
15093 // var t = date.getTime();
15094 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15095 // Roo.log('using add remove');
15097 // this.fireEvent('monthchange', this, date);
15099 // this.cells.removeClass("fc-state-highlight");
15100 // this.cells.each(function(c){
15101 // if(c.dateValue == t){
15102 // c.addClass("fc-state-highlight");
15103 // setTimeout(function(){
15104 // try{c.dom.firstChild.focus();}catch(e){}
15114 var days = date.getDaysInMonth();
15116 var firstOfMonth = date.getFirstDateOfMonth();
15117 var startingPos = firstOfMonth.getDay()-this.startDay;
15119 if(startingPos < this.startDay){
15123 var pm = date.add(Date.MONTH, -1);
15124 var prevStart = pm.getDaysInMonth()-startingPos;
15126 this.cells = this.el.select('.fc-day',true);
15127 this.textNodes = this.el.query('.fc-day-number');
15128 this.cells.addClassOnOver('fc-state-hover');
15130 var cells = this.cells.elements;
15131 var textEls = this.textNodes;
15133 Roo.each(cells, function(cell){
15134 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15137 days += startingPos;
15139 // convert everything to numbers so it's fast
15140 var day = 86400000;
15141 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15144 //Roo.log(prevStart);
15146 var today = new Date().clearTime().getTime();
15147 var sel = date.clearTime().getTime();
15148 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15149 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15150 var ddMatch = this.disabledDatesRE;
15151 var ddText = this.disabledDatesText;
15152 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15153 var ddaysText = this.disabledDaysText;
15154 var format = this.format;
15156 var setCellClass = function(cal, cell){
15160 //Roo.log('set Cell Class');
15162 var t = d.getTime();
15166 cell.dateValue = t;
15168 cell.className += " fc-today";
15169 cell.className += " fc-state-highlight";
15170 cell.title = cal.todayText;
15173 // disable highlight in other month..
15174 //cell.className += " fc-state-highlight";
15179 cell.className = " fc-state-disabled";
15180 cell.title = cal.minText;
15184 cell.className = " fc-state-disabled";
15185 cell.title = cal.maxText;
15189 if(ddays.indexOf(d.getDay()) != -1){
15190 cell.title = ddaysText;
15191 cell.className = " fc-state-disabled";
15194 if(ddMatch && format){
15195 var fvalue = d.dateFormat(format);
15196 if(ddMatch.test(fvalue)){
15197 cell.title = ddText.replace("%0", fvalue);
15198 cell.className = " fc-state-disabled";
15202 if (!cell.initialClassName) {
15203 cell.initialClassName = cell.dom.className;
15206 cell.dom.className = cell.initialClassName + ' ' + cell.className;
15211 for(; i < startingPos; i++) {
15212 textEls[i].innerHTML = (++prevStart);
15213 d.setDate(d.getDate()+1);
15215 cells[i].className = "fc-past fc-other-month";
15216 setCellClass(this, cells[i]);
15221 for(; i < days; i++){
15222 intDay = i - startingPos + 1;
15223 textEls[i].innerHTML = (intDay);
15224 d.setDate(d.getDate()+1);
15226 cells[i].className = ''; // "x-date-active";
15227 setCellClass(this, cells[i]);
15231 for(; i < 42; i++) {
15232 textEls[i].innerHTML = (++extraDays);
15233 d.setDate(d.getDate()+1);
15235 cells[i].className = "fc-future fc-other-month";
15236 setCellClass(this, cells[i]);
15239 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15241 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15243 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15244 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15246 if(totalRows != 6){
15247 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15248 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15251 this.fireEvent('monthchange', this, date);
15255 if(!this.internalRender){
15256 var main = this.el.dom.firstChild;
15257 var w = main.offsetWidth;
15258 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15259 Roo.fly(main).setWidth(w);
15260 this.internalRender = true;
15261 // opera does not respect the auto grow header center column
15262 // then, after it gets a width opera refuses to recalculate
15263 // without a second pass
15264 if(Roo.isOpera && !this.secondPass){
15265 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15266 this.secondPass = true;
15267 this.update.defer(10, this, [date]);
15274 findCell : function(dt) {
15275 dt = dt.clearTime().getTime();
15277 this.cells.each(function(c){
15278 //Roo.log("check " +c.dateValue + '?=' + dt);
15279 if(c.dateValue == dt){
15289 findCells : function(ev) {
15290 var s = ev.start.clone().clearTime().getTime();
15292 var e= ev.end.clone().clearTime().getTime();
15295 this.cells.each(function(c){
15296 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15298 if(c.dateValue > e){
15301 if(c.dateValue < s){
15310 // findBestRow: function(cells)
15314 // for (var i =0 ; i < cells.length;i++) {
15315 // ret = Math.max(cells[i].rows || 0,ret);
15322 addItem : function(ev)
15324 // look for vertical location slot in
15325 var cells = this.findCells(ev);
15327 // ev.row = this.findBestRow(cells);
15329 // work out the location.
15333 for(var i =0; i < cells.length; i++) {
15335 cells[i].row = cells[0].row;
15338 cells[i].row = cells[i].row + 1;
15348 if (crow.start.getY() == cells[i].getY()) {
15350 crow.end = cells[i];
15367 cells[0].events.push(ev);
15369 this.calevents.push(ev);
15372 clearEvents: function() {
15374 if(!this.calevents){
15378 Roo.each(this.cells.elements, function(c){
15384 Roo.each(this.calevents, function(e) {
15385 Roo.each(e.els, function(el) {
15386 el.un('mouseenter' ,this.onEventEnter, this);
15387 el.un('mouseleave' ,this.onEventLeave, this);
15392 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15398 renderEvents: function()
15402 this.cells.each(function(c) {
15411 if(c.row != c.events.length){
15412 r = 4 - (4 - (c.row - c.events.length));
15415 c.events = ev.slice(0, r);
15416 c.more = ev.slice(r);
15418 if(c.more.length && c.more.length == 1){
15419 c.events.push(c.more.pop());
15422 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15426 this.cells.each(function(c) {
15428 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15431 for (var e = 0; e < c.events.length; e++){
15432 var ev = c.events[e];
15433 var rows = ev.rows;
15435 for(var i = 0; i < rows.length; i++) {
15437 // how many rows should it span..
15440 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15441 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15443 unselectable : "on",
15446 cls: 'fc-event-inner',
15450 // cls: 'fc-event-time',
15451 // html : cells.length > 1 ? '' : ev.time
15455 cls: 'fc-event-title',
15456 html : String.format('{0}', ev.title)
15463 cls: 'ui-resizable-handle ui-resizable-e',
15464 html : '  '
15471 cfg.cls += ' fc-event-start';
15473 if ((i+1) == rows.length) {
15474 cfg.cls += ' fc-event-end';
15477 var ctr = _this.el.select('.fc-event-container',true).first();
15478 var cg = ctr.createChild(cfg);
15480 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15481 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15483 var r = (c.more.length) ? 1 : 0;
15484 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15485 cg.setWidth(ebox.right - sbox.x -2);
15487 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15488 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15489 cg.on('click', _this.onEventClick, _this, ev);
15500 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15501 style : 'position: absolute',
15502 unselectable : "on",
15505 cls: 'fc-event-inner',
15509 cls: 'fc-event-title',
15517 cls: 'ui-resizable-handle ui-resizable-e',
15518 html : '  '
15524 var ctr = _this.el.select('.fc-event-container',true).first();
15525 var cg = ctr.createChild(cfg);
15527 var sbox = c.select('.fc-day-content',true).first().getBox();
15528 var ebox = c.select('.fc-day-content',true).first().getBox();
15530 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15531 cg.setWidth(ebox.right - sbox.x -2);
15533 cg.on('click', _this.onMoreEventClick, _this, c.more);
15543 onEventEnter: function (e, el,event,d) {
15544 this.fireEvent('evententer', this, el, event);
15547 onEventLeave: function (e, el,event,d) {
15548 this.fireEvent('eventleave', this, el, event);
15551 onEventClick: function (e, el,event,d) {
15552 this.fireEvent('eventclick', this, el, event);
15555 onMonthChange: function () {
15559 onMoreEventClick: function(e, el, more)
15563 this.calpopover.placement = 'right';
15564 this.calpopover.setTitle('More');
15566 this.calpopover.setContent('');
15568 var ctr = this.calpopover.el.select('.popover-content', true).first();
15570 Roo.each(more, function(m){
15572 cls : 'fc-event-hori fc-event-draggable',
15575 var cg = ctr.createChild(cfg);
15577 cg.on('click', _this.onEventClick, _this, m);
15580 this.calpopover.show(el);
15585 onLoad: function ()
15587 this.calevents = [];
15590 if(this.store.getCount() > 0){
15591 this.store.data.each(function(d){
15594 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15595 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15596 time : d.data.start_time,
15597 title : d.data.title,
15598 description : d.data.description,
15599 venue : d.data.venue
15604 this.renderEvents();
15606 if(this.calevents.length && this.loadMask){
15607 this.maskEl.hide();
15611 onBeforeLoad: function()
15613 this.clearEvents();
15615 this.maskEl.show();
15629 * @class Roo.bootstrap.Popover
15630 * @extends Roo.bootstrap.Component
15631 * Bootstrap Popover class
15632 * @cfg {String} html contents of the popover (or false to use children..)
15633 * @cfg {String} title of popover (or false to hide)
15634 * @cfg {String} placement how it is placed
15635 * @cfg {String} trigger click || hover (or false to trigger manually)
15636 * @cfg {String} over what (parent or false to trigger manually.)
15637 * @cfg {Number} delay - delay before showing
15640 * Create a new Popover
15641 * @param {Object} config The config object
15644 Roo.bootstrap.Popover = function(config){
15645 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15648 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
15650 title: 'Fill in a title',
15653 placement : 'right',
15654 trigger : 'hover', // hover
15660 can_build_overlaid : false,
15662 getChildContainer : function()
15664 return this.el.select('.popover-content',true).first();
15667 getAutoCreate : function(){
15670 cls : 'popover roo-dynamic',
15671 style: 'display:block',
15677 cls : 'popover-inner',
15681 cls: 'popover-title',
15685 cls : 'popover-content',
15696 setTitle: function(str)
15699 this.el.select('.popover-title',true).first().dom.innerHTML = str;
15701 setContent: function(str)
15704 this.el.select('.popover-content',true).first().dom.innerHTML = str;
15706 // as it get's added to the bottom of the page.
15707 onRender : function(ct, position)
15709 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15711 var cfg = Roo.apply({}, this.getAutoCreate());
15715 cfg.cls += ' ' + this.cls;
15718 cfg.style = this.style;
15720 //Roo.log("adding to ");
15721 this.el = Roo.get(document.body).createChild(cfg, position);
15722 // Roo.log(this.el);
15727 initEvents : function()
15729 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15730 this.el.enableDisplayMode('block');
15732 if (this.over === false) {
15735 if (this.triggers === false) {
15738 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15739 var triggers = this.trigger ? this.trigger.split(' ') : [];
15740 Roo.each(triggers, function(trigger) {
15742 if (trigger == 'click') {
15743 on_el.on('click', this.toggle, this);
15744 } else if (trigger != 'manual') {
15745 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
15746 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15748 on_el.on(eventIn ,this.enter, this);
15749 on_el.on(eventOut, this.leave, this);
15760 toggle : function () {
15761 this.hoverState == 'in' ? this.leave() : this.enter();
15764 enter : function () {
15767 clearTimeout(this.timeout);
15769 this.hoverState = 'in';
15771 if (!this.delay || !this.delay.show) {
15776 this.timeout = setTimeout(function () {
15777 if (_t.hoverState == 'in') {
15780 }, this.delay.show)
15782 leave : function() {
15783 clearTimeout(this.timeout);
15785 this.hoverState = 'out';
15787 if (!this.delay || !this.delay.hide) {
15792 this.timeout = setTimeout(function () {
15793 if (_t.hoverState == 'out') {
15796 }, this.delay.hide)
15799 show : function (on_el)
15802 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15805 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15806 if (this.html !== false) {
15807 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15809 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15810 if (!this.title.length) {
15811 this.el.select('.popover-title',true).hide();
15814 var placement = typeof this.placement == 'function' ?
15815 this.placement.call(this, this.el, on_el) :
15818 var autoToken = /\s?auto?\s?/i;
15819 var autoPlace = autoToken.test(placement);
15821 placement = placement.replace(autoToken, '') || 'top';
15825 //this.el.setXY([0,0]);
15827 this.el.dom.style.display='block';
15828 this.el.addClass(placement);
15830 //this.el.appendTo(on_el);
15832 var p = this.getPosition();
15833 var box = this.el.getBox();
15838 var align = Roo.bootstrap.Popover.alignment[placement];
15839 this.el.alignTo(on_el, align[0],align[1]);
15840 //var arrow = this.el.select('.arrow',true).first();
15841 //arrow.set(align[2],
15843 this.el.addClass('in');
15846 if (this.el.hasClass('fade')) {
15853 this.el.setXY([0,0]);
15854 this.el.removeClass('in');
15856 this.hoverState = null;
15862 Roo.bootstrap.Popover.alignment = {
15863 'left' : ['r-l', [-10,0], 'right'],
15864 'right' : ['l-r', [10,0], 'left'],
15865 'bottom' : ['t-b', [0,10], 'top'],
15866 'top' : [ 'b-t', [0,-10], 'bottom']
15877 * @class Roo.bootstrap.Progress
15878 * @extends Roo.bootstrap.Component
15879 * Bootstrap Progress class
15880 * @cfg {Boolean} striped striped of the progress bar
15881 * @cfg {Boolean} active animated of the progress bar
15885 * Create a new Progress
15886 * @param {Object} config The config object
15889 Roo.bootstrap.Progress = function(config){
15890 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15893 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
15898 getAutoCreate : function(){
15906 cfg.cls += ' progress-striped';
15910 cfg.cls += ' active';
15929 * @class Roo.bootstrap.ProgressBar
15930 * @extends Roo.bootstrap.Component
15931 * Bootstrap ProgressBar class
15932 * @cfg {Number} aria_valuenow aria-value now
15933 * @cfg {Number} aria_valuemin aria-value min
15934 * @cfg {Number} aria_valuemax aria-value max
15935 * @cfg {String} label label for the progress bar
15936 * @cfg {String} panel (success | info | warning | danger )
15937 * @cfg {String} role role of the progress bar
15938 * @cfg {String} sr_only text
15942 * Create a new ProgressBar
15943 * @param {Object} config The config object
15946 Roo.bootstrap.ProgressBar = function(config){
15947 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15950 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
15954 aria_valuemax : 100,
15960 getAutoCreate : function()
15965 cls: 'progress-bar',
15966 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15978 cfg.role = this.role;
15981 if(this.aria_valuenow){
15982 cfg['aria-valuenow'] = this.aria_valuenow;
15985 if(this.aria_valuemin){
15986 cfg['aria-valuemin'] = this.aria_valuemin;
15989 if(this.aria_valuemax){
15990 cfg['aria-valuemax'] = this.aria_valuemax;
15993 if(this.label && !this.sr_only){
15994 cfg.html = this.label;
15998 cfg.cls += ' progress-bar-' + this.panel;
16004 update : function(aria_valuenow)
16006 this.aria_valuenow = aria_valuenow;
16008 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16023 * @class Roo.bootstrap.TabGroup
16024 * @extends Roo.bootstrap.Column
16025 * Bootstrap Column class
16026 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16027 * @cfg {Boolean} carousel true to make the group behave like a carousel
16028 * @cfg {Boolean} bullets show bullets for the panels
16029 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16030 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16031 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16034 * Create a new TabGroup
16035 * @param {Object} config The config object
16038 Roo.bootstrap.TabGroup = function(config){
16039 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16041 this.navId = Roo.id();
16044 Roo.bootstrap.TabGroup.register(this);
16048 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16051 transition : false,
16056 slideOnTouch : false,
16058 getAutoCreate : function()
16060 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16062 cfg.cls += ' tab-content';
16064 if (this.carousel) {
16065 cfg.cls += ' carousel slide';
16068 cls : 'carousel-inner'
16071 if(this.bullets && !Roo.isTouch){
16074 cls : 'carousel-bullets',
16078 if(this.bullets_cls){
16079 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16082 for (var i = 0; i < this.bullets; i++){
16084 cls : 'bullet bullet-' + i
16092 cfg.cn[0].cn = bullets;
16099 initEvents: function()
16101 if(Roo.isTouch && this.slideOnTouch){
16102 this.el.on("touchstart", this.onTouchStart, this);
16105 if(this.autoslide){
16108 this.slideFn = window.setInterval(function() {
16109 _this.showPanelNext();
16115 onTouchStart : function(e, el, o)
16117 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16121 this.showPanelNext();
16124 getChildContainer : function()
16126 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16130 * register a Navigation item
16131 * @param {Roo.bootstrap.NavItem} the navitem to add
16133 register : function(item)
16135 this.tabs.push( item);
16136 item.navId = this.navId; // not really needed..
16141 getActivePanel : function()
16144 Roo.each(this.tabs, function(t) {
16154 getPanelByName : function(n)
16157 Roo.each(this.tabs, function(t) {
16158 if (t.tabId == n) {
16166 indexOfPanel : function(p)
16169 Roo.each(this.tabs, function(t,i) {
16170 if (t.tabId == p.tabId) {
16179 * show a specific panel
16180 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16181 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16183 showPanel : function (pan)
16185 if(this.transition || typeof(pan) == 'undefined'){
16186 Roo.log("waiting for the transitionend");
16190 if (typeof(pan) == 'number') {
16191 pan = this.tabs[pan];
16194 if (typeof(pan) == 'string') {
16195 pan = this.getPanelByName(pan);
16198 var cur = this.getActivePanel();
16201 Roo.log('pan or acitve pan is undefined');
16205 if (pan.tabId == this.getActivePanel().tabId) {
16209 if (false === cur.fireEvent('beforedeactivate')) {
16213 if(this.bullets > 0 && !Roo.isTouch){
16214 this.setActiveBullet(this.indexOfPanel(pan));
16217 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16219 this.transition = true;
16220 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16221 var lr = dir == 'next' ? 'left' : 'right';
16222 pan.el.addClass(dir); // or prev
16223 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16224 cur.el.addClass(lr); // or right
16225 pan.el.addClass(lr);
16228 cur.el.on('transitionend', function() {
16229 Roo.log("trans end?");
16231 pan.el.removeClass([lr,dir]);
16232 pan.setActive(true);
16234 cur.el.removeClass([lr]);
16235 cur.setActive(false);
16237 _this.transition = false;
16239 }, this, { single: true } );
16244 cur.setActive(false);
16245 pan.setActive(true);
16250 showPanelNext : function()
16252 var i = this.indexOfPanel(this.getActivePanel());
16254 if (i >= this.tabs.length - 1 && !this.autoslide) {
16258 if (i >= this.tabs.length - 1 && this.autoslide) {
16262 this.showPanel(this.tabs[i+1]);
16265 showPanelPrev : function()
16267 var i = this.indexOfPanel(this.getActivePanel());
16269 if (i < 1 && !this.autoslide) {
16273 if (i < 1 && this.autoslide) {
16274 i = this.tabs.length;
16277 this.showPanel(this.tabs[i-1]);
16281 addBullet: function()
16283 if(!this.bullets || Roo.isTouch){
16286 var ctr = this.el.select('.carousel-bullets',true).first();
16287 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16288 var bullet = ctr.createChild({
16289 cls : 'bullet bullet-' + i
16290 },ctr.dom.lastChild);
16295 bullet.on('click', (function(e, el, o, ii, t){
16297 e.preventDefault();
16299 this.showPanel(ii);
16301 if(this.autoslide && this.slideFn){
16302 clearInterval(this.slideFn);
16303 this.slideFn = window.setInterval(function() {
16304 _this.showPanelNext();
16308 }).createDelegate(this, [i, bullet], true));
16313 setActiveBullet : function(i)
16319 Roo.each(this.el.select('.bullet', true).elements, function(el){
16320 el.removeClass('selected');
16323 var bullet = this.el.select('.bullet-' + i, true).first();
16329 bullet.addClass('selected');
16340 Roo.apply(Roo.bootstrap.TabGroup, {
16344 * register a Navigation Group
16345 * @param {Roo.bootstrap.NavGroup} the navgroup to add
16347 register : function(navgrp)
16349 this.groups[navgrp.navId] = navgrp;
16353 * fetch a Navigation Group based on the navigation ID
16354 * if one does not exist , it will get created.
16355 * @param {string} the navgroup to add
16356 * @returns {Roo.bootstrap.NavGroup} the navgroup
16358 get: function(navId) {
16359 if (typeof(this.groups[navId]) == 'undefined') {
16360 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16362 return this.groups[navId] ;
16377 * @class Roo.bootstrap.TabPanel
16378 * @extends Roo.bootstrap.Component
16379 * Bootstrap TabPanel class
16380 * @cfg {Boolean} active panel active
16381 * @cfg {String} html panel content
16382 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16383 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16387 * Create a new TabPanel
16388 * @param {Object} config The config object
16391 Roo.bootstrap.TabPanel = function(config){
16392 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16396 * Fires when the active status changes
16397 * @param {Roo.bootstrap.TabPanel} this
16398 * @param {Boolean} state the new state
16403 * @event beforedeactivate
16404 * Fires before a tab is de-activated - can be used to do validation on a form.
16405 * @param {Roo.bootstrap.TabPanel} this
16406 * @return {Boolean} false if there is an error
16409 'beforedeactivate': true
16412 this.tabId = this.tabId || Roo.id();
16416 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16423 getAutoCreate : function(){
16426 // item is needed for carousel - not sure if it has any effect otherwise
16427 cls: 'tab-pane item',
16428 html: this.html || ''
16432 cfg.cls += ' active';
16436 cfg.tabId = this.tabId;
16443 initEvents: function()
16445 var p = this.parent();
16446 this.navId = this.navId || p.navId;
16448 if (typeof(this.navId) != 'undefined') {
16449 // not really needed.. but just in case.. parent should be a NavGroup.
16450 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16454 var i = tg.tabs.length - 1;
16456 if(this.active && tg.bullets > 0 && i < tg.bullets){
16457 tg.setActiveBullet(i);
16464 onRender : function(ct, position)
16466 // Roo.log("Call onRender: " + this.xtype);
16468 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16476 setActive: function(state)
16478 Roo.log("panel - set active " + this.tabId + "=" + state);
16480 this.active = state;
16482 this.el.removeClass('active');
16484 } else if (!this.el.hasClass('active')) {
16485 this.el.addClass('active');
16488 this.fireEvent('changed', this, state);
16505 * @class Roo.bootstrap.DateField
16506 * @extends Roo.bootstrap.Input
16507 * Bootstrap DateField class
16508 * @cfg {Number} weekStart default 0
16509 * @cfg {String} viewMode default empty, (months|years)
16510 * @cfg {String} minViewMode default empty, (months|years)
16511 * @cfg {Number} startDate default -Infinity
16512 * @cfg {Number} endDate default Infinity
16513 * @cfg {Boolean} todayHighlight default false
16514 * @cfg {Boolean} todayBtn default false
16515 * @cfg {Boolean} calendarWeeks default false
16516 * @cfg {Object} daysOfWeekDisabled default empty
16517 * @cfg {Boolean} singleMode default false (true | false)
16519 * @cfg {Boolean} keyboardNavigation default true
16520 * @cfg {String} language default en
16523 * Create a new DateField
16524 * @param {Object} config The config object
16527 Roo.bootstrap.DateField = function(config){
16528 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16532 * Fires when this field show.
16533 * @param {Roo.bootstrap.DateField} this
16534 * @param {Mixed} date The date value
16539 * Fires when this field hide.
16540 * @param {Roo.bootstrap.DateField} this
16541 * @param {Mixed} date The date value
16546 * Fires when select a date.
16547 * @param {Roo.bootstrap.DateField} this
16548 * @param {Mixed} date The date value
16552 * @event beforeselect
16553 * Fires when before select a date.
16554 * @param {Roo.bootstrap.DateField} this
16555 * @param {Mixed} date The date value
16557 beforeselect : true
16561 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16564 * @cfg {String} format
16565 * The default date format string which can be overriden for localization support. The format must be
16566 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16570 * @cfg {String} altFormats
16571 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16572 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16574 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16582 todayHighlight : false,
16588 keyboardNavigation: true,
16590 calendarWeeks: false,
16592 startDate: -Infinity,
16596 daysOfWeekDisabled: [],
16600 singleMode : false,
16602 UTCDate: function()
16604 return new Date(Date.UTC.apply(Date, arguments));
16607 UTCToday: function()
16609 var today = new Date();
16610 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16613 getDate: function() {
16614 var d = this.getUTCDate();
16615 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16618 getUTCDate: function() {
16622 setDate: function(d) {
16623 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16626 setUTCDate: function(d) {
16628 this.setValue(this.formatDate(this.date));
16631 onRender: function(ct, position)
16634 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16636 this.language = this.language || 'en';
16637 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16638 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16640 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16641 this.format = this.format || 'm/d/y';
16642 this.isInline = false;
16643 this.isInput = true;
16644 this.component = this.el.select('.add-on', true).first() || false;
16645 this.component = (this.component && this.component.length === 0) ? false : this.component;
16646 this.hasInput = this.component && this.inputEL().length;
16648 if (typeof(this.minViewMode === 'string')) {
16649 switch (this.minViewMode) {
16651 this.minViewMode = 1;
16654 this.minViewMode = 2;
16657 this.minViewMode = 0;
16662 if (typeof(this.viewMode === 'string')) {
16663 switch (this.viewMode) {
16676 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16678 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16680 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16682 this.picker().on('mousedown', this.onMousedown, this);
16683 this.picker().on('click', this.onClick, this);
16685 this.picker().addClass('datepicker-dropdown');
16687 this.startViewMode = this.viewMode;
16689 if(this.singleMode){
16690 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16691 v.setVisibilityMode(Roo.Element.DISPLAY);
16695 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16696 v.setStyle('width', '189px');
16700 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16701 if(!this.calendarWeeks){
16706 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16707 v.attr('colspan', function(i, val){
16708 return parseInt(val) + 1;
16713 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16715 this.setStartDate(this.startDate);
16716 this.setEndDate(this.endDate);
16718 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16725 if(this.isInline) {
16730 picker : function()
16732 return this.pickerEl;
16733 // return this.el.select('.datepicker', true).first();
16736 fillDow: function()
16738 var dowCnt = this.weekStart;
16747 if(this.calendarWeeks){
16755 while (dowCnt < this.weekStart + 7) {
16759 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16763 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16766 fillMonths: function()
16769 var months = this.picker().select('>.datepicker-months td', true).first();
16771 months.dom.innerHTML = '';
16777 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16780 months.createChild(month);
16787 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;
16789 if (this.date < this.startDate) {
16790 this.viewDate = new Date(this.startDate);
16791 } else if (this.date > this.endDate) {
16792 this.viewDate = new Date(this.endDate);
16794 this.viewDate = new Date(this.date);
16802 var d = new Date(this.viewDate),
16803 year = d.getUTCFullYear(),
16804 month = d.getUTCMonth(),
16805 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16806 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16807 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16808 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16809 currentDate = this.date && this.date.valueOf(),
16810 today = this.UTCToday();
16812 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16814 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16816 // this.picker.select('>tfoot th.today').
16817 // .text(dates[this.language].today)
16818 // .toggle(this.todayBtn !== false);
16820 this.updateNavArrows();
16823 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16825 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16827 prevMonth.setUTCDate(day);
16829 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16831 var nextMonth = new Date(prevMonth);
16833 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16835 nextMonth = nextMonth.valueOf();
16837 var fillMonths = false;
16839 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16841 while(prevMonth.valueOf() < nextMonth) {
16844 if (prevMonth.getUTCDay() === this.weekStart) {
16846 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16854 if(this.calendarWeeks){
16855 // ISO 8601: First week contains first thursday.
16856 // ISO also states week starts on Monday, but we can be more abstract here.
16858 // Start of current week: based on weekstart/current date
16859 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16860 // Thursday of this week
16861 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16862 // First Thursday of year, year from thursday
16863 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16864 // Calendar week: ms between thursdays, div ms per day, div 7 days
16865 calWeek = (th - yth) / 864e5 / 7 + 1;
16867 fillMonths.cn.push({
16875 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16877 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16880 if (this.todayHighlight &&
16881 prevMonth.getUTCFullYear() == today.getFullYear() &&
16882 prevMonth.getUTCMonth() == today.getMonth() &&
16883 prevMonth.getUTCDate() == today.getDate()) {
16884 clsName += ' today';
16887 if (currentDate && prevMonth.valueOf() === currentDate) {
16888 clsName += ' active';
16891 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16892 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16893 clsName += ' disabled';
16896 fillMonths.cn.push({
16898 cls: 'day ' + clsName,
16899 html: prevMonth.getDate()
16902 prevMonth.setDate(prevMonth.getDate()+1);
16905 var currentYear = this.date && this.date.getUTCFullYear();
16906 var currentMonth = this.date && this.date.getUTCMonth();
16908 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16910 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16911 v.removeClass('active');
16913 if(currentYear === year && k === currentMonth){
16914 v.addClass('active');
16917 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16918 v.addClass('disabled');
16924 year = parseInt(year/10, 10) * 10;
16926 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16928 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16931 for (var i = -1; i < 11; i++) {
16932 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16934 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16942 showMode: function(dir)
16945 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16948 Roo.each(this.picker().select('>div',true).elements, function(v){
16949 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16952 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16957 if(this.isInline) {
16961 this.picker().removeClass(['bottom', 'top']);
16963 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16965 * place to the top of element!
16969 this.picker().addClass('top');
16970 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16975 this.picker().addClass('bottom');
16977 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16980 parseDate : function(value)
16982 if(!value || value instanceof Date){
16985 var v = Date.parseDate(value, this.format);
16986 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16987 v = Date.parseDate(value, 'Y-m-d');
16989 if(!v && this.altFormats){
16990 if(!this.altFormatsArray){
16991 this.altFormatsArray = this.altFormats.split("|");
16993 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16994 v = Date.parseDate(value, this.altFormatsArray[i]);
17000 formatDate : function(date, fmt)
17002 return (!date || !(date instanceof Date)) ?
17003 date : date.dateFormat(fmt || this.format);
17006 onFocus : function()
17008 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17012 onBlur : function()
17014 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17016 var d = this.inputEl().getValue();
17025 this.picker().show();
17029 this.fireEvent('show', this, this.date);
17034 if(this.isInline) {
17037 this.picker().hide();
17038 this.viewMode = this.startViewMode;
17041 this.fireEvent('hide', this, this.date);
17045 onMousedown: function(e)
17047 e.stopPropagation();
17048 e.preventDefault();
17053 Roo.bootstrap.DateField.superclass.keyup.call(this);
17057 setValue: function(v)
17059 if(this.fireEvent('beforeselect', this, v) !== false){
17060 var d = new Date(this.parseDate(v) ).clearTime();
17062 if(isNaN(d.getTime())){
17063 this.date = this.viewDate = '';
17064 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17068 v = this.formatDate(d);
17070 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17072 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17076 this.fireEvent('select', this, this.date);
17080 getValue: function()
17082 return this.formatDate(this.date);
17085 fireKey: function(e)
17087 if (!this.picker().isVisible()){
17088 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17094 var dateChanged = false,
17096 newDate, newViewDate;
17101 e.preventDefault();
17105 if (!this.keyboardNavigation) {
17108 dir = e.keyCode == 37 ? -1 : 1;
17111 newDate = this.moveYear(this.date, dir);
17112 newViewDate = this.moveYear(this.viewDate, dir);
17113 } else if (e.shiftKey){
17114 newDate = this.moveMonth(this.date, dir);
17115 newViewDate = this.moveMonth(this.viewDate, dir);
17117 newDate = new Date(this.date);
17118 newDate.setUTCDate(this.date.getUTCDate() + dir);
17119 newViewDate = new Date(this.viewDate);
17120 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17122 if (this.dateWithinRange(newDate)){
17123 this.date = newDate;
17124 this.viewDate = newViewDate;
17125 this.setValue(this.formatDate(this.date));
17127 e.preventDefault();
17128 dateChanged = true;
17133 if (!this.keyboardNavigation) {
17136 dir = e.keyCode == 38 ? -1 : 1;
17138 newDate = this.moveYear(this.date, dir);
17139 newViewDate = this.moveYear(this.viewDate, dir);
17140 } else if (e.shiftKey){
17141 newDate = this.moveMonth(this.date, dir);
17142 newViewDate = this.moveMonth(this.viewDate, dir);
17144 newDate = new Date(this.date);
17145 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17146 newViewDate = new Date(this.viewDate);
17147 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17149 if (this.dateWithinRange(newDate)){
17150 this.date = newDate;
17151 this.viewDate = newViewDate;
17152 this.setValue(this.formatDate(this.date));
17154 e.preventDefault();
17155 dateChanged = true;
17159 this.setValue(this.formatDate(this.date));
17161 e.preventDefault();
17164 this.setValue(this.formatDate(this.date));
17178 onClick: function(e)
17180 e.stopPropagation();
17181 e.preventDefault();
17183 var target = e.getTarget();
17185 if(target.nodeName.toLowerCase() === 'i'){
17186 target = Roo.get(target).dom.parentNode;
17189 var nodeName = target.nodeName;
17190 var className = target.className;
17191 var html = target.innerHTML;
17192 //Roo.log(nodeName);
17194 switch(nodeName.toLowerCase()) {
17196 switch(className) {
17202 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17203 switch(this.viewMode){
17205 this.viewDate = this.moveMonth(this.viewDate, dir);
17209 this.viewDate = this.moveYear(this.viewDate, dir);
17215 var date = new Date();
17216 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17218 this.setValue(this.formatDate(this.date));
17225 if (className.indexOf('disabled') < 0) {
17226 this.viewDate.setUTCDate(1);
17227 if (className.indexOf('month') > -1) {
17228 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17230 var year = parseInt(html, 10) || 0;
17231 this.viewDate.setUTCFullYear(year);
17235 if(this.singleMode){
17236 this.setValue(this.formatDate(this.viewDate));
17247 //Roo.log(className);
17248 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17249 var day = parseInt(html, 10) || 1;
17250 var year = this.viewDate.getUTCFullYear(),
17251 month = this.viewDate.getUTCMonth();
17253 if (className.indexOf('old') > -1) {
17260 } else if (className.indexOf('new') > -1) {
17268 //Roo.log([year,month,day]);
17269 this.date = this.UTCDate(year, month, day,0,0,0,0);
17270 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17272 //Roo.log(this.formatDate(this.date));
17273 this.setValue(this.formatDate(this.date));
17280 setStartDate: function(startDate)
17282 this.startDate = startDate || -Infinity;
17283 if (this.startDate !== -Infinity) {
17284 this.startDate = this.parseDate(this.startDate);
17287 this.updateNavArrows();
17290 setEndDate: function(endDate)
17292 this.endDate = endDate || Infinity;
17293 if (this.endDate !== Infinity) {
17294 this.endDate = this.parseDate(this.endDate);
17297 this.updateNavArrows();
17300 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17302 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17303 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17304 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17306 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17307 return parseInt(d, 10);
17310 this.updateNavArrows();
17313 updateNavArrows: function()
17315 if(this.singleMode){
17319 var d = new Date(this.viewDate),
17320 year = d.getUTCFullYear(),
17321 month = d.getUTCMonth();
17323 Roo.each(this.picker().select('.prev', true).elements, function(v){
17325 switch (this.viewMode) {
17328 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17334 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17341 Roo.each(this.picker().select('.next', true).elements, function(v){
17343 switch (this.viewMode) {
17346 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17352 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17360 moveMonth: function(date, dir)
17365 var new_date = new Date(date.valueOf()),
17366 day = new_date.getUTCDate(),
17367 month = new_date.getUTCMonth(),
17368 mag = Math.abs(dir),
17370 dir = dir > 0 ? 1 : -1;
17373 // If going back one month, make sure month is not current month
17374 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17376 return new_date.getUTCMonth() == month;
17378 // If going forward one month, make sure month is as expected
17379 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17381 return new_date.getUTCMonth() != new_month;
17383 new_month = month + dir;
17384 new_date.setUTCMonth(new_month);
17385 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17386 if (new_month < 0 || new_month > 11) {
17387 new_month = (new_month + 12) % 12;
17390 // For magnitudes >1, move one month at a time...
17391 for (var i=0; i<mag; i++) {
17392 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17393 new_date = this.moveMonth(new_date, dir);
17395 // ...then reset the day, keeping it in the new month
17396 new_month = new_date.getUTCMonth();
17397 new_date.setUTCDate(day);
17399 return new_month != new_date.getUTCMonth();
17402 // Common date-resetting loop -- if date is beyond end of month, make it
17405 new_date.setUTCDate(--day);
17406 new_date.setUTCMonth(new_month);
17411 moveYear: function(date, dir)
17413 return this.moveMonth(date, dir*12);
17416 dateWithinRange: function(date)
17418 return date >= this.startDate && date <= this.endDate;
17424 this.picker().remove();
17429 Roo.apply(Roo.bootstrap.DateField, {
17440 html: '<i class="fa fa-arrow-left"/>'
17450 html: '<i class="fa fa-arrow-right"/>'
17492 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17493 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17494 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17495 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17496 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17509 navFnc: 'FullYear',
17514 navFnc: 'FullYear',
17519 Roo.apply(Roo.bootstrap.DateField, {
17523 cls: 'datepicker dropdown-menu roo-dynamic',
17527 cls: 'datepicker-days',
17531 cls: 'table-condensed',
17533 Roo.bootstrap.DateField.head,
17537 Roo.bootstrap.DateField.footer
17544 cls: 'datepicker-months',
17548 cls: 'table-condensed',
17550 Roo.bootstrap.DateField.head,
17551 Roo.bootstrap.DateField.content,
17552 Roo.bootstrap.DateField.footer
17559 cls: 'datepicker-years',
17563 cls: 'table-condensed',
17565 Roo.bootstrap.DateField.head,
17566 Roo.bootstrap.DateField.content,
17567 Roo.bootstrap.DateField.footer
17586 * @class Roo.bootstrap.TimeField
17587 * @extends Roo.bootstrap.Input
17588 * Bootstrap DateField class
17592 * Create a new TimeField
17593 * @param {Object} config The config object
17596 Roo.bootstrap.TimeField = function(config){
17597 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17601 * Fires when this field show.
17602 * @param {Roo.bootstrap.DateField} thisthis
17603 * @param {Mixed} date The date value
17608 * Fires when this field hide.
17609 * @param {Roo.bootstrap.DateField} this
17610 * @param {Mixed} date The date value
17615 * Fires when select a date.
17616 * @param {Roo.bootstrap.DateField} this
17617 * @param {Mixed} date The date value
17623 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17626 * @cfg {String} format
17627 * The default time format string which can be overriden for localization support. The format must be
17628 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17632 onRender: function(ct, position)
17635 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17637 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17639 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17641 this.pop = this.picker().select('>.datepicker-time',true).first();
17642 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17644 this.picker().on('mousedown', this.onMousedown, this);
17645 this.picker().on('click', this.onClick, this);
17647 this.picker().addClass('datepicker-dropdown');
17652 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17653 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17654 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17655 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17656 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17657 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17661 fireKey: function(e){
17662 if (!this.picker().isVisible()){
17663 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17669 e.preventDefault();
17677 this.onTogglePeriod();
17680 this.onIncrementMinutes();
17683 this.onDecrementMinutes();
17692 onClick: function(e) {
17693 e.stopPropagation();
17694 e.preventDefault();
17697 picker : function()
17699 return this.el.select('.datepicker', true).first();
17702 fillTime: function()
17704 var time = this.pop.select('tbody', true).first();
17706 time.dom.innerHTML = '';
17721 cls: 'hours-up glyphicon glyphicon-chevron-up'
17741 cls: 'minutes-up glyphicon glyphicon-chevron-up'
17762 cls: 'timepicker-hour',
17777 cls: 'timepicker-minute',
17792 cls: 'btn btn-primary period',
17814 cls: 'hours-down glyphicon glyphicon-chevron-down'
17834 cls: 'minutes-down glyphicon glyphicon-chevron-down'
17852 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17859 var hours = this.time.getHours();
17860 var minutes = this.time.getMinutes();
17873 hours = hours - 12;
17877 hours = '0' + hours;
17881 minutes = '0' + minutes;
17884 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17885 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17886 this.pop.select('button', true).first().dom.innerHTML = period;
17892 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17894 var cls = ['bottom'];
17896 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17903 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17908 this.picker().addClass(cls.join('-'));
17912 Roo.each(cls, function(c){
17914 _this.picker().setTop(_this.inputEl().getHeight());
17918 _this.picker().setTop(0 - _this.picker().getHeight());
17923 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17927 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17934 onFocus : function()
17936 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17940 onBlur : function()
17942 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17948 this.picker().show();
17953 this.fireEvent('show', this, this.date);
17958 this.picker().hide();
17961 this.fireEvent('hide', this, this.date);
17964 setTime : function()
17967 this.setValue(this.time.format(this.format));
17969 this.fireEvent('select', this, this.date);
17974 onMousedown: function(e){
17975 e.stopPropagation();
17976 e.preventDefault();
17979 onIncrementHours: function()
17981 Roo.log('onIncrementHours');
17982 this.time = this.time.add(Date.HOUR, 1);
17987 onDecrementHours: function()
17989 Roo.log('onDecrementHours');
17990 this.time = this.time.add(Date.HOUR, -1);
17994 onIncrementMinutes: function()
17996 Roo.log('onIncrementMinutes');
17997 this.time = this.time.add(Date.MINUTE, 1);
18001 onDecrementMinutes: function()
18003 Roo.log('onDecrementMinutes');
18004 this.time = this.time.add(Date.MINUTE, -1);
18008 onTogglePeriod: function()
18010 Roo.log('onTogglePeriod');
18011 this.time = this.time.add(Date.HOUR, 12);
18018 Roo.apply(Roo.bootstrap.TimeField, {
18048 cls: 'btn btn-info ok',
18060 Roo.apply(Roo.bootstrap.TimeField, {
18064 cls: 'datepicker dropdown-menu',
18068 cls: 'datepicker-time',
18072 cls: 'table-condensed',
18074 Roo.bootstrap.TimeField.content,
18075 Roo.bootstrap.TimeField.footer
18094 * @class Roo.bootstrap.MonthField
18095 * @extends Roo.bootstrap.Input
18096 * Bootstrap MonthField class
18098 * @cfg {String} language default en
18101 * Create a new MonthField
18102 * @param {Object} config The config object
18105 Roo.bootstrap.MonthField = function(config){
18106 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18111 * Fires when this field show.
18112 * @param {Roo.bootstrap.MonthField} this
18113 * @param {Mixed} date The date value
18118 * Fires when this field hide.
18119 * @param {Roo.bootstrap.MonthField} this
18120 * @param {Mixed} date The date value
18125 * Fires when select a date.
18126 * @param {Roo.bootstrap.MonthField} this
18127 * @param {String} oldvalue The old value
18128 * @param {String} newvalue The new value
18134 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
18136 onRender: function(ct, position)
18139 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18141 this.language = this.language || 'en';
18142 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18143 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18145 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18146 this.isInline = false;
18147 this.isInput = true;
18148 this.component = this.el.select('.add-on', true).first() || false;
18149 this.component = (this.component && this.component.length === 0) ? false : this.component;
18150 this.hasInput = this.component && this.inputEL().length;
18152 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18154 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18156 this.picker().on('mousedown', this.onMousedown, this);
18157 this.picker().on('click', this.onClick, this);
18159 this.picker().addClass('datepicker-dropdown');
18161 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18162 v.setStyle('width', '189px');
18169 if(this.isInline) {
18175 setValue: function(v, suppressEvent)
18177 var o = this.getValue();
18179 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18183 if(suppressEvent !== true){
18184 this.fireEvent('select', this, o, v);
18189 getValue: function()
18194 onClick: function(e)
18196 e.stopPropagation();
18197 e.preventDefault();
18199 var target = e.getTarget();
18201 if(target.nodeName.toLowerCase() === 'i'){
18202 target = Roo.get(target).dom.parentNode;
18205 var nodeName = target.nodeName;
18206 var className = target.className;
18207 var html = target.innerHTML;
18209 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18213 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18215 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18221 picker : function()
18223 return this.pickerEl;
18226 fillMonths: function()
18229 var months = this.picker().select('>.datepicker-months td', true).first();
18231 months.dom.innerHTML = '';
18237 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18240 months.createChild(month);
18249 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18250 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18253 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18254 e.removeClass('active');
18256 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18257 e.addClass('active');
18264 if(this.isInline) {
18268 this.picker().removeClass(['bottom', 'top']);
18270 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18272 * place to the top of element!
18276 this.picker().addClass('top');
18277 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18282 this.picker().addClass('bottom');
18284 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18287 onFocus : function()
18289 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18293 onBlur : function()
18295 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18297 var d = this.inputEl().getValue();
18306 this.picker().show();
18307 this.picker().select('>.datepicker-months', true).first().show();
18311 this.fireEvent('show', this, this.date);
18316 if(this.isInline) {
18319 this.picker().hide();
18320 this.fireEvent('hide', this, this.date);
18324 onMousedown: function(e)
18326 e.stopPropagation();
18327 e.preventDefault();
18332 Roo.bootstrap.MonthField.superclass.keyup.call(this);
18336 fireKey: function(e)
18338 if (!this.picker().isVisible()){
18339 if (e.keyCode == 27) {// allow escape to hide and re-show picker
18350 e.preventDefault();
18354 dir = e.keyCode == 37 ? -1 : 1;
18356 this.vIndex = this.vIndex + dir;
18358 if(this.vIndex < 0){
18362 if(this.vIndex > 11){
18366 if(isNaN(this.vIndex)){
18370 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18376 dir = e.keyCode == 38 ? -1 : 1;
18378 this.vIndex = this.vIndex + dir * 4;
18380 if(this.vIndex < 0){
18384 if(this.vIndex > 11){
18388 if(isNaN(this.vIndex)){
18392 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18397 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18398 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18402 e.preventDefault();
18405 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18406 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18422 this.picker().remove();
18427 Roo.apply(Roo.bootstrap.MonthField, {
18446 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18447 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18452 Roo.apply(Roo.bootstrap.MonthField, {
18456 cls: 'datepicker dropdown-menu roo-dynamic',
18460 cls: 'datepicker-months',
18464 cls: 'table-condensed',
18466 Roo.bootstrap.DateField.content
18486 * @class Roo.bootstrap.CheckBox
18487 * @extends Roo.bootstrap.Input
18488 * Bootstrap CheckBox class
18490 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18491 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18492 * @cfg {String} boxLabel The text that appears beside the checkbox
18493 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18494 * @cfg {Boolean} checked initnal the element
18495 * @cfg {Boolean} inline inline the element (default false)
18496 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18499 * Create a new CheckBox
18500 * @param {Object} config The config object
18503 Roo.bootstrap.CheckBox = function(config){
18504 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18509 * Fires when the element is checked or unchecked.
18510 * @param {Roo.bootstrap.CheckBox} this This input
18511 * @param {Boolean} checked The new checked value
18518 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18520 inputType: 'checkbox',
18528 getAutoCreate : function()
18530 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18536 cfg.cls = 'form-group ' + this.inputType; //input-group
18539 cfg.cls += ' ' + this.inputType + '-inline';
18545 type : this.inputType,
18546 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18547 cls : 'roo-' + this.inputType, //'form-box',
18548 placeholder : this.placeholder || ''
18552 if (this.weight) { // Validity check?
18553 cfg.cls += " " + this.inputType + "-" + this.weight;
18556 if (this.disabled) {
18557 input.disabled=true;
18561 input.checked = this.checked;
18565 input.name = this.name;
18569 input.cls += ' input-' + this.size;
18574 ['xs','sm','md','lg'].map(function(size){
18575 if (settings[size]) {
18576 cfg.cls += ' col-' + size + '-' + settings[size];
18580 var inputblock = input;
18582 if (this.before || this.after) {
18585 cls : 'input-group',
18590 inputblock.cn.push({
18592 cls : 'input-group-addon',
18597 inputblock.cn.push(input);
18600 inputblock.cn.push({
18602 cls : 'input-group-addon',
18609 if (align ==='left' && this.fieldLabel.length) {
18610 // Roo.log("left and has label");
18616 cls : 'control-label col-md-' + this.labelWidth,
18617 html : this.fieldLabel
18621 cls : "col-md-" + (12 - this.labelWidth),
18628 } else if ( this.fieldLabel.length) {
18629 // Roo.log(" label");
18633 tag: this.boxLabel ? 'span' : 'label',
18635 cls: 'control-label box-input-label',
18636 //cls : 'input-group-addon',
18637 html : this.fieldLabel
18647 // Roo.log(" no label && no align");
18648 cfg.cn = [ inputblock ] ;
18653 var boxLabelCfg = {
18655 //'for': id, // box label is handled by onclick - so no for...
18657 html: this.boxLabel
18661 boxLabelCfg.tooltip = this.tooltip;
18664 cfg.cn.push(boxLabelCfg);
18674 * return the real input element.
18676 inputEl: function ()
18678 return this.el.select('input.roo-' + this.inputType,true).first();
18681 labelEl: function()
18683 return this.el.select('label.control-label',true).first();
18685 /* depricated... */
18689 return this.labelEl();
18692 initEvents : function()
18694 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18696 this.inputEl().on('click', this.onClick, this);
18698 if (this.boxLabel) {
18699 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18702 this.startValue = this.getValue();
18705 Roo.bootstrap.CheckBox.register(this);
18709 onClick : function()
18711 this.setChecked(!this.checked);
18714 setChecked : function(state,suppressEvent)
18716 this.startValue = this.getValue();
18718 if(this.inputType == 'radio'){
18720 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18721 e.dom.checked = false;
18724 this.inputEl().dom.checked = true;
18726 this.inputEl().dom.value = this.inputValue;
18728 if(suppressEvent !== true){
18729 this.fireEvent('check', this, true);
18737 this.checked = state;
18739 this.inputEl().dom.checked = state;
18741 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18743 if(suppressEvent !== true){
18744 this.fireEvent('check', this, state);
18750 getValue : function()
18752 if(this.inputType == 'radio'){
18753 return this.getGroupValue();
18756 return this.inputEl().getValue();
18760 getGroupValue : function()
18762 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18766 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18769 setValue : function(v,suppressEvent)
18771 if(this.inputType == 'radio'){
18772 this.setGroupValue(v, suppressEvent);
18776 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18781 setGroupValue : function(v, suppressEvent)
18783 this.startValue = this.getValue();
18785 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18786 e.dom.checked = false;
18788 if(e.dom.value == v){
18789 e.dom.checked = true;
18793 if(suppressEvent !== true){
18794 this.fireEvent('check', this, true);
18802 validate : function()
18806 (this.inputType == 'radio' && this.validateRadio()) ||
18807 (this.inputType == 'checkbox' && this.validateCheckbox())
18813 this.markInvalid();
18817 validateRadio : function()
18821 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18822 if(!e.dom.checked){
18834 validateCheckbox : function()
18837 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18840 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18848 for(var i in group){
18853 r = (group[i].getValue() == group[i].inputValue) ? true : false;
18860 * Mark this field as valid
18862 markValid : function()
18864 if(this.allowBlank){
18870 this.fireEvent('valid', this);
18872 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18875 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18882 if(this.inputType == 'radio'){
18883 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18884 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18885 e.findParent('.form-group', false, true).addClass(_this.validClass);
18892 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18893 this.el.findParent('.form-group', false, true).addClass(this.validClass);
18897 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18903 for(var i in group){
18904 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18905 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18910 * Mark this field as invalid
18911 * @param {String} msg The validation message
18913 markInvalid : function(msg)
18915 if(this.allowBlank){
18921 this.fireEvent('invalid', this, msg);
18923 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18926 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18930 label.markInvalid();
18933 if(this.inputType == 'radio'){
18934 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18935 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18936 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18943 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18944 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18948 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18954 for(var i in group){
18955 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18956 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18963 Roo.apply(Roo.bootstrap.CheckBox, {
18968 * register a CheckBox Group
18969 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18971 register : function(checkbox)
18973 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18974 this.groups[checkbox.groupId] = {};
18977 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18981 this.groups[checkbox.groupId][checkbox.name] = checkbox;
18985 * fetch a CheckBox Group based on the group ID
18986 * @param {string} the group ID
18987 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18989 get: function(groupId) {
18990 if (typeof(this.groups[groupId]) == 'undefined') {
18994 return this.groups[groupId] ;
19006 *<div class="radio">
19008 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19009 Option one is this and that—be sure to include why it's great
19016 *<label class="radio-inline">fieldLabel</label>
19017 *<label class="radio-inline">
19018 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19026 * @class Roo.bootstrap.Radio
19027 * @extends Roo.bootstrap.CheckBox
19028 * Bootstrap Radio class
19031 * Create a new Radio
19032 * @param {Object} config The config object
19035 Roo.bootstrap.Radio = function(config){
19036 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19040 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
19042 inputType: 'radio',
19046 getAutoCreate : function()
19048 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19049 align = align || 'left'; // default...
19056 tag : this.inline ? 'span' : 'div',
19061 var inline = this.inline ? ' radio-inline' : '';
19065 // does not need for, as we wrap the input with it..
19067 cls : 'control-label box-label' + inline,
19070 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19074 //cls : 'control-label' + inline,
19075 html : this.fieldLabel,
19076 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19085 type : this.inputType,
19086 //value : (!this.checked) ? this.valueOff : this.inputValue,
19087 value : this.inputValue,
19089 placeholder : this.placeholder || '' // ?? needed????
19092 if (this.weight) { // Validity check?
19093 input.cls += " radio-" + this.weight;
19095 if (this.disabled) {
19096 input.disabled=true;
19100 input.checked = this.checked;
19104 input.name = this.name;
19108 input.cls += ' input-' + this.size;
19111 //?? can span's inline have a width??
19114 ['xs','sm','md','lg'].map(function(size){
19115 if (settings[size]) {
19116 cfg.cls += ' col-' + size + '-' + settings[size];
19120 var inputblock = input;
19122 if (this.before || this.after) {
19125 cls : 'input-group',
19130 inputblock.cn.push({
19132 cls : 'input-group-addon',
19136 inputblock.cn.push(input);
19138 inputblock.cn.push({
19140 cls : 'input-group-addon',
19148 if (this.fieldLabel && this.fieldLabel.length) {
19149 cfg.cn.push(fieldLabel);
19152 // normal bootstrap puts the input inside the label.
19153 // however with our styled version - it has to go after the input.
19155 //lbl.cn.push(inputblock);
19159 cls: 'radio' + inline,
19166 cfg.cn.push( lblwrap);
19171 html: this.boxLabel
19180 initEvents : function()
19182 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19184 this.inputEl().on('click', this.onClick, this);
19185 if (this.boxLabel) {
19186 //Roo.log('find label');
19187 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
19192 inputEl: function ()
19194 return this.el.select('input.roo-radio',true).first();
19196 onClick : function()
19199 this.setChecked(true);
19202 setChecked : function(state,suppressEvent)
19205 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19206 v.dom.checked = false;
19209 Roo.log(this.inputEl().dom);
19210 this.checked = state;
19211 this.inputEl().dom.checked = state;
19213 if(suppressEvent !== true){
19214 this.fireEvent('check', this, state);
19217 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19221 getGroupValue : function()
19224 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19225 if(v.dom.checked == true){
19226 value = v.dom.value;
19234 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
19235 * @return {Mixed} value The field value
19237 getValue : function(){
19238 return this.getGroupValue();
19244 //<script type="text/javascript">
19247 * Based Ext JS Library 1.1.1
19248 * Copyright(c) 2006-2007, Ext JS, LLC.
19254 * @class Roo.HtmlEditorCore
19255 * @extends Roo.Component
19256 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19258 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19261 Roo.HtmlEditorCore = function(config){
19264 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19269 * @event initialize
19270 * Fires when the editor is fully initialized (including the iframe)
19271 * @param {Roo.HtmlEditorCore} this
19276 * Fires when the editor is first receives the focus. Any insertion must wait
19277 * until after this event.
19278 * @param {Roo.HtmlEditorCore} this
19282 * @event beforesync
19283 * Fires before the textarea is updated with content from the editor iframe. Return false
19284 * to cancel the sync.
19285 * @param {Roo.HtmlEditorCore} this
19286 * @param {String} html
19290 * @event beforepush
19291 * Fires before the iframe editor is updated with content from the textarea. Return false
19292 * to cancel the push.
19293 * @param {Roo.HtmlEditorCore} this
19294 * @param {String} html
19299 * Fires when the textarea is updated with content from the editor iframe.
19300 * @param {Roo.HtmlEditorCore} this
19301 * @param {String} html
19306 * Fires when the iframe editor is updated with content from the textarea.
19307 * @param {Roo.HtmlEditorCore} this
19308 * @param {String} html
19313 * @event editorevent
19314 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19315 * @param {Roo.HtmlEditorCore} this
19321 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19323 // defaults : white / black...
19324 this.applyBlacklists();
19331 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
19335 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
19341 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
19346 * @cfg {Number} height (in pixels)
19350 * @cfg {Number} width (in pixels)
19355 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19358 stylesheets: false,
19363 // private properties
19364 validationEvent : false,
19366 initialized : false,
19368 sourceEditMode : false,
19369 onFocus : Roo.emptyFn,
19371 hideMode:'offsets',
19375 // blacklist + whitelisted elements..
19382 * Protected method that will not generally be called directly. It
19383 * is called when the editor initializes the iframe with HTML contents. Override this method if you
19384 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19386 getDocMarkup : function(){
19390 // inherit styels from page...??
19391 if (this.stylesheets === false) {
19393 Roo.get(document.head).select('style').each(function(node) {
19394 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19397 Roo.get(document.head).select('link').each(function(node) {
19398 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19401 } else if (!this.stylesheets.length) {
19403 st = '<style type="text/css">' +
19404 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19410 st += '<style type="text/css">' +
19411 'IMG { cursor: pointer } ' +
19415 return '<html><head>' + st +
19416 //<style type="text/css">' +
19417 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19419 ' </head><body class="roo-htmleditor-body"></body></html>';
19423 onRender : function(ct, position)
19426 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19427 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19430 this.el.dom.style.border = '0 none';
19431 this.el.dom.setAttribute('tabIndex', -1);
19432 this.el.addClass('x-hidden hide');
19436 if(Roo.isIE){ // fix IE 1px bogus margin
19437 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19441 this.frameId = Roo.id();
19445 var iframe = this.owner.wrap.createChild({
19447 cls: 'form-control', // bootstrap..
19449 name: this.frameId,
19450 frameBorder : 'no',
19451 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19456 this.iframe = iframe.dom;
19458 this.assignDocWin();
19460 this.doc.designMode = 'on';
19463 this.doc.write(this.getDocMarkup());
19467 var task = { // must defer to wait for browser to be ready
19469 //console.log("run task?" + this.doc.readyState);
19470 this.assignDocWin();
19471 if(this.doc.body || this.doc.readyState == 'complete'){
19473 this.doc.designMode="on";
19477 Roo.TaskMgr.stop(task);
19478 this.initEditor.defer(10, this);
19485 Roo.TaskMgr.start(task);
19490 onResize : function(w, h)
19492 Roo.log('resize: ' +w + ',' + h );
19493 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19497 if(typeof w == 'number'){
19499 this.iframe.style.width = w + 'px';
19501 if(typeof h == 'number'){
19503 this.iframe.style.height = h + 'px';
19505 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19512 * Toggles the editor between standard and source edit mode.
19513 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19515 toggleSourceEdit : function(sourceEditMode){
19517 this.sourceEditMode = sourceEditMode === true;
19519 if(this.sourceEditMode){
19521 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19524 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19525 //this.iframe.className = '';
19528 //this.setSize(this.owner.wrap.getSize());
19529 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19536 * Protected method that will not generally be called directly. If you need/want
19537 * custom HTML cleanup, this is the method you should override.
19538 * @param {String} html The HTML to be cleaned
19539 * return {String} The cleaned HTML
19541 cleanHtml : function(html){
19542 html = String(html);
19543 if(html.length > 5){
19544 if(Roo.isSafari){ // strip safari nonsense
19545 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19548 if(html == ' '){
19555 * HTML Editor -> Textarea
19556 * Protected method that will not generally be called directly. Syncs the contents
19557 * of the editor iframe with the textarea.
19559 syncValue : function(){
19560 if(this.initialized){
19561 var bd = (this.doc.body || this.doc.documentElement);
19562 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19563 var html = bd.innerHTML;
19565 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19566 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19568 html = '<div style="'+m[0]+'">' + html + '</div>';
19571 html = this.cleanHtml(html);
19572 // fix up the special chars.. normaly like back quotes in word...
19573 // however we do not want to do this with chinese..
19574 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19575 var cc = b.charCodeAt();
19577 (cc >= 0x4E00 && cc < 0xA000 ) ||
19578 (cc >= 0x3400 && cc < 0x4E00 ) ||
19579 (cc >= 0xf900 && cc < 0xfb00 )
19585 if(this.owner.fireEvent('beforesync', this, html) !== false){
19586 this.el.dom.value = html;
19587 this.owner.fireEvent('sync', this, html);
19593 * Protected method that will not generally be called directly. Pushes the value of the textarea
19594 * into the iframe editor.
19596 pushValue : function(){
19597 if(this.initialized){
19598 var v = this.el.dom.value.trim();
19600 // if(v.length < 1){
19604 if(this.owner.fireEvent('beforepush', this, v) !== false){
19605 var d = (this.doc.body || this.doc.documentElement);
19607 this.cleanUpPaste();
19608 this.el.dom.value = d.innerHTML;
19609 this.owner.fireEvent('push', this, v);
19615 deferFocus : function(){
19616 this.focus.defer(10, this);
19620 focus : function(){
19621 if(this.win && !this.sourceEditMode){
19628 assignDocWin: function()
19630 var iframe = this.iframe;
19633 this.doc = iframe.contentWindow.document;
19634 this.win = iframe.contentWindow;
19636 // if (!Roo.get(this.frameId)) {
19639 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19640 // this.win = Roo.get(this.frameId).dom.contentWindow;
19642 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19646 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19647 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19652 initEditor : function(){
19653 //console.log("INIT EDITOR");
19654 this.assignDocWin();
19658 this.doc.designMode="on";
19660 this.doc.write(this.getDocMarkup());
19663 var dbody = (this.doc.body || this.doc.documentElement);
19664 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19665 // this copies styles from the containing element into thsi one..
19666 // not sure why we need all of this..
19667 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19669 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19670 //ss['background-attachment'] = 'fixed'; // w3c
19671 dbody.bgProperties = 'fixed'; // ie
19672 //Roo.DomHelper.applyStyles(dbody, ss);
19673 Roo.EventManager.on(this.doc, {
19674 //'mousedown': this.onEditorEvent,
19675 'mouseup': this.onEditorEvent,
19676 'dblclick': this.onEditorEvent,
19677 'click': this.onEditorEvent,
19678 'keyup': this.onEditorEvent,
19683 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19685 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19686 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19688 this.initialized = true;
19690 this.owner.fireEvent('initialize', this);
19695 onDestroy : function(){
19701 //for (var i =0; i < this.toolbars.length;i++) {
19702 // // fixme - ask toolbars for heights?
19703 // this.toolbars[i].onDestroy();
19706 //this.wrap.dom.innerHTML = '';
19707 //this.wrap.remove();
19712 onFirstFocus : function(){
19714 this.assignDocWin();
19717 this.activated = true;
19720 if(Roo.isGecko){ // prevent silly gecko errors
19722 var s = this.win.getSelection();
19723 if(!s.focusNode || s.focusNode.nodeType != 3){
19724 var r = s.getRangeAt(0);
19725 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19730 this.execCmd('useCSS', true);
19731 this.execCmd('styleWithCSS', false);
19734 this.owner.fireEvent('activate', this);
19738 adjustFont: function(btn){
19739 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19740 //if(Roo.isSafari){ // safari
19743 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19744 if(Roo.isSafari){ // safari
19745 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19746 v = (v < 10) ? 10 : v;
19747 v = (v > 48) ? 48 : v;
19748 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19753 v = Math.max(1, v+adjust);
19755 this.execCmd('FontSize', v );
19758 onEditorEvent : function(e)
19760 this.owner.fireEvent('editorevent', this, e);
19761 // this.updateToolbar();
19762 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19765 insertTag : function(tg)
19767 // could be a bit smarter... -> wrap the current selected tRoo..
19768 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19770 range = this.createRange(this.getSelection());
19771 var wrappingNode = this.doc.createElement(tg.toLowerCase());
19772 wrappingNode.appendChild(range.extractContents());
19773 range.insertNode(wrappingNode);
19780 this.execCmd("formatblock", tg);
19784 insertText : function(txt)
19788 var range = this.createRange();
19789 range.deleteContents();
19790 //alert(Sender.getAttribute('label'));
19792 range.insertNode(this.doc.createTextNode(txt));
19798 * Executes a Midas editor command on the editor document and performs necessary focus and
19799 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19800 * @param {String} cmd The Midas command
19801 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19803 relayCmd : function(cmd, value){
19805 this.execCmd(cmd, value);
19806 this.owner.fireEvent('editorevent', this);
19807 //this.updateToolbar();
19808 this.owner.deferFocus();
19812 * Executes a Midas editor command directly on the editor document.
19813 * For visual commands, you should use {@link #relayCmd} instead.
19814 * <b>This should only be called after the editor is initialized.</b>
19815 * @param {String} cmd The Midas command
19816 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19818 execCmd : function(cmd, value){
19819 this.doc.execCommand(cmd, false, value === undefined ? null : value);
19826 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19828 * @param {String} text | dom node..
19830 insertAtCursor : function(text)
19835 if(!this.activated){
19841 var r = this.doc.selection.createRange();
19852 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19856 // from jquery ui (MIT licenced)
19858 var win = this.win;
19860 if (win.getSelection && win.getSelection().getRangeAt) {
19861 range = win.getSelection().getRangeAt(0);
19862 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19863 range.insertNode(node);
19864 } else if (win.document.selection && win.document.selection.createRange) {
19865 // no firefox support
19866 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19867 win.document.selection.createRange().pasteHTML(txt);
19869 // no firefox support
19870 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19871 this.execCmd('InsertHTML', txt);
19880 mozKeyPress : function(e){
19882 var c = e.getCharCode(), cmd;
19885 c = String.fromCharCode(c).toLowerCase();
19899 this.cleanUpPaste.defer(100, this);
19907 e.preventDefault();
19915 fixKeys : function(){ // load time branching for fastest keydown performance
19917 return function(e){
19918 var k = e.getKey(), r;
19921 r = this.doc.selection.createRange();
19924 r.pasteHTML('    ');
19931 r = this.doc.selection.createRange();
19933 var target = r.parentElement();
19934 if(!target || target.tagName.toLowerCase() != 'li'){
19936 r.pasteHTML('<br />');
19942 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19943 this.cleanUpPaste.defer(100, this);
19949 }else if(Roo.isOpera){
19950 return function(e){
19951 var k = e.getKey();
19955 this.execCmd('InsertHTML','    ');
19958 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19959 this.cleanUpPaste.defer(100, this);
19964 }else if(Roo.isSafari){
19965 return function(e){
19966 var k = e.getKey();
19970 this.execCmd('InsertText','\t');
19974 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19975 this.cleanUpPaste.defer(100, this);
19983 getAllAncestors: function()
19985 var p = this.getSelectedNode();
19988 a.push(p); // push blank onto stack..
19989 p = this.getParentElement();
19993 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19997 a.push(this.doc.body);
20001 lastSelNode : false,
20004 getSelection : function()
20006 this.assignDocWin();
20007 return Roo.isIE ? this.doc.selection : this.win.getSelection();
20010 getSelectedNode: function()
20012 // this may only work on Gecko!!!
20014 // should we cache this!!!!
20019 var range = this.createRange(this.getSelection()).cloneRange();
20022 var parent = range.parentElement();
20024 var testRange = range.duplicate();
20025 testRange.moveToElementText(parent);
20026 if (testRange.inRange(range)) {
20029 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20032 parent = parent.parentElement;
20037 // is ancestor a text element.
20038 var ac = range.commonAncestorContainer;
20039 if (ac.nodeType == 3) {
20040 ac = ac.parentNode;
20043 var ar = ac.childNodes;
20046 var other_nodes = [];
20047 var has_other_nodes = false;
20048 for (var i=0;i<ar.length;i++) {
20049 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
20052 // fullly contained node.
20054 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20059 // probably selected..
20060 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20061 other_nodes.push(ar[i]);
20065 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
20070 has_other_nodes = true;
20072 if (!nodes.length && other_nodes.length) {
20073 nodes= other_nodes;
20075 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20081 createRange: function(sel)
20083 // this has strange effects when using with
20084 // top toolbar - not sure if it's a great idea.
20085 //this.editor.contentWindow.focus();
20086 if (typeof sel != "undefined") {
20088 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20090 return this.doc.createRange();
20093 return this.doc.createRange();
20096 getParentElement: function()
20099 this.assignDocWin();
20100 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20102 var range = this.createRange(sel);
20105 var p = range.commonAncestorContainer;
20106 while (p.nodeType == 3) { // text node
20117 * Range intersection.. the hard stuff...
20121 * [ -- selected range --- ]
20125 * if end is before start or hits it. fail.
20126 * if start is after end or hits it fail.
20128 * if either hits (but other is outside. - then it's not
20134 // @see http://www.thismuchiknow.co.uk/?p=64.
20135 rangeIntersectsNode : function(range, node)
20137 var nodeRange = node.ownerDocument.createRange();
20139 nodeRange.selectNode(node);
20141 nodeRange.selectNodeContents(node);
20144 var rangeStartRange = range.cloneRange();
20145 rangeStartRange.collapse(true);
20147 var rangeEndRange = range.cloneRange();
20148 rangeEndRange.collapse(false);
20150 var nodeStartRange = nodeRange.cloneRange();
20151 nodeStartRange.collapse(true);
20153 var nodeEndRange = nodeRange.cloneRange();
20154 nodeEndRange.collapse(false);
20156 return rangeStartRange.compareBoundaryPoints(
20157 Range.START_TO_START, nodeEndRange) == -1 &&
20158 rangeEndRange.compareBoundaryPoints(
20159 Range.START_TO_START, nodeStartRange) == 1;
20163 rangeCompareNode : function(range, node)
20165 var nodeRange = node.ownerDocument.createRange();
20167 nodeRange.selectNode(node);
20169 nodeRange.selectNodeContents(node);
20173 range.collapse(true);
20175 nodeRange.collapse(true);
20177 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20178 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
20180 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20182 var nodeIsBefore = ss == 1;
20183 var nodeIsAfter = ee == -1;
20185 if (nodeIsBefore && nodeIsAfter) {
20188 if (!nodeIsBefore && nodeIsAfter) {
20189 return 1; //right trailed.
20192 if (nodeIsBefore && !nodeIsAfter) {
20193 return 2; // left trailed.
20199 // private? - in a new class?
20200 cleanUpPaste : function()
20202 // cleans up the whole document..
20203 Roo.log('cleanuppaste');
20205 this.cleanUpChildren(this.doc.body);
20206 var clean = this.cleanWordChars(this.doc.body.innerHTML);
20207 if (clean != this.doc.body.innerHTML) {
20208 this.doc.body.innerHTML = clean;
20213 cleanWordChars : function(input) {// change the chars to hex code
20214 var he = Roo.HtmlEditorCore;
20216 var output = input;
20217 Roo.each(he.swapCodes, function(sw) {
20218 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20220 output = output.replace(swapper, sw[1]);
20227 cleanUpChildren : function (n)
20229 if (!n.childNodes.length) {
20232 for (var i = n.childNodes.length-1; i > -1 ; i--) {
20233 this.cleanUpChild(n.childNodes[i]);
20240 cleanUpChild : function (node)
20243 //console.log(node);
20244 if (node.nodeName == "#text") {
20245 // clean up silly Windows -- stuff?
20248 if (node.nodeName == "#comment") {
20249 node.parentNode.removeChild(node);
20250 // clean up silly Windows -- stuff?
20253 var lcname = node.tagName.toLowerCase();
20254 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20255 // whitelist of tags..
20257 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20259 node.parentNode.removeChild(node);
20264 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20266 // remove <a name=....> as rendering on yahoo mailer is borked with this.
20267 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20269 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20270 // remove_keep_children = true;
20273 if (remove_keep_children) {
20274 this.cleanUpChildren(node);
20275 // inserts everything just before this node...
20276 while (node.childNodes.length) {
20277 var cn = node.childNodes[0];
20278 node.removeChild(cn);
20279 node.parentNode.insertBefore(cn, node);
20281 node.parentNode.removeChild(node);
20285 if (!node.attributes || !node.attributes.length) {
20286 this.cleanUpChildren(node);
20290 function cleanAttr(n,v)
20293 if (v.match(/^\./) || v.match(/^\//)) {
20296 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20299 if (v.match(/^#/)) {
20302 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20303 node.removeAttribute(n);
20307 var cwhite = this.cwhite;
20308 var cblack = this.cblack;
20310 function cleanStyle(n,v)
20312 if (v.match(/expression/)) { //XSS?? should we even bother..
20313 node.removeAttribute(n);
20317 var parts = v.split(/;/);
20320 Roo.each(parts, function(p) {
20321 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20325 var l = p.split(':').shift().replace(/\s+/g,'');
20326 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20328 if ( cwhite.length && cblack.indexOf(l) > -1) {
20329 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20330 //node.removeAttribute(n);
20334 // only allow 'c whitelisted system attributes'
20335 if ( cwhite.length && cwhite.indexOf(l) < 0) {
20336 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20337 //node.removeAttribute(n);
20347 if (clean.length) {
20348 node.setAttribute(n, clean.join(';'));
20350 node.removeAttribute(n);
20356 for (var i = node.attributes.length-1; i > -1 ; i--) {
20357 var a = node.attributes[i];
20360 if (a.name.toLowerCase().substr(0,2)=='on') {
20361 node.removeAttribute(a.name);
20364 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20365 node.removeAttribute(a.name);
20368 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20369 cleanAttr(a.name,a.value); // fixme..
20372 if (a.name == 'style') {
20373 cleanStyle(a.name,a.value);
20376 /// clean up MS crap..
20377 // tecnically this should be a list of valid class'es..
20380 if (a.name == 'class') {
20381 if (a.value.match(/^Mso/)) {
20382 node.className = '';
20385 if (a.value.match(/body/)) {
20386 node.className = '';
20397 this.cleanUpChildren(node);
20403 * Clean up MS wordisms...
20405 cleanWord : function(node)
20410 this.cleanWord(this.doc.body);
20413 if (node.nodeName == "#text") {
20414 // clean up silly Windows -- stuff?
20417 if (node.nodeName == "#comment") {
20418 node.parentNode.removeChild(node);
20419 // clean up silly Windows -- stuff?
20423 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20424 node.parentNode.removeChild(node);
20428 // remove - but keep children..
20429 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20430 while (node.childNodes.length) {
20431 var cn = node.childNodes[0];
20432 node.removeChild(cn);
20433 node.parentNode.insertBefore(cn, node);
20435 node.parentNode.removeChild(node);
20436 this.iterateChildren(node, this.cleanWord);
20440 if (node.className.length) {
20442 var cn = node.className.split(/\W+/);
20444 Roo.each(cn, function(cls) {
20445 if (cls.match(/Mso[a-zA-Z]+/)) {
20450 node.className = cna.length ? cna.join(' ') : '';
20452 node.removeAttribute("class");
20456 if (node.hasAttribute("lang")) {
20457 node.removeAttribute("lang");
20460 if (node.hasAttribute("style")) {
20462 var styles = node.getAttribute("style").split(";");
20464 Roo.each(styles, function(s) {
20465 if (!s.match(/:/)) {
20468 var kv = s.split(":");
20469 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20472 // what ever is left... we allow.
20475 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20476 if (!nstyle.length) {
20477 node.removeAttribute('style');
20480 this.iterateChildren(node, this.cleanWord);
20486 * iterateChildren of a Node, calling fn each time, using this as the scole..
20487 * @param {DomNode} node node to iterate children of.
20488 * @param {Function} fn method of this class to call on each item.
20490 iterateChildren : function(node, fn)
20492 if (!node.childNodes.length) {
20495 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20496 fn.call(this, node.childNodes[i])
20502 * cleanTableWidths.
20504 * Quite often pasting from word etc.. results in tables with column and widths.
20505 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20508 cleanTableWidths : function(node)
20513 this.cleanTableWidths(this.doc.body);
20518 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20521 Roo.log(node.tagName);
20522 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20523 this.iterateChildren(node, this.cleanTableWidths);
20526 if (node.hasAttribute('width')) {
20527 node.removeAttribute('width');
20531 if (node.hasAttribute("style")) {
20534 var styles = node.getAttribute("style").split(";");
20536 Roo.each(styles, function(s) {
20537 if (!s.match(/:/)) {
20540 var kv = s.split(":");
20541 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20544 // what ever is left... we allow.
20547 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20548 if (!nstyle.length) {
20549 node.removeAttribute('style');
20553 this.iterateChildren(node, this.cleanTableWidths);
20561 domToHTML : function(currentElement, depth, nopadtext) {
20563 depth = depth || 0;
20564 nopadtext = nopadtext || false;
20566 if (!currentElement) {
20567 return this.domToHTML(this.doc.body);
20570 //Roo.log(currentElement);
20572 var allText = false;
20573 var nodeName = currentElement.nodeName;
20574 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20576 if (nodeName == '#text') {
20578 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20583 if (nodeName != 'BODY') {
20586 // Prints the node tagName, such as <A>, <IMG>, etc
20589 for(i = 0; i < currentElement.attributes.length;i++) {
20591 var aname = currentElement.attributes.item(i).name;
20592 if (!currentElement.attributes.item(i).value.length) {
20595 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20598 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20607 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20610 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20615 // Traverse the tree
20617 var currentElementChild = currentElement.childNodes.item(i);
20618 var allText = true;
20619 var innerHTML = '';
20621 while (currentElementChild) {
20622 // Formatting code (indent the tree so it looks nice on the screen)
20623 var nopad = nopadtext;
20624 if (lastnode == 'SPAN') {
20628 if (currentElementChild.nodeName == '#text') {
20629 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20630 toadd = nopadtext ? toadd : toadd.trim();
20631 if (!nopad && toadd.length > 80) {
20632 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20634 innerHTML += toadd;
20637 currentElementChild = currentElement.childNodes.item(i);
20643 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20645 // Recursively traverse the tree structure of the child node
20646 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20647 lastnode = currentElementChild.nodeName;
20649 currentElementChild=currentElement.childNodes.item(i);
20655 // The remaining code is mostly for formatting the tree
20656 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20661 ret+= "</"+tagName+">";
20667 applyBlacklists : function()
20669 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20670 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20674 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20675 if (b.indexOf(tag) > -1) {
20678 this.white.push(tag);
20682 Roo.each(w, function(tag) {
20683 if (b.indexOf(tag) > -1) {
20686 if (this.white.indexOf(tag) > -1) {
20689 this.white.push(tag);
20694 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20695 if (w.indexOf(tag) > -1) {
20698 this.black.push(tag);
20702 Roo.each(b, function(tag) {
20703 if (w.indexOf(tag) > -1) {
20706 if (this.black.indexOf(tag) > -1) {
20709 this.black.push(tag);
20714 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20715 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20719 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20720 if (b.indexOf(tag) > -1) {
20723 this.cwhite.push(tag);
20727 Roo.each(w, function(tag) {
20728 if (b.indexOf(tag) > -1) {
20731 if (this.cwhite.indexOf(tag) > -1) {
20734 this.cwhite.push(tag);
20739 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20740 if (w.indexOf(tag) > -1) {
20743 this.cblack.push(tag);
20747 Roo.each(b, function(tag) {
20748 if (w.indexOf(tag) > -1) {
20751 if (this.cblack.indexOf(tag) > -1) {
20754 this.cblack.push(tag);
20759 setStylesheets : function(stylesheets)
20761 if(typeof(stylesheets) == 'string'){
20762 Roo.get(this.iframe.contentDocument.head).createChild({
20764 rel : 'stylesheet',
20773 Roo.each(stylesheets, function(s) {
20778 Roo.get(_this.iframe.contentDocument.head).createChild({
20780 rel : 'stylesheet',
20789 removeStylesheets : function()
20793 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20798 // hide stuff that is not compatible
20812 * @event specialkey
20816 * @cfg {String} fieldClass @hide
20819 * @cfg {String} focusClass @hide
20822 * @cfg {String} autoCreate @hide
20825 * @cfg {String} inputType @hide
20828 * @cfg {String} invalidClass @hide
20831 * @cfg {String} invalidText @hide
20834 * @cfg {String} msgFx @hide
20837 * @cfg {String} validateOnBlur @hide
20841 Roo.HtmlEditorCore.white = [
20842 'area', 'br', 'img', 'input', 'hr', 'wbr',
20844 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
20845 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
20846 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
20847 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
20848 'table', 'ul', 'xmp',
20850 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
20853 'dir', 'menu', 'ol', 'ul', 'dl',
20859 Roo.HtmlEditorCore.black = [
20860 // 'embed', 'object', // enable - backend responsiblity to clean thiese
20862 'base', 'basefont', 'bgsound', 'blink', 'body',
20863 'frame', 'frameset', 'head', 'html', 'ilayer',
20864 'iframe', 'layer', 'link', 'meta', 'object',
20865 'script', 'style' ,'title', 'xml' // clean later..
20867 Roo.HtmlEditorCore.clean = [
20868 'script', 'style', 'title', 'xml'
20870 Roo.HtmlEditorCore.remove = [
20875 Roo.HtmlEditorCore.ablack = [
20879 Roo.HtmlEditorCore.aclean = [
20880 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
20884 Roo.HtmlEditorCore.pwhite= [
20885 'http', 'https', 'mailto'
20888 // white listed style attributes.
20889 Roo.HtmlEditorCore.cwhite= [
20890 // 'text-align', /// default is to allow most things..
20896 // black listed style attributes.
20897 Roo.HtmlEditorCore.cblack= [
20898 // 'font-size' -- this can be set by the project
20902 Roo.HtmlEditorCore.swapCodes =[
20921 * @class Roo.bootstrap.HtmlEditor
20922 * @extends Roo.bootstrap.TextArea
20923 * Bootstrap HtmlEditor class
20926 * Create a new HtmlEditor
20927 * @param {Object} config The config object
20930 Roo.bootstrap.HtmlEditor = function(config){
20931 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20932 if (!this.toolbars) {
20933 this.toolbars = [];
20935 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20938 * @event initialize
20939 * Fires when the editor is fully initialized (including the iframe)
20940 * @param {HtmlEditor} this
20945 * Fires when the editor is first receives the focus. Any insertion must wait
20946 * until after this event.
20947 * @param {HtmlEditor} this
20951 * @event beforesync
20952 * Fires before the textarea is updated with content from the editor iframe. Return false
20953 * to cancel the sync.
20954 * @param {HtmlEditor} this
20955 * @param {String} html
20959 * @event beforepush
20960 * Fires before the iframe editor is updated with content from the textarea. Return false
20961 * to cancel the push.
20962 * @param {HtmlEditor} this
20963 * @param {String} html
20968 * Fires when the textarea is updated with content from the editor iframe.
20969 * @param {HtmlEditor} this
20970 * @param {String} html
20975 * Fires when the iframe editor is updated with content from the textarea.
20976 * @param {HtmlEditor} this
20977 * @param {String} html
20981 * @event editmodechange
20982 * Fires when the editor switches edit modes
20983 * @param {HtmlEditor} this
20984 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20986 editmodechange: true,
20988 * @event editorevent
20989 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20990 * @param {HtmlEditor} this
20994 * @event firstfocus
20995 * Fires when on first focus - needed by toolbars..
20996 * @param {HtmlEditor} this
21001 * Auto save the htmlEditor value as a file into Events
21002 * @param {HtmlEditor} this
21006 * @event savedpreview
21007 * preview the saved version of htmlEditor
21008 * @param {HtmlEditor} this
21015 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
21019 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21024 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21029 * @cfg {Number} height (in pixels)
21033 * @cfg {Number} width (in pixels)
21038 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21041 stylesheets: false,
21046 // private properties
21047 validationEvent : false,
21049 initialized : false,
21052 onFocus : Roo.emptyFn,
21054 hideMode:'offsets',
21057 tbContainer : false,
21059 toolbarContainer :function() {
21060 return this.wrap.select('.x-html-editor-tb',true).first();
21064 * Protected method that will not generally be called directly. It
21065 * is called when the editor creates its toolbar. Override this method if you need to
21066 * add custom toolbar buttons.
21067 * @param {HtmlEditor} editor
21069 createToolbar : function(){
21071 Roo.log("create toolbars");
21073 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21074 this.toolbars[0].render(this.toolbarContainer());
21078 // if (!editor.toolbars || !editor.toolbars.length) {
21079 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21082 // for (var i =0 ; i < editor.toolbars.length;i++) {
21083 // editor.toolbars[i] = Roo.factory(
21084 // typeof(editor.toolbars[i]) == 'string' ?
21085 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
21086 // Roo.bootstrap.HtmlEditor);
21087 // editor.toolbars[i].init(editor);
21093 onRender : function(ct, position)
21095 // Roo.log("Call onRender: " + this.xtype);
21097 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21099 this.wrap = this.inputEl().wrap({
21100 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21103 this.editorcore.onRender(ct, position);
21105 if (this.resizable) {
21106 this.resizeEl = new Roo.Resizable(this.wrap, {
21110 minHeight : this.height,
21111 height: this.height,
21112 handles : this.resizable,
21115 resize : function(r, w, h) {
21116 _t.onResize(w,h); // -something
21122 this.createToolbar(this);
21125 if(!this.width && this.resizable){
21126 this.setSize(this.wrap.getSize());
21128 if (this.resizeEl) {
21129 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21130 // should trigger onReize..
21136 onResize : function(w, h)
21138 Roo.log('resize: ' +w + ',' + h );
21139 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21143 if(this.inputEl() ){
21144 if(typeof w == 'number'){
21145 var aw = w - this.wrap.getFrameWidth('lr');
21146 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21149 if(typeof h == 'number'){
21150 var tbh = -11; // fixme it needs to tool bar size!
21151 for (var i =0; i < this.toolbars.length;i++) {
21152 // fixme - ask toolbars for heights?
21153 tbh += this.toolbars[i].el.getHeight();
21154 //if (this.toolbars[i].footer) {
21155 // tbh += this.toolbars[i].footer.el.getHeight();
21163 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21164 ah -= 5; // knock a few pixes off for look..
21165 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21169 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21170 this.editorcore.onResize(ew,eh);
21175 * Toggles the editor between standard and source edit mode.
21176 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21178 toggleSourceEdit : function(sourceEditMode)
21180 this.editorcore.toggleSourceEdit(sourceEditMode);
21182 if(this.editorcore.sourceEditMode){
21183 Roo.log('editor - showing textarea');
21186 // Roo.log(this.syncValue());
21188 this.inputEl().removeClass(['hide', 'x-hidden']);
21189 this.inputEl().dom.removeAttribute('tabIndex');
21190 this.inputEl().focus();
21192 Roo.log('editor - hiding textarea');
21194 // Roo.log(this.pushValue());
21197 this.inputEl().addClass(['hide', 'x-hidden']);
21198 this.inputEl().dom.setAttribute('tabIndex', -1);
21199 //this.deferFocus();
21202 if(this.resizable){
21203 this.setSize(this.wrap.getSize());
21206 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21209 // private (for BoxComponent)
21210 adjustSize : Roo.BoxComponent.prototype.adjustSize,
21212 // private (for BoxComponent)
21213 getResizeEl : function(){
21217 // private (for BoxComponent)
21218 getPositionEl : function(){
21223 initEvents : function(){
21224 this.originalValue = this.getValue();
21228 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21231 // markInvalid : Roo.emptyFn,
21233 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21236 // clearInvalid : Roo.emptyFn,
21238 setValue : function(v){
21239 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21240 this.editorcore.pushValue();
21245 deferFocus : function(){
21246 this.focus.defer(10, this);
21250 focus : function(){
21251 this.editorcore.focus();
21257 onDestroy : function(){
21263 for (var i =0; i < this.toolbars.length;i++) {
21264 // fixme - ask toolbars for heights?
21265 this.toolbars[i].onDestroy();
21268 this.wrap.dom.innerHTML = '';
21269 this.wrap.remove();
21274 onFirstFocus : function(){
21275 //Roo.log("onFirstFocus");
21276 this.editorcore.onFirstFocus();
21277 for (var i =0; i < this.toolbars.length;i++) {
21278 this.toolbars[i].onFirstFocus();
21284 syncValue : function()
21286 this.editorcore.syncValue();
21289 pushValue : function()
21291 this.editorcore.pushValue();
21295 // hide stuff that is not compatible
21309 * @event specialkey
21313 * @cfg {String} fieldClass @hide
21316 * @cfg {String} focusClass @hide
21319 * @cfg {String} autoCreate @hide
21322 * @cfg {String} inputType @hide
21325 * @cfg {String} invalidClass @hide
21328 * @cfg {String} invalidText @hide
21331 * @cfg {String} msgFx @hide
21334 * @cfg {String} validateOnBlur @hide
21343 Roo.namespace('Roo.bootstrap.htmleditor');
21345 * @class Roo.bootstrap.HtmlEditorToolbar1
21350 new Roo.bootstrap.HtmlEditor({
21353 new Roo.bootstrap.HtmlEditorToolbar1({
21354 disable : { fonts: 1 , format: 1, ..., ... , ...],
21360 * @cfg {Object} disable List of elements to disable..
21361 * @cfg {Array} btns List of additional buttons.
21365 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21368 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21371 Roo.apply(this, config);
21373 // default disabled, based on 'good practice'..
21374 this.disable = this.disable || {};
21375 Roo.applyIf(this.disable, {
21378 specialElements : true
21380 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21382 this.editor = config.editor;
21383 this.editorcore = config.editor.editorcore;
21385 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21387 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21388 // dont call parent... till later.
21390 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
21395 editorcore : false,
21400 "h1","h2","h3","h4","h5","h6",
21402 "abbr", "acronym", "address", "cite", "samp", "var",
21406 onRender : function(ct, position)
21408 // Roo.log("Call onRender: " + this.xtype);
21410 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21412 this.el.dom.style.marginBottom = '0';
21414 var editorcore = this.editorcore;
21415 var editor= this.editor;
21418 var btn = function(id,cmd , toggle, handler){
21420 var event = toggle ? 'toggle' : 'click';
21425 xns: Roo.bootstrap,
21428 enableToggle:toggle !== false,
21430 pressed : toggle ? false : null,
21433 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21434 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21443 xns: Roo.bootstrap,
21444 glyphicon : 'font',
21448 xns: Roo.bootstrap,
21452 Roo.each(this.formats, function(f) {
21453 style.menu.items.push({
21455 xns: Roo.bootstrap,
21456 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21461 editorcore.insertTag(this.tagname);
21468 children.push(style);
21471 btn('bold',false,true);
21472 btn('italic',false,true);
21473 btn('align-left', 'justifyleft',true);
21474 btn('align-center', 'justifycenter',true);
21475 btn('align-right' , 'justifyright',true);
21476 btn('link', false, false, function(btn) {
21477 //Roo.log("create link?");
21478 var url = prompt(this.createLinkText, this.defaultLinkValue);
21479 if(url && url != 'http:/'+'/'){
21480 this.editorcore.relayCmd('createlink', url);
21483 btn('list','insertunorderedlist',true);
21484 btn('pencil', false,true, function(btn){
21487 this.toggleSourceEdit(btn.pressed);
21493 xns: Roo.bootstrap,
21498 xns: Roo.bootstrap,
21503 cog.menu.items.push({
21505 xns: Roo.bootstrap,
21506 html : Clean styles,
21511 editorcore.insertTag(this.tagname);
21520 this.xtype = 'NavSimplebar';
21522 for(var i=0;i< children.length;i++) {
21524 this.buttons.add(this.addxtypeChild(children[i]));
21528 editor.on('editorevent', this.updateToolbar, this);
21530 onBtnClick : function(id)
21532 this.editorcore.relayCmd(id);
21533 this.editorcore.focus();
21537 * Protected method that will not generally be called directly. It triggers
21538 * a toolbar update by reading the markup state of the current selection in the editor.
21540 updateToolbar: function(){
21542 if(!this.editorcore.activated){
21543 this.editor.onFirstFocus(); // is this neeed?
21547 var btns = this.buttons;
21548 var doc = this.editorcore.doc;
21549 btns.get('bold').setActive(doc.queryCommandState('bold'));
21550 btns.get('italic').setActive(doc.queryCommandState('italic'));
21551 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21553 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21554 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21555 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21557 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21558 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21561 var ans = this.editorcore.getAllAncestors();
21562 if (this.formatCombo) {
21565 var store = this.formatCombo.store;
21566 this.formatCombo.setValue("");
21567 for (var i =0; i < ans.length;i++) {
21568 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21570 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21578 // hides menus... - so this cant be on a menu...
21579 Roo.bootstrap.MenuMgr.hideAll();
21581 Roo.bootstrap.MenuMgr.hideAll();
21582 //this.editorsyncValue();
21584 onFirstFocus: function() {
21585 this.buttons.each(function(item){
21589 toggleSourceEdit : function(sourceEditMode){
21592 if(sourceEditMode){
21593 Roo.log("disabling buttons");
21594 this.buttons.each( function(item){
21595 if(item.cmd != 'pencil'){
21601 Roo.log("enabling buttons");
21602 if(this.editorcore.initialized){
21603 this.buttons.each( function(item){
21609 Roo.log("calling toggole on editor");
21610 // tell the editor that it's been pressed..
21611 this.editor.toggleSourceEdit(sourceEditMode);
21621 * @class Roo.bootstrap.Table.AbstractSelectionModel
21622 * @extends Roo.util.Observable
21623 * Abstract base class for grid SelectionModels. It provides the interface that should be
21624 * implemented by descendant classes. This class should not be directly instantiated.
21627 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21628 this.locked = false;
21629 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21633 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21634 /** @ignore Called by the grid automatically. Do not call directly. */
21635 init : function(grid){
21641 * Locks the selections.
21644 this.locked = true;
21648 * Unlocks the selections.
21650 unlock : function(){
21651 this.locked = false;
21655 * Returns true if the selections are locked.
21656 * @return {Boolean}
21658 isLocked : function(){
21659 return this.locked;
21663 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21664 * @class Roo.bootstrap.Table.RowSelectionModel
21665 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21666 * It supports multiple selections and keyboard selection/navigation.
21668 * @param {Object} config
21671 Roo.bootstrap.Table.RowSelectionModel = function(config){
21672 Roo.apply(this, config);
21673 this.selections = new Roo.util.MixedCollection(false, function(o){
21678 this.lastActive = false;
21682 * @event selectionchange
21683 * Fires when the selection changes
21684 * @param {SelectionModel} this
21686 "selectionchange" : true,
21688 * @event afterselectionchange
21689 * Fires after the selection changes (eg. by key press or clicking)
21690 * @param {SelectionModel} this
21692 "afterselectionchange" : true,
21694 * @event beforerowselect
21695 * Fires when a row is selected being selected, return false to cancel.
21696 * @param {SelectionModel} this
21697 * @param {Number} rowIndex The selected index
21698 * @param {Boolean} keepExisting False if other selections will be cleared
21700 "beforerowselect" : true,
21703 * Fires when a row is selected.
21704 * @param {SelectionModel} this
21705 * @param {Number} rowIndex The selected index
21706 * @param {Roo.data.Record} r The record
21708 "rowselect" : true,
21710 * @event rowdeselect
21711 * Fires when a row is deselected.
21712 * @param {SelectionModel} this
21713 * @param {Number} rowIndex The selected index
21715 "rowdeselect" : true
21717 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21718 this.locked = false;
21721 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21723 * @cfg {Boolean} singleSelect
21724 * True to allow selection of only one row at a time (defaults to false)
21726 singleSelect : false,
21729 initEvents : function(){
21731 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21732 this.grid.on("mousedown", this.handleMouseDown, this);
21733 }else{ // allow click to work like normal
21734 this.grid.on("rowclick", this.handleDragableRowClick, this);
21737 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21738 "up" : function(e){
21740 this.selectPrevious(e.shiftKey);
21741 }else if(this.last !== false && this.lastActive !== false){
21742 var last = this.last;
21743 this.selectRange(this.last, this.lastActive-1);
21744 this.grid.getView().focusRow(this.lastActive);
21745 if(last !== false){
21749 this.selectFirstRow();
21751 this.fireEvent("afterselectionchange", this);
21753 "down" : function(e){
21755 this.selectNext(e.shiftKey);
21756 }else if(this.last !== false && this.lastActive !== false){
21757 var last = this.last;
21758 this.selectRange(this.last, this.lastActive+1);
21759 this.grid.getView().focusRow(this.lastActive);
21760 if(last !== false){
21764 this.selectFirstRow();
21766 this.fireEvent("afterselectionchange", this);
21771 var view = this.grid.view;
21772 view.on("refresh", this.onRefresh, this);
21773 view.on("rowupdated", this.onRowUpdated, this);
21774 view.on("rowremoved", this.onRemove, this);
21778 onRefresh : function(){
21779 var ds = this.grid.dataSource, i, v = this.grid.view;
21780 var s = this.selections;
21781 s.each(function(r){
21782 if((i = ds.indexOfId(r.id)) != -1){
21791 onRemove : function(v, index, r){
21792 this.selections.remove(r);
21796 onRowUpdated : function(v, index, r){
21797 if(this.isSelected(r)){
21798 v.onRowSelect(index);
21804 * @param {Array} records The records to select
21805 * @param {Boolean} keepExisting (optional) True to keep existing selections
21807 selectRecords : function(records, keepExisting){
21809 this.clearSelections();
21811 var ds = this.grid.dataSource;
21812 for(var i = 0, len = records.length; i < len; i++){
21813 this.selectRow(ds.indexOf(records[i]), true);
21818 * Gets the number of selected rows.
21821 getCount : function(){
21822 return this.selections.length;
21826 * Selects the first row in the grid.
21828 selectFirstRow : function(){
21833 * Select the last row.
21834 * @param {Boolean} keepExisting (optional) True to keep existing selections
21836 selectLastRow : function(keepExisting){
21837 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21841 * Selects the row immediately following the last selected row.
21842 * @param {Boolean} keepExisting (optional) True to keep existing selections
21844 selectNext : function(keepExisting){
21845 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21846 this.selectRow(this.last+1, keepExisting);
21847 this.grid.getView().focusRow(this.last);
21852 * Selects the row that precedes the last selected row.
21853 * @param {Boolean} keepExisting (optional) True to keep existing selections
21855 selectPrevious : function(keepExisting){
21857 this.selectRow(this.last-1, keepExisting);
21858 this.grid.getView().focusRow(this.last);
21863 * Returns the selected records
21864 * @return {Array} Array of selected records
21866 getSelections : function(){
21867 return [].concat(this.selections.items);
21871 * Returns the first selected record.
21874 getSelected : function(){
21875 return this.selections.itemAt(0);
21880 * Clears all selections.
21882 clearSelections : function(fast){
21887 var ds = this.grid.dataSource;
21888 var s = this.selections;
21889 s.each(function(r){
21890 this.deselectRow(ds.indexOfId(r.id));
21894 this.selections.clear();
21901 * Selects all rows.
21903 selectAll : function(){
21907 this.selections.clear();
21908 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21909 this.selectRow(i, true);
21914 * Returns True if there is a selection.
21915 * @return {Boolean}
21917 hasSelection : function(){
21918 return this.selections.length > 0;
21922 * Returns True if the specified row is selected.
21923 * @param {Number/Record} record The record or index of the record to check
21924 * @return {Boolean}
21926 isSelected : function(index){
21927 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21928 return (r && this.selections.key(r.id) ? true : false);
21932 * Returns True if the specified record id is selected.
21933 * @param {String} id The id of record to check
21934 * @return {Boolean}
21936 isIdSelected : function(id){
21937 return (this.selections.key(id) ? true : false);
21941 handleMouseDown : function(e, t){
21942 var view = this.grid.getView(), rowIndex;
21943 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21946 if(e.shiftKey && this.last !== false){
21947 var last = this.last;
21948 this.selectRange(last, rowIndex, e.ctrlKey);
21949 this.last = last; // reset the last
21950 view.focusRow(rowIndex);
21952 var isSelected = this.isSelected(rowIndex);
21953 if(e.button !== 0 && isSelected){
21954 view.focusRow(rowIndex);
21955 }else if(e.ctrlKey && isSelected){
21956 this.deselectRow(rowIndex);
21957 }else if(!isSelected){
21958 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21959 view.focusRow(rowIndex);
21962 this.fireEvent("afterselectionchange", this);
21965 handleDragableRowClick : function(grid, rowIndex, e)
21967 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21968 this.selectRow(rowIndex, false);
21969 grid.view.focusRow(rowIndex);
21970 this.fireEvent("afterselectionchange", this);
21975 * Selects multiple rows.
21976 * @param {Array} rows Array of the indexes of the row to select
21977 * @param {Boolean} keepExisting (optional) True to keep existing selections
21979 selectRows : function(rows, keepExisting){
21981 this.clearSelections();
21983 for(var i = 0, len = rows.length; i < len; i++){
21984 this.selectRow(rows[i], true);
21989 * Selects a range of rows. All rows in between startRow and endRow are also selected.
21990 * @param {Number} startRow The index of the first row in the range
21991 * @param {Number} endRow The index of the last row in the range
21992 * @param {Boolean} keepExisting (optional) True to retain existing selections
21994 selectRange : function(startRow, endRow, keepExisting){
21999 this.clearSelections();
22001 if(startRow <= endRow){
22002 for(var i = startRow; i <= endRow; i++){
22003 this.selectRow(i, true);
22006 for(var i = startRow; i >= endRow; i--){
22007 this.selectRow(i, true);
22013 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22014 * @param {Number} startRow The index of the first row in the range
22015 * @param {Number} endRow The index of the last row in the range
22017 deselectRange : function(startRow, endRow, preventViewNotify){
22021 for(var i = startRow; i <= endRow; i++){
22022 this.deselectRow(i, preventViewNotify);
22028 * @param {Number} row The index of the row to select
22029 * @param {Boolean} keepExisting (optional) True to keep existing selections
22031 selectRow : function(index, keepExisting, preventViewNotify){
22032 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22035 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22036 if(!keepExisting || this.singleSelect){
22037 this.clearSelections();
22039 var r = this.grid.dataSource.getAt(index);
22040 this.selections.add(r);
22041 this.last = this.lastActive = index;
22042 if(!preventViewNotify){
22043 this.grid.getView().onRowSelect(index);
22045 this.fireEvent("rowselect", this, index, r);
22046 this.fireEvent("selectionchange", this);
22052 * @param {Number} row The index of the row to deselect
22054 deselectRow : function(index, preventViewNotify){
22058 if(this.last == index){
22061 if(this.lastActive == index){
22062 this.lastActive = false;
22064 var r = this.grid.dataSource.getAt(index);
22065 this.selections.remove(r);
22066 if(!preventViewNotify){
22067 this.grid.getView().onRowDeselect(index);
22069 this.fireEvent("rowdeselect", this, index);
22070 this.fireEvent("selectionchange", this);
22074 restoreLast : function(){
22076 this.last = this._last;
22081 acceptsNav : function(row, col, cm){
22082 return !cm.isHidden(col) && cm.isCellEditable(col, row);
22086 onEditorKey : function(field, e){
22087 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22092 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22094 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22096 }else if(k == e.ENTER && !e.ctrlKey){
22100 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22102 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22104 }else if(k == e.ESC){
22108 g.startEditing(newCell[0], newCell[1]);
22113 * Ext JS Library 1.1.1
22114 * Copyright(c) 2006-2007, Ext JS, LLC.
22116 * Originally Released Under LGPL - original licence link has changed is not relivant.
22119 * <script type="text/javascript">
22123 * @class Roo.bootstrap.PagingToolbar
22124 * @extends Roo.bootstrap.NavSimplebar
22125 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22127 * Create a new PagingToolbar
22128 * @param {Object} config The config object
22129 * @param {Roo.data.Store} store
22131 Roo.bootstrap.PagingToolbar = function(config)
22133 // old args format still supported... - xtype is prefered..
22134 // created from xtype...
22136 this.ds = config.dataSource;
22138 if (config.store && !this.ds) {
22139 this.store= Roo.factory(config.store, Roo.data);
22140 this.ds = this.store;
22141 this.ds.xmodule = this.xmodule || false;
22144 this.toolbarItems = [];
22145 if (config.items) {
22146 this.toolbarItems = config.items;
22149 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22154 this.bind(this.ds);
22157 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22161 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22163 * @cfg {Roo.data.Store} dataSource
22164 * The underlying data store providing the paged data
22167 * @cfg {String/HTMLElement/Element} container
22168 * container The id or element that will contain the toolbar
22171 * @cfg {Boolean} displayInfo
22172 * True to display the displayMsg (defaults to false)
22175 * @cfg {Number} pageSize
22176 * The number of records to display per page (defaults to 20)
22180 * @cfg {String} displayMsg
22181 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22183 displayMsg : 'Displaying {0} - {1} of {2}',
22185 * @cfg {String} emptyMsg
22186 * The message to display when no records are found (defaults to "No data to display")
22188 emptyMsg : 'No data to display',
22190 * Customizable piece of the default paging text (defaults to "Page")
22193 beforePageText : "Page",
22195 * Customizable piece of the default paging text (defaults to "of %0")
22198 afterPageText : "of {0}",
22200 * Customizable piece of the default paging text (defaults to "First Page")
22203 firstText : "First Page",
22205 * Customizable piece of the default paging text (defaults to "Previous Page")
22208 prevText : "Previous Page",
22210 * Customizable piece of the default paging text (defaults to "Next Page")
22213 nextText : "Next Page",
22215 * Customizable piece of the default paging text (defaults to "Last Page")
22218 lastText : "Last Page",
22220 * Customizable piece of the default paging text (defaults to "Refresh")
22223 refreshText : "Refresh",
22227 onRender : function(ct, position)
22229 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22230 this.navgroup.parentId = this.id;
22231 this.navgroup.onRender(this.el, null);
22232 // add the buttons to the navgroup
22234 if(this.displayInfo){
22235 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22236 this.displayEl = this.el.select('.x-paging-info', true).first();
22237 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22238 // this.displayEl = navel.el.select('span',true).first();
22244 Roo.each(_this.buttons, function(e){ // this might need to use render????
22245 Roo.factory(e).onRender(_this.el, null);
22249 Roo.each(_this.toolbarItems, function(e) {
22250 _this.navgroup.addItem(e);
22254 this.first = this.navgroup.addItem({
22255 tooltip: this.firstText,
22257 icon : 'fa fa-backward',
22259 preventDefault: true,
22260 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22263 this.prev = this.navgroup.addItem({
22264 tooltip: this.prevText,
22266 icon : 'fa fa-step-backward',
22268 preventDefault: true,
22269 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
22271 //this.addSeparator();
22274 var field = this.navgroup.addItem( {
22276 cls : 'x-paging-position',
22278 html : this.beforePageText +
22279 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22280 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
22283 this.field = field.el.select('input', true).first();
22284 this.field.on("keydown", this.onPagingKeydown, this);
22285 this.field.on("focus", function(){this.dom.select();});
22288 this.afterTextEl = field.el.select('.x-paging-after',true).first();
22289 //this.field.setHeight(18);
22290 //this.addSeparator();
22291 this.next = this.navgroup.addItem({
22292 tooltip: this.nextText,
22294 html : ' <i class="fa fa-step-forward">',
22296 preventDefault: true,
22297 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
22299 this.last = this.navgroup.addItem({
22300 tooltip: this.lastText,
22301 icon : 'fa fa-forward',
22304 preventDefault: true,
22305 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
22307 //this.addSeparator();
22308 this.loading = this.navgroup.addItem({
22309 tooltip: this.refreshText,
22310 icon: 'fa fa-refresh',
22311 preventDefault: true,
22312 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22318 updateInfo : function(){
22319 if(this.displayEl){
22320 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22321 var msg = count == 0 ?
22325 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
22327 this.displayEl.update(msg);
22332 onLoad : function(ds, r, o){
22333 this.cursor = o.params ? o.params.start : 0;
22334 var d = this.getPageData(),
22338 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22339 this.field.dom.value = ap;
22340 this.first.setDisabled(ap == 1);
22341 this.prev.setDisabled(ap == 1);
22342 this.next.setDisabled(ap == ps);
22343 this.last.setDisabled(ap == ps);
22344 this.loading.enable();
22349 getPageData : function(){
22350 var total = this.ds.getTotalCount();
22353 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22354 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22359 onLoadError : function(){
22360 this.loading.enable();
22364 onPagingKeydown : function(e){
22365 var k = e.getKey();
22366 var d = this.getPageData();
22368 var v = this.field.dom.value, pageNum;
22369 if(!v || isNaN(pageNum = parseInt(v, 10))){
22370 this.field.dom.value = d.activePage;
22373 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22374 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22377 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))
22379 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22380 this.field.dom.value = pageNum;
22381 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22384 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22386 var v = this.field.dom.value, pageNum;
22387 var increment = (e.shiftKey) ? 10 : 1;
22388 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22391 if(!v || isNaN(pageNum = parseInt(v, 10))) {
22392 this.field.dom.value = d.activePage;
22395 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22397 this.field.dom.value = parseInt(v, 10) + increment;
22398 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22399 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22406 beforeLoad : function(){
22408 this.loading.disable();
22413 onClick : function(which){
22422 ds.load({params:{start: 0, limit: this.pageSize}});
22425 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22428 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22431 var total = ds.getTotalCount();
22432 var extra = total % this.pageSize;
22433 var lastStart = extra ? (total - extra) : total-this.pageSize;
22434 ds.load({params:{start: lastStart, limit: this.pageSize}});
22437 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22443 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22444 * @param {Roo.data.Store} store The data store to unbind
22446 unbind : function(ds){
22447 ds.un("beforeload", this.beforeLoad, this);
22448 ds.un("load", this.onLoad, this);
22449 ds.un("loadexception", this.onLoadError, this);
22450 ds.un("remove", this.updateInfo, this);
22451 ds.un("add", this.updateInfo, this);
22452 this.ds = undefined;
22456 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22457 * @param {Roo.data.Store} store The data store to bind
22459 bind : function(ds){
22460 ds.on("beforeload", this.beforeLoad, this);
22461 ds.on("load", this.onLoad, this);
22462 ds.on("loadexception", this.onLoadError, this);
22463 ds.on("remove", this.updateInfo, this);
22464 ds.on("add", this.updateInfo, this);
22475 * @class Roo.bootstrap.MessageBar
22476 * @extends Roo.bootstrap.Component
22477 * Bootstrap MessageBar class
22478 * @cfg {String} html contents of the MessageBar
22479 * @cfg {String} weight (info | success | warning | danger) default info
22480 * @cfg {String} beforeClass insert the bar before the given class
22481 * @cfg {Boolean} closable (true | false) default false
22482 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22485 * Create a new Element
22486 * @param {Object} config The config object
22489 Roo.bootstrap.MessageBar = function(config){
22490 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22493 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22499 beforeClass: 'bootstrap-sticky-wrap',
22501 getAutoCreate : function(){
22505 cls: 'alert alert-dismissable alert-' + this.weight,
22510 html: this.html || ''
22516 cfg.cls += ' alert-messages-fixed';
22530 onRender : function(ct, position)
22532 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22535 var cfg = Roo.apply({}, this.getAutoCreate());
22539 cfg.cls += ' ' + this.cls;
22542 cfg.style = this.style;
22544 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22546 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22549 this.el.select('>button.close').on('click', this.hide, this);
22555 if (!this.rendered) {
22561 this.fireEvent('show', this);
22567 if (!this.rendered) {
22573 this.fireEvent('hide', this);
22576 update : function()
22578 // var e = this.el.dom.firstChild;
22580 // if(this.closable){
22581 // e = e.nextSibling;
22584 // e.data = this.html || '';
22586 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22602 * @class Roo.bootstrap.Graph
22603 * @extends Roo.bootstrap.Component
22604 * Bootstrap Graph class
22608 @cfg {String} graphtype bar | vbar | pie
22609 @cfg {number} g_x coodinator | centre x (pie)
22610 @cfg {number} g_y coodinator | centre y (pie)
22611 @cfg {number} g_r radius (pie)
22612 @cfg {number} g_height height of the chart (respected by all elements in the set)
22613 @cfg {number} g_width width of the chart (respected by all elements in the set)
22614 @cfg {Object} title The title of the chart
22617 -opts (object) options for the chart
22619 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22620 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22622 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.
22623 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22625 o stretch (boolean)
22627 -opts (object) options for the pie
22630 o startAngle (number)
22631 o endAngle (number)
22635 * Create a new Input
22636 * @param {Object} config The config object
22639 Roo.bootstrap.Graph = function(config){
22640 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22646 * The img click event for the img.
22647 * @param {Roo.EventObject} e
22653 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22664 //g_colors: this.colors,
22671 getAutoCreate : function(){
22682 onRender : function(ct,position){
22683 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22684 this.raphael = Raphael(this.el.dom);
22686 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22687 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22688 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22689 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22691 r.text(160, 10, "Single Series Chart").attr(txtattr);
22692 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22693 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22694 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22696 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22697 r.barchart(330, 10, 300, 220, data1);
22698 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22699 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22702 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22703 // r.barchart(30, 30, 560, 250, xdata, {
22704 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22705 // axis : "0 0 1 1",
22706 // axisxlabels : xdata
22707 // //yvalues : cols,
22710 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22712 // this.load(null,xdata,{
22713 // axis : "0 0 1 1",
22714 // axisxlabels : xdata
22719 load : function(graphtype,xdata,opts){
22720 this.raphael.clear();
22722 graphtype = this.graphtype;
22727 var r = this.raphael,
22728 fin = function () {
22729 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22731 fout = function () {
22732 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22734 pfin = function() {
22735 this.sector.stop();
22736 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22739 this.label[0].stop();
22740 this.label[0].attr({ r: 7.5 });
22741 this.label[1].attr({ "font-weight": 800 });
22744 pfout = function() {
22745 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22748 this.label[0].animate({ r: 5 }, 500, "bounce");
22749 this.label[1].attr({ "font-weight": 400 });
22755 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22758 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22761 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
22762 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22764 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22771 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22776 setTitle: function(o)
22781 initEvents: function() {
22784 this.el.on('click', this.onClick, this);
22788 onClick : function(e)
22790 Roo.log('img onclick');
22791 this.fireEvent('click', this, e);
22803 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22806 * @class Roo.bootstrap.dash.NumberBox
22807 * @extends Roo.bootstrap.Component
22808 * Bootstrap NumberBox class
22809 * @cfg {String} headline Box headline
22810 * @cfg {String} content Box content
22811 * @cfg {String} icon Box icon
22812 * @cfg {String} footer Footer text
22813 * @cfg {String} fhref Footer href
22816 * Create a new NumberBox
22817 * @param {Object} config The config object
22821 Roo.bootstrap.dash.NumberBox = function(config){
22822 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22826 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
22835 getAutoCreate : function(){
22839 cls : 'small-box ',
22847 cls : 'roo-headline',
22848 html : this.headline
22852 cls : 'roo-content',
22853 html : this.content
22867 cls : 'ion ' + this.icon
22876 cls : 'small-box-footer',
22877 href : this.fhref || '#',
22881 cfg.cn.push(footer);
22888 onRender : function(ct,position){
22889 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22896 setHeadline: function (value)
22898 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22901 setFooter: function (value, href)
22903 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22906 this.el.select('a.small-box-footer',true).first().attr('href', href);
22911 setContent: function (value)
22913 this.el.select('.roo-content',true).first().dom.innerHTML = value;
22916 initEvents: function()
22930 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22933 * @class Roo.bootstrap.dash.TabBox
22934 * @extends Roo.bootstrap.Component
22935 * Bootstrap TabBox class
22936 * @cfg {String} title Title of the TabBox
22937 * @cfg {String} icon Icon of the TabBox
22938 * @cfg {Boolean} showtabs (true|false) show the tabs default true
22939 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22942 * Create a new TabBox
22943 * @param {Object} config The config object
22947 Roo.bootstrap.dash.TabBox = function(config){
22948 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22953 * When a pane is added
22954 * @param {Roo.bootstrap.dash.TabPane} pane
22958 * @event activatepane
22959 * When a pane is activated
22960 * @param {Roo.bootstrap.dash.TabPane} pane
22962 "activatepane" : true
22970 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
22975 tabScrollable : false,
22977 getChildContainer : function()
22979 return this.el.select('.tab-content', true).first();
22982 getAutoCreate : function(){
22986 cls: 'pull-left header',
22994 cls: 'fa ' + this.icon
23000 cls: 'nav nav-tabs pull-right',
23006 if(this.tabScrollable){
23013 cls: 'nav nav-tabs pull-right',
23024 cls: 'nav-tabs-custom',
23029 cls: 'tab-content no-padding',
23037 initEvents : function()
23039 //Roo.log('add add pane handler');
23040 this.on('addpane', this.onAddPane, this);
23043 * Updates the box title
23044 * @param {String} html to set the title to.
23046 setTitle : function(value)
23048 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23050 onAddPane : function(pane)
23052 this.panes.push(pane);
23053 //Roo.log('addpane');
23055 // tabs are rendere left to right..
23056 if(!this.showtabs){
23060 var ctr = this.el.select('.nav-tabs', true).first();
23063 var existing = ctr.select('.nav-tab',true);
23064 var qty = existing.getCount();;
23067 var tab = ctr.createChild({
23069 cls : 'nav-tab' + (qty ? '' : ' active'),
23077 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23080 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23082 pane.el.addClass('active');
23087 onTabClick : function(ev,un,ob,pane)
23089 //Roo.log('tab - prev default');
23090 ev.preventDefault();
23093 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23094 pane.tab.addClass('active');
23095 //Roo.log(pane.title);
23096 this.getChildContainer().select('.tab-pane',true).removeClass('active');
23097 // technically we should have a deactivate event.. but maybe add later.
23098 // and it should not de-activate the selected tab...
23099 this.fireEvent('activatepane', pane);
23100 pane.el.addClass('active');
23101 pane.fireEvent('activate');
23106 getActivePane : function()
23109 Roo.each(this.panes, function(p) {
23110 if(p.el.hasClass('active')){
23131 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23133 * @class Roo.bootstrap.TabPane
23134 * @extends Roo.bootstrap.Component
23135 * Bootstrap TabPane class
23136 * @cfg {Boolean} active (false | true) Default false
23137 * @cfg {String} title title of panel
23141 * Create a new TabPane
23142 * @param {Object} config The config object
23145 Roo.bootstrap.dash.TabPane = function(config){
23146 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23152 * When a pane is activated
23153 * @param {Roo.bootstrap.dash.TabPane} pane
23160 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
23165 // the tabBox that this is attached to.
23168 getAutoCreate : function()
23176 cfg.cls += ' active';
23181 initEvents : function()
23183 //Roo.log('trigger add pane handler');
23184 this.parent().fireEvent('addpane', this)
23188 * Updates the tab title
23189 * @param {String} html to set the title to.
23191 setTitle: function(str)
23197 this.tab.select('a', true).first().dom.innerHTML = str;
23214 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23217 * @class Roo.bootstrap.menu.Menu
23218 * @extends Roo.bootstrap.Component
23219 * Bootstrap Menu class - container for Menu
23220 * @cfg {String} html Text of the menu
23221 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23222 * @cfg {String} icon Font awesome icon
23223 * @cfg {String} pos Menu align to (top | bottom) default bottom
23227 * Create a new Menu
23228 * @param {Object} config The config object
23232 Roo.bootstrap.menu.Menu = function(config){
23233 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23237 * @event beforeshow
23238 * Fires before this menu is displayed
23239 * @param {Roo.bootstrap.menu.Menu} this
23243 * @event beforehide
23244 * Fires before this menu is hidden
23245 * @param {Roo.bootstrap.menu.Menu} this
23250 * Fires after this menu is displayed
23251 * @param {Roo.bootstrap.menu.Menu} this
23256 * Fires after this menu is hidden
23257 * @param {Roo.bootstrap.menu.Menu} this
23262 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23263 * @param {Roo.bootstrap.menu.Menu} this
23264 * @param {Roo.EventObject} e
23271 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
23275 weight : 'default',
23280 getChildContainer : function() {
23281 if(this.isSubMenu){
23285 return this.el.select('ul.dropdown-menu', true).first();
23288 getAutoCreate : function()
23293 cls : 'roo-menu-text',
23301 cls : 'fa ' + this.icon
23312 cls : 'dropdown-button btn btn-' + this.weight,
23317 cls : 'dropdown-toggle btn btn-' + this.weight,
23327 cls : 'dropdown-menu'
23333 if(this.pos == 'top'){
23334 cfg.cls += ' dropup';
23337 if(this.isSubMenu){
23340 cls : 'dropdown-menu'
23347 onRender : function(ct, position)
23349 this.isSubMenu = ct.hasClass('dropdown-submenu');
23351 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23354 initEvents : function()
23356 if(this.isSubMenu){
23360 this.hidden = true;
23362 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23363 this.triggerEl.on('click', this.onTriggerPress, this);
23365 this.buttonEl = this.el.select('button.dropdown-button', true).first();
23366 this.buttonEl.on('click', this.onClick, this);
23372 if(this.isSubMenu){
23376 return this.el.select('ul.dropdown-menu', true).first();
23379 onClick : function(e)
23381 this.fireEvent("click", this, e);
23384 onTriggerPress : function(e)
23386 if (this.isVisible()) {
23393 isVisible : function(){
23394 return !this.hidden;
23399 this.fireEvent("beforeshow", this);
23401 this.hidden = false;
23402 this.el.addClass('open');
23404 Roo.get(document).on("mouseup", this.onMouseUp, this);
23406 this.fireEvent("show", this);
23413 this.fireEvent("beforehide", this);
23415 this.hidden = true;
23416 this.el.removeClass('open');
23418 Roo.get(document).un("mouseup", this.onMouseUp);
23420 this.fireEvent("hide", this);
23423 onMouseUp : function()
23437 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23440 * @class Roo.bootstrap.menu.Item
23441 * @extends Roo.bootstrap.Component
23442 * Bootstrap MenuItem class
23443 * @cfg {Boolean} submenu (true | false) default false
23444 * @cfg {String} html text of the item
23445 * @cfg {String} href the link
23446 * @cfg {Boolean} disable (true | false) default false
23447 * @cfg {Boolean} preventDefault (true | false) default true
23448 * @cfg {String} icon Font awesome icon
23449 * @cfg {String} pos Submenu align to (left | right) default right
23453 * Create a new Item
23454 * @param {Object} config The config object
23458 Roo.bootstrap.menu.Item = function(config){
23459 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23463 * Fires when the mouse is hovering over this menu
23464 * @param {Roo.bootstrap.menu.Item} this
23465 * @param {Roo.EventObject} e
23470 * Fires when the mouse exits this menu
23471 * @param {Roo.bootstrap.menu.Item} this
23472 * @param {Roo.EventObject} e
23478 * The raw click event for the entire grid.
23479 * @param {Roo.EventObject} e
23485 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23490 preventDefault: true,
23495 getAutoCreate : function()
23500 cls : 'roo-menu-item-text',
23508 cls : 'fa ' + this.icon
23517 href : this.href || '#',
23524 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23528 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23530 if(this.pos == 'left'){
23531 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23538 initEvents : function()
23540 this.el.on('mouseover', this.onMouseOver, this);
23541 this.el.on('mouseout', this.onMouseOut, this);
23543 this.el.select('a', true).first().on('click', this.onClick, this);
23547 onClick : function(e)
23549 if(this.preventDefault){
23550 e.preventDefault();
23553 this.fireEvent("click", this, e);
23556 onMouseOver : function(e)
23558 if(this.submenu && this.pos == 'left'){
23559 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23562 this.fireEvent("mouseover", this, e);
23565 onMouseOut : function(e)
23567 this.fireEvent("mouseout", this, e);
23579 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23582 * @class Roo.bootstrap.menu.Separator
23583 * @extends Roo.bootstrap.Component
23584 * Bootstrap Separator class
23587 * Create a new Separator
23588 * @param {Object} config The config object
23592 Roo.bootstrap.menu.Separator = function(config){
23593 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23596 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23598 getAutoCreate : function(){
23619 * @class Roo.bootstrap.Tooltip
23620 * Bootstrap Tooltip class
23621 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23622 * to determine which dom element triggers the tooltip.
23624 * It needs to add support for additional attributes like tooltip-position
23627 * Create a new Toolti
23628 * @param {Object} config The config object
23631 Roo.bootstrap.Tooltip = function(config){
23632 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23635 Roo.apply(Roo.bootstrap.Tooltip, {
23637 * @function init initialize tooltip monitoring.
23641 currentTip : false,
23642 currentRegion : false,
23648 Roo.get(document).on('mouseover', this.enter ,this);
23649 Roo.get(document).on('mouseout', this.leave, this);
23652 this.currentTip = new Roo.bootstrap.Tooltip();
23655 enter : function(ev)
23657 var dom = ev.getTarget();
23659 //Roo.log(['enter',dom]);
23660 var el = Roo.fly(dom);
23661 if (this.currentEl) {
23663 //Roo.log(this.currentEl);
23664 //Roo.log(this.currentEl.contains(dom));
23665 if (this.currentEl == el) {
23668 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23674 if (this.currentTip.el) {
23675 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23680 // you can not look for children, as if el is the body.. then everythign is the child..
23681 if (!el.attr('tooltip')) { //
23682 if (!el.select("[tooltip]").elements.length) {
23685 // is the mouse over this child...?
23686 bindEl = el.select("[tooltip]").first();
23687 var xy = ev.getXY();
23688 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23689 //Roo.log("not in region.");
23692 //Roo.log("child element over..");
23695 this.currentEl = bindEl;
23696 this.currentTip.bind(bindEl);
23697 this.currentRegion = Roo.lib.Region.getRegion(dom);
23698 this.currentTip.enter();
23701 leave : function(ev)
23703 var dom = ev.getTarget();
23704 //Roo.log(['leave',dom]);
23705 if (!this.currentEl) {
23710 if (dom != this.currentEl.dom) {
23713 var xy = ev.getXY();
23714 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23717 // only activate leave if mouse cursor is outside... bounding box..
23722 if (this.currentTip) {
23723 this.currentTip.leave();
23725 //Roo.log('clear currentEl');
23726 this.currentEl = false;
23731 'left' : ['r-l', [-2,0], 'right'],
23732 'right' : ['l-r', [2,0], 'left'],
23733 'bottom' : ['t-b', [0,2], 'top'],
23734 'top' : [ 'b-t', [0,-2], 'bottom']
23740 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
23745 delay : null, // can be { show : 300 , hide: 500}
23749 hoverState : null, //???
23751 placement : 'bottom',
23753 getAutoCreate : function(){
23760 cls : 'tooltip-arrow'
23763 cls : 'tooltip-inner'
23770 bind : function(el)
23776 enter : function () {
23778 if (this.timeout != null) {
23779 clearTimeout(this.timeout);
23782 this.hoverState = 'in';
23783 //Roo.log("enter - show");
23784 if (!this.delay || !this.delay.show) {
23789 this.timeout = setTimeout(function () {
23790 if (_t.hoverState == 'in') {
23793 }, this.delay.show);
23797 clearTimeout(this.timeout);
23799 this.hoverState = 'out';
23800 if (!this.delay || !this.delay.hide) {
23806 this.timeout = setTimeout(function () {
23807 //Roo.log("leave - timeout");
23809 if (_t.hoverState == 'out') {
23811 Roo.bootstrap.Tooltip.currentEl = false;
23819 this.render(document.body);
23822 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23824 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23826 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23828 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23830 var placement = typeof this.placement == 'function' ?
23831 this.placement.call(this, this.el, on_el) :
23834 var autoToken = /\s?auto?\s?/i;
23835 var autoPlace = autoToken.test(placement);
23837 placement = placement.replace(autoToken, '') || 'top';
23841 //this.el.setXY([0,0]);
23843 //this.el.dom.style.display='block';
23845 //this.el.appendTo(on_el);
23847 var p = this.getPosition();
23848 var box = this.el.getBox();
23854 var align = Roo.bootstrap.Tooltip.alignment[placement];
23856 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23858 if(placement == 'top' || placement == 'bottom'){
23860 placement = 'right';
23863 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23864 placement = 'left';
23868 align = Roo.bootstrap.Tooltip.alignment[placement];
23870 this.el.alignTo(this.bindEl, align[0],align[1]);
23871 //var arrow = this.el.select('.arrow',true).first();
23872 //arrow.set(align[2],
23874 this.el.addClass(placement);
23876 this.el.addClass('in fade');
23878 this.hoverState = null;
23880 if (this.el.hasClass('fade')) {
23891 //this.el.setXY([0,0]);
23892 this.el.removeClass('in');
23908 * @class Roo.bootstrap.LocationPicker
23909 * @extends Roo.bootstrap.Component
23910 * Bootstrap LocationPicker class
23911 * @cfg {Number} latitude Position when init default 0
23912 * @cfg {Number} longitude Position when init default 0
23913 * @cfg {Number} zoom default 15
23914 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23915 * @cfg {Boolean} mapTypeControl default false
23916 * @cfg {Boolean} disableDoubleClickZoom default false
23917 * @cfg {Boolean} scrollwheel default true
23918 * @cfg {Boolean} streetViewControl default false
23919 * @cfg {Number} radius default 0
23920 * @cfg {String} locationName
23921 * @cfg {Boolean} draggable default true
23922 * @cfg {Boolean} enableAutocomplete default false
23923 * @cfg {Boolean} enableReverseGeocode default true
23924 * @cfg {String} markerTitle
23927 * Create a new LocationPicker
23928 * @param {Object} config The config object
23932 Roo.bootstrap.LocationPicker = function(config){
23934 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23939 * Fires when the picker initialized.
23940 * @param {Roo.bootstrap.LocationPicker} this
23941 * @param {Google Location} location
23945 * @event positionchanged
23946 * Fires when the picker position changed.
23947 * @param {Roo.bootstrap.LocationPicker} this
23948 * @param {Google Location} location
23950 positionchanged : true,
23953 * Fires when the map resize.
23954 * @param {Roo.bootstrap.LocationPicker} this
23959 * Fires when the map show.
23960 * @param {Roo.bootstrap.LocationPicker} this
23965 * Fires when the map hide.
23966 * @param {Roo.bootstrap.LocationPicker} this
23971 * Fires when click the map.
23972 * @param {Roo.bootstrap.LocationPicker} this
23973 * @param {Map event} e
23977 * @event mapRightClick
23978 * Fires when right click the map.
23979 * @param {Roo.bootstrap.LocationPicker} this
23980 * @param {Map event} e
23982 mapRightClick : true,
23984 * @event markerClick
23985 * Fires when click the marker.
23986 * @param {Roo.bootstrap.LocationPicker} this
23987 * @param {Map event} e
23989 markerClick : true,
23991 * @event markerRightClick
23992 * Fires when right click the marker.
23993 * @param {Roo.bootstrap.LocationPicker} this
23994 * @param {Map event} e
23996 markerRightClick : true,
23998 * @event OverlayViewDraw
23999 * Fires when OverlayView Draw
24000 * @param {Roo.bootstrap.LocationPicker} this
24002 OverlayViewDraw : true,
24004 * @event OverlayViewOnAdd
24005 * Fires when OverlayView Draw
24006 * @param {Roo.bootstrap.LocationPicker} this
24008 OverlayViewOnAdd : true,
24010 * @event OverlayViewOnRemove
24011 * Fires when OverlayView Draw
24012 * @param {Roo.bootstrap.LocationPicker} this
24014 OverlayViewOnRemove : true,
24016 * @event OverlayViewShow
24017 * Fires when OverlayView Draw
24018 * @param {Roo.bootstrap.LocationPicker} this
24019 * @param {Pixel} cpx
24021 OverlayViewShow : true,
24023 * @event OverlayViewHide
24024 * Fires when OverlayView Draw
24025 * @param {Roo.bootstrap.LocationPicker} this
24027 OverlayViewHide : true,
24029 * @event loadexception
24030 * Fires when load google lib failed.
24031 * @param {Roo.bootstrap.LocationPicker} this
24033 loadexception : true
24038 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
24040 gMapContext: false,
24046 mapTypeControl: false,
24047 disableDoubleClickZoom: false,
24049 streetViewControl: false,
24053 enableAutocomplete: false,
24054 enableReverseGeocode: true,
24057 getAutoCreate: function()
24062 cls: 'roo-location-picker'
24068 initEvents: function(ct, position)
24070 if(!this.el.getWidth() || this.isApplied()){
24074 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24079 initial: function()
24081 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24082 this.fireEvent('loadexception', this);
24086 if(!this.mapTypeId){
24087 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24090 this.gMapContext = this.GMapContext();
24092 this.initOverlayView();
24094 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24098 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24099 _this.setPosition(_this.gMapContext.marker.position);
24102 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24103 _this.fireEvent('mapClick', this, event);
24107 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24108 _this.fireEvent('mapRightClick', this, event);
24112 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24113 _this.fireEvent('markerClick', this, event);
24117 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24118 _this.fireEvent('markerRightClick', this, event);
24122 this.setPosition(this.gMapContext.location);
24124 this.fireEvent('initial', this, this.gMapContext.location);
24127 initOverlayView: function()
24131 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24135 _this.fireEvent('OverlayViewDraw', _this);
24140 _this.fireEvent('OverlayViewOnAdd', _this);
24143 onRemove: function()
24145 _this.fireEvent('OverlayViewOnRemove', _this);
24148 show: function(cpx)
24150 _this.fireEvent('OverlayViewShow', _this, cpx);
24155 _this.fireEvent('OverlayViewHide', _this);
24161 fromLatLngToContainerPixel: function(event)
24163 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24166 isApplied: function()
24168 return this.getGmapContext() == false ? false : true;
24171 getGmapContext: function()
24173 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24176 GMapContext: function()
24178 var position = new google.maps.LatLng(this.latitude, this.longitude);
24180 var _map = new google.maps.Map(this.el.dom, {
24183 mapTypeId: this.mapTypeId,
24184 mapTypeControl: this.mapTypeControl,
24185 disableDoubleClickZoom: this.disableDoubleClickZoom,
24186 scrollwheel: this.scrollwheel,
24187 streetViewControl: this.streetViewControl,
24188 locationName: this.locationName,
24189 draggable: this.draggable,
24190 enableAutocomplete: this.enableAutocomplete,
24191 enableReverseGeocode: this.enableReverseGeocode
24194 var _marker = new google.maps.Marker({
24195 position: position,
24197 title: this.markerTitle,
24198 draggable: this.draggable
24205 location: position,
24206 radius: this.radius,
24207 locationName: this.locationName,
24208 addressComponents: {
24209 formatted_address: null,
24210 addressLine1: null,
24211 addressLine2: null,
24213 streetNumber: null,
24217 stateOrProvince: null
24220 domContainer: this.el.dom,
24221 geodecoder: new google.maps.Geocoder()
24225 drawCircle: function(center, radius, options)
24227 if (this.gMapContext.circle != null) {
24228 this.gMapContext.circle.setMap(null);
24232 options = Roo.apply({}, options, {
24233 strokeColor: "#0000FF",
24234 strokeOpacity: .35,
24236 fillColor: "#0000FF",
24240 options.map = this.gMapContext.map;
24241 options.radius = radius;
24242 options.center = center;
24243 this.gMapContext.circle = new google.maps.Circle(options);
24244 return this.gMapContext.circle;
24250 setPosition: function(location)
24252 this.gMapContext.location = location;
24253 this.gMapContext.marker.setPosition(location);
24254 this.gMapContext.map.panTo(location);
24255 this.drawCircle(location, this.gMapContext.radius, {});
24259 if (this.gMapContext.settings.enableReverseGeocode) {
24260 this.gMapContext.geodecoder.geocode({
24261 latLng: this.gMapContext.location
24262 }, function(results, status) {
24264 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24265 _this.gMapContext.locationName = results[0].formatted_address;
24266 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24268 _this.fireEvent('positionchanged', this, location);
24275 this.fireEvent('positionchanged', this, location);
24280 google.maps.event.trigger(this.gMapContext.map, "resize");
24282 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24284 this.fireEvent('resize', this);
24287 setPositionByLatLng: function(latitude, longitude)
24289 this.setPosition(new google.maps.LatLng(latitude, longitude));
24292 getCurrentPosition: function()
24295 latitude: this.gMapContext.location.lat(),
24296 longitude: this.gMapContext.location.lng()
24300 getAddressName: function()
24302 return this.gMapContext.locationName;
24305 getAddressComponents: function()
24307 return this.gMapContext.addressComponents;
24310 address_component_from_google_geocode: function(address_components)
24314 for (var i = 0; i < address_components.length; i++) {
24315 var component = address_components[i];
24316 if (component.types.indexOf("postal_code") >= 0) {
24317 result.postalCode = component.short_name;
24318 } else if (component.types.indexOf("street_number") >= 0) {
24319 result.streetNumber = component.short_name;
24320 } else if (component.types.indexOf("route") >= 0) {
24321 result.streetName = component.short_name;
24322 } else if (component.types.indexOf("neighborhood") >= 0) {
24323 result.city = component.short_name;
24324 } else if (component.types.indexOf("locality") >= 0) {
24325 result.city = component.short_name;
24326 } else if (component.types.indexOf("sublocality") >= 0) {
24327 result.district = component.short_name;
24328 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24329 result.stateOrProvince = component.short_name;
24330 } else if (component.types.indexOf("country") >= 0) {
24331 result.country = component.short_name;
24335 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24336 result.addressLine2 = "";
24340 setZoomLevel: function(zoom)
24342 this.gMapContext.map.setZoom(zoom);
24355 this.fireEvent('show', this);
24366 this.fireEvent('hide', this);
24371 Roo.apply(Roo.bootstrap.LocationPicker, {
24373 OverlayView : function(map, options)
24375 options = options || {};
24389 * @class Roo.bootstrap.Alert
24390 * @extends Roo.bootstrap.Component
24391 * Bootstrap Alert class
24392 * @cfg {String} title The title of alert
24393 * @cfg {String} html The content of alert
24394 * @cfg {String} weight ( success | info | warning | danger )
24395 * @cfg {String} faicon font-awesomeicon
24398 * Create a new alert
24399 * @param {Object} config The config object
24403 Roo.bootstrap.Alert = function(config){
24404 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24408 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
24415 getAutoCreate : function()
24424 cls : 'roo-alert-icon'
24429 cls : 'roo-alert-title',
24434 cls : 'roo-alert-text',
24441 cfg.cn[0].cls += ' fa ' + this.faicon;
24445 cfg.cls += ' alert-' + this.weight;
24451 initEvents: function()
24453 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24456 setTitle : function(str)
24458 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24461 setText : function(str)
24463 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24466 setWeight : function(weight)
24469 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24472 this.weight = weight;
24474 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24477 setIcon : function(icon)
24480 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24483 this.faicon = icon;
24485 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24506 * @class Roo.bootstrap.UploadCropbox
24507 * @extends Roo.bootstrap.Component
24508 * Bootstrap UploadCropbox class
24509 * @cfg {String} emptyText show when image has been loaded
24510 * @cfg {String} rotateNotify show when image too small to rotate
24511 * @cfg {Number} errorTimeout default 3000
24512 * @cfg {Number} minWidth default 300
24513 * @cfg {Number} minHeight default 300
24514 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24515 * @cfg {Boolean} isDocument (true|false) default false
24516 * @cfg {String} url action url
24517 * @cfg {String} paramName default 'imageUpload'
24518 * @cfg {String} method default POST
24519 * @cfg {Boolean} loadMask (true|false) default true
24520 * @cfg {Boolean} loadingText default 'Loading...'
24523 * Create a new UploadCropbox
24524 * @param {Object} config The config object
24527 Roo.bootstrap.UploadCropbox = function(config){
24528 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24532 * @event beforeselectfile
24533 * Fire before select file
24534 * @param {Roo.bootstrap.UploadCropbox} this
24536 "beforeselectfile" : true,
24539 * Fire after initEvent
24540 * @param {Roo.bootstrap.UploadCropbox} this
24545 * Fire after initEvent
24546 * @param {Roo.bootstrap.UploadCropbox} this
24547 * @param {String} data
24552 * Fire when preparing the file data
24553 * @param {Roo.bootstrap.UploadCropbox} this
24554 * @param {Object} file
24559 * Fire when get exception
24560 * @param {Roo.bootstrap.UploadCropbox} this
24561 * @param {XMLHttpRequest} xhr
24563 "exception" : true,
24565 * @event beforeloadcanvas
24566 * Fire before load the canvas
24567 * @param {Roo.bootstrap.UploadCropbox} this
24568 * @param {String} src
24570 "beforeloadcanvas" : true,
24573 * Fire when trash image
24574 * @param {Roo.bootstrap.UploadCropbox} this
24579 * Fire when download the image
24580 * @param {Roo.bootstrap.UploadCropbox} this
24584 * @event footerbuttonclick
24585 * Fire when footerbuttonclick
24586 * @param {Roo.bootstrap.UploadCropbox} this
24587 * @param {String} type
24589 "footerbuttonclick" : true,
24593 * @param {Roo.bootstrap.UploadCropbox} this
24598 * Fire when rotate the image
24599 * @param {Roo.bootstrap.UploadCropbox} this
24600 * @param {String} pos
24605 * Fire when inspect the file
24606 * @param {Roo.bootstrap.UploadCropbox} this
24607 * @param {Object} file
24612 * Fire when xhr upload the file
24613 * @param {Roo.bootstrap.UploadCropbox} this
24614 * @param {Object} data
24619 * Fire when arrange the file data
24620 * @param {Roo.bootstrap.UploadCropbox} this
24621 * @param {Object} formData
24626 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24629 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24631 emptyText : 'Click to upload image',
24632 rotateNotify : 'Image is too small to rotate',
24633 errorTimeout : 3000,
24647 cropType : 'image/jpeg',
24649 canvasLoaded : false,
24650 isDocument : false,
24652 paramName : 'imageUpload',
24654 loadingText : 'Loading...',
24657 getAutoCreate : function()
24661 cls : 'roo-upload-cropbox',
24665 cls : 'roo-upload-cropbox-selector',
24670 cls : 'roo-upload-cropbox-body',
24671 style : 'cursor:pointer',
24675 cls : 'roo-upload-cropbox-preview'
24679 cls : 'roo-upload-cropbox-thumb'
24683 cls : 'roo-upload-cropbox-empty-notify',
24684 html : this.emptyText
24688 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24689 html : this.rotateNotify
24695 cls : 'roo-upload-cropbox-footer',
24698 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24708 onRender : function(ct, position)
24710 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24712 if (this.buttons.length) {
24714 Roo.each(this.buttons, function(bb) {
24716 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24718 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24724 this.maskEl = this.el;
24728 initEvents : function()
24730 this.urlAPI = (window.createObjectURL && window) ||
24731 (window.URL && URL.revokeObjectURL && URL) ||
24732 (window.webkitURL && webkitURL);
24734 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24735 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24737 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24738 this.selectorEl.hide();
24740 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24741 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24743 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24744 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24745 this.thumbEl.hide();
24747 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24748 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24750 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24751 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24752 this.errorEl.hide();
24754 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24755 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24756 this.footerEl.hide();
24758 this.setThumbBoxSize();
24764 this.fireEvent('initial', this);
24771 window.addEventListener("resize", function() { _this.resize(); } );
24773 this.bodyEl.on('click', this.beforeSelectFile, this);
24776 this.bodyEl.on('touchstart', this.onTouchStart, this);
24777 this.bodyEl.on('touchmove', this.onTouchMove, this);
24778 this.bodyEl.on('touchend', this.onTouchEnd, this);
24782 this.bodyEl.on('mousedown', this.onMouseDown, this);
24783 this.bodyEl.on('mousemove', this.onMouseMove, this);
24784 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24785 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24786 Roo.get(document).on('mouseup', this.onMouseUp, this);
24789 this.selectorEl.on('change', this.onFileSelected, this);
24795 this.baseScale = 1;
24797 this.baseRotate = 1;
24798 this.dragable = false;
24799 this.pinching = false;
24802 this.cropData = false;
24803 this.notifyEl.dom.innerHTML = this.emptyText;
24805 this.selectorEl.dom.value = '';
24809 resize : function()
24811 if(this.fireEvent('resize', this) != false){
24812 this.setThumbBoxPosition();
24813 this.setCanvasPosition();
24817 onFooterButtonClick : function(e, el, o, type)
24820 case 'rotate-left' :
24821 this.onRotateLeft(e);
24823 case 'rotate-right' :
24824 this.onRotateRight(e);
24827 this.beforeSelectFile(e);
24842 this.fireEvent('footerbuttonclick', this, type);
24845 beforeSelectFile : function(e)
24847 e.preventDefault();
24849 if(this.fireEvent('beforeselectfile', this) != false){
24850 this.selectorEl.dom.click();
24854 onFileSelected : function(e)
24856 e.preventDefault();
24858 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24862 var file = this.selectorEl.dom.files[0];
24864 if(this.fireEvent('inspect', this, file) != false){
24865 this.prepare(file);
24870 trash : function(e)
24872 this.fireEvent('trash', this);
24875 download : function(e)
24877 this.fireEvent('download', this);
24880 loadCanvas : function(src)
24882 if(this.fireEvent('beforeloadcanvas', this, src) != false){
24886 this.imageEl = document.createElement('img');
24890 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24892 this.imageEl.src = src;
24896 onLoadCanvas : function()
24898 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24899 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24901 this.bodyEl.un('click', this.beforeSelectFile, this);
24903 this.notifyEl.hide();
24904 this.thumbEl.show();
24905 this.footerEl.show();
24907 this.baseRotateLevel();
24909 if(this.isDocument){
24910 this.setThumbBoxSize();
24913 this.setThumbBoxPosition();
24915 this.baseScaleLevel();
24921 this.canvasLoaded = true;
24924 this.maskEl.unmask();
24929 setCanvasPosition : function()
24931 if(!this.canvasEl){
24935 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24936 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24938 this.previewEl.setLeft(pw);
24939 this.previewEl.setTop(ph);
24943 onMouseDown : function(e)
24947 this.dragable = true;
24948 this.pinching = false;
24950 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24951 this.dragable = false;
24955 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24956 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24960 onMouseMove : function(e)
24964 if(!this.canvasLoaded){
24968 if (!this.dragable){
24972 var minX = Math.ceil(this.thumbEl.getLeft(true));
24973 var minY = Math.ceil(this.thumbEl.getTop(true));
24975 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24976 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24978 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24979 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24981 x = x - this.mouseX;
24982 y = y - this.mouseY;
24984 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24985 var bgY = Math.ceil(y + this.previewEl.getTop(true));
24987 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24988 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24990 this.previewEl.setLeft(bgX);
24991 this.previewEl.setTop(bgY);
24993 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24994 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24997 onMouseUp : function(e)
25001 this.dragable = false;
25004 onMouseWheel : function(e)
25008 this.startScale = this.scale;
25010 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25012 if(!this.zoomable()){
25013 this.scale = this.startScale;
25022 zoomable : function()
25024 var minScale = this.thumbEl.getWidth() / this.minWidth;
25026 if(this.minWidth < this.minHeight){
25027 minScale = this.thumbEl.getHeight() / this.minHeight;
25030 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25031 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25035 (this.rotate == 0 || this.rotate == 180) &&
25037 width > this.imageEl.OriginWidth ||
25038 height > this.imageEl.OriginHeight ||
25039 (width < this.minWidth && height < this.minHeight)
25047 (this.rotate == 90 || this.rotate == 270) &&
25049 width > this.imageEl.OriginWidth ||
25050 height > this.imageEl.OriginHeight ||
25051 (width < this.minHeight && height < this.minWidth)
25058 !this.isDocument &&
25059 (this.rotate == 0 || this.rotate == 180) &&
25061 width < this.minWidth ||
25062 width > this.imageEl.OriginWidth ||
25063 height < this.minHeight ||
25064 height > this.imageEl.OriginHeight
25071 !this.isDocument &&
25072 (this.rotate == 90 || this.rotate == 270) &&
25074 width < this.minHeight ||
25075 width > this.imageEl.OriginWidth ||
25076 height < this.minWidth ||
25077 height > this.imageEl.OriginHeight
25087 onRotateLeft : function(e)
25089 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25091 var minScale = this.thumbEl.getWidth() / this.minWidth;
25093 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25094 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25096 this.startScale = this.scale;
25098 while (this.getScaleLevel() < minScale){
25100 this.scale = this.scale + 1;
25102 if(!this.zoomable()){
25107 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25108 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25113 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25120 this.scale = this.startScale;
25122 this.onRotateFail();
25127 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25129 if(this.isDocument){
25130 this.setThumbBoxSize();
25131 this.setThumbBoxPosition();
25132 this.setCanvasPosition();
25137 this.fireEvent('rotate', this, 'left');
25141 onRotateRight : function(e)
25143 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25145 var minScale = this.thumbEl.getWidth() / this.minWidth;
25147 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25148 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25150 this.startScale = this.scale;
25152 while (this.getScaleLevel() < minScale){
25154 this.scale = this.scale + 1;
25156 if(!this.zoomable()){
25161 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25162 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25167 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25174 this.scale = this.startScale;
25176 this.onRotateFail();
25181 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25183 if(this.isDocument){
25184 this.setThumbBoxSize();
25185 this.setThumbBoxPosition();
25186 this.setCanvasPosition();
25191 this.fireEvent('rotate', this, 'right');
25194 onRotateFail : function()
25196 this.errorEl.show(true);
25200 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25205 this.previewEl.dom.innerHTML = '';
25207 var canvasEl = document.createElement("canvas");
25209 var contextEl = canvasEl.getContext("2d");
25211 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25212 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25213 var center = this.imageEl.OriginWidth / 2;
25215 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25216 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25217 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25218 center = this.imageEl.OriginHeight / 2;
25221 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25223 contextEl.translate(center, center);
25224 contextEl.rotate(this.rotate * Math.PI / 180);
25226 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25228 this.canvasEl = document.createElement("canvas");
25230 this.contextEl = this.canvasEl.getContext("2d");
25232 switch (this.rotate) {
25235 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25236 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25238 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25243 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25244 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25246 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25247 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);
25251 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25256 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25257 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25259 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25260 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);
25264 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);
25269 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25270 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25272 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25273 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25277 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);
25284 this.previewEl.appendChild(this.canvasEl);
25286 this.setCanvasPosition();
25291 if(!this.canvasLoaded){
25295 var imageCanvas = document.createElement("canvas");
25297 var imageContext = imageCanvas.getContext("2d");
25299 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25300 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25302 var center = imageCanvas.width / 2;
25304 imageContext.translate(center, center);
25306 imageContext.rotate(this.rotate * Math.PI / 180);
25308 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25310 var canvas = document.createElement("canvas");
25312 var context = canvas.getContext("2d");
25314 canvas.width = this.minWidth;
25315 canvas.height = this.minHeight;
25317 switch (this.rotate) {
25320 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25321 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25323 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25324 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25326 var targetWidth = this.minWidth - 2 * x;
25327 var targetHeight = this.minHeight - 2 * y;
25331 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25332 scale = targetWidth / width;
25335 if(x > 0 && y == 0){
25336 scale = targetHeight / height;
25339 if(x > 0 && y > 0){
25340 scale = targetWidth / width;
25342 if(width < height){
25343 scale = targetHeight / height;
25347 context.scale(scale, scale);
25349 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25350 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25352 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25353 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25355 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25360 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25361 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25363 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25364 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25366 var targetWidth = this.minWidth - 2 * x;
25367 var targetHeight = this.minHeight - 2 * y;
25371 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25372 scale = targetWidth / width;
25375 if(x > 0 && y == 0){
25376 scale = targetHeight / height;
25379 if(x > 0 && y > 0){
25380 scale = targetWidth / width;
25382 if(width < height){
25383 scale = targetHeight / height;
25387 context.scale(scale, scale);
25389 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25390 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25392 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25393 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25395 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25397 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25402 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25403 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25405 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25406 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25408 var targetWidth = this.minWidth - 2 * x;
25409 var targetHeight = this.minHeight - 2 * y;
25413 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25414 scale = targetWidth / width;
25417 if(x > 0 && y == 0){
25418 scale = targetHeight / height;
25421 if(x > 0 && y > 0){
25422 scale = targetWidth / width;
25424 if(width < height){
25425 scale = targetHeight / height;
25429 context.scale(scale, scale);
25431 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25432 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25434 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25435 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25437 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25438 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25440 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25445 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25446 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25448 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25449 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25451 var targetWidth = this.minWidth - 2 * x;
25452 var targetHeight = this.minHeight - 2 * y;
25456 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25457 scale = targetWidth / width;
25460 if(x > 0 && y == 0){
25461 scale = targetHeight / height;
25464 if(x > 0 && y > 0){
25465 scale = targetWidth / width;
25467 if(width < height){
25468 scale = targetHeight / height;
25472 context.scale(scale, scale);
25474 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25475 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25477 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25478 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25480 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25482 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25489 this.cropData = canvas.toDataURL(this.cropType);
25491 if(this.fireEvent('crop', this, this.cropData) !== false){
25492 this.process(this.file, this.cropData);
25499 setThumbBoxSize : function()
25503 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25504 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25505 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25507 this.minWidth = width;
25508 this.minHeight = height;
25510 if(this.rotate == 90 || this.rotate == 270){
25511 this.minWidth = height;
25512 this.minHeight = width;
25517 width = Math.ceil(this.minWidth * height / this.minHeight);
25519 if(this.minWidth > this.minHeight){
25521 height = Math.ceil(this.minHeight * width / this.minWidth);
25524 this.thumbEl.setStyle({
25525 width : width + 'px',
25526 height : height + 'px'
25533 setThumbBoxPosition : function()
25535 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25536 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25538 this.thumbEl.setLeft(x);
25539 this.thumbEl.setTop(y);
25543 baseRotateLevel : function()
25545 this.baseRotate = 1;
25548 typeof(this.exif) != 'undefined' &&
25549 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25550 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25552 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25555 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25559 baseScaleLevel : function()
25563 if(this.isDocument){
25565 if(this.baseRotate == 6 || this.baseRotate == 8){
25567 height = this.thumbEl.getHeight();
25568 this.baseScale = height / this.imageEl.OriginWidth;
25570 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25571 width = this.thumbEl.getWidth();
25572 this.baseScale = width / this.imageEl.OriginHeight;
25578 height = this.thumbEl.getHeight();
25579 this.baseScale = height / this.imageEl.OriginHeight;
25581 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25582 width = this.thumbEl.getWidth();
25583 this.baseScale = width / this.imageEl.OriginWidth;
25589 if(this.baseRotate == 6 || this.baseRotate == 8){
25591 width = this.thumbEl.getHeight();
25592 this.baseScale = width / this.imageEl.OriginHeight;
25594 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25595 height = this.thumbEl.getWidth();
25596 this.baseScale = height / this.imageEl.OriginHeight;
25599 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25600 height = this.thumbEl.getWidth();
25601 this.baseScale = height / this.imageEl.OriginHeight;
25603 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25604 width = this.thumbEl.getHeight();
25605 this.baseScale = width / this.imageEl.OriginWidth;
25612 width = this.thumbEl.getWidth();
25613 this.baseScale = width / this.imageEl.OriginWidth;
25615 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25616 height = this.thumbEl.getHeight();
25617 this.baseScale = height / this.imageEl.OriginHeight;
25620 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25622 height = this.thumbEl.getHeight();
25623 this.baseScale = height / this.imageEl.OriginHeight;
25625 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25626 width = this.thumbEl.getWidth();
25627 this.baseScale = width / this.imageEl.OriginWidth;
25635 getScaleLevel : function()
25637 return this.baseScale * Math.pow(1.1, this.scale);
25640 onTouchStart : function(e)
25642 if(!this.canvasLoaded){
25643 this.beforeSelectFile(e);
25647 var touches = e.browserEvent.touches;
25653 if(touches.length == 1){
25654 this.onMouseDown(e);
25658 if(touches.length != 2){
25664 for(var i = 0, finger; finger = touches[i]; i++){
25665 coords.push(finger.pageX, finger.pageY);
25668 var x = Math.pow(coords[0] - coords[2], 2);
25669 var y = Math.pow(coords[1] - coords[3], 2);
25671 this.startDistance = Math.sqrt(x + y);
25673 this.startScale = this.scale;
25675 this.pinching = true;
25676 this.dragable = false;
25680 onTouchMove : function(e)
25682 if(!this.pinching && !this.dragable){
25686 var touches = e.browserEvent.touches;
25693 this.onMouseMove(e);
25699 for(var i = 0, finger; finger = touches[i]; i++){
25700 coords.push(finger.pageX, finger.pageY);
25703 var x = Math.pow(coords[0] - coords[2], 2);
25704 var y = Math.pow(coords[1] - coords[3], 2);
25706 this.endDistance = Math.sqrt(x + y);
25708 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25710 if(!this.zoomable()){
25711 this.scale = this.startScale;
25719 onTouchEnd : function(e)
25721 this.pinching = false;
25722 this.dragable = false;
25726 process : function(file, crop)
25729 this.maskEl.mask(this.loadingText);
25732 this.xhr = new XMLHttpRequest();
25734 file.xhr = this.xhr;
25736 this.xhr.open(this.method, this.url, true);
25739 "Accept": "application/json",
25740 "Cache-Control": "no-cache",
25741 "X-Requested-With": "XMLHttpRequest"
25744 for (var headerName in headers) {
25745 var headerValue = headers[headerName];
25747 this.xhr.setRequestHeader(headerName, headerValue);
25753 this.xhr.onload = function()
25755 _this.xhrOnLoad(_this.xhr);
25758 this.xhr.onerror = function()
25760 _this.xhrOnError(_this.xhr);
25763 var formData = new FormData();
25765 formData.append('returnHTML', 'NO');
25768 formData.append('crop', crop);
25771 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25772 formData.append(this.paramName, file, file.name);
25775 if(typeof(file.filename) != 'undefined'){
25776 formData.append('filename', file.filename);
25779 if(typeof(file.mimetype) != 'undefined'){
25780 formData.append('mimetype', file.mimetype);
25783 if(this.fireEvent('arrange', this, formData) != false){
25784 this.xhr.send(formData);
25788 xhrOnLoad : function(xhr)
25791 this.maskEl.unmask();
25794 if (xhr.readyState !== 4) {
25795 this.fireEvent('exception', this, xhr);
25799 var response = Roo.decode(xhr.responseText);
25801 if(!response.success){
25802 this.fireEvent('exception', this, xhr);
25806 var response = Roo.decode(xhr.responseText);
25808 this.fireEvent('upload', this, response);
25812 xhrOnError : function()
25815 this.maskEl.unmask();
25818 Roo.log('xhr on error');
25820 var response = Roo.decode(xhr.responseText);
25826 prepare : function(file)
25829 this.maskEl.mask(this.loadingText);
25835 if(typeof(file) === 'string'){
25836 this.loadCanvas(file);
25840 if(!file || !this.urlAPI){
25845 this.cropType = file.type;
25849 if(this.fireEvent('prepare', this, this.file) != false){
25851 var reader = new FileReader();
25853 reader.onload = function (e) {
25854 if (e.target.error) {
25855 Roo.log(e.target.error);
25859 var buffer = e.target.result,
25860 dataView = new DataView(buffer),
25862 maxOffset = dataView.byteLength - 4,
25866 if (dataView.getUint16(0) === 0xffd8) {
25867 while (offset < maxOffset) {
25868 markerBytes = dataView.getUint16(offset);
25870 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25871 markerLength = dataView.getUint16(offset + 2) + 2;
25872 if (offset + markerLength > dataView.byteLength) {
25873 Roo.log('Invalid meta data: Invalid segment size.');
25877 if(markerBytes == 0xffe1){
25878 _this.parseExifData(
25885 offset += markerLength;
25895 var url = _this.urlAPI.createObjectURL(_this.file);
25897 _this.loadCanvas(url);
25902 reader.readAsArrayBuffer(this.file);
25908 parseExifData : function(dataView, offset, length)
25910 var tiffOffset = offset + 10,
25914 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25915 // No Exif data, might be XMP data instead
25919 // Check for the ASCII code for "Exif" (0x45786966):
25920 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25921 // No Exif data, might be XMP data instead
25924 if (tiffOffset + 8 > dataView.byteLength) {
25925 Roo.log('Invalid Exif data: Invalid segment size.');
25928 // Check for the two null bytes:
25929 if (dataView.getUint16(offset + 8) !== 0x0000) {
25930 Roo.log('Invalid Exif data: Missing byte alignment offset.');
25933 // Check the byte alignment:
25934 switch (dataView.getUint16(tiffOffset)) {
25936 littleEndian = true;
25939 littleEndian = false;
25942 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25945 // Check for the TIFF tag marker (0x002A):
25946 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25947 Roo.log('Invalid Exif data: Missing TIFF marker.');
25950 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25951 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25953 this.parseExifTags(
25956 tiffOffset + dirOffset,
25961 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25966 if (dirOffset + 6 > dataView.byteLength) {
25967 Roo.log('Invalid Exif data: Invalid directory offset.');
25970 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25971 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25972 if (dirEndOffset + 4 > dataView.byteLength) {
25973 Roo.log('Invalid Exif data: Invalid directory size.');
25976 for (i = 0; i < tagsNumber; i += 1) {
25980 dirOffset + 2 + 12 * i, // tag offset
25984 // Return the offset to the next directory:
25985 return dataView.getUint32(dirEndOffset, littleEndian);
25988 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
25990 var tag = dataView.getUint16(offset, littleEndian);
25992 this.exif[tag] = this.getExifValue(
25996 dataView.getUint16(offset + 2, littleEndian), // tag type
25997 dataView.getUint32(offset + 4, littleEndian), // tag length
26002 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26004 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26013 Roo.log('Invalid Exif data: Invalid tag type.');
26017 tagSize = tagType.size * length;
26018 // Determine if the value is contained in the dataOffset bytes,
26019 // or if the value at the dataOffset is a pointer to the actual data:
26020 dataOffset = tagSize > 4 ?
26021 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26022 if (dataOffset + tagSize > dataView.byteLength) {
26023 Roo.log('Invalid Exif data: Invalid data offset.');
26026 if (length === 1) {
26027 return tagType.getValue(dataView, dataOffset, littleEndian);
26030 for (i = 0; i < length; i += 1) {
26031 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26034 if (tagType.ascii) {
26036 // Concatenate the chars:
26037 for (i = 0; i < values.length; i += 1) {
26039 // Ignore the terminating NULL byte(s):
26040 if (c === '\u0000') {
26052 Roo.apply(Roo.bootstrap.UploadCropbox, {
26054 'Orientation': 0x0112
26058 1: 0, //'top-left',
26060 3: 180, //'bottom-right',
26061 // 4: 'bottom-left',
26063 6: 90, //'right-top',
26064 // 7: 'right-bottom',
26065 8: 270 //'left-bottom'
26069 // byte, 8-bit unsigned int:
26071 getValue: function (dataView, dataOffset) {
26072 return dataView.getUint8(dataOffset);
26076 // ascii, 8-bit byte:
26078 getValue: function (dataView, dataOffset) {
26079 return String.fromCharCode(dataView.getUint8(dataOffset));
26084 // short, 16 bit int:
26086 getValue: function (dataView, dataOffset, littleEndian) {
26087 return dataView.getUint16(dataOffset, littleEndian);
26091 // long, 32 bit int:
26093 getValue: function (dataView, dataOffset, littleEndian) {
26094 return dataView.getUint32(dataOffset, littleEndian);
26098 // rational = two long values, first is numerator, second is denominator:
26100 getValue: function (dataView, dataOffset, littleEndian) {
26101 return dataView.getUint32(dataOffset, littleEndian) /
26102 dataView.getUint32(dataOffset + 4, littleEndian);
26106 // slong, 32 bit signed int:
26108 getValue: function (dataView, dataOffset, littleEndian) {
26109 return dataView.getInt32(dataOffset, littleEndian);
26113 // srational, two slongs, first is numerator, second is denominator:
26115 getValue: function (dataView, dataOffset, littleEndian) {
26116 return dataView.getInt32(dataOffset, littleEndian) /
26117 dataView.getInt32(dataOffset + 4, littleEndian);
26127 cls : 'btn-group roo-upload-cropbox-rotate-left',
26128 action : 'rotate-left',
26132 cls : 'btn btn-default',
26133 html : '<i class="fa fa-undo"></i>'
26139 cls : 'btn-group roo-upload-cropbox-picture',
26140 action : 'picture',
26144 cls : 'btn btn-default',
26145 html : '<i class="fa fa-picture-o"></i>'
26151 cls : 'btn-group roo-upload-cropbox-rotate-right',
26152 action : 'rotate-right',
26156 cls : 'btn btn-default',
26157 html : '<i class="fa fa-repeat"></i>'
26165 cls : 'btn-group roo-upload-cropbox-rotate-left',
26166 action : 'rotate-left',
26170 cls : 'btn btn-default',
26171 html : '<i class="fa fa-undo"></i>'
26177 cls : 'btn-group roo-upload-cropbox-download',
26178 action : 'download',
26182 cls : 'btn btn-default',
26183 html : '<i class="fa fa-download"></i>'
26189 cls : 'btn-group roo-upload-cropbox-crop',
26194 cls : 'btn btn-default',
26195 html : '<i class="fa fa-crop"></i>'
26201 cls : 'btn-group roo-upload-cropbox-trash',
26206 cls : 'btn btn-default',
26207 html : '<i class="fa fa-trash"></i>'
26213 cls : 'btn-group roo-upload-cropbox-rotate-right',
26214 action : 'rotate-right',
26218 cls : 'btn btn-default',
26219 html : '<i class="fa fa-repeat"></i>'
26227 cls : 'btn-group roo-upload-cropbox-rotate-left',
26228 action : 'rotate-left',
26232 cls : 'btn btn-default',
26233 html : '<i class="fa fa-undo"></i>'
26239 cls : 'btn-group roo-upload-cropbox-rotate-right',
26240 action : 'rotate-right',
26244 cls : 'btn btn-default',
26245 html : '<i class="fa fa-repeat"></i>'
26258 * @class Roo.bootstrap.DocumentManager
26259 * @extends Roo.bootstrap.Component
26260 * Bootstrap DocumentManager class
26261 * @cfg {String} paramName default 'imageUpload'
26262 * @cfg {String} method default POST
26263 * @cfg {String} url action url
26264 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26265 * @cfg {Boolean} multiple multiple upload default true
26266 * @cfg {Number} thumbSize default 300
26267 * @cfg {String} fieldLabel
26268 * @cfg {Number} labelWidth default 4
26269 * @cfg {String} labelAlign (left|top) default left
26270 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26273 * Create a new DocumentManager
26274 * @param {Object} config The config object
26277 Roo.bootstrap.DocumentManager = function(config){
26278 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26283 * Fire when initial the DocumentManager
26284 * @param {Roo.bootstrap.DocumentManager} this
26289 * inspect selected file
26290 * @param {Roo.bootstrap.DocumentManager} this
26291 * @param {File} file
26296 * Fire when xhr load exception
26297 * @param {Roo.bootstrap.DocumentManager} this
26298 * @param {XMLHttpRequest} xhr
26300 "exception" : true,
26303 * prepare the form data
26304 * @param {Roo.bootstrap.DocumentManager} this
26305 * @param {Object} formData
26310 * Fire when remove the file
26311 * @param {Roo.bootstrap.DocumentManager} this
26312 * @param {Object} file
26317 * Fire after refresh the file
26318 * @param {Roo.bootstrap.DocumentManager} this
26323 * Fire after click the image
26324 * @param {Roo.bootstrap.DocumentManager} this
26325 * @param {Object} file
26330 * Fire when upload a image and editable set to true
26331 * @param {Roo.bootstrap.DocumentManager} this
26332 * @param {Object} file
26336 * @event beforeselectfile
26337 * Fire before select file
26338 * @param {Roo.bootstrap.DocumentManager} this
26340 "beforeselectfile" : true,
26343 * Fire before process file
26344 * @param {Roo.bootstrap.DocumentManager} this
26345 * @param {Object} file
26352 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
26361 paramName : 'imageUpload',
26364 labelAlign : 'left',
26371 getAutoCreate : function()
26373 var managerWidget = {
26375 cls : 'roo-document-manager',
26379 cls : 'roo-document-manager-selector',
26384 cls : 'roo-document-manager-uploader',
26388 cls : 'roo-document-manager-upload-btn',
26389 html : '<i class="fa fa-plus"></i>'
26400 cls : 'column col-md-12',
26405 if(this.fieldLabel.length){
26410 cls : 'column col-md-12',
26411 html : this.fieldLabel
26415 cls : 'column col-md-12',
26420 if(this.labelAlign == 'left'){
26424 cls : 'column col-md-' + this.labelWidth,
26425 html : this.fieldLabel
26429 cls : 'column col-md-' + (12 - this.labelWidth),
26439 cls : 'row clearfix',
26447 initEvents : function()
26449 this.managerEl = this.el.select('.roo-document-manager', true).first();
26450 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26452 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26453 this.selectorEl.hide();
26456 this.selectorEl.attr('multiple', 'multiple');
26459 this.selectorEl.on('change', this.onFileSelected, this);
26461 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26462 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26464 this.uploader.on('click', this.onUploaderClick, this);
26466 this.renderProgressDialog();
26470 window.addEventListener("resize", function() { _this.refresh(); } );
26472 this.fireEvent('initial', this);
26475 renderProgressDialog : function()
26479 this.progressDialog = new Roo.bootstrap.Modal({
26480 cls : 'roo-document-manager-progress-dialog',
26481 allow_close : false,
26491 btnclick : function() {
26492 _this.uploadCancel();
26498 this.progressDialog.render(Roo.get(document.body));
26500 this.progress = new Roo.bootstrap.Progress({
26501 cls : 'roo-document-manager-progress',
26506 this.progress.render(this.progressDialog.getChildContainer());
26508 this.progressBar = new Roo.bootstrap.ProgressBar({
26509 cls : 'roo-document-manager-progress-bar',
26512 aria_valuemax : 12,
26516 this.progressBar.render(this.progress.getChildContainer());
26519 onUploaderClick : function(e)
26521 e.preventDefault();
26523 if(this.fireEvent('beforeselectfile', this) != false){
26524 this.selectorEl.dom.click();
26529 onFileSelected : function(e)
26531 e.preventDefault();
26533 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26537 Roo.each(this.selectorEl.dom.files, function(file){
26538 if(this.fireEvent('inspect', this, file) != false){
26539 this.files.push(file);
26549 this.selectorEl.dom.value = '';
26551 if(!this.files.length){
26555 if(this.boxes > 0 && this.files.length > this.boxes){
26556 this.files = this.files.slice(0, this.boxes);
26559 this.uploader.show();
26561 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26562 this.uploader.hide();
26571 Roo.each(this.files, function(file){
26573 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26574 var f = this.renderPreview(file);
26579 if(file.type.indexOf('image') != -1){
26580 this.delegates.push(
26582 _this.process(file);
26583 }).createDelegate(this)
26591 _this.process(file);
26592 }).createDelegate(this)
26597 this.files = files;
26599 this.delegates = this.delegates.concat(docs);
26601 if(!this.delegates.length){
26606 this.progressBar.aria_valuemax = this.delegates.length;
26613 arrange : function()
26615 if(!this.delegates.length){
26616 this.progressDialog.hide();
26621 var delegate = this.delegates.shift();
26623 this.progressDialog.show();
26625 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26627 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26632 refresh : function()
26634 this.uploader.show();
26636 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26637 this.uploader.hide();
26640 Roo.isTouch ? this.closable(false) : this.closable(true);
26642 this.fireEvent('refresh', this);
26645 onRemove : function(e, el, o)
26647 e.preventDefault();
26649 this.fireEvent('remove', this, o);
26653 remove : function(o)
26657 Roo.each(this.files, function(file){
26658 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26667 this.files = files;
26674 Roo.each(this.files, function(file){
26679 file.target.remove();
26688 onClick : function(e, el, o)
26690 e.preventDefault();
26692 this.fireEvent('click', this, o);
26696 closable : function(closable)
26698 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26700 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26712 xhrOnLoad : function(xhr)
26714 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26718 if (xhr.readyState !== 4) {
26720 this.fireEvent('exception', this, xhr);
26724 var response = Roo.decode(xhr.responseText);
26726 if(!response.success){
26728 this.fireEvent('exception', this, xhr);
26732 var file = this.renderPreview(response.data);
26734 this.files.push(file);
26740 xhrOnError : function(xhr)
26742 Roo.log('xhr on error');
26744 var response = Roo.decode(xhr.responseText);
26751 process : function(file)
26753 if(this.fireEvent('process', this, file) !== false){
26754 if(this.editable && file.type.indexOf('image') != -1){
26755 this.fireEvent('edit', this, file);
26759 this.uploadStart(file, false);
26766 uploadStart : function(file, crop)
26768 this.xhr = new XMLHttpRequest();
26770 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26775 file.xhr = this.xhr;
26777 this.managerEl.createChild({
26779 cls : 'roo-document-manager-loading',
26783 tooltip : file.name,
26784 cls : 'roo-document-manager-thumb',
26785 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26791 this.xhr.open(this.method, this.url, true);
26794 "Accept": "application/json",
26795 "Cache-Control": "no-cache",
26796 "X-Requested-With": "XMLHttpRequest"
26799 for (var headerName in headers) {
26800 var headerValue = headers[headerName];
26802 this.xhr.setRequestHeader(headerName, headerValue);
26808 this.xhr.onload = function()
26810 _this.xhrOnLoad(_this.xhr);
26813 this.xhr.onerror = function()
26815 _this.xhrOnError(_this.xhr);
26818 var formData = new FormData();
26820 formData.append('returnHTML', 'NO');
26823 formData.append('crop', crop);
26826 formData.append(this.paramName, file, file.name);
26828 if(this.fireEvent('prepare', this, formData) != false){
26829 this.xhr.send(formData);
26833 uploadCancel : function()
26840 this.delegates = [];
26842 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26849 renderPreview : function(file)
26851 if(typeof(file.target) != 'undefined' && file.target){
26855 var previewEl = this.managerEl.createChild({
26857 cls : 'roo-document-manager-preview',
26861 tooltip : file.filename,
26862 cls : 'roo-document-manager-thumb',
26863 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26868 html : '<i class="fa fa-times-circle"></i>'
26873 var close = previewEl.select('button.close', true).first();
26875 close.on('click', this.onRemove, this, file);
26877 file.target = previewEl;
26879 var image = previewEl.select('img', true).first();
26883 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26885 image.on('click', this.onClick, this, file);
26891 onPreviewLoad : function(file, image)
26893 if(typeof(file.target) == 'undefined' || !file.target){
26897 var width = image.dom.naturalWidth || image.dom.width;
26898 var height = image.dom.naturalHeight || image.dom.height;
26900 if(width > height){
26901 file.target.addClass('wide');
26905 file.target.addClass('tall');
26910 uploadFromSource : function(file, crop)
26912 this.xhr = new XMLHttpRequest();
26914 this.managerEl.createChild({
26916 cls : 'roo-document-manager-loading',
26920 tooltip : file.name,
26921 cls : 'roo-document-manager-thumb',
26922 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26928 this.xhr.open(this.method, this.url, true);
26931 "Accept": "application/json",
26932 "Cache-Control": "no-cache",
26933 "X-Requested-With": "XMLHttpRequest"
26936 for (var headerName in headers) {
26937 var headerValue = headers[headerName];
26939 this.xhr.setRequestHeader(headerName, headerValue);
26945 this.xhr.onload = function()
26947 _this.xhrOnLoad(_this.xhr);
26950 this.xhr.onerror = function()
26952 _this.xhrOnError(_this.xhr);
26955 var formData = new FormData();
26957 formData.append('returnHTML', 'NO');
26959 formData.append('crop', crop);
26961 if(typeof(file.filename) != 'undefined'){
26962 formData.append('filename', file.filename);
26965 if(typeof(file.mimetype) != 'undefined'){
26966 formData.append('mimetype', file.mimetype);
26969 if(this.fireEvent('prepare', this, formData) != false){
26970 this.xhr.send(formData);
26980 * @class Roo.bootstrap.DocumentViewer
26981 * @extends Roo.bootstrap.Component
26982 * Bootstrap DocumentViewer class
26985 * Create a new DocumentViewer
26986 * @param {Object} config The config object
26989 Roo.bootstrap.DocumentViewer = function(config){
26990 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26995 * Fire after initEvent
26996 * @param {Roo.bootstrap.DocumentViewer} this
27002 * @param {Roo.bootstrap.DocumentViewer} this
27007 * Fire after trash button
27008 * @param {Roo.bootstrap.DocumentViewer} this
27015 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
27017 getAutoCreate : function()
27021 cls : 'roo-document-viewer',
27025 cls : 'roo-document-viewer-body',
27029 cls : 'roo-document-viewer-thumb',
27033 cls : 'roo-document-viewer-image'
27041 cls : 'roo-document-viewer-footer',
27044 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27052 cls : 'btn btn-default roo-document-viewer-trash',
27053 html : '<i class="fa fa-trash"></i>'
27066 initEvents : function()
27069 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27070 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27072 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27073 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27075 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27076 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27078 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27079 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27081 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27082 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27084 this.bodyEl.on('click', this.onClick, this);
27086 this.trashBtn.on('click', this.onTrash, this);
27090 initial : function()
27092 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27095 this.fireEvent('initial', this);
27099 onClick : function(e)
27101 e.preventDefault();
27103 this.fireEvent('click', this);
27106 onTrash : function(e)
27108 e.preventDefault();
27110 this.fireEvent('trash', this);
27122 * @class Roo.bootstrap.NavProgressBar
27123 * @extends Roo.bootstrap.Component
27124 * Bootstrap NavProgressBar class
27127 * Create a new nav progress bar
27128 * @param {Object} config The config object
27131 Roo.bootstrap.NavProgressBar = function(config){
27132 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27134 this.bullets = this.bullets || [];
27136 // Roo.bootstrap.NavProgressBar.register(this);
27140 * Fires when the active item changes
27141 * @param {Roo.bootstrap.NavProgressBar} this
27142 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27143 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
27150 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
27155 getAutoCreate : function()
27157 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27161 cls : 'roo-navigation-bar-group',
27165 cls : 'roo-navigation-top-bar'
27169 cls : 'roo-navigation-bullets-bar',
27173 cls : 'roo-navigation-bar'
27180 cls : 'roo-navigation-bottom-bar'
27190 initEvents: function()
27195 onRender : function(ct, position)
27197 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27199 if(this.bullets.length){
27200 Roo.each(this.bullets, function(b){
27209 addItem : function(cfg)
27211 var item = new Roo.bootstrap.NavProgressItem(cfg);
27213 item.parentId = this.id;
27214 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27217 var top = new Roo.bootstrap.Element({
27219 cls : 'roo-navigation-bar-text'
27222 var bottom = new Roo.bootstrap.Element({
27224 cls : 'roo-navigation-bar-text'
27227 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27228 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27230 var topText = new Roo.bootstrap.Element({
27232 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27235 var bottomText = new Roo.bootstrap.Element({
27237 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27240 topText.onRender(top.el, null);
27241 bottomText.onRender(bottom.el, null);
27244 item.bottomEl = bottom;
27247 this.barItems.push(item);
27252 getActive : function()
27254 var active = false;
27256 Roo.each(this.barItems, function(v){
27258 if (!v.isActive()) {
27270 setActiveItem : function(item)
27274 Roo.each(this.barItems, function(v){
27275 if (v.rid == item.rid) {
27279 if (v.isActive()) {
27280 v.setActive(false);
27285 item.setActive(true);
27287 this.fireEvent('changed', this, item, prev);
27290 getBarItem: function(rid)
27294 Roo.each(this.barItems, function(e) {
27295 if (e.rid != rid) {
27306 indexOfItem : function(item)
27310 Roo.each(this.barItems, function(v, i){
27312 if (v.rid != item.rid) {
27323 setActiveNext : function()
27325 var i = this.indexOfItem(this.getActive());
27327 if (i > this.barItems.length) {
27331 this.setActiveItem(this.barItems[i+1]);
27334 setActivePrev : function()
27336 var i = this.indexOfItem(this.getActive());
27342 this.setActiveItem(this.barItems[i-1]);
27345 format : function()
27347 if(!this.barItems.length){
27351 var width = 100 / this.barItems.length;
27353 Roo.each(this.barItems, function(i){
27354 i.el.setStyle('width', width + '%');
27355 i.topEl.el.setStyle('width', width + '%');
27356 i.bottomEl.el.setStyle('width', width + '%');
27365 * Nav Progress Item
27370 * @class Roo.bootstrap.NavProgressItem
27371 * @extends Roo.bootstrap.Component
27372 * Bootstrap NavProgressItem class
27373 * @cfg {String} rid the reference id
27374 * @cfg {Boolean} active (true|false) Is item active default false
27375 * @cfg {Boolean} disabled (true|false) Is item active default false
27376 * @cfg {String} html
27377 * @cfg {String} position (top|bottom) text position default bottom
27378 * @cfg {String} icon show icon instead of number
27381 * Create a new NavProgressItem
27382 * @param {Object} config The config object
27384 Roo.bootstrap.NavProgressItem = function(config){
27385 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27390 * The raw click event for the entire grid.
27391 * @param {Roo.bootstrap.NavProgressItem} this
27392 * @param {Roo.EventObject} e
27399 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
27405 position : 'bottom',
27408 getAutoCreate : function()
27410 var iconCls = 'roo-navigation-bar-item-icon';
27412 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27416 cls: 'roo-navigation-bar-item',
27426 cfg.cls += ' active';
27429 cfg.cls += ' disabled';
27435 disable : function()
27437 this.setDisabled(true);
27440 enable : function()
27442 this.setDisabled(false);
27445 initEvents: function()
27447 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27449 this.iconEl.on('click', this.onClick, this);
27452 onClick : function(e)
27454 e.preventDefault();
27460 if(this.fireEvent('click', this, e) === false){
27464 this.parent().setActiveItem(this);
27467 isActive: function ()
27469 return this.active;
27472 setActive : function(state)
27474 if(this.active == state){
27478 this.active = state;
27481 this.el.addClass('active');
27485 this.el.removeClass('active');
27490 setDisabled : function(state)
27492 if(this.disabled == state){
27496 this.disabled = state;
27499 this.el.addClass('disabled');
27503 this.el.removeClass('disabled');
27506 tooltipEl : function()
27508 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27521 * @class Roo.bootstrap.FieldLabel
27522 * @extends Roo.bootstrap.Component
27523 * Bootstrap FieldLabel class
27524 * @cfg {String} html contents of the element
27525 * @cfg {String} tag tag of the element default label
27526 * @cfg {String} cls class of the element
27527 * @cfg {String} target label target
27528 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27529 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27530 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27531 * @cfg {String} iconTooltip default "This field is required"
27534 * Create a new FieldLabel
27535 * @param {Object} config The config object
27538 Roo.bootstrap.FieldLabel = function(config){
27539 Roo.bootstrap.Element.superclass.constructor.call(this, config);
27544 * Fires after the field has been marked as invalid.
27545 * @param {Roo.form.FieldLabel} this
27546 * @param {String} msg The validation message
27551 * Fires after the field has been validated with no errors.
27552 * @param {Roo.form.FieldLabel} this
27558 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
27565 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27566 validClass : 'text-success fa fa-lg fa-check',
27567 iconTooltip : 'This field is required',
27569 getAutoCreate : function(){
27573 cls : 'roo-bootstrap-field-label ' + this.cls,
27579 tooltip : this.iconTooltip
27591 initEvents: function()
27593 Roo.bootstrap.Element.superclass.initEvents.call(this);
27595 this.iconEl = this.el.select('i', true).first();
27597 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27599 Roo.bootstrap.FieldLabel.register(this);
27603 * Mark this field as valid
27605 markValid : function()
27607 this.iconEl.show();
27609 this.iconEl.removeClass(this.invalidClass);
27611 this.iconEl.addClass(this.validClass);
27613 this.fireEvent('valid', this);
27617 * Mark this field as invalid
27618 * @param {String} msg The validation message
27620 markInvalid : function(msg)
27622 this.iconEl.show();
27624 this.iconEl.removeClass(this.validClass);
27626 this.iconEl.addClass(this.invalidClass);
27628 this.fireEvent('invalid', this, msg);
27634 Roo.apply(Roo.bootstrap.FieldLabel, {
27639 * register a FieldLabel Group
27640 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27642 register : function(label)
27644 if(this.groups.hasOwnProperty(label.target)){
27648 this.groups[label.target] = label;
27652 * fetch a FieldLabel Group based on the target
27653 * @param {string} target
27654 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27656 get: function(target) {
27657 if (typeof(this.groups[target]) == 'undefined') {
27661 return this.groups[target] ;
27670 * page DateSplitField.
27676 * @class Roo.bootstrap.DateSplitField
27677 * @extends Roo.bootstrap.Component
27678 * Bootstrap DateSplitField class
27679 * @cfg {string} fieldLabel - the label associated
27680 * @cfg {Number} labelWidth set the width of label (0-12)
27681 * @cfg {String} labelAlign (top|left)
27682 * @cfg {Boolean} dayAllowBlank (true|false) default false
27683 * @cfg {Boolean} monthAllowBlank (true|false) default false
27684 * @cfg {Boolean} yearAllowBlank (true|false) default false
27685 * @cfg {string} dayPlaceholder
27686 * @cfg {string} monthPlaceholder
27687 * @cfg {string} yearPlaceholder
27688 * @cfg {string} dayFormat default 'd'
27689 * @cfg {string} monthFormat default 'm'
27690 * @cfg {string} yearFormat default 'Y'
27694 * Create a new DateSplitField
27695 * @param {Object} config The config object
27698 Roo.bootstrap.DateSplitField = function(config){
27699 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27705 * getting the data of years
27706 * @param {Roo.bootstrap.DateSplitField} this
27707 * @param {Object} years
27712 * getting the data of days
27713 * @param {Roo.bootstrap.DateSplitField} this
27714 * @param {Object} days
27719 * Fires after the field has been marked as invalid.
27720 * @param {Roo.form.Field} this
27721 * @param {String} msg The validation message
27726 * Fires after the field has been validated with no errors.
27727 * @param {Roo.form.Field} this
27733 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
27736 labelAlign : 'top',
27738 dayAllowBlank : false,
27739 monthAllowBlank : false,
27740 yearAllowBlank : false,
27741 dayPlaceholder : '',
27742 monthPlaceholder : '',
27743 yearPlaceholder : '',
27747 isFormField : true,
27749 getAutoCreate : function()
27753 cls : 'row roo-date-split-field-group',
27758 cls : 'form-hidden-field roo-date-split-field-group-value',
27764 if(this.fieldLabel){
27767 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27771 html : this.fieldLabel
27777 Roo.each(['day', 'month', 'year'], function(t){
27780 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27787 inputEl: function ()
27789 return this.el.select('.roo-date-split-field-group-value', true).first();
27792 onRender : function(ct, position)
27796 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27798 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27800 this.dayField = new Roo.bootstrap.ComboBox({
27801 allowBlank : this.dayAllowBlank,
27802 alwaysQuery : true,
27803 displayField : 'value',
27806 forceSelection : true,
27808 placeholder : this.dayPlaceholder,
27809 selectOnFocus : true,
27810 tpl : '<div class="select2-result"><b>{value}</b></div>',
27811 triggerAction : 'all',
27813 valueField : 'value',
27814 store : new Roo.data.SimpleStore({
27815 data : (function() {
27817 _this.fireEvent('days', _this, days);
27820 fields : [ 'value' ]
27823 select : function (_self, record, index)
27825 _this.setValue(_this.getValue());
27830 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27832 this.monthField = new Roo.bootstrap.MonthField({
27833 after : '<i class=\"fa fa-calendar\"></i>',
27834 allowBlank : this.monthAllowBlank,
27835 placeholder : this.monthPlaceholder,
27838 render : function (_self)
27840 this.el.select('span.input-group-addon', true).first().on('click', function(e){
27841 e.preventDefault();
27845 select : function (_self, oldvalue, newvalue)
27847 _this.setValue(_this.getValue());
27852 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27854 this.yearField = new Roo.bootstrap.ComboBox({
27855 allowBlank : this.yearAllowBlank,
27856 alwaysQuery : true,
27857 displayField : 'value',
27860 forceSelection : true,
27862 placeholder : this.yearPlaceholder,
27863 selectOnFocus : true,
27864 tpl : '<div class="select2-result"><b>{value}</b></div>',
27865 triggerAction : 'all',
27867 valueField : 'value',
27868 store : new Roo.data.SimpleStore({
27869 data : (function() {
27871 _this.fireEvent('years', _this, years);
27874 fields : [ 'value' ]
27877 select : function (_self, record, index)
27879 _this.setValue(_this.getValue());
27884 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
27887 setValue : function(v, format)
27889 this.inputEl.dom.value = v;
27891 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
27893 var d = Date.parseDate(v, f);
27900 this.setDay(d.format(this.dayFormat));
27901 this.setMonth(d.format(this.monthFormat));
27902 this.setYear(d.format(this.yearFormat));
27909 setDay : function(v)
27911 this.dayField.setValue(v);
27912 this.inputEl.dom.value = this.getValue();
27917 setMonth : function(v)
27919 this.monthField.setValue(v, true);
27920 this.inputEl.dom.value = this.getValue();
27925 setYear : function(v)
27927 this.yearField.setValue(v);
27928 this.inputEl.dom.value = this.getValue();
27933 getDay : function()
27935 return this.dayField.getValue();
27938 getMonth : function()
27940 return this.monthField.getValue();
27943 getYear : function()
27945 return this.yearField.getValue();
27948 getValue : function()
27950 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
27952 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
27962 this.inputEl.dom.value = '';
27967 validate : function()
27969 var d = this.dayField.validate();
27970 var m = this.monthField.validate();
27971 var y = this.yearField.validate();
27976 (!this.dayAllowBlank && !d) ||
27977 (!this.monthAllowBlank && !m) ||
27978 (!this.yearAllowBlank && !y)
27983 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
27992 this.markInvalid();
27997 markValid : function()
28000 var label = this.el.select('label', true).first();
28001 var icon = this.el.select('i.fa-star', true).first();
28007 this.fireEvent('valid', this);
28011 * Mark this field as invalid
28012 * @param {String} msg The validation message
28014 markInvalid : function(msg)
28017 var label = this.el.select('label', true).first();
28018 var icon = this.el.select('i.fa-star', true).first();
28020 if(label && !icon){
28021 this.el.select('.roo-date-split-field-label', true).createChild({
28023 cls : 'text-danger fa fa-lg fa-star',
28024 tooltip : 'This field is required',
28025 style : 'margin-right:5px;'
28029 this.fireEvent('invalid', this, msg);
28032 clearInvalid : function()
28034 var label = this.el.select('label', true).first();
28035 var icon = this.el.select('i.fa-star', true).first();
28041 this.fireEvent('valid', this);
28044 getName: function()